diff options
72 files changed, 1015 insertions, 432 deletions
diff --git a/Documentation/RelNotes/1.8.1.2.txt b/Documentation/RelNotes/1.8.1.2.txt index 76ad0b3d01..5ab7b18906 100644 --- a/Documentation/RelNotes/1.8.1.2.txt +++ b/Documentation/RelNotes/1.8.1.2.txt @@ -4,6 +4,16 @@ Git 1.8.1.2 Release Notes Fixes since v1.8.1.1 -------------------- + * An element on GIT_CEILING_DIRECTORIES list that does not name the + real path to a directory (i.e. a symbolic link) could have caused + the GIT_DIR discovery logic to escape the ceiling. + + * Command line completion for "tcsh" emitted an unwanted space + after completing a single directory name. + + * Command line completion leaked an unnecessary error message while + looking for possible matches with paths in <tree-ish>. + * "git archive" did not record uncompressed size in the header when streaming a zip archive, which confused some implementations of unzip. @@ -11,3 +21,5 @@ Fixes since v1.8.1.1 trailer part, "git send-email" failed to pick up the addresses from there. As e-mail headers field names are case insensitive, this script should follow suit and treat "cc:" and "Cc:" the same way. + +Also contains various documentation fixes. diff --git a/Documentation/RelNotes/1.8.2.txt b/Documentation/RelNotes/1.8.2.txt index 7cbe8a6b94..aa3c1ea3ea 100644 --- a/Documentation/RelNotes/1.8.2.txt +++ b/Documentation/RelNotes/1.8.2.txt @@ -50,6 +50,14 @@ UI, Workflows & Features E.g. "foo/**/bar" matches "bar" in "foo" itself or in a subdirectory of "foo". + * When giving arguments without "--" disambiguation, object names + that come earlier on the command line must not be interpretable as + pathspecs and pathspecs that come later on the command line must + not be interpretable as object names. This disambiguation rule has + been tweaked so that ":/" (no other string before or after) is + always interpreted as a pathspec; "git cmd -- :/" is no longer + needed, you can just say "git cmd :/". + * "git blame" (and "git diff") learned the "--no-follow" option. * "git check-ignore" command to help debugging .gitignore files has @@ -112,7 +120,8 @@ Foreign Interface * A new remote helper to interact with bzr has been added to contrib/. - * "git p4" got various bugfixes around its branch handling. + * "git p4" got various bugfixes around its branch handling. It is + also made usable with Python 2.4/2.5. * The remote helper to interact with Hg in contrib/ has seen a few fixes. @@ -126,6 +135,11 @@ Performance, Internal Implementation, etc. * Matching paths with common forms of pathspecs that contain wildcard characters has been optimized further. + * We stopped paying attention to $GIT_CONFIG environment that points + at a single configuration file from any command other than "git config" + quite a while ago, but "git clone" internally set, exported, and + then unexported the variable during its operation unnecessarily. + * "git reset" internals has been reworked and should be faster in general. We tried to be careful not to break any behaviour but there could be corner cases, especially when running the command @@ -153,6 +167,13 @@ Performance, Internal Implementation, etc. USE_WILDMATCH, using the resulting Git daily and reporting when you find breakages, you can help us get closer to that goal. + * Some reimplementations of Git do not write all the stat info back + to the index due to their implementation limitations (e.g. jgit). + A configuration option can tell Git to ignore changes to most of + the stat fields and only pay attention to mtime and size, which + these implementations can reliably update. This can be used to + avoid excessive revalidation of contents. + Also contains minor documentation updates and code clean-ups. @@ -167,13 +188,11 @@ details). * An element on GIT_CEILING_DIRECTORIES list that does not name the real path to a directory (i.e. a symbolic link) could have caused the GIT_DIR discovery logic to escape the ceiling. - (merge 059b379 mh/ceiling later to maint). * When attempting to read the XDG-style $HOME/.config/git/config and finding that $HOME/.config/git is a file, we gave a wrong error message, instead of treating the case as "a custom config file does not exist there" and moving on. - (merge 8f2bbe4 jn/warn-on-inaccessible-loosen later to maint). * The behaviour visible to the end users was confusing, when they attempt to kill a process spawned in the editor that was in turn @@ -184,11 +203,14 @@ details). * A child process that was killed by a signal (e.g. SIGINT) was reported in an inconsistent way depending on how the process was spawned by us, with or without a shell in between. - (merge 709ca73 jk/unify-exit-code-by-receiving-signal later to maint). * After failing to create a temporary file using mkstemp(), failing pathname was not reported correctly on some platforms. - (merge f7be59b jc/mkstemp-more-careful-error-reporting later to maint). + + * We used to stuff "user@" and then append what we read from + /etc/mailname to come up with a default e-mail ident, but a bug + lost the "user@" part. + (merge dc342a2 jn/do-not-drop-username-when-reading-from-etc-mailname later to maint). * The attribute mechanism didn't allow limiting attributes to be applied to only a single directory itself with "path/" like the @@ -197,17 +219,22 @@ details). degradations and needs to merge a fix-up topic. (merge 9db9eec nd/fix-directory-attrs-off-by-one later to maint). + * "git am" did not parse datestamp correctly from Hg generated patch, + when it is run in a locale outside C (or en). + (merge 5185b97 dl/am-hg-locale later to maint). + * "git apply" misbehaved when fixing whitespace breakages by removing excess trailing blank lines. - (merge 5de7166 jc/apply-trailing-blank-removal later to maint). * A tar archive created by "git archive" recorded a directory in a way that made NetBSD's implementation of "tar" sometimes unhappy. - (merge 22f0dcd rs/leave-base-name-in-name-field-of-tar later to maint). * "git archive" did not record uncompressed size in the header when streaming a zip archive, which confused some implementations of unzip. - (merge 5ea2c84 rs/zip-with-uncompressed-size-in-the-header later to maint). + + * Attempt to "branch --edit-description" an existing branch, while + being on a detached HEAD, errored out. + (merge 75135b2 nd/edit-branch-desc-while-detached later to maint). * "git clean" showed what it was going to do, but sometimes end up finding that it was not allowed to do so, which resulted in a @@ -220,27 +247,27 @@ details). * When "git clone --separate-git-dir=$over_there" is interrupted, it failed to remove the real location of the $GIT_DIR it created. This was most visible when interrupting a submodule update. - (merge 9be1980 jl/interrupt-clone-remove-separate-git-dir later to maint). + + * "git fetch --depth" was broken in at least three ways. The + resulting history was deeper than specified by one commit, it was + unclear how to wipe the shallowness of the repository with the + command, and documentation was misleading. + (merge cfb70e1 nd/fetch-depth-is-broken later to maint). * The way "git svn" asked for password using SSH_ASKPASS and GIT_ASKPASS was not in line with the rest of the system. - (merge e9263e4 ss/svn-prompt later to maint). * The --graph code fell into infinite loop when asked to do what the code did not expect. - (merge 656197a mk/maint-graph-infinity-loop later to maint). * http transport was wrong to ask for the username when the authentication is done by certificate identity. - (merge 75e9a40 rb/http-cert-cred-no-username-prompt later to maint). * "git pack-refs" that ran in parallel to another process that created new refs had a nasty race. - (merge b3f1280 jk/repack-ref-racefix later to maint). * After "git add -N" and then writing a tree object out of the index, the cache-tree data structure got corrupted. - (merge eec3e7e nd/invalidate-i-t-a-cache-tree later to maint). * "git clone" used to allow --bare and --separate-git-dir=$there options at the same time, which was nonsensical. @@ -253,49 +280,43 @@ details). * "git merge --no-edit" computed who were involved in the work done on the side branch, even though that information is to be discarded without getting seen in the editor. - (merge 9bcbb1c jc/maint-fmt-merge-msg-no-edit-lose-credit later to maint). * "git merge" started calling prepare-commit-msg hook like "git commit" does some time ago, but forgot to pay attention to the exit status of the hook. - (merge 3e4141d ap/merge-stop-at-prepare-commit-msg-failure later to maint). * When users spell "cc:" in lowercase in the fake "header" in the trailer part, "git send-email" failed to pick up the addresses from there. As e-mail headers field names are case insensitive, this script should follow suit and treat "cc:" and "Cc:" the same way. - (merge 6310071 nz/send-email-headers-are-case-insensitive later to maint). * Output from "git status --ignored" showed an unexpected interaction with "--untracked". - (merge a45fb69 ap/status-ignored-in-ignored-directory later to maint). * "gitweb", when sorting by age to show repositories with new activities first, used to sort repositories with absolutely nothing in it early, which was not very useful. - (merge 28dae18 md/gitweb-sort-by-age later to maint). * "gitweb"'s code to sanitize control characters before passing it to "highlight" filter lost known-to-be-safe control characters by mistake. - (merge 0e901d2 os/gitweb-highlight-uncaptured later to maint). * When a line to be wrapped has a solid run of non space characters whose length exactly is the wrap width, "git shortlog -w" failed to add a newline after such a line. - (merge e0db176 sp/shortlog-missing-lf later to maint). * Command line completion leaked an unnecessary error message while looking for possible matches with paths in <tree-ish>. - (merge ca87dd6 ds/completion-silence-in-tree-path-probe later to maint). * Command line completion for "tcsh" emitted an unwanted space after completing a single directory name. - (merge 92f1c04 mk/complete-tcsh later to maint). + + * Command line completion code was inadvertently made incompatible with + older versions of bash by using a newer array notation. + (merge 50c5885 bc/fix-array-syntax-for-3.0-in-completion-bash later to maint). * Some shells do not behave correctly when IFS is unset; work it around by explicitly setting it to the default value. - (merge 393050c jc/maint-fbsd-sh-ifs-workaround later to maint). * Some scripted programs written in Python did not get updated when PYTHON_PATH changed. @@ -303,24 +324,25 @@ details). * When autoconf is used, any build on a different commit always ran "config.status --recheck" even when unnecessary. - (merge 1226504 jn/less-reconfigure later to maint). * We have been carrying a translated and long-unmaintained copy of an old version of the tutorial; removed. - (merge 0a85441 ta/remove-stale-translated-tut later to maint). + + * t0050 had tests expecting failures from a bug that was fixed some + time ago. + (merge 336e2e2 tb/t0050-maint later to maint). * t4014, t9502 and t0200 tests had various portability issues that broke on OpenBSD. - (merge 27f6342 jc/maint-test-portability later to maint). * t9020 and t3600 tests had various portability issues. - (merge 5a02966 jc/test-portability later to maint). * t9200 runs "cvs init" on a directory that already exists, but a platform can configure this fail for the current user (e.g. you need to be in the cvsadmin group on NetBSD 6.0). - (merge 8666df0 jc/test-cvs-no-init-in-existing-dir later to maint). * t9020 and t9810 had a few non-portable shell script construct. - (merge 2797914 tb/test-t9020-no-which later to maint). - (merge 6f4e505 tb/test-t9810-no-sed-i later to maint). + + * Scripts to test bash completion was inherently flaky as it was + affected by whatever random things the user may have on $PATH. + (merge 5047822 jc/do-not-let-random-file-interfere-with-completion-tests later to maint). diff --git a/Documentation/config.txt b/Documentation/config.txt index b87f744643..c8abe86ed5 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -143,7 +143,8 @@ advice.*:: pushUpdateRejected:: Set this variable to 'false' if you want to disable 'pushNonFFCurrent', 'pushNonFFDefault', - 'pushNonFFMatching', and 'pushAlreadyExists' + 'pushNonFFMatching', 'pushAlreadyExists', + 'pushFetchFirst', and 'pushNeedsForce' simultaneously. pushNonFFCurrent:: Advice shown when linkgit:git-push[1] fails due to a @@ -162,6 +163,15 @@ advice.*:: pushAlreadyExists:: Shown when linkgit:git-push[1] rejects an update that does not qualify for fast-forwarding (e.g., a tag.) + pushFetchFirst:: + Shown when linkgit:git-push[1] rejects an update that + tries to overwrite a remote ref that points at an + object we do not have. + pushNeedsForce:: + Shown when linkgit:git-push[1] rejects an update that + tries to overwrite a remote ref that points at an + object that is not a committish, or make the remote + ref point at an object that is not a committish. statusHints:: Show directions on how to proceed from the current state in the output of linkgit:git-status[1], in @@ -235,6 +245,12 @@ core.trustctime:: crawlers and some backup systems). See linkgit:git-update-index[1]. True by default. +core.checkstat:: + Determines which stat fields to match between the index + and work tree. The user can set this to 'default' or + 'minimal'. Default (or explicitly 'default'), is to check + all fields, including the sub-second part of mtime and ctime. + core.quotepath:: The commands that output paths (e.g. 'ls-files', 'diff'), when not given the `-z` option, will quote @@ -528,6 +544,12 @@ core.editor:: variable when it is set, and the environment variable `GIT_EDITOR` is not set. See linkgit:git-var[1]. +core.commentchar:: + Commands such as `commit` and `tag` that lets you edit + messages consider a line that begins with this character + commented, and removes them after the editor returns + (default '#'). + sequence.editor:: Text editor used by `git rebase -i` for editing the rebase insn file. The value is meant to be interpreted by the shell when it is used. diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 6e98bdf149..9cb649673d 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -8,11 +8,15 @@ option old data in `.git/FETCH_HEAD` will be overwritten. --depth=<depth>:: - Deepen the history of a 'shallow' repository created by + Deepen or shorten the history of a 'shallow' repository created by `git clone` with `--depth=<depth>` option (see linkgit:git-clone[1]) to the specified number of commits from the tip of each remote branch history. Tags for the deepened commits are not fetched. +--unshallow:: + Convert a shallow repository to a complete one, removing all + the limitations imposed by shallow repositories. + ifndef::git-pull[] --dry-run:: Show what would be done, without making any changes. diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index fd9e36b99f..533355984c 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -107,9 +107,10 @@ apply to the index. See EDITING PATCHES below. from the index if the corresponding files in the working tree have been removed. + -If no <filepattern> is given, default to "."; in other words, -update all tracked files in the current directory and its -subdirectories. +If no <filepattern> is given, the current version of Git defaults to +"."; in other words, update all tracked files in the current directory +and its subdirectories. This default will change in a future version +of Git, hence the form without <filepattern> should not be used. -A:: --all:: diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt index 9d5353e8be..f059ea94da 100644 --- a/Documentation/git-cvsimport.txt +++ b/Documentation/git-cvsimport.txt @@ -18,6 +18,12 @@ SYNOPSIS DESCRIPTION ----------- +*WARNING:* `git cvsimport` uses cvsps version 2, which is considered +deprecated; it does not work with cvsps version 3 and later. If you are +performing a one-shot import of a CVS repository consider using +link:http://cvs2svn.tigris.org/cvs2git.html[cvs2git] or +link:https://github.com/BartMassey/parsecvs[parsecvs]. + Imports a CVS repository into git. It will either create a new repository, or incrementally import into an existing one. diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt index 8c751202d7..b81e90d8e7 100644 --- a/Documentation/git-fetch-pack.txt +++ b/Documentation/git-fetch-pack.txt @@ -84,6 +84,8 @@ be in a separate packet, and the list must end with a flush packet. --depth=<n>:: Limit fetching to ancestor-chains not longer than n. + 'git-upload-pack' treats the special depth 2147483647 as + infinite even if there is an ancestor-chain that long. --no-progress:: Do not show the progress. diff --git a/Documentation/git-stripspace.txt b/Documentation/git-stripspace.txt index a80d94650d..e6fdfcb994 100644 --- a/Documentation/git-stripspace.txt +++ b/Documentation/git-stripspace.txt @@ -35,7 +35,13 @@ OPTIONS ------- -s:: --strip-comments:: - Skip and remove all lines starting with '#'. + Skip and remove all lines starting with comment character (default '#'). + +-c:: +--comment-lines:: + Prepend comment character and blank to each line. Lines will automatically + be terminated with a newline. On empty lines, only the comment character + will be prepended. EXAMPLES -------- diff --git a/Documentation/technical/api-strbuf.txt b/Documentation/technical/api-strbuf.txt index 84686b5c69..2c59cb2259 100644 --- a/Documentation/technical/api-strbuf.txt +++ b/Documentation/technical/api-strbuf.txt @@ -156,6 +156,11 @@ then they will free() it. Remove the bytes between `pos..pos+len` and replace it with the given data. +`strbuf_add_commented_lines`:: + + Add a NUL-terminated string to the buffer. Each line will be prepended + by a comment character and a blank. + `strbuf_add`:: Add data of given length to the buffer. @@ -229,6 +234,11 @@ which can be used by the programmer of the callback as she sees fit. Add a formatted string to the buffer. +`strbuf_commented_addf`:: + + Add a formatted string prepended by a comment character and a + blank to the buffer. + `strbuf_fread`:: Read a given size of data from a FILE* pointer to the buffer. diff --git a/Documentation/technical/shallow.txt b/Documentation/technical/shallow.txt index 0502a5471e..ea2f69faf5 100644 --- a/Documentation/technical/shallow.txt +++ b/Documentation/technical/shallow.txt @@ -53,3 +53,6 @@ It also writes an appropriate $GIT_DIR/shallow. You can deepen a shallow repository with "git-fetch --depth 20 repo branch", which will fetch branch from repo, but stop at depth 20, updating $GIT_DIR/shallow. + +The special depth 2147483647 (or 0x7fffffff, the largest positive +number a signed 32-bit integer can contain) means infinite depth. @@ -131,8 +131,9 @@ Issues of note: use English. Under autoconf the configure script will do this automatically if it can't find libintl on the system. - - Python version 2.6 or later is needed to use the git-p4 - interface to Perforce. + - Python version 2.4 or later (but not 3.x, which is not + supported by Perforce) is needed to use the git-p4 interface + to Perforce. - Some platform specific issues are dealt with Makefile rules, but depending on your specific installation, you may not @@ -47,11 +47,10 @@ requests, comments and patches to git@vger.kernel.org (read Documentation/SubmittingPatches for instructions on patch submission). To subscribe to the list, send an email with just "subscribe git" in the body to majordomo@vger.kernel.org. The mailing list archives are -available at http://marc.theaimsgroup.com/?l=git and other archival -sites. - -The messages titled "A note from the maintainer", "What's in -git.git (stable)" and "What's cooking in git.git (topics)" and -the discussion following them on the mailing list give a good -reference for project status, development direction and -remaining tasks. +available at http://news.gmane.org/gmane.comp.version-control.git/, +http://marc.info/?l=git and other archival sites. + +The maintainer frequently sends the "What's cooking" reports that +list the current status of various development topics to the mailing +list. The discussion following them give a good reference for +project status, development direction and remaining tasks. @@ -5,6 +5,8 @@ int advice_push_non_ff_current = 1; int advice_push_non_ff_default = 1; int advice_push_non_ff_matching = 1; int advice_push_already_exists = 1; +int advice_push_fetch_first = 1; +int advice_push_needs_force = 1; int advice_status_hints = 1; int advice_commit_before_merge = 1; int advice_resolve_conflict = 1; @@ -20,6 +22,8 @@ static struct { { "pushnonffdefault", &advice_push_non_ff_default }, { "pushnonffmatching", &advice_push_non_ff_matching }, { "pushalreadyexists", &advice_push_already_exists }, + { "pushfetchfirst", &advice_push_fetch_first }, + { "pushneedsforce", &advice_push_needs_force }, { "statushints", &advice_status_hints }, { "commitbeforemerge", &advice_commit_before_merge }, { "resolveconflict", &advice_resolve_conflict }, @@ -8,6 +8,8 @@ extern int advice_push_non_ff_current; extern int advice_push_non_ff_default; extern int advice_push_non_ff_matching; extern int advice_push_already_exists; +extern int advice_push_fetch_first; +extern int advice_push_needs_force; extern int advice_status_hints; extern int advice_commit_before_merge; extern int advice_resolve_conflict; diff --git a/archive-tar.c b/archive-tar.c index d1cce46e33..719b6298e6 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -327,20 +327,12 @@ static struct archiver *find_tar_filter(const char *name, int len) static int tar_filter_config(const char *var, const char *value, void *data) { struct archiver *ar; - const char *dot; const char *name; const char *type; int namelen; - if (prefixcmp(var, "tar.")) + if (parse_config_key(var, "tar", &name, &namelen, &type) < 0 || !name) return 0; - dot = strrchr(var, '.'); - if (dot == var + 9) - return 0; - - name = var + 4; - namelen = dot - name; - type = dot + 1; ar = find_tar_filter(name, namelen); if (!ar) { diff --git a/builtin/add.c b/builtin/add.c index 7cb6cca56d..7738025a2e 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -321,6 +321,35 @@ static int add_files(struct dir_struct *dir, int flags) return exit_status; } +static void warn_pathless_add(const char *option_name, const char *short_name) { + /* + * To be consistent with "git add -p" and most Git + * commands, we should default to being tree-wide, but + * this is not the original behavior and can't be + * changed until users trained themselves not to type + * "git add -u" or "git add -A". For now, we warn and + * keep the old behavior. Later, this warning can be + * turned into a die(...), and eventually we may + * reallow the command with a new behavior. + */ + warning(_("The behavior of 'git add %s (or %s)' with no path argument from a\n" + "subdirectory of the tree will change in Git 2.0 and should not be used anymore.\n" + "To add content for the whole tree, run:\n" + "\n" + " git add %s :/\n" + " (or git add %s :/)\n" + "\n" + "To restrict the command to the current directory, run:\n" + "\n" + " git add %s .\n" + " (or git add %s .)\n" + "\n" + "With the current Git version, the command is restricted to the current directory."), + option_name, short_name, + option_name, short_name, + option_name, short_name); +} + int cmd_add(int argc, const char **argv, const char *prefix) { int exit_status = 0; @@ -331,6 +360,8 @@ int cmd_add(int argc, const char **argv, const char *prefix) int add_new_files; int require_pathspec; char *seen = NULL; + const char *option_with_implicit_dot = NULL; + const char *short_option_with_implicit_dot = NULL; git_config(add_config, NULL); @@ -350,8 +381,19 @@ int cmd_add(int argc, const char **argv, const char *prefix) die(_("-A and -u are mutually incompatible")); if (!show_only && ignore_missing) die(_("Option --ignore-missing can only be used together with --dry-run")); - if ((addremove || take_worktree_changes) && !argc) { + if (addremove) { + option_with_implicit_dot = "--all"; + short_option_with_implicit_dot = "-A"; + } + if (take_worktree_changes) { + option_with_implicit_dot = "--update"; + short_option_with_implicit_dot = "-u"; + } + if (option_with_implicit_dot && !argc) { static const char *here[2] = { ".", NULL }; + if (prefix) + warn_pathless_add(option_with_implicit_dot, + short_option_with_implicit_dot); argc = 1; argv = here; } diff --git a/builtin/blame.c b/builtin/blame.c index b431ba3209..86100e9662 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -1420,32 +1420,18 @@ static void get_commit_info(struct commit *commit, { int len; const char *subject, *encoding; - char *reencoded, *message; + char *message; commit_info_init(ret); - /* - * We've operated without save_commit_buffer, so - * we now need to populate them for output. - */ - if (!commit->buffer) { - enum object_type type; - unsigned long size; - commit->buffer = - read_sha1_file(commit->object.sha1, &type, &size); - if (!commit->buffer) - die("Cannot read commit %s", - sha1_to_hex(commit->object.sha1)); - } encoding = get_log_output_encoding(); - reencoded = logmsg_reencode(commit, encoding); - message = reencoded ? reencoded : commit->buffer; + message = logmsg_reencode(commit, encoding); get_ac_line(message, "\nauthor ", &ret->author, &ret->author_mail, &ret->author_time, &ret->author_tz); if (!detailed) { - free(reencoded); + logmsg_free(message, commit); return; } @@ -1459,7 +1445,7 @@ static void get_commit_info(struct commit *commit, else strbuf_addf(&ret->summary, "(%s)", sha1_to_hex(commit->object.sha1)); - free(reencoded); + logmsg_free(message, commit); } /* diff --git a/builtin/branch.c b/builtin/branch.c index 873f624d1c..77b435825c 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -706,11 +706,11 @@ static int edit_branch_description(const char *branch_name) read_branch_desc(&buf, branch_name); if (!buf.len || buf.buf[buf.len-1] != '\n') strbuf_addch(&buf, '\n'); - strbuf_addf(&buf, - "# Please edit the description for the branch\n" - "# %s\n" - "# Lines starting with '#' will be stripped.\n", - branch_name); + strbuf_commented_addf(&buf, + "Please edit the description for the branch\n" + " %s\n" + "Lines starting with '%c' will be stripped.\n", + branch_name, comment_line_char); fp = fopen(git_path(edit_description), "w"); if ((fwrite(buf.buf, 1, buf.len, fp) < buf.len) || fclose(fp)) { strbuf_release(&buf); @@ -850,11 +850,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix) const char *branch_name; struct strbuf branch_ref = STRBUF_INIT; - if (detached) - die("Cannot give description to detached HEAD"); - if (!argc) + if (!argc) { + if (detached) + die("Cannot give description to detached HEAD"); branch_name = head; - else if (argc == 1) + } else if (argc == 1) branch_name = argv[0]; else usage_with_options(builtin_branch_usage, options); diff --git a/builtin/clone.c b/builtin/clone.c index 36ec99db3f..e0aaf13583 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -767,8 +767,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix) atexit(remove_junk); sigchain_push_common(remove_junk_on_signal); - setenv(CONFIG_ENVIRONMENT, mkpath("%s/config", git_dir), 1); - if (safe_create_leading_directories_const(git_dir) < 0) die(_("could not create leading directories of '%s'"), git_dir); @@ -787,13 +785,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix) init_db(option_template, INIT_DB_QUIET); write_config(&option_config); - /* - * At this point, the config exists, so we do not need the - * environment variable. We actually need to unset it, too, to - * re-enable parsing of the global configs. - */ - unsetenv(CONFIG_ENVIRONMENT); - git_config(git_default_config, NULL); if (option_bare) { diff --git a/builtin/commit.c b/builtin/commit.c index 38b9a9cc0d..1a0e5f14a3 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -733,15 +733,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (cleanup_mode == CLEANUP_ALL) status_printf(s, GIT_COLOR_NORMAL, _("Please enter the commit message for your changes." - " Lines starting\nwith '#' will be ignored, and an empty" - " message aborts the commit.\n")); + " Lines starting\nwith '%c' will be ignored, and an empty" + " message aborts the commit.\n"), comment_line_char); else /* CLEANUP_SPACE, that is. */ status_printf(s, GIT_COLOR_NORMAL, _("Please enter the commit message for your changes." - " Lines starting\n" - "with '#' will be kept; you may remove them" - " yourself if you want to.\n" - "An empty message aborts the commit.\n")); + " Lines starting\n" + "with '%c' will be kept; you may remove them" + " yourself if you want to.\n" + "An empty message aborts the commit.\n"), comment_line_char); if (only_include_assumed) status_printf_ln(s, GIT_COLOR_NORMAL, "%s", only_include_assumed); @@ -946,24 +946,14 @@ static void handle_untracked_files_arg(struct wt_status *s) static const char *read_commit_message(const char *name) { - const char *out_enc, *out; + const char *out_enc; struct commit *commit; commit = lookup_commit_reference_by_name(name); if (!commit) die(_("could not lookup commit %s"), name); out_enc = get_commit_output_encoding(); - out = logmsg_reencode(commit, out_enc); - - /* - * If we failed to reencode the buffer, just copy it - * byte for byte so the user can try to fix it up. - * This also handles the case where input and output - * encodings are identical. - */ - if (out == NULL) - out = xstrdup(commit->buffer); - return out; + return logmsg_reencode(commit, out_enc); } static int parse_and_validate_options(int argc, const char *argv[], diff --git a/builtin/fetch.c b/builtin/fetch.c index 4b5a89839b..4b6b1dfe66 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -32,7 +32,7 @@ enum { static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity; static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT; -static int tags = TAGS_DEFAULT; +static int tags = TAGS_DEFAULT, unshallow; static const char *depth; static const char *upload_pack; static struct strbuf default_rla = STRBUF_INIT; @@ -82,6 +82,9 @@ static struct option builtin_fetch_options[] = { OPT_BOOL(0, "progress", &progress, N_("force progress reporting")), OPT_STRING(0, "depth", &depth, N_("depth"), N_("deepen history of shallow clone")), + { OPTION_SET_INT, 0, "unshallow", &unshallow, NULL, + N_("convert to a complete repository"), + PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 }, { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"), N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN }, { OPTION_STRING, 0, "recurse-submodules-default", @@ -959,6 +962,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) struct string_list list = STRING_LIST_INIT_NODUP; struct remote *remote; int result = 0; + static const char *argv_gc_auto[] = { + "gc", "--auto", NULL, + }; packet_trace_identity("fetch"); @@ -970,6 +976,18 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, builtin_fetch_options, builtin_fetch_usage, 0); + if (unshallow) { + if (depth) + die(_("--depth and --unshallow cannot be used together")); + else if (!is_repository_shallow()) + die(_("--unshallow on a complete repository does not make sense")); + else { + static char inf_depth[12]; + sprintf(inf_depth, "%d", INFINITE_DEPTH); + depth = inf_depth; + } + } + if (recurse_submodules != RECURSE_SUBMODULES_OFF) { if (recurse_submodules_default) { int arg = parse_fetch_recurse_submodules_arg("--recurse-submodules-default", recurse_submodules_default); @@ -1026,5 +1044,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) list.strdup_strings = 1; string_list_clear(&list, 0); + run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); + return result; } diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index d9af43c257..b49612f0ce 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -470,7 +470,7 @@ static void fmt_tag_signature(struct strbuf *tagbuf, strbuf_complete_line(tagbuf); if (sig->len) { strbuf_addch(tagbuf, '\n'); - strbuf_add_lines(tagbuf, "# ", sig->buf, sig->len); + strbuf_add_commented_lines(tagbuf, sig->buf, sig->len); } } diff --git a/builtin/grep.c b/builtin/grep.c index 0e1b6c860e..8025964987 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -823,6 +823,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) struct object *object = parse_object(sha1); if (!object) die(_("bad object %s"), arg); + if (!seen_dashdash) + verify_non_filename(prefix, arg); add_object_array(object, arg, &list); continue; } diff --git a/builtin/help.c b/builtin/help.c index bd86253d83..d1d71816a9 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -6,7 +6,6 @@ #include "cache.h" #include "builtin.h" #include "exec_cmd.h" -#include "common-cmds.h" #include "parse-options.h" #include "run-command.h" #include "column.h" @@ -237,21 +236,21 @@ static int add_man_viewer_cmd(const char *name, static int add_man_viewer_info(const char *var, const char *value) { - const char *name = var + 4; - const char *subkey = strrchr(name, '.'); + const char *name, *subkey; + int namelen; - if (!subkey) + if (parse_config_key(var, "man", &name, &namelen, &subkey) < 0 || !name) return 0; - if (!strcmp(subkey, ".path")) { + if (!strcmp(subkey, "path")) { if (!value) return config_error_nonbool(var); - return add_man_viewer_path(name, subkey - name, value); + return add_man_viewer_path(name, namelen, value); } - if (!strcmp(subkey, ".cmd")) { + if (!strcmp(subkey, "cmd")) { if (!value) return config_error_nonbool(var); - return add_man_viewer_cmd(name, subkey - name, value); + return add_man_viewer_cmd(name, namelen, value); } return 0; @@ -287,23 +286,6 @@ static int git_help_config(const char *var, const char *value, void *cb) static struct cmdnames main_cmds, other_cmds; -void list_common_cmds_help(void) -{ - int i, longest = 0; - - for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { - if (longest < strlen(common_cmds[i].name)) - longest = strlen(common_cmds[i].name); - } - - puts(_("The most commonly used git commands are:")); - for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { - printf(" %s ", common_cmds[i].name); - mput_char(' ', longest - strlen(common_cmds[i].name)); - puts(_(common_cmds[i].help)); - } -} - static int is_git_command(const char *s) { return is_in_cmdlist(&main_cmds, s) || diff --git a/builtin/merge.c b/builtin/merge.c index 9307e9c726..7c8922c8b0 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -788,17 +788,16 @@ static const char merge_editor_comment[] = N_("Please enter a commit message to explain why this merge is necessary,\n" "especially if it merges an updated upstream into a topic branch.\n" "\n" - "Lines starting with '#' will be ignored, and an empty message aborts\n" + "Lines starting with '%c' will be ignored, and an empty message aborts\n" "the commit.\n"); static void prepare_to_commit(struct commit_list *remoteheads) { struct strbuf msg = STRBUF_INIT; - const char *comment = _(merge_editor_comment); strbuf_addbuf(&msg, &merge_msg); strbuf_addch(&msg, '\n'); if (0 < option_edit) - strbuf_add_lines(&msg, "# ", comment, strlen(comment)); + strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char); write_merge_msg(&msg); if (run_hook(get_index_file(), "prepare-commit-msg", git_path("MERGE_MSG"), "merge", NULL, NULL)) diff --git a/builtin/notes.c b/builtin/notes.c index 453457adb9..57748a6fb6 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -92,10 +92,7 @@ static const char * const git_notes_get_ref_usage[] = { }; static const char note_template[] = - "\n" - "#\n" - "# Write/edit the notes for the following object:\n" - "#\n"; + "\nWrite/edit the notes for the following object:\n"; struct msg_arg { int given; @@ -129,7 +126,7 @@ static void write_commented_object(int fd, const unsigned char *object) {"show", "--stat", "--no-notes", sha1_to_hex(object), NULL}; struct child_process show; struct strbuf buf = STRBUF_INIT; - FILE *show_out; + struct strbuf cbuf = STRBUF_INIT; /* Invoke "git show --stat --no-notes $object" */ memset(&show, 0, sizeof(show)); @@ -142,21 +139,14 @@ static void write_commented_object(int fd, const unsigned char *object) die(_("unable to start 'show' for object '%s'"), sha1_to_hex(object)); - /* Open the output as FILE* so strbuf_getline() can be used. */ - show_out = xfdopen(show.out, "r"); - if (show_out == NULL) - die_errno(_("can't fdopen 'show' output fd")); + if (strbuf_read(&buf, show.out, 0) < 0) + die_errno(_("could not read 'show' output")); + strbuf_add_commented_lines(&cbuf, buf.buf, buf.len); + write_or_die(fd, cbuf.buf, cbuf.len); - /* Prepend "# " to each output line and write result to 'fd' */ - while (strbuf_getline(&buf, show_out, '\n') != EOF) { - write_or_die(fd, "# ", 2); - write_or_die(fd, buf.buf, buf.len); - write_or_die(fd, "\n", 1); - } + strbuf_release(&cbuf); strbuf_release(&buf); - if (fclose(show_out)) - die_errno(_("failed to close pipe to 'show' for object '%s'"), - sha1_to_hex(object)); + if (finish_command(&show)) die(_("failed to finish 'show' for object '%s'"), sha1_to_hex(object)); @@ -170,6 +160,7 @@ static void create_note(const unsigned char *object, struct msg_arg *msg, if (msg->use_editor || !msg->given) { int fd; + struct strbuf buf = STRBUF_INIT; /* write the template message before editing: */ path = git_pathdup("NOTES_EDITMSG"); @@ -181,11 +172,16 @@ static void create_note(const unsigned char *object, struct msg_arg *msg, write_or_die(fd, msg->buf.buf, msg->buf.len); else if (prev && !append_only) write_note_data(fd, prev); - write_or_die(fd, note_template, strlen(note_template)); + + strbuf_addch(&buf, '\n'); + strbuf_add_commented_lines(&buf, note_template, strlen(note_template)); + strbuf_addch(&buf, '\n'); + write_or_die(fd, buf.buf, buf.len); write_commented_object(fd, object); close(fd); + strbuf_release(&buf); strbuf_reset(&(msg->buf)); if (launch_editor(path, &(msg->buf), NULL)) { diff --git a/builtin/push.c b/builtin/push.c index b158028be8..42b129d36c 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -220,9 +220,20 @@ static const char message_advice_checkout_pull_push[] = "(e.g. 'git pull') before pushing again.\n" "See the 'Note about fast-forwards' in 'git push --help' for details."); +static const char message_advice_ref_fetch_first[] = + N_("Updates were rejected because the remote contains work that you do\n" + "not have locally. This is usually caused by another repository pushing\n" + "to the same ref. You may want to first merge the remote changes (e.g.,\n" + "'git pull') before pushing again.\n" + "See the 'Note about fast-forwards' in 'git push --help' for details."); + static const char message_advice_ref_already_exists[] = - N_("Updates were rejected because the destination reference already exists\n" - "in the remote."); + N_("Updates were rejected because the tag already exists in the remote."); + +static const char message_advice_ref_needs_force[] = + N_("You cannot update a remote ref that points at a non-commit object,\n" + "or update a remote ref to make it point at a non-commit object,\n" + "without using the '--force' option.\n"); static void advise_pull_before_push(void) { @@ -252,6 +263,20 @@ static void advise_ref_already_exists(void) advise(_(message_advice_ref_already_exists)); } +static void advise_ref_fetch_first(void) +{ + if (!advice_push_fetch_first || !advice_push_update_rejected) + return; + advise(_(message_advice_ref_fetch_first)); +} + +static void advise_ref_needs_force(void) +{ + if (!advice_push_needs_force || !advice_push_update_rejected) + return; + advise(_(message_advice_ref_needs_force)); +} + static int push_with_options(struct transport *transport, int flags) { int err; @@ -285,6 +310,10 @@ static int push_with_options(struct transport *transport, int flags) advise_checkout_pull_push(); } else if (reject_reasons & REJECT_ALREADY_EXISTS) { advise_ref_already_exists(); + } else if (reject_reasons & REJECT_FETCH_FIRST) { + advise_ref_fetch_first(); + } else if (reject_reasons & REJECT_NEEDS_FORCE) { + advise_ref_needs_force(); } return 1; diff --git a/builtin/reflog.c b/builtin/reflog.c index b3c9e27bde..1fedf66329 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -510,26 +510,27 @@ static int parse_expire_cfg_value(const char *var, const char *value, unsigned l static int reflog_expire_config(const char *var, const char *value, void *cb) { - const char *lastdot = strrchr(var, '.'); + const char *pattern, *key; + int pattern_len; unsigned long expire; int slot; struct reflog_expire_cfg *ent; - if (!lastdot || prefixcmp(var, "gc.")) + if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0) return git_default_config(var, value, cb); - if (!strcmp(lastdot, ".reflogexpire")) { + if (!strcmp(key, "reflogexpire")) { slot = EXPIRE_TOTAL; if (parse_expire_cfg_value(var, value, &expire)) return -1; - } else if (!strcmp(lastdot, ".reflogexpireunreachable")) { + } else if (!strcmp(key, "reflogexpireunreachable")) { slot = EXPIRE_UNREACH; if (parse_expire_cfg_value(var, value, &expire)) return -1; } else return git_default_config(var, value, cb); - if (lastdot == var + 2) { + if (!pattern) { switch (slot) { case EXPIRE_TOTAL: default_reflog_expire = expire; @@ -541,7 +542,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb) return 0; } - ent = find_cfg_ent(var + 3, lastdot - (var+3)); + ent = find_cfg_ent(pattern, pattern_len); if (!ent) return -1; switch (slot) { diff --git a/builtin/send-pack.c b/builtin/send-pack.c index f849e0a4a0..57a46b2654 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -44,6 +44,16 @@ static void print_helper_status(struct ref *ref) msg = "non-fast forward"; break; + case REF_STATUS_REJECT_FETCH_FIRST: + res = "error"; + msg = "fetch first"; + break; + + case REF_STATUS_REJECT_NEEDS_FORCE: + res = "error"; + msg = "needs force"; + break; + case REF_STATUS_REJECT_ALREADY_EXISTS: res = "error"; msg = "already exists"; diff --git a/builtin/stripspace.c b/builtin/stripspace.c index f16986c0ae..e981dfb9f0 100644 --- a/builtin/stripspace.c +++ b/builtin/stripspace.c @@ -30,7 +30,8 @@ static size_t cleanup(char *line, size_t len) * * If last line does not have a newline at the end, one is added. * - * Enable skip_comments to skip every line starting with "#". + * Enable skip_comments to skip every line starting with comment + * character. */ void stripspace(struct strbuf *sb, int skip_comments) { @@ -45,7 +46,7 @@ void stripspace(struct strbuf *sb, int skip_comments) eol = memchr(sb->buf + i, '\n', sb->len - i); len = eol ? eol - (sb->buf + i) + 1 : sb->len - i; - if (skip_comments && len && sb->buf[i] == '#') { + if (skip_comments && len && sb->buf[i] == comment_line_char) { newlen = 0; continue; } @@ -66,21 +67,53 @@ void stripspace(struct strbuf *sb, int skip_comments) strbuf_setlen(sb, j); } +static void comment_lines(struct strbuf *buf) +{ + char *msg; + size_t len; + + msg = strbuf_detach(buf, &len); + strbuf_add_commented_lines(buf, msg, len); + free(msg); +} + +static const char *usage_msg = "\n" +" git stripspace [-s | --strip-comments] < input\n" +" git stripspace [-c | --comment-lines] < input"; + int cmd_stripspace(int argc, const char **argv, const char *prefix) { struct strbuf buf = STRBUF_INIT; int strip_comments = 0; + enum { INVAL = 0, STRIP_SPACE = 1, COMMENT_LINES = 2 } mode = STRIP_SPACE; + + if (argc == 2) { + if (!strcmp(argv[1], "-s") || + !strcmp(argv[1], "--strip-comments")) { + strip_comments = 1; + } else if (!strcmp(argv[1], "-c") || + !strcmp(argv[1], "--comment-lines")) { + mode = COMMENT_LINES; + } else { + mode = INVAL; + } + } else if (argc > 1) { + mode = INVAL; + } + + if (mode == INVAL) + usage(usage_msg); - if (argc == 2 && (!strcmp(argv[1], "-s") || - !strcmp(argv[1], "--strip-comments"))) - strip_comments = 1; - else if (argc > 1) - usage("git stripspace [-s | --strip-comments] < input"); + if (strip_comments || mode == COMMENT_LINES) + git_config(git_default_config, NULL); if (strbuf_read(&buf, 0, 1024) < 0) die_errno("could not read the input"); - stripspace(&buf, strip_comments); + if (mode == STRIP_SPACE) + stripspace(&buf, strip_comments); + else + comment_lines(&buf); write_or_die(1, buf.buf, buf.len); strbuf_release(&buf); diff --git a/builtin/tag.c b/builtin/tag.c index 9c3e0673d5..f8266888cc 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -246,19 +246,13 @@ static int do_sign(struct strbuf *buffer) } static const char tag_template[] = - N_("\n" - "#\n" - "# Write a tag message\n" - "# Lines starting with '#' will be ignored.\n" - "#\n"); + N_("\nWrite a tag message\n" + "Lines starting with '%c' will be ignored.\n"); static const char tag_template_nocleanup[] = - N_("\n" - "#\n" - "# Write a tag message\n" - "# Lines starting with '#' will be kept; you may remove them" - " yourself if you want to.\n" - "#\n"); + N_("\nWrite a tag message\n" + "Lines starting with '%c' will be kept; you may remove them" + " yourself if you want to.\n"); static int git_tag_config(const char *var, const char *value, void *cb) { @@ -346,14 +340,18 @@ static void create_tag(const unsigned char *object, const char *tag, if (fd < 0) die_errno(_("could not create file '%s'"), path); - if (!is_null_sha1(prev)) + if (!is_null_sha1(prev)) { write_tag_body(fd, prev); - else if (opt->cleanup_mode == CLEANUP_ALL) - write_or_die(fd, _(tag_template), - strlen(_(tag_template))); - else - write_or_die(fd, _(tag_template_nocleanup), - strlen(_(tag_template_nocleanup))); + } else { + struct strbuf buf = STRBUF_INIT; + strbuf_addch(&buf, '\n'); + if (opt->cleanup_mode == CLEANUP_ALL) + strbuf_commented_addf(&buf, _(tag_template), comment_line_char); + else + strbuf_commented_addf(&buf, _(tag_template_nocleanup), comment_line_char); + write_or_die(fd, buf.buf, buf.len); + strbuf_release(&buf); + } close(fd); if (launch_editor(path, buf, NULL)) { @@ -536,6 +536,7 @@ extern int delete_ref(const char *, const unsigned char *sha1, int delopt); /* Environment bits from configuration mechanism */ extern int trust_executable_bit; extern int trust_ctime; +extern int check_stat; extern int quote_path_fully; extern int has_symlinks; extern int minimum_abbrev, default_abbrev; @@ -562,6 +563,12 @@ extern int core_preload_index; extern int core_apply_sparse_checkout; extern int precomposed_unicode; +/* + * The character that begins a commented line in user-editable file + * that is subject to stripspace. + */ +extern char comment_line_char; + enum branch_track { BRANCH_TRACK_UNSPECIFIED = -1, BRANCH_TRACK_NEVER = 0, @@ -1008,10 +1015,8 @@ struct ref { char *symref; unsigned int force:1, - requires_force:1, + forced_update:1, merge:1, - nonfastforward:1, - update:1, deletion:1; enum { REF_STATUS_NONE = 0, @@ -1019,6 +1024,8 @@ struct ref { REF_STATUS_REJECT_NONFASTFORWARD, REF_STATUS_REJECT_ALREADY_EXISTS, REF_STATUS_REJECT_NODELETE, + REF_STATUS_REJECT_FETCH_FIRST, + REF_STATUS_REJECT_NEEDS_FORCE, REF_STATUS_UPTODATE, REF_STATUS_REMOTE_REJECT, REF_STATUS_EXPECTING_REPORT @@ -1163,6 +1170,21 @@ struct config_include_data { #define CONFIG_INCLUDE_INIT { 0 } extern int git_config_include(const char *name, const char *value, void *data); +/* + * Match and parse a config key of the form: + * + * section.(subsection.)?key + * + * (i.e., what gets handed to a config_fn_t). The caller provides the section; + * we return -1 if it does not match, 0 otherwise. The subsection and key + * out-parameters are filled by the function (and subsection is NULL if it is + * missing). + */ +extern int parse_config_key(const char *var, + const char *section, + const char **subsection, int *subsection_len, + const char **key); + extern int committer_ident_sufficiently_given(void); extern int author_ident_sufficiently_given(void); @@ -101,6 +101,7 @@ extern int has_non_ascii(const char *text); struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */ extern char *logmsg_reencode(const struct commit *commit, const char *output_encoding); +extern void logmsg_free(char *msg, const struct commit *commit); extern void get_commit_format(const char *arg, struct rev_info *); extern const char *format_subject(struct strbuf *sb, const char *msg, const char *line_separator); @@ -163,6 +164,9 @@ extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *r extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup); extern struct commit_list *get_octopus_merge_bases(struct commit_list *in); +/* largest postive number a signed 32-bit integer can contain */ +#define INFINITE_DEPTH 0x7fffffff + extern int register_shallow(const unsigned char *sha1); extern int unregister_shallow(const unsigned char *sha1); extern int for_each_commit_graft(each_commit_graft_fn, void *); @@ -566,6 +566,12 @@ static int git_default_core_config(const char *var, const char *value) trust_ctime = git_config_bool(var, value); return 0; } + if (!strcmp(var, "core.statinfo")) { + if (!strcasecmp(value, "default")) + check_stat = 1; + else if (!strcasecmp(value, "minimal")) + check_stat = 0; + } if (!strcmp(var, "core.quotepath")) { quote_path_fully = git_config_bool(var, value); @@ -717,6 +723,14 @@ static int git_default_core_config(const char *var, const char *value) if (!strcmp(var, "core.editor")) return git_config_string(&editor_program, var, value); + if (!strcmp(var, "core.commentchar")) { + const char *comment; + int ret = git_config_string(&comment, var, value); + if (!ret) + comment_line_char = comment[0]; + return ret; + } + if (!strcmp(var, "core.askpass")) return git_config_string(&askpass_program, var, value); @@ -1667,3 +1681,36 @@ int config_error_nonbool(const char *var) { return error("Missing value for '%s'", var); } + +int parse_config_key(const char *var, + const char *section, + const char **subsection, int *subsection_len, + const char **key) +{ + int section_len = strlen(section); + const char *dot; + + /* Does it start with "section." ? */ + if (prefixcmp(var, section) || var[section_len] != '.') + return -1; + + /* + * Find the key; we don't know yet if we have a subsection, but we must + * parse backwards from the end, since the subsection may have dots in + * it, too. + */ + dot = strrchr(var, '.'); + *key = dot + 1; + + /* Did we have a subsection at all? */ + if (dot == var + section_len) { + *subsection = NULL; + *subsection_len = 0; + } + else { + *subsection = var + section_len + 1; + *subsection_len = dot - *subsection; + } + + return 0; +} diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 7147d64af2..5770b6f2d0 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -531,10 +531,19 @@ __git_complete_strategy () return 1 } +__git_commands () { + if test -n "${GIT_TESTING_COMMAND_COMPLETION:-}" + then + printf "%s" "${GIT_TESTING_COMMAND_COMPLETION}" + else + git help -a|egrep '^ [a-zA-Z0-9]' + fi +} + __git_list_all_commands () { local i IFS=" "$'\n' - for i in $(git help -a|egrep '^ [a-zA-Z0-9]') + for i in $(__git_commands) do case $i in *--*) : helper pattern;; @@ -2432,7 +2441,7 @@ if [[ -n ${ZSH_VERSION-} ]]; then --*=*|*.) ;; *) c="$c " ;; esac - array+=("$c") + array[$#array+1]="$c" done compset -P '*[=:]' compadd -Q -S '' -p "${2-}" -a -- array && _ret=0 @@ -457,7 +457,7 @@ static struct convert_driver { static int read_convert_config(const char *var, const char *value, void *cb) { - const char *ep, *name; + const char *key, *name; int namelen; struct convert_driver *drv; @@ -465,10 +465,8 @@ static int read_convert_config(const char *var, const char *value, void *cb) * External conversion drivers are configured using * "filter.<name>.variable". */ - if (prefixcmp(var, "filter.") || (ep = strrchr(var, '.')) == var + 6) + if (parse_config_key(var, "filter", &name, &namelen, &key) < 0 || !name) return 0; - name = var + 7; - namelen = ep - name; for (drv = user_convert; drv; drv = drv->next) if (!strncmp(drv->name, name, namelen) && !drv->name[namelen]) break; @@ -479,8 +477,6 @@ static int read_convert_config(const char *var, const char *value, void *cb) user_convert_tail = &(drv->next); } - ep++; - /* * filter.<name>.smudge and filter.<name>.clean specifies * the command line: @@ -490,13 +486,13 @@ static int read_convert_config(const char *var, const char *value, void *cb) * The command-line will not be interpolated in any way. */ - if (!strcmp("smudge", ep)) + if (!strcmp("smudge", key)) return git_config_string(&drv->smudge, var, value); - if (!strcmp("clean", ep)) + if (!strcmp("clean", key)) return git_config_string(&drv->clean, var, value); - if (!strcmp("required", ep)) { + if (!strcmp("required", key)) { drv->required = git_config_bool(var, value); return 0; } diff --git a/environment.c b/environment.c index 85edd7f95a..89d6c70c15 100644 --- a/environment.c +++ b/environment.c @@ -13,6 +13,7 @@ int trust_executable_bit = 1; int trust_ctime = 1; +int check_stat = 1; int has_symlinks = 1; int minimum_abbrev = 4, default_abbrev = 7; int ignore_case; @@ -62,6 +63,12 @@ int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */ struct startup_info *startup_info; unsigned long pack_size_limit_cfg; +/* + * The character that begins a commented line in user-editable file + * that is subject to stripspace. + */ +char comment_line_char = '#'; + /* Parallel index stat data preload? */ int core_preload_index = 0; diff --git a/fetch-pack.c b/fetch-pack.c index f0acdf7331..6d8926a550 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -594,6 +594,9 @@ static int everything_local(struct fetch_pack_args *args, for (ref = *refs; ref; ref = ref->next) { struct object *o; + if (!has_sha1_file(ref->old_sha1)) + continue; + o = parse_object(ref->old_sha1); if (!o) continue; @@ -334,7 +334,7 @@ split_patches () { # Since we cannot guarantee that the commit message is in # git-friendly format, we put no Subject: line and just consume # all of the message as the body - perl -M'POSIX qw(strftime)' -ne 'BEGIN { $subject = 0 } + LANG=C LC_ALL=C perl -M'POSIX qw(strftime)' -ne 'BEGIN { $subject = 0 } if ($subject) { print ; } elsif (/^\# User /) { s/\# User/From:/ ; print ; } elsif (/^\# Date /) { @@ -23,6 +23,21 @@ import re import shutil import stat +try: + from subprocess import CalledProcessError +except ImportError: + # from python2.7:subprocess.py + # Exception classes used by this module. + class CalledProcessError(Exception): + """This exception is raised when a process run by check_call() returns + a non-zero exit status. The exit status will be stored in the + returncode attribute.""" + def __init__(self, returncode, cmd): + self.returncode = returncode + self.cmd = cmd + def __str__(self): + return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode) + verbose = False # Only labels/tags matching this will be imported/exported @@ -163,13 +178,17 @@ def system(cmd): expand = isinstance(cmd,basestring) if verbose: sys.stderr.write("executing %s\n" % str(cmd)) - subprocess.check_call(cmd, shell=expand) + retcode = subprocess.call(cmd, shell=expand) + if retcode: + raise CalledProcessError(retcode, cmd) def p4_system(cmd): """Specifically invoke p4 as the system command. """ real_cmd = p4_build_cmd(cmd) expand = isinstance(real_cmd, basestring) - subprocess.check_call(real_cmd, shell=expand) + retcode = subprocess.call(real_cmd, shell=expand) + if retcode: + raise CalledProcessError(retcode, real_cmd) _p4_version_string = None def p4_version_string(): @@ -800,7 +819,8 @@ def wildcard_encode(path): return path def wildcard_present(path): - return path.translate(None, "*#@%") != path + m = re.search("[*#@%]", path) + return m is not None class Command: def __init__(self): @@ -3214,7 +3234,9 @@ class P4Clone(P4Sync): init_cmd = [ "git", "init" ] if self.cloneBare: init_cmd.append("--bare") - subprocess.check_call(init_cmd) + retcode = subprocess.call(init_cmd) + if retcode: + raise CalledProcessError(retcode, init_cmd) if not P4Sync.run(self, depotPaths): return False diff --git a/git-submodule.sh b/git-submodule.sh index 22ec5b63b4..004c034bc0 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -976,12 +976,12 @@ cmd_summary() { done | if test -n "$for_status"; then if [ -n "$files" ]; then - gettextln "# Submodules changed but not updated:" + gettextln "Submodules changed but not updated:" | git stripspace -c else - gettextln "# Submodule changes to be committed:" + gettextln "Submodule changes to be committed:" | git stripspace -c fi - echo "#" - sed -e 's|^|# |' -e 's|^# $|#|' + printf "\n" | git stripspace -c + git stripspace -c else cat fi diff --git a/gitk-git/.gitignore b/gitk-git/.gitignore new file mode 100644 index 0000000000..d7ebcaf366 --- /dev/null +++ b/gitk-git/.gitignore @@ -0,0 +1,2 @@ +/GIT-TCLTK-VARS +/gitk-wish diff --git a/gitk-git/Makefile b/gitk-git/Makefile index e1b6045605..5acdc900ab 100644 --- a/gitk-git/Makefile +++ b/gitk-git/Makefile @@ -17,6 +17,16 @@ DESTDIR_SQ = $(subst ','\'',$(DESTDIR)) bindir_SQ = $(subst ','\'',$(bindir)) TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH)) +### Detect Tck/Tk interpreter path changes +TRACK_TCLTK = $(subst ','\'',-DTCLTK_PATH='$(TCLTK_PATH_SQ)') + +GIT-TCLTK-VARS: FORCE + @VARS='$(TRACK_TCLTK)'; \ + if test x"$$VARS" != x"`cat $@ 2>/dev/null`" ; then \ + echo 1>&2 " * new Tcl/Tk interpreter location"; \ + echo "$$VARS" >$@; \ + fi + ## po-file creation rules XGETTEXT ?= xgettext ifdef NO_MSGFMT @@ -49,9 +59,9 @@ uninstall:: $(RM) '$(DESTDIR_SQ)$(bindir_SQ)'/gitk clean:: - $(RM) gitk-wish po/*.msg + $(RM) gitk-wish po/*.msg GIT-TCLTK-VARS -gitk-wish: gitk +gitk-wish: gitk GIT-TCLTK-VARS $(QUIET_GEN)$(RM) $@ $@+ && \ sed -e '1,3s|^exec .* "$$0"|exec $(subst |,'\|',$(TCLTK_PATH_SQ)) "$$0"|' <gitk >$@+ && \ chmod +x $@+ && \ @@ -65,3 +75,5 @@ $(ALL_MSGFILES): %.msg : %.po @echo Generating catalog $@ $(MSGFMT) --statistics --tcl $< -l $(basename $(notdir $<)) -d $(dir $@) +.PHONY: all install uninstall clean update-po +.PHONY: FORCE @@ -223,6 +223,23 @@ void list_commands(unsigned int colopts, } } +void list_common_cmds_help(void) +{ + int i, longest = 0; + + for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { + if (longest < strlen(common_cmds[i].name)) + longest = strlen(common_cmds[i].name); + } + + puts(_("The most commonly used git commands are:")); + for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { + printf(" %s ", common_cmds[i].name); + mput_char(' ', longest - strlen(common_cmds[i].name)); + puts(_(common_cmds[i].help)); + } +} + int is_in_cmdlist(struct cmdnames *c, const char *s) { int i; @@ -46,6 +46,7 @@ static void copy_gecos(const struct passwd *w, struct strbuf *name) static int add_mailname_host(struct strbuf *buf) { FILE *mailname; + struct strbuf mailnamebuf = STRBUF_INIT; mailname = fopen("/etc/mailname", "r"); if (!mailname) { @@ -54,14 +55,17 @@ static int add_mailname_host(struct strbuf *buf) strerror(errno)); return -1; } - if (strbuf_getline(buf, mailname, '\n') == EOF) { + if (strbuf_getline(&mailnamebuf, mailname, '\n') == EOF) { if (ferror(mailname)) warning("cannot read /etc/mailname: %s", strerror(errno)); + strbuf_release(&mailnamebuf); fclose(mailname); return -1; } /* success! */ + strbuf_addbuf(buf, &mailnamebuf); + strbuf_release(&mailnamebuf); fclose(mailname); return 0; } diff --git a/ll-merge.c b/ll-merge.c index acea33bf1b..fb61ea66a1 100644 --- a/ll-merge.c +++ b/ll-merge.c @@ -222,7 +222,7 @@ static const char *default_ll_merge; static int read_merge_config(const char *var, const char *value, void *cb) { struct ll_merge_driver *fn; - const char *ep, *name; + const char *key, *name; int namelen; if (!strcmp(var, "merge.default")) { @@ -236,15 +236,13 @@ static int read_merge_config(const char *var, const char *value, void *cb) * especially, we do not want to look at variables such as * "merge.summary", "merge.tool", and "merge.verbosity". */ - if (prefixcmp(var, "merge.") || (ep = strrchr(var, '.')) == var + 5) + if (parse_config_key(var, "merge", &name, &namelen, &key) < 0 || !name) return 0; /* * Find existing one as we might be processing merge.<name>.var2 * after seeing merge.<name>.var1. */ - name = var + 6; - namelen = ep - name; for (fn = ll_user_merge; fn; fn = fn->next) if (!strncmp(fn->name, name, namelen) && !fn->name[namelen]) break; @@ -256,16 +254,14 @@ static int read_merge_config(const char *var, const char *value, void *cb) ll_user_merge_tail = &(fn->next); } - ep++; - - if (!strcmp("name", ep)) { + if (!strcmp("name", key)) { if (!value) return error("%s: lacks value", var); fn->description = xstrdup(value); return 0; } - if (!strcmp("driver", ep)) { + if (!strcmp("driver", key)) { if (!value) return error("%s: lacks value", var); /* @@ -289,7 +285,7 @@ static int read_merge_config(const char *var, const char *value, void *cb) return 0; } - if (!strcmp("recursive", ep)) { + if (!strcmp("recursive", key)) { if (!value) return error("%s: lacks value", var); fn->recursive = xstrdup(value); @@ -524,10 +524,11 @@ static void add_merge_info(const struct pretty_print_context *pp, strbuf_addch(sb, '\n'); } -static char *get_header(const struct commit *commit, const char *key) +static char *get_header(const struct commit *commit, const char *msg, + const char *key) { int key_len = strlen(key); - const char *line = commit->buffer; + const char *line = msg; while (line) { const char *eol = strchr(line, '\n'), *next; @@ -588,25 +589,77 @@ char *logmsg_reencode(const struct commit *commit, static const char *utf8 = "UTF-8"; const char *use_encoding; char *encoding; + char *msg = commit->buffer; char *out; + if (!msg) { + enum object_type type; + unsigned long size; + + msg = read_sha1_file(commit->object.sha1, &type, &size); + if (!msg) + die("Cannot read commit object %s", + sha1_to_hex(commit->object.sha1)); + if (type != OBJ_COMMIT) + die("Expected commit for '%s', got %s", + sha1_to_hex(commit->object.sha1), typename(type)); + } + if (!output_encoding || !*output_encoding) - return NULL; - encoding = get_header(commit, "encoding"); + return msg; + encoding = get_header(commit, msg, "encoding"); use_encoding = encoding ? encoding : utf8; - if (same_encoding(use_encoding, output_encoding)) - if (encoding) /* we'll strip encoding header later */ - out = xstrdup(commit->buffer); - else - return NULL; /* nothing to do */ - else - out = reencode_string(commit->buffer, - output_encoding, use_encoding); + if (same_encoding(use_encoding, output_encoding)) { + /* + * No encoding work to be done. If we have no encoding header + * at all, then there's nothing to do, and we can return the + * message verbatim (whether newly allocated or not). + */ + if (!encoding) + return msg; + + /* + * Otherwise, we still want to munge the encoding header in the + * result, which will be done by modifying the buffer. If we + * are using a fresh copy, we can reuse it. But if we are using + * the cached copy from commit->buffer, we need to duplicate it + * to avoid munging commit->buffer. + */ + out = msg; + if (out == commit->buffer) + out = xstrdup(out); + } + else { + /* + * There's actual encoding work to do. Do the reencoding, which + * still leaves the header to be replaced in the next step. At + * this point, we are done with msg. If we allocated a fresh + * copy, we can free it. + */ + out = reencode_string(msg, output_encoding, use_encoding); + if (out && msg != commit->buffer) + free(msg); + } + + /* + * This replacement actually consumes the buffer we hand it, so we do + * not have to worry about freeing the old "out" here. + */ if (out) out = replace_encoding_header(out, output_encoding); free(encoding); - return out; + /* + * If the re-encoding failed, out might be NULL here; in that + * case we just return the commit message verbatim. + */ + return out ? out : msg; +} + +void logmsg_free(char *msg, const struct commit *commit) +{ + if (msg != commit->buffer) + free(msg); } static int mailmap_name(const char **email, size_t *email_len, @@ -1278,14 +1331,11 @@ void format_commit_message(const struct commit *commit, context.pretty_ctx = pretty_ctx; context.wrap_start = sb->len; context.message = logmsg_reencode(commit, output_enc); - if (!context.message) - context.message = commit->buffer; strbuf_expand(sb, format, format_commit_item, &context); rewrap_message_tail(sb, &context, 0, 0, 0); - if (context.message != commit->buffer) - free(context.message); + logmsg_free(context.message, commit); free(context.signature.gpg_output); free(context.signature.signer); } @@ -1432,7 +1482,7 @@ void pretty_print_commit(const struct pretty_print_context *pp, { unsigned long beginning_of_body; int indent = 4; - const char *msg = commit->buffer; + const char *msg; char *reencoded; const char *encoding; int need_8bit_cte = pp->need_8bit_cte; @@ -1443,10 +1493,7 @@ void pretty_print_commit(const struct pretty_print_context *pp, } encoding = get_log_output_encoding(); - reencoded = logmsg_reencode(commit, encoding); - if (reencoded) { - msg = reencoded; - } + msg = reencoded = logmsg_reencode(commit, encoding); if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL) indent = 0; @@ -1503,7 +1550,7 @@ void pretty_print_commit(const struct pretty_print_context *pp, if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body) strbuf_addch(sb, '\n'); - free(reencoded); + logmsg_free(reencoded, commit); } void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit, diff --git a/read-cache.c b/read-cache.c index fda78bc353..827ae55c50 100644 --- a/read-cache.c +++ b/read-cache.c @@ -197,21 +197,25 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) } if (ce->ce_mtime.sec != (unsigned int)st->st_mtime) changed |= MTIME_CHANGED; - if (trust_ctime && ce->ce_ctime.sec != (unsigned int)st->st_ctime) + if (trust_ctime && check_stat && + ce->ce_ctime.sec != (unsigned int)st->st_ctime) changed |= CTIME_CHANGED; #ifdef USE_NSEC - if (ce->ce_mtime.nsec != ST_MTIME_NSEC(*st)) + if (check_stat && ce->ce_mtime.nsec != ST_MTIME_NSEC(*st)) changed |= MTIME_CHANGED; - if (trust_ctime && ce->ce_ctime.nsec != ST_CTIME_NSEC(*st)) + if (trust_ctime && check_stat && + ce->ce_ctime.nsec != ST_CTIME_NSEC(*st)) changed |= CTIME_CHANGED; #endif - if (ce->ce_uid != (unsigned int) st->st_uid || - ce->ce_gid != (unsigned int) st->st_gid) - changed |= OWNER_CHANGED; - if (ce->ce_ino != (unsigned int) st->st_ino) - changed |= INODE_CHANGED; + if (check_stat) { + if (ce->ce_uid != (unsigned int) st->st_uid || + ce->ce_gid != (unsigned int) st->st_gid) + changed |= OWNER_CHANGED; + if (ce->ce_ino != (unsigned int) st->st_ino) + changed |= INODE_CHANGED; + } #ifdef USE_STDEV /* @@ -219,8 +223,8 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) * clients will have different views of what "device" * the filesystem is on */ - if (ce->ce_dev != (unsigned int) st->st_dev) - changed |= INODE_CHANGED; + if (check_stat && ce->ce_dev != (unsigned int) st->st_dev) + changed |= INODE_CHANGED; #endif if (ce->ce_size != (unsigned int) st->st_size) @@ -1317,28 +1317,23 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, * passing the --force argument */ - ref->update = - !ref->deletion && - !is_null_sha1(ref->old_sha1); - - if (ref->update) { - ref->nonfastforward = - !has_sha1_file(ref->old_sha1) - || !ref_newer(ref->new_sha1, ref->old_sha1); - - if (!prefixcmp(ref->name, "refs/tags/")) { - ref->requires_force = 1; - if (!force_ref_update) { - ref->status = REF_STATUS_REJECT_ALREADY_EXISTS; - continue; - } - } else if (ref->nonfastforward) { - ref->requires_force = 1; - if (!force_ref_update) { - ref->status = REF_STATUS_REJECT_NONFASTFORWARD; - continue; - } - } + if (!ref->deletion && !is_null_sha1(ref->old_sha1)) { + int why = 0; /* why would this push require --force? */ + + if (!prefixcmp(ref->name, "refs/tags/")) + why = REF_STATUS_REJECT_ALREADY_EXISTS; + else if (!has_sha1_file(ref->old_sha1)) + why = REF_STATUS_REJECT_FETCH_FIRST; + else if (!lookup_commit_reference_gently(ref->old_sha1, 1) || + !lookup_commit_reference_gently(ref->new_sha1, 1)) + why = REF_STATUS_REJECT_NEEDS_FORCE; + else if (!ref_newer(ref->new_sha1, ref->old_sha1)) + why = REF_STATUS_REJECT_NONFASTFORWARD; + + if (!force_ref_update) + ref->status = why; + else if (why) + ref->forced_update = 1; } } } @@ -1532,7 +1527,8 @@ int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1) struct commit_list *list, *used; int found = 0; - /* Both new and old must be commit-ish and new is descendant of + /* + * Both new and old must be commit-ish and new is descendant of * old. Otherwise we require --force. */ o = deref_tag(parse_object(old_sha1), NULL, 0); diff --git a/send-pack.c b/send-pack.c index 1c375f0a28..97ab336097 100644 --- a/send-pack.c +++ b/send-pack.c @@ -230,6 +230,8 @@ int send_pack(struct send_pack_args *args, switch (ref->status) { case REF_STATUS_REJECT_NONFASTFORWARD: case REF_STATUS_REJECT_ALREADY_EXISTS: + case REF_STATUS_REJECT_FETCH_FIRST: + case REF_STATUS_REJECT_NEEDS_FORCE: case REF_STATUS_UPTODATE: continue; default: @@ -66,7 +66,14 @@ int check_filename(const char *prefix, const char *arg) const char *name; struct stat st; - name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg; + if (!prefixcmp(arg, ":/")) { + if (arg[2] == '\0') /* ":/" is root dir, always exists */ + return 1; + name = arg + 2; + } else if (prefix) + name = prefix_filename(prefix, strlen(prefix), arg); + else + name = arg; if (!lstat(name, &st)) return 1; /* file exists */ if (errno == ENOENT || errno == ENOTDIR) @@ -72,8 +72,14 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth, } if (parse_commit(commit)) die("invalid commit"); - commit->object.flags |= not_shallow_flag; cur_depth++; + if (cur_depth >= depth) { + commit_list_insert(commit, &result); + commit->object.flags |= shallow_flag; + commit = NULL; + continue; + } + commit->object.flags |= not_shallow_flag; for (p = commit->parents, commit = NULL; p; p = p->next) { if (!p->item->util) { int *pointer = xmalloc(sizeof(int)); @@ -204,6 +204,54 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...) va_end(ap); } +static void add_lines(struct strbuf *out, + const char *prefix1, + const char *prefix2, + const char *buf, size_t size) +{ + while (size) { + const char *prefix; + const char *next = memchr(buf, '\n', size); + next = next ? (next + 1) : (buf + size); + + prefix = (prefix2 && buf[0] == '\n') ? prefix2 : prefix1; + strbuf_addstr(out, prefix); + strbuf_add(out, buf, next - buf); + size -= next - buf; + buf = next; + } + strbuf_complete_line(out); +} + +void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size) +{ + static char prefix1[3]; + static char prefix2[2]; + + if (prefix1[0] != comment_line_char) { + sprintf(prefix1, "%c ", comment_line_char); + sprintf(prefix2, "%c", comment_line_char); + } + add_lines(out, prefix1, prefix2, buf, size); +} + +void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...) +{ + va_list params; + struct strbuf buf = STRBUF_INIT; + int incomplete_line = sb->len && sb->buf[sb->len - 1] != '\n'; + + va_start(params, fmt); + strbuf_vaddf(&buf, fmt, params); + va_end(params); + + strbuf_add_commented_lines(sb, buf.buf, buf.len); + if (incomplete_line) + sb->buf[--sb->len] = '\0'; + + strbuf_release(&buf); +} + void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap) { int len; @@ -414,15 +462,7 @@ int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint) void strbuf_add_lines(struct strbuf *out, const char *prefix, const char *buf, size_t size) { - while (size) { - const char *next = memchr(buf, '\n', size); - next = next ? (next + 1) : (buf + size); - strbuf_addstr(out, prefix); - strbuf_add(out, buf, next - buf); - size -= next - buf; - buf = next; - } - strbuf_complete_line(out); + add_lines(out, prefix, NULL, buf, size); } void strbuf_addstr_xml_quoted(struct strbuf *buf, const char *s) @@ -110,6 +110,8 @@ extern void strbuf_remove(struct strbuf *, size_t pos, size_t len); extern void strbuf_splice(struct strbuf *, size_t pos, size_t len, const void *, size_t); +extern void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size); + extern void strbuf_add(struct strbuf *, const void *, size_t); static inline void strbuf_addstr(struct strbuf *sb, const char *s) { strbuf_add(sb, s, strlen(s)); @@ -131,6 +133,8 @@ extern void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf * __attribute__((format (printf,2,3))) extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); +__attribute__((format (printf, 2, 3))) +extern void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...); __attribute__((format (printf,2,0))) extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap); diff --git a/submodule.c b/submodule.c index 2f55436234..9ba1496543 100644 --- a/submodule.c +++ b/submodule.c @@ -126,45 +126,44 @@ void gitmodules_config(void) int parse_submodule_config_option(const char *var, const char *value) { - int len; struct string_list_item *config; - struct strbuf submodname = STRBUF_INIT; + const char *name, *key; + int namelen; - var += 10; /* Skip "submodule." */ + if (parse_config_key(var, "submodule", &name, &namelen, &key) < 0 || !name) + return 0; - len = strlen(var); - if ((len > 5) && !strcmp(var + len - 5, ".path")) { - strbuf_add(&submodname, var, len - 5); + if (!strcmp(key, "path")) { config = unsorted_string_list_lookup(&config_name_for_path, value); if (config) free(config->util); else config = string_list_append(&config_name_for_path, xstrdup(value)); - config->util = strbuf_detach(&submodname, NULL); - strbuf_release(&submodname); - } else if ((len > 23) && !strcmp(var + len - 23, ".fetchrecursesubmodules")) { - strbuf_add(&submodname, var, len - 23); - config = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, submodname.buf); + config->util = xmemdupz(name, namelen); + } else if (!strcmp(key, "fetchrecursesubmodules")) { + char *name_cstr = xmemdupz(name, namelen); + config = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, name_cstr); if (!config) - config = string_list_append(&config_fetch_recurse_submodules_for_name, - strbuf_detach(&submodname, NULL)); + config = string_list_append(&config_fetch_recurse_submodules_for_name, name_cstr); + else + free(name_cstr); config->util = (void *)(intptr_t)parse_fetch_recurse_submodules_arg(var, value); - strbuf_release(&submodname); - } else if ((len > 7) && !strcmp(var + len - 7, ".ignore")) { + } else if (!strcmp(key, "ignore")) { + char *name_cstr; + if (strcmp(value, "untracked") && strcmp(value, "dirty") && strcmp(value, "all") && strcmp(value, "none")) { warning("Invalid parameter \"%s\" for config option \"submodule.%s.ignore\"", value, var); return 0; } - strbuf_add(&submodname, var, len - 7); - config = unsorted_string_list_lookup(&config_ignore_for_name, submodname.buf); - if (config) + name_cstr = xmemdupz(name, namelen); + config = unsorted_string_list_lookup(&config_ignore_for_name, name_cstr); + if (config) { free(config->util); - else - config = string_list_append(&config_ignore_for_name, - strbuf_detach(&submodname, NULL)); - strbuf_release(&submodname); + free(name_cstr); + } else + config = string_list_append(&config_ignore_for_name, name_cstr); config->util = xstrdup(value); return 0; } diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh index ccb0a3cb61..a8e84d8546 100755 --- a/t/t0030-stripspace.sh +++ b/t/t0030-stripspace.sh @@ -397,4 +397,39 @@ test_expect_success 'strip comments, too' ' test -z "$(echo "# comment" | git stripspace -s)" ' +test_expect_success 'strip comments with changed comment char' ' + test ! -z "$(echo "; comment" | git -c core.commentchar=";" stripspace)" && + test -z "$(echo "; comment" | git -c core.commentchar=";" stripspace -s)" +' + +test_expect_success '-c with single line' ' + printf "# foo\n" >expect && + printf "foo" | git stripspace -c >actual && + test_cmp expect actual +' + +test_expect_success '-c with single line followed by empty line' ' + printf "# foo\n#\n" >expect && + printf "foo\n\n" | git stripspace -c >actual && + test_cmp expect actual +' + +test_expect_success '-c with newline only' ' + printf "#\n" >expect && + printf "\n" | git stripspace -c >actual && + test_cmp expect actual +' + +test_expect_success '--comment-lines with single line' ' + printf "# foo\n" >expect && + printf "foo" | git stripspace -c >actual && + test_cmp expect actual +' + +test_expect_success '-c with changed comment char' ' + printf "; foo\n" >expect && + printf "foo" | git -c core.commentchar=";" stripspace -c >actual && + test_cmp expect actual +' + test_done diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh index 78816d9d93..05d78d22a6 100755 --- a/t/t0050-filesystem.sh +++ b/t/t0050-filesystem.sh @@ -29,12 +29,10 @@ test_have_prereq SYMLINKS || if test_have_prereq CASE_INSENSITIVE_FS then test_expect_success "detection of case insensitive filesystem during repo init" ' - test $(git config --bool core.ignorecase) = true ' else test_expect_success "detection of case insensitive filesystem during repo init" ' - test_must_fail git config --bool core.ignorecase >/dev/null || test $(git config --bool core.ignorecase) = false ' @@ -43,20 +41,17 @@ fi if test_have_prereq SYMLINKS then test_expect_success "detection of filesystem w/o symlink support during repo init" ' - test_must_fail git config --bool core.symlinks || test "$(git config --bool core.symlinks)" = true ' else test_expect_success "detection of filesystem w/o symlink support during repo init" ' - v=$(git config --bool core.symlinks) && test "$v" = false ' fi test_expect_success "setup case tests" ' - git config core.ignorecase true && touch camelcase && git add camelcase && @@ -67,29 +62,23 @@ test_expect_success "setup case tests" ' git mv tmp CamelCase && git commit -m "rename" && git checkout -f master - ' $test_case 'rename (case change)' ' - git mv camelcase CamelCase && git commit -m "rename" - ' -$test_case 'merge (case change)' ' - +test_expect_success 'merge (case change)' ' rm -f CamelCase && rm -f camelcase && git reset --hard initial && git merge topic - ' -test_expect_failure 'add (with different case)' ' - +test_expect_failure CASE_INSENSITIVE_FS 'add (with different case)' ' git reset --hard initial && rm camelcase && echo 1 >CamelCase && @@ -97,37 +86,30 @@ test_expect_failure 'add (with different case)' ' camel=$(git ls-files | grep -i camelcase) && test $(echo "$camel" | wc -l) = 1 && test "z$(git cat-file blob :$camel)" = z1 - ' test_expect_success "setup unicode normalization tests" ' - - test_create_repo unicode && - cd unicode && - touch "$aumlcdiar" && - git add "$aumlcdiar" && - git commit -m initial && - git tag initial && - git checkout -b topic && - git mv $aumlcdiar tmp && - git mv tmp "$auml" && - git commit -m rename && - git checkout -f master - + test_create_repo unicode && + cd unicode && + touch "$aumlcdiar" && + git add "$aumlcdiar" && + git commit -m initial && + git tag initial && + git checkout -b topic && + git mv $aumlcdiar tmp && + git mv tmp "$auml" && + git commit -m rename && + git checkout -f master ' $test_unicode 'rename (silent unicode normalization)' ' - - git mv "$aumlcdiar" "$auml" && - git commit -m rename - + git mv "$aumlcdiar" "$auml" && + git commit -m rename ' $test_unicode 'merge (silent unicode normalization)' ' - - git reset --hard initial && - git merge topic - + git reset --hard initial && + git merge topic ' test_done diff --git a/t/t4042-diff-textconv-caching.sh b/t/t4042-diff-textconv-caching.sh index 91f8198f05..04a44d5c61 100755 --- a/t/t4042-diff-textconv-caching.sh +++ b/t/t4042-diff-textconv-caching.sh @@ -106,4 +106,12 @@ test_expect_success 'switching diff driver produces correct results' ' test_cmp expect actual ' +# The point here is to test that we can log the notes cache and still use it to +# produce a diff later (older versions of git would segfault on this). It's +# much more likely to come up in the real world with "log --all -p", but using +# --no-walk lets us reliably reproduce the order of traversal. +test_expect_success 'log notes cache and still use cache for -p' ' + git log --no-walk -p refs/notes/textconv/magic HEAD +' + test_done diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh index 2c482b622b..72300b5f24 100755 --- a/t/t4208-log-magic-pathspec.sh +++ b/t/t4208-log-magic-pathspec.sh @@ -11,11 +11,24 @@ test_expect_success 'setup' ' mkdir sub ' -test_expect_success '"git log :/" should be ambiguous' ' - test_must_fail git log :/ 2>error && +test_expect_success '"git log :/" should not be ambiguous' ' + git log :/ +' + +test_expect_success '"git log :/a" should be ambiguous (applied both rev and worktree)' ' + : >a && + test_must_fail git log :/a 2>error && grep ambiguous error ' +test_expect_success '"git log :/a -- " should not be ambiguous' ' + git log :/a -- +' + +test_expect_success '"git log -- :/a" should not be ambiguous' ' + git log -- :/a +' + test_expect_success '"git log :" should be ambiguous' ' test_must_fail git log : 2>error && grep ambiguous error diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index e7c240fc1f..3fbd366ec3 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -212,7 +212,8 @@ test_expect_success 'git-archive --prefix=olde-' ' test_expect_success 'setup tar filters' ' git config tar.tar.foo.command "tr ab ba" && git config tar.bar.command "tr ab ba" && - git config tar.bar.remote true + git config tar.bar.remote true && + git config tar.invalid baz ' test_expect_success 'archive --list mentions user filter' ' diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 6322e8ade8..354d32c584 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -130,16 +130,25 @@ test_expect_success 'single given branch clone' ' test_must_fail git --git-dir=branch-a/.git rev-parse origin/B ' +test_expect_success 'clone shallow depth 1' ' + git clone --no-single-branch --depth 1 "file://$(pwd)/." shallow0 && + test "`git --git-dir=shallow0/.git rev-list --count HEAD`" = 1 +' + test_expect_success 'clone shallow' ' git clone --no-single-branch --depth 2 "file://$(pwd)/." shallow ' +test_expect_success 'clone shallow depth count' ' + test "`git --git-dir=shallow/.git rev-list --count HEAD`" = 2 +' + test_expect_success 'clone shallow object count' ' ( cd shallow && git count-objects -v ) > count.shallow && - grep "^in-pack: 18" count.shallow + grep "^in-pack: 12" count.shallow ' test_expect_success 'clone shallow object count (part 2)' ' @@ -256,12 +265,36 @@ test_expect_success 'additional simple shallow deepenings' ' ) ' +test_expect_success 'clone shallow depth count' ' + test "`git --git-dir=shallow/.git rev-list --count HEAD`" = 11 +' + test_expect_success 'clone shallow object count' ' ( cd shallow && git count-objects -v ) > count.shallow && - grep "^count: 52" count.shallow + grep "^count: 55" count.shallow +' + +test_expect_success 'fetch --no-shallow on full repo' ' + test_must_fail git fetch --noshallow +' + +test_expect_success 'fetch --depth --no-shallow' ' + ( + cd shallow && + test_must_fail git fetch --depth=1 --noshallow + ) +' + +test_expect_success 'turn shallow to complete repository' ' + ( + cd shallow && + git fetch --unshallow && + ! test -f .git/shallow && + git fsck --full + ) ' test_expect_success 'clone shallow without --no-single-branch' ' @@ -273,7 +306,7 @@ test_expect_success 'clone shallow object count' ' cd shallow2 && git count-objects -v ) > count.shallow2 && - grep "^in-pack: 6" count.shallow2 + grep "^in-pack: 3" count.shallow2 ' test_expect_success 'clone shallow with --branch' ' @@ -281,7 +314,7 @@ test_expect_success 'clone shallow with --branch' ' ' test_expect_success 'clone shallow object count' ' - echo "in-pack: 6" > count3.expected && + echo "in-pack: 3" > count3.expected && GIT_DIR=shallow3/.git git count-objects -v | grep "^in-pack" > count3.actual && test_cmp count3.expected count3.actual @@ -310,7 +343,7 @@ EOF GIT_DIR=shallow6/.git git tag -l >taglist.actual && test_cmp taglist.expected taglist.actual && - echo "in-pack: 7" > count6.expected && + echo "in-pack: 4" > count6.expected && GIT_DIR=shallow6/.git git count-objects -v | grep "^in-pack" > count6.actual && test_cmp count6.expected count6.actual @@ -325,7 +358,7 @@ EOF GIT_DIR=shallow7/.git git tag -l >taglist.actual && test_cmp taglist.expected taglist.actual && - echo "in-pack: 7" > count7.expected && + echo "in-pack: 4" > count7.expected && GIT_DIR=shallow7/.git git count-objects -v | grep "^in-pack" > count7.actual && test_cmp count7.expected count7.actual diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index 1fa2a5fc23..df82ec9dda 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -28,7 +28,8 @@ test_expect_success 'creating initial files and commits' ' echo "1st line 2nd file" >secondfile && echo "2nd line 2nd file" >>secondfile && - git commit -a -m "modify 2nd file" + git commit -a -m "modify 2nd file" && + head5=$(git rev-parse --verify HEAD) ' # git log --pretty=oneline # to see those SHA1 involved @@ -56,7 +57,7 @@ test_expect_success 'giving a non existing revision should fail' ' test_must_fail git reset --mixed aaaaaa && test_must_fail git reset --soft aaaaaa && test_must_fail git reset --hard aaaaaa && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + check_changes $head5 ' test_expect_success 'reset --soft with unmerged index should fail' ' @@ -74,7 +75,7 @@ test_expect_success \ test_must_fail git reset --hard -- first && test_must_fail git reset --soft HEAD^ -- first && test_must_fail git reset --hard HEAD^ -- first && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + check_changes $head5 ' test_expect_success 'giving unrecognized options should fail' ' @@ -86,7 +87,7 @@ test_expect_success 'giving unrecognized options should fail' ' test_must_fail git reset --soft -o && test_must_fail git reset --hard --other && test_must_fail git reset --hard -o && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + check_changes $head5 ' test_expect_success \ @@ -110,7 +111,7 @@ test_expect_success \ git checkout master && git branch -D branch1 branch2 && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + check_changes $head5 ' test_expect_success \ @@ -133,27 +134,27 @@ test_expect_success \ git checkout master && git branch -D branch3 branch4 && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + check_changes $head5 ' test_expect_success \ 'resetting to HEAD with no changes should succeed and do nothing' ' git reset --hard && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git reset --hard HEAD && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git reset --soft && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git reset --soft HEAD && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git reset --mixed && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git reset --mixed HEAD && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git reset && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git reset HEAD && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + check_changes $head5 ' >.diff_expect @@ -176,7 +177,7 @@ test_expect_success '--soft reset only should show changes in diff --cached' ' git reset --soft HEAD^ && check_changes d1a4bc3abce4829628ae2dcb0d60ef3d1a78b1c4 && test "$(git rev-parse ORIG_HEAD)" = \ - 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + $head5 ' >.diff_expect @@ -193,7 +194,7 @@ test_expect_success \ git commit -a -C ORIG_HEAD && check_changes 3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d && test "$(git rev-parse ORIG_HEAD)" = \ - 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + $head5 ' >.diff_expect @@ -303,7 +304,7 @@ test_expect_success 'redoing the last two commits should succeed' ' echo "1st line 2nd file" >secondfile && echo "2nd line 2nd file" >>secondfile && git commit -a -m "modify 2nd file" && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + check_changes $head5 ' >.diff_expect @@ -341,15 +342,15 @@ EOF test_expect_success \ '--hard reset to ORIG_HEAD should clear a fast-forward merge' ' git reset --hard HEAD^ && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git pull . branch1 && git reset --hard ORIG_HEAD && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git checkout master && git branch -D branch1 branch2 && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + check_changes $head5 ' cat > expect << EOF diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh index b1c7648386..cbd7a45927 100755 --- a/t/t7502-commit.sh +++ b/t/t7502-commit.sh @@ -517,4 +517,11 @@ use_template="-t template" try_commit_status_combo +test_expect_success 'commit --status with custom comment character' ' + test_when_finished "git config --unset core.commentchar" && + git config core.commentchar ";" && + try_commit --status && + test_i18ngrep "^; Changes to be committed:" .git/COMMIT_EDITMSG +' + test_done diff --git a/t/t7508-status.sh b/t/t7508-status.sh index e313ef196e..a79c032ffd 100755 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh @@ -1254,6 +1254,56 @@ test_expect_success ".git/config ignore=dirty doesn't suppress submodule summary ' cat > expect << EOF +; On branch master +; Changes to be committed: +; (use "git reset HEAD <file>..." to unstage) +; +; modified: sm +; +; Changes not staged for commit: +; (use "git add <file>..." to update what will be committed) +; (use "git checkout -- <file>..." to discard changes in working directory) +; +; modified: dir1/modified +; modified: sm (new commits) +; +; Submodule changes to be committed: +; +; * sm $head...$new_head (1): +; > Add bar +; +; Submodules changed but not updated: +; +; * sm $new_head...$head2 (1): +; > 2nd commit +; +; Untracked files: +; (use "git add <file>..." to include in what will be committed) +; +; .gitmodules +; dir1/untracked +; dir2/modified +; dir2/untracked +; expect +; output +; untracked +EOF + +test_expect_success "status (core.commentchar with submodule summary)" ' + test_when_finished "git config --unset core.commentchar" && + git config core.commentchar ";" && + git status >output && + test_i18ncmp expect output +' + +test_expect_success "status (core.commentchar with two chars with submodule summary)" ' + test_when_finished "git config --unset core.commentchar" && + git config core.commentchar ";;" && + git status >output && + test_i18ncmp expect output +' + +cat > expect << EOF # On branch master # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh index c5ab626699..eeefa67904 100755 --- a/t/t9802-git-p4-filetype.sh +++ b/t/t9802-git-p4-filetype.sh @@ -222,12 +222,13 @@ build_gendouble() { cat >gendouble.py <<-\EOF import sys import struct - import array - s = array.array("c", '\0' * 26) - struct.pack_into(">L", s, 0, 0x00051607) # AppleDouble - struct.pack_into(">L", s, 4, 0x00020000) # version 2 - s.tofile(sys.stdout) + s = struct.pack(">LL18s", + 0x00051607, # AppleDouble + 0x00020000, # version 2 + "" # pad to 26 bytes + ) + sys.stdout.write(s) EOF } diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 3cd53f87fb..adc1372b3c 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -13,6 +13,25 @@ complete () return 0 } +# Be careful when updating this list: +# +# (1) The build tree may have build artifact from different branch, or +# the user's $PATH may have a random executable that may begin +# with "git-check" that are not part of the subcommands this build +# will ship, e.g. "check-ignore". The tests for completion for +# subcommand names tests how "check" is expanded; we limit the +# possible candidates to "checkout" and "check-attr" to make sure +# "check-attr", which is known by the filter function as a +# subcommand to be thrown out, while excluding other random files +# that happen to begin with "check" to avoid letting them get in +# the way. +# +# (2) A test makes sure that common subcommands are included in the +# completion for "git <TAB>", and a plumbing is excluded. "add", +# "filter-branch" and "ls-files" are listed for this. + +GIT_TESTING_COMMAND_COMPLETION='add checkout check-attr filter-branch ls-files' + . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" # We don't need this function to actually join words or do anything special. @@ -196,7 +215,6 @@ test_expect_success 'general options plus command' ' test_completion "git --paginate check" "checkout " && test_completion "git --git-dir=foo check" "checkout " && test_completion "git --bare check" "checkout " && - test_completion "git --help des" "describe " && test_completion "git --exec-path=foo check" "checkout " && test_completion "git --html-path check" "checkout " && test_completion "git --no-pager check" "checkout " && @@ -207,6 +225,11 @@ test_expect_success 'general options plus command' ' test_completion "git --no-replace-objects check" "checkout " ' +test_expect_success 'git --help completion' ' + test_completion "git --help ad" "add " && + test_completion "git --help core" "core-tutorial " +' + test_expect_success 'setup for ref completion' ' echo content >file1 && echo more >file2 && diff --git a/transport-helper.c b/transport-helper.c index 965b778cb3..cb3ef7d38e 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -666,6 +666,16 @@ static void push_update_ref_status(struct strbuf *buf, free(msg); msg = NULL; } + else if (!strcmp(msg, "fetch first")) { + status = REF_STATUS_REJECT_FETCH_FIRST; + free(msg); + msg = NULL; + } + else if (!strcmp(msg, "needs force")) { + status = REF_STATUS_REJECT_NEEDS_FORCE; + free(msg); + msg = NULL; + } } if (*ref) diff --git a/transport.c b/transport.c index 0750a5fbbe..384ff9acf1 100644 --- a/transport.c +++ b/transport.c @@ -659,7 +659,7 @@ static void print_ok_ref_status(struct ref *ref, int porcelain) const char *msg; strcpy(quickref, status_abbrev(ref->old_sha1)); - if (ref->requires_force) { + if (ref->forced_update) { strcat(quickref, "..."); type = '+'; msg = "forced update"; @@ -699,6 +699,14 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i print_ref_status('!', "[rejected]", ref, ref->peer_ref, "already exists", porcelain); break; + case REF_STATUS_REJECT_FETCH_FIRST: + print_ref_status('!', "[rejected]", ref, ref->peer_ref, + "fetch first", porcelain); + break; + case REF_STATUS_REJECT_NEEDS_FORCE: + print_ref_status('!', "[rejected]", ref, ref->peer_ref, + "needs force", porcelain); + break; case REF_STATUS_REMOTE_REJECT: print_ref_status('!', "[remote rejected]", ref, ref->deletion ? NULL : ref->peer_ref, @@ -750,6 +758,10 @@ void transport_print_push_status(const char *dest, struct ref *refs, *reject_reasons |= REJECT_NON_FF_OTHER; } else if (ref->status == REF_STATUS_REJECT_ALREADY_EXISTS) { *reject_reasons |= REJECT_ALREADY_EXISTS; + } else if (ref->status == REF_STATUS_REJECT_FETCH_FIRST) { + *reject_reasons |= REJECT_FETCH_FIRST; + } else if (ref->status == REF_STATUS_REJECT_NEEDS_FORCE) { + *reject_reasons |= REJECT_NEEDS_FORCE; } } } diff --git a/transport.h b/transport.h index ac5a9f57d1..a3450e97c0 100644 --- a/transport.h +++ b/transport.h @@ -144,6 +144,8 @@ void transport_set_verbosity(struct transport *transport, int verbosity, #define REJECT_NON_FF_HEAD 0x01 #define REJECT_NON_FF_OTHER 0x02 #define REJECT_ALREADY_EXISTS 0x04 +#define REJECT_FETCH_FIRST 0x08 +#define REJECT_NEEDS_FORCE 0x10 int transport_push(struct transport *connection, int refspec_nr, const char **refspec, int flags, diff --git a/upload-pack.c b/upload-pack.c index 95d83135ae..7c05b15e68 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -672,10 +672,17 @@ static void receive_needs(void) if (depth == 0 && shallows.nr == 0) return; if (depth > 0) { - struct commit_list *result, *backup; + struct commit_list *result = NULL, *backup = NULL; int i; - backup = result = get_shallow_commits(&want_obj, depth, - SHALLOW, NOT_SHALLOW); + if (depth == INFINITE_DEPTH) + for (i = 0; i < shallows.nr; i++) { + struct object *object = shallows.objects[i].item; + object->flags |= NOT_SHALLOW; + } + else + backup = result = + get_shallow_commits(&want_obj, depth, + SHALLOW, NOT_SHALLOW); while (result) { struct object *object = &result->item->object; if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) { diff --git a/userdiff.c b/userdiff.c index ed958ef6b8..ea43a0306f 100644 --- a/userdiff.c +++ b/userdiff.c @@ -184,35 +184,6 @@ static struct userdiff_driver *userdiff_find_by_namelen(const char *k, int len) return NULL; } -static struct userdiff_driver *parse_driver(const char *var, - const char *value, const char *type) -{ - struct userdiff_driver *drv; - const char *dot; - const char *name; - int namelen; - - if (prefixcmp(var, "diff.")) - return NULL; - dot = strrchr(var, '.'); - if (dot == var + 4) - return NULL; - if (strcmp(type, dot+1)) - return NULL; - - name = var + 5; - namelen = dot - name; - drv = userdiff_find_by_namelen(name, namelen); - if (!drv) { - ALLOC_GROW(drivers, ndrivers+1, drivers_alloc); - drv = &drivers[ndrivers++]; - memset(drv, 0, sizeof(*drv)); - drv->name = xmemdupz(name, namelen); - drv->binary = -1; - } - return drv; -} - static int parse_funcname(struct userdiff_funcname *f, const char *k, const char *v, int cflags) { @@ -240,20 +211,34 @@ static int parse_bool(int *b, const char *k, const char *v) int userdiff_config(const char *k, const char *v) { struct userdiff_driver *drv; + const char *name, *type; + int namelen; + + if (parse_config_key(k, "diff", &name, &namelen, &type) || !name) + return 0; + + drv = userdiff_find_by_namelen(name, namelen); + if (!drv) { + ALLOC_GROW(drivers, ndrivers+1, drivers_alloc); + drv = &drivers[ndrivers++]; + memset(drv, 0, sizeof(*drv)); + drv->name = xmemdupz(name, namelen); + drv->binary = -1; + } - if ((drv = parse_driver(k, v, "funcname"))) + if (!strcmp(type, "funcname")) return parse_funcname(&drv->funcname, k, v, 0); - if ((drv = parse_driver(k, v, "xfuncname"))) + if (!strcmp(type, "xfuncname")) return parse_funcname(&drv->funcname, k, v, REG_EXTENDED); - if ((drv = parse_driver(k, v, "binary"))) + if (!strcmp(type, "binary")) return parse_tristate(&drv->binary, k, v); - if ((drv = parse_driver(k, v, "command"))) + if (!strcmp(type, "command")) return git_config_string(&drv->external, k, v); - if ((drv = parse_driver(k, v, "textconv"))) + if (!strcmp(type, "textconv")) return git_config_string(&drv->textconv, k, v); - if ((drv = parse_driver(k, v, "cachetextconv"))) + if (!strcmp(type, "cachetextconv")) return parse_bool(&drv->textconv_want_cache, k, v); - if ((drv = parse_driver(k, v, "wordregex"))) + if (!strcmp(type, "wordregex")) return git_config_string(&drv->word_regex, k, v); return 0; diff --git a/wt-status.c b/wt-status.c index d7cfe8f31c..aa2734fcbe 100644 --- a/wt-status.c +++ b/wt-status.c @@ -45,7 +45,7 @@ static void status_vprintf(struct wt_status *s, int at_bol, const char *color, strbuf_vaddf(&sb, fmt, ap); if (!sb.len) { - strbuf_addch(&sb, '#'); + strbuf_addch(&sb, comment_line_char); if (!trail) strbuf_addch(&sb, ' '); color_print_strbuf(s->fp, color, &sb); @@ -59,7 +59,7 @@ static void status_vprintf(struct wt_status *s, int at_bol, const char *color, strbuf_reset(&linebuf); if (at_bol) { - strbuf_addch(&linebuf, '#'); + strbuf_addch(&linebuf, comment_line_char); if (*line != '\n' && *line != '\t') strbuf_addch(&linebuf, ' '); } @@ -762,8 +762,10 @@ static void wt_status_print_tracking(struct wt_status *s) for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1) color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), - "# %.*s", (int)(ep - cp), cp); - color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#"); + "%c %.*s", comment_line_char, + (int)(ep - cp), cp); + color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c", + comment_line_char); } static int has_unmerged(struct wt_status *s) |