diff --git a/.gitignore b/.gitignore index dab5bc2a3c..7f8421dcd0 100644 --- a/.gitignore +++ b/.gitignore @@ -41,7 +41,6 @@ git-fetch git-fetch--tool git-fetch-pack git-filter-branch -git-findtags git-fmt-merge-msg git-for-each-ref git-format-patch @@ -59,7 +58,6 @@ git-index-pack git-init git-init-db git-instaweb -git-local-fetch git-log git-lost-found git-ls-files @@ -120,10 +118,6 @@ git-show git-show-branch git-show-index git-show-ref -git-ssh-fetch -git-ssh-pull -git-ssh-push -git-ssh-upload git-stash git-status git-stripspace diff --git a/Documentation/Makefile b/Documentation/Makefile index 76df06cd61..c4486d3d47 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -2,7 +2,7 @@ MAN1_TXT= \ $(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \ $(wildcard git-*.txt)) \ gitk.txt -MAN5_TXT=gitattributes.txt gitignore.txt gitmodules.txt +MAN5_TXT=gitattributes.txt gitignore.txt gitcli.txt gitmodules.txt MAN7_TXT=git.txt MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT) diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes-1.5.4.txt index cd79124958..e6fbcdb2e1 100644 --- a/Documentation/RelNotes-1.5.4.txt +++ b/Documentation/RelNotes-1.5.4.txt @@ -14,28 +14,28 @@ Removal Deprecation notices ------------------- - * Next feature release of git (this change is scheduled for v1.6.0) + * The next feature release of git (this change is scheduled for v1.6.0) will by default install dashed form of commands (e.g. "git-commit") outside of users' normal $PATH, and will install only selected commands ("git" itself, and "gitk") in $PATH. This implies: - - Using dashed form of git commands (e.g. "git-commit") from the + - Using dashed forms of git commands (e.g. "git-commit") from the command line has been informally deprecated since early 2006, but now it officially is, and will be removed in the future. Use - dashless form (e.g. "git commit") instead. + dashless forms (e.g. "git commit") instead. - - Using dashed from from your scripts, without first prepending the + - Using dashed forms from your scripts, without first prepending the return value from "git --exec-path" to the scripts' PATH, has been informally deprecated since early 2006, but now it officially is. - - Use of dashed form with "PATH=$(git --exec-path):$PATH; export + - Use of dashed forms with "PATH=$(git --exec-path):$PATH; export PATH" early in your script is not deprecated with this change. Users are strongly encouraged to adjust their habits and scripts now to prepare for this. * The post-receive hook was introduced in March 2007 to supersede - post-update hook, primarily to overcome the command line length + the post-update hook, primarily to overcome the command line length limitation of the latter. Use of post-update hook will be deprecated in future versions of git, starting from v1.6.0. @@ -43,10 +43,11 @@ Deprecation notices option, and will be removed in the future. * "git peek-remote" is deprecated, as "git ls-remote" was written in C - and works for all transports, and will be removed in the future. + and works for all transports; "git peek-remote" will be removed in + the future. * From v1.6.0, the repack.usedeltabaseoffset config option will default - to true, which will give denser packfile (i.e. more efficient storage). + to true, which will give denser packfiles (i.e. more efficient storage). The downside is that git older than version 1.4.4 will not be able to directly use a repository packed using this setting. @@ -67,7 +68,7 @@ Updates since v1.5.3 * gitk is now merged as a subdirectory of git.git project, in preparation for its i18n. - * progress display from many commands are a lot nicer to the eye. + * progress displays from many commands are a lot nicer to the eye. Transfer commands show throughput data. * many commands that pay attention to per-directory .gitignore now do @@ -76,12 +77,12 @@ Updates since v1.5.3 * Output processing for '--pretty=format:' has been optimized. - * Rename detection of diff family, while detecting exact matches, has + * Rename detection of diff family while detecting exact matches has been greatly optimized. - * Rename detection of diff family tries to make more naturally looking - pairing. Earlier if more than one identical rename sources were - found in the preimage, they were picked pretty much at random. + * Rename detection of diff family tries to make more natural looking + pairing. Earlier, if multiple identical rename sources were + found in the preimage, the source used was picked pretty much at random. * Value "true" for color.diff and color.status configuration used to mean "always" (even when the output is not going to a terminal). @@ -90,6 +91,16 @@ Updates since v1.5.3 * "git diff" Porcelain now respects diff.external configuration, which is another way to specify GIT_EXTERNAL_DIFF. + * "git diff" can be told to use different prefixes other than + "a/" and "b/" e.g. "git diff --src-prefix=l/ --dst-prefix=k/". + + * "git diff" sometimes did not quote paths with funny + characters properly. + + * "git log" (and any revision traversal commands) misbehaved + when --diff-filter is given but was not asked to actually + produce diff. + * HTTP proxy can be specified per remote repository using remote.*.httpproxy configuration, or global http.proxy configuration variable. @@ -114,7 +125,7 @@ Updates since v1.5.3 * "git rebase --interactive" mode can now work on detached HEAD. - * Other minor to serious bugs in "git rebase -i" has been fixed. + * Other minor to serious bugs in "git rebase -i" have been fixed. * "git rebase" now detaches head during its operation, so after a successful "git rebase" operation, the reflog entry branch@{1} for @@ -175,7 +186,9 @@ Updates since v1.5.3 * "git remote" learned "rm" subcommand. - * "git cvsserver" can be run via "git shell". + * "git cvsserver" can be run via "git shell". Also, "cvs" is + recognized as a synonym for "git cvsserver", so that CVS users + can be switched to git just by changing their login shell. * "git cvsserver" acts more like receive-pack by running post-receive and post-update hooks. @@ -205,6 +218,10 @@ Updates since v1.5.3 * "git commit --amend" can amend a merge that does not change the tree from its first parent. + * "git commit" used to unconditionally strip comment lines that + began with '#' and removed excess blank lines. This + behaviour has been made configurable. + * "git commit" has been rewritten in C. * "git stash random-text" does not create a new stash anymore. It was @@ -245,9 +262,12 @@ Updates since v1.5.3 * The format "git show" outputs an annotated tag has been updated to include "Tagger: " and "Date: " lines from the tag itself. Strictly speaking this is a backward incompatible change, but this is a - reasonable usability fix and people's script shouldn't have been + reasonable usability fix and people's scripts shouldn't have been relying on the exact output from "git show" Porcelain anyway. + * "git cvsimport" did not notice errors from underlying "cvsps" + and produced a corrupt import silently. + * "git cvsexportcommit" learned -w option to specify and switch to the CVS working directory. @@ -281,7 +301,7 @@ Updates since v1.5.3 makes copy-and-pasting for git-checkout/git-add/git-rm easier. The traditional behaviour to show the full path relative to the top of the work tree can be had by setting status.relativepaths - configuration variable to true. + configuration variable to false. * "git blame" kept text for each annotated revision in core needlessly; this has been corrected. @@ -324,6 +344,6 @@ series. -- exec >/var/tmp/1 -O=v1.5.4-rc0-85-gdbedf97 +O=v1.5.4-rc1-36-g49e6be5 echo O=`git describe refs/heads/master` git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 9ecc1d7bc4..1a78635fba 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -211,5 +211,14 @@ endif::git-format-patch[] --no-ext-diff:: Disallow external diff drivers. +--src-prefix=:: + Show the given source prefix instead of "a/". + +--dst-prefix=:: + Show the given destination prefix instead of "b/". + +--no-prefix:: + Do not show any source or destination prefix. + For more detailed explanation on these common options, see also link:diffcore.html[diffcore documentation]. diff --git a/Documentation/everyday.txt b/Documentation/everyday.txt index ce7c170d69..f1993e2935 100644 --- a/Documentation/everyday.txt +++ b/Documentation/everyday.txt @@ -30,10 +30,6 @@ Everybody uses these commands to maintain git repositories. * gitlink:git-fsck[1] to check the repository for errors. - * gitlink:git-prune[1] to remove unused objects in the repository. - - * gitlink:git-repack[1] to pack loose objects for efficiency. - * gitlink:git-gc[1] to do common housekeeping tasks such as repack and prune. @@ -45,24 +41,21 @@ Check health and remove cruft.:: ------------ $ git fsck <1> $ git count-objects <2> -$ git repack <3> -$ git gc <4> +$ git gc <3> ------------ + <1> running without `\--full` is usually cheap and assures the repository health reasonably well. <2> check how many loose objects there are and how much disk space is wasted by not repacking. -<3> without `-a` repacks incrementally. repacking every 4-5MB -of loose objects accumulation may be a good rule of thumb. -<4> it is easier to use `git gc` than individual housekeeping commands -such as `prune` and `repack`. This runs `repack -a -d`. +<3> repacks the local repository and performs other housekeeping tasks. Running +without `--prune` is a safe operation even while other ones are in progress. Repack a small project into single pack.:: + ------------ -$ git repack -a -d <1> -$ git prune +$ git gc <1> +$ git gc --prune ------------ + <1> pack all the objects reachable from the refs into one pack, @@ -189,7 +182,7 @@ $ git pull <3> $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <4> $ git pull git://git.kernel.org/pub/.../jgarzik/libata-dev.git ALL <5> $ git reset --hard ORIG_HEAD <6> -$ git prune <7> +$ git gc --prune <7> $ git fetch --tags <8> ------------ + diff --git a/Documentation/git-check-attr.txt b/Documentation/git-check-attr.txt index 856d2af2ed..47cb1bdfa3 100644 --- a/Documentation/git-check-attr.txt +++ b/Documentation/git-check-attr.txt @@ -23,6 +23,11 @@ OPTIONS be treated as an attribute. +SEE ALSO +-------- +gitlink:gitattributes[5]. + + Author ------ Written by Junio C Hamano diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 4261384158..96383b6543 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -11,7 +11,7 @@ SYNOPSIS 'git-commit' [-a | --interactive] [-s] [-v] [-u] [(-c | -C) | -F | -m | --amend] [--allow-empty] [--no-verify] [-e] [--author ] - [--] [[-i | -o ]...] + [--cleanup=] [--] [[-i | -o ]...] DESCRIPTION ----------- @@ -95,6 +95,16 @@ OPTIONS from making such a commit. This option bypasses the safety, and is primarily for use by foreign scm interface scripts. +--cleanup=:: + This option sets how the commit message is cleaned up. + The '' can be one of 'verbatim', 'whitespace', 'strip', + and 'default'. The 'default' mode will strip leading and + trailing empty lines and #commentary from the commit message + only if the message is to be edited. Otherwise only whitespace + removed. The 'verbatim' mode does not change message at all, + 'whitespace' removes just leading/trailing whitespace lines + and 'strip' removes both whitespace and commentary. + -e|--edit:: The message taken from file with `-F`, command line with `-m`, and from file with `-C` are usually used as the diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index 5920d1799a..d71e51ad46 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -43,8 +43,12 @@ include::diff-options.txt[] --first-parent:: Follow only the first parent commit upon seeing a merge - commit. This option gives a better overview of the - evolution of a particular branch. + commit. This option can give a better overview when + viewing the evolution of a particular topic branch, + because merges into a topic branch tend to be only about + adjusting to updated upstream from time to time, and + this option allows you to ignore the individual commits + brought in to your history by such a merge. -g, \--walk-reflogs:: Show commits as they were recorded in the reflog. The log contains diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index 2ec0c0d270..d0c3aa21d2 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -105,7 +105,8 @@ OPTIONS -v:: Similar to `-t`, but use lowercase letters for files - that are marked as 'always matching index'. + that are marked as 'assume unchanged' (see + gitlink:git-update-index[1]). --full-name:: When run from a subdirectory, the command usually diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index eabd7ef33f..1521a9ee0f 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -163,7 +163,8 @@ After seeing a conflict, you can do two things: SEE ALSO -------- -gitlink:git-fmt-merge-msg[1], gitlink:git-pull[1] +gitlink:git-fmt-merge-msg[1], gitlink:git-pull[1], +gitlink:gitattributes[5] Author diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index a03f9fe5fa..438dae02e2 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -15,6 +15,7 @@ SYNOPSIS [ \--min-age=timestamp ] [ \--sparse ] [ \--no-merges ] + [ \--first-parent ] [ \--remove-empty ] [ \--full-history ] [ \--not ] @@ -256,6 +257,15 @@ limiting may be applied. Do not print commits with more than one parent. +--first-parent:: + Follow only the first parent commit upon seeing a merge + commit. This option can give a better overview when + viewing the evolution of a particular topic branch, + because merges into a topic branch tend to be only about + adjusting to updated upstream from time to time, and + this option allows you to ignore the individual commits + brought in to your history by such a merge. + --not:: Reverses the meaning of the '{caret}' prefix (or lack thereof) diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt index e14720b73f..36510a8451 100644 --- a/Documentation/git-shortlog.txt +++ b/Documentation/git-shortlog.txt @@ -38,15 +38,16 @@ OPTIONS FILES ----- -.mailmap:: - If this file exists, it will be used for mapping author email - addresses to a real author name. One mapping per line, first - the author name followed by the email address enclosed by - '<' and '>'. Use hash '#' for comments. Example: +If the file `.mailmap` exists, it will be used for mapping author +email addresses to a real author name. One mapping per line, first +the author name followed by the email address enclosed by +'<' and '>'. Use hash '#' for comments. Example: - # Keep alphabetized - Adam Morrow - Eve Jones +------------ +# Keep alphabetized +Adam Morrow +Eve Jones +------------ Author ------ diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 71c7ad76d5..cc9c7c52c0 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -322,12 +322,43 @@ String:: requested with "binary". +Built-in merge drivers +^^^^^^^^^^^^^^^^^^^^^^ + +There are a few built-in low-level merge drivers defined that +can be asked for via the `merge` attribute. + +text:: + + Usual 3-way file level merge for text files. Conflicted + regions are marked with conflict markers `<<<<<<<`, + `=======` and `>>>>>>>`. The version from your branch + appears before the `=======` marker, and the version + from the merged branch appears after the `=======` + marker. + +binary:: + + Keep the version from your branch in the work tree, but + leave the path in the conflicted state for the user to + sort out. + +union:: + + Run 3-way file level merge for text files, but take + lines from both versions, instead of leaving conflict + markers. This tends to leave the added lines in the + resulting file in random order and the user should + verify the result. Do not use this if you do not + understand the implications. + + Defining a custom merge driver ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The definition of a merge driver is done in `gitconfig` not -`gitattributes` file, so strictly speaking this manual page is a -wrong place to talk about it. However... +The definition of a merge driver is done in the `.git/config` +file, not in the `gitattributes` file, so strictly speaking this +manual page is a wrong place to talk about it. However... To define a custom merge driver `filfre`, add a section to your `$GIT_DIR/config` file (or `$HOME/.gitconfig` file) like this: diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt new file mode 100644 index 0000000000..b7dcf9ca10 --- /dev/null +++ b/Documentation/gitcli.txt @@ -0,0 +1,113 @@ +gitcli(5) +========= + +NAME +---- +gitcli - git command line interface and conventions + +SYNOPSIS +-------- +gitcli + + +DESCRIPTION +----------- + +This manual describes best practice in how to use git CLI. Here are +the rules that you should follow when you are scripting git: + + * it's preferred to use the non dashed form of git commands, which means that + you should prefer `"git foo"` to `"git-foo"`. + + * splitting short options to separate words (prefer `"git foo -a -b"` + to `"git foo -ab"`, the latter may not even work). + + * when a command line option takes an argument, use the 'sticked' form. In + other words, write `"git foo -oArg"` instead of `"git foo -o Arg"` for short + options, and `"git foo --long-opt=Arg"` instead of `"git foo --long-opt Arg"` + for long options. An option that takes optional option-argument must be + written in the 'sticked' form. + + * when you give a revision parameter to a command, make sure the parameter is + not ambiguous with a name of a file in the work tree. E.g. do not write + `"git log -1 HEAD"` but write `"git log -1 HEAD --"`; the former will not work + if you happen to have a file called `HEAD` in the work tree. + + +ENHANCED CLI +------------ +From the git 1.5.4 series and further, many git commands (not all of them at the +time of the writing though) come with an enhanced option parser. + +Here is an exhaustive list of the facilities provided by this option parser. + + +Magic Options +~~~~~~~~~~~~~ +Commands which have the enhanced option parser activated all understand a +couple of magic command line options: + +-h:: + gives a pretty printed usage of the command. ++ +--------------------------------------------- +$ git describe -h +usage: git-describe [options] * + + --contains find the tag that comes after the commit + --debug debug search strategy on stderr + --all use any ref in .git/refs + --tags use any tag in .git/refs/tags + --abbrev [] use digits to display SHA-1s + --candidates consider most recent tags (default: 10) +--------------------------------------------- + +--help-all:: + Some git commands take options that are only used for plumbing or that + are deprecated, and such options are hidden from the default usage. This + option gives the full list of options. + + +Negating options +~~~~~~~~~~~~~~~~ +Options with long option names can be negated by prefixing `"--no-"`. For +example, `"git branch"` has the option `"--track"` which is 'on' by default. You +can use `"--no-track"` to override that behaviour. The same goes for `"--color"` +and `"--no-color"`. + + +Aggregating short options +~~~~~~~~~~~~~~~~~~~~~~~~~ +Commands that support the enhanced option parser allow you to aggregate short +options. This means that you can for example use `"git rm -rf"` or +`"git clean -fdx"`. + + +Separating argument from the option +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +You can write the mandatory option parameter to an option as a separate +word on the command line. That means that all the following uses work: + +---------------------------- +$ git foo --long-opt=Arg +$ git foo --long-opt Arg +$ git foo -oArg +$ git foo -o Arg +---------------------------- + +However, this is *NOT* allowed for switches with an optionnal value, where the +'sticked' form must be used: +---------------------------- +$ git describe --abbrev HEAD # correct +$ git describe --abbrev=10 HEAD # correct +$ git describe --abbrev 10 HEAD # NOT WHAT YOU MEANT +---------------------------- + + +Documentation +------------- +Documentation by Pierre Habouzit. + +GIT +--- +Part of the gitlink:git[7] suite diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index ad7e056620..817df1798c 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.4-rc1.GIT +DEF_VER=v1.5.4-rc2.GIT LF=' ' diff --git a/Makefile b/Makefile index dce9d6fc89..0029b5032a 100644 --- a/Makefile +++ b/Makefile @@ -1206,6 +1206,7 @@ check-docs:: documented,gitattributes | \ documented,gitignore | \ documented,gitmodules | \ + documented,gitcli | \ documented,git-tools | \ sentinel,not,matching,is,ok ) continue ;; \ esac; \ diff --git a/builtin-commit.c b/builtin-commit.c index 0a9101324f..73f1e3576a 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -47,8 +47,21 @@ static char *logfile, *force_author, *template_file; static char *edit_message, *use_message; static int all, edit_flag, also, interactive, only, amend, signoff; static int quiet, verbose, untracked_files, no_verify, allow_empty; +/* + * The default commit message cleanup mode will remove the lines + * beginning with # (shell comments) and leading and trailing + * whitespaces (empty lines or containing only whitespaces) + * if editor is used, and only the whitespaces if the message + * is specified explicitly. + */ +static enum { + CLEANUP_SPACE, + CLEANUP_NONE, + CLEANUP_ALL, +} cleanup_mode; +static char *cleanup_arg; -static int no_edit, initial_commit, in_merge; +static int use_editor = 1, initial_commit, in_merge; const char *only_include_assumed; struct strbuf message; @@ -88,6 +101,7 @@ static struct option builtin_commit_options[] = { OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"), OPT_BOOLEAN(0, "untracked-files", &untracked_files, "show all untracked files"), OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"), + OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"), OPT_END() }; @@ -346,7 +360,8 @@ static int prepare_log_message(const char *index_file, const char *prefix) if (fp == NULL) die("could not open %s", git_path(commit_editmsg)); - stripspace(&sb, 0); + if (cleanup_mode != CLEANUP_NONE) + stripspace(&sb, 0); if (signoff) { struct strbuf sob; @@ -372,21 +387,25 @@ static int prepare_log_message(const char *index_file, const char *prefix) strbuf_release(&sb); - if (no_edit) { + if (!use_editor) { struct rev_info rev; - unsigned char sha1[40]; + unsigned char sha1[20]; + const char *parent = "HEAD"; fclose(fp); if (!active_nr && read_cache() < 0) die("Cannot read index"); - if (get_sha1("HEAD", sha1) != 0) + if (amend) + parent = "HEAD^1"; + + if (get_sha1(parent, sha1)) return !!active_nr; init_revisions(&rev, ""); rev.abbrev = 0; - setup_revisions(0, NULL, &rev, "HEAD"); + setup_revisions(0, NULL, &rev, parent); DIFF_OPT_SET(&rev.diffopt, QUIET); DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS); run_diff_index(&rev, 1 /* cached */); @@ -394,7 +413,7 @@ static int prepare_log_message(const char *index_file, const char *prefix) return !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES); } - if (in_merge && !no_edit) + if (in_merge) fprintf(fp, "#\n" "# It looks like you may be committing a MERGE.\n" @@ -407,7 +426,12 @@ static int prepare_log_message(const char *index_file, const char *prefix) fprintf(fp, "\n" "# Please enter the commit message for your changes.\n" - "# (Comment lines starting with '#' will not be included)\n"); + "# (Comment lines starting with '#' will "); + if (cleanup_mode == CLEANUP_ALL) + fprintf(fp, "not be included)\n"); + else /* CLEANUP_SPACE, that is. */ + fprintf(fp, "be kept.\n" + "# You can remove them yourself if you want to)\n"); if (only_include_assumed) fprintf(fp, "# %s\n", only_include_assumed); @@ -431,10 +455,13 @@ static int message_is_empty(struct strbuf *sb, int start) const char *nl; int eol, i; + if (cleanup_mode == CLEANUP_NONE && sb->len) + return 0; + /* See if the template is just a prefix of the message. */ strbuf_init(&tmpl, 0); if (template_file && strbuf_read_file(&tmpl, template_file, 0) > 0) { - stripspace(&tmpl, 1); + stripspace(&tmpl, cleanup_mode == CLEANUP_ALL); if (start + tmpl.len <= sb->len && memcmp(tmpl.buf, sb->buf + start, tmpl.len) == 0) start += tmpl.len; @@ -509,9 +536,9 @@ static int parse_and_validate_options(int argc, const char *argv[], argc = parse_options(argc, argv, builtin_commit_options, usage, 0); if (logfile || message.len || use_message) - no_edit = 1; + use_editor = 0; if (edit_flag) - no_edit = 0; + use_editor = 1; if (get_sha1("HEAD", head_sha1)) initial_commit = 1; @@ -587,6 +614,16 @@ static int parse_and_validate_options(int argc, const char *argv[], only_include_assumed = "Explicit paths specified without -i nor -o; assuming --only paths..."; also = 0; } + if (!cleanup_arg || !strcmp(cleanup_arg, "default")) + cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE; + else if (!strcmp(cleanup_arg, "verbatim")) + cleanup_mode = CLEANUP_NONE; + else if (!strcmp(cleanup_arg, "whitespace")) + cleanup_mode = CLEANUP_SPACE; + else if (!strcmp(cleanup_arg, "strip")) + cleanup_mode = CLEANUP_ALL; + else + die("Invalid cleanup mode %s", cleanup_arg); if (all && argc > 0) die("Paths with -a does not make sense."); @@ -792,7 +829,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) /* Get the commit message and validate it */ header_len = sb.len; - if (!no_edit) { + if (use_editor) { char index[PATH_MAX]; const char *env[2] = { index, NULL }; snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); @@ -813,7 +850,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) if (p != NULL) strbuf_setlen(&sb, p - sb.buf + 1); - stripspace(&sb, 1); + if (cleanup_mode != CLEANUP_NONE) + stripspace(&sb, cleanup_mode == CLEANUP_ALL); if (sb.len < header_len || message_is_empty(&sb, header_len)) { rollback_index_files(); die("no commit message? aborting commit."); diff --git a/builtin-tag.c b/builtin-tag.c index 274901a408..03e70155fc 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -16,7 +16,7 @@ static const char * const git_tag_usage[] = { "git-tag [-a|-s|-u ] [-f] [-m |-F ] []", "git-tag -d ...", - "git-tag [-n []] -l []", + "git-tag -l [-n []] []", "git-tag -v ...", NULL }; @@ -47,7 +47,19 @@ void launch_editor(const char *path, struct strbuf *buffer, const char *const *e editor = "vi"; if (strcmp(editor, ":")) { - const char *args[] = { editor, path, NULL }; + size_t len = strlen(editor); + int i = 0; + const char *args[6]; + + if (strcspn(editor, "$ \t'") != len) { + /* there are specials */ + args[i++] = "sh"; + args[i++] = "-c"; + args[i++] = "$0 \"$@\""; + } + args[i++] = editor; + args[i++] = path; + args[i] = NULL; if (run_command_v_opt_cd_env(args, 0, NULL, env)) die("There was a problem with the editor %s.", editor); @@ -370,13 +382,11 @@ int cmd_tag(int argc, const char **argv, const char *prefix) struct ref_lock *lock; int annotate = 0, sign = 0, force = 0, lines = 0, - delete = 0, verify = 0; - char *list = NULL, *msgfile = NULL, *keyid = NULL; - const char *no_pattern = "NO_PATTERN"; + list = 0, delete = 0, verify = 0; + char *msgfile = NULL, *keyid = NULL; struct msg_arg msg = { 0, STRBUF_INIT }; struct option options[] = { - { OPTION_STRING, 'l', NULL, &list, "pattern", "list tag names", - PARSE_OPT_OPTARG, NULL, (intptr_t) no_pattern }, + OPT_BOOLEAN('l', NULL, &list, "list tag names"), { OPTION_INTEGER, 'n', NULL, &lines, NULL, "print n lines of each tag message", PARSE_OPT_OPTARG, NULL, 1 }, @@ -408,7 +418,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) annotate = 1; if (list) - return list_tags(list == no_pattern ? NULL : list, lines); + return list_tags(argv[0], lines); if (delete) return for_each_tag_name(argv, delete_tag); if (verify) diff --git a/combine-diff.c b/combine-diff.c index e22db89932..0e19cbaacc 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -646,12 +646,19 @@ static void reuse_combine_diff(struct sline *sline, unsigned long cnt, sline->p_lno[i] = sline->p_lno[j]; } -static void dump_quoted_path(const char *prefix, const char *path, +static void dump_quoted_path(const char *head, + const char *prefix, + const char *path, const char *c_meta, const char *c_reset) { - printf("%s%s", c_meta, prefix); - quote_c_style(path, NULL, stdout, 0); - printf("%s\n", c_reset); + static struct strbuf buf = STRBUF_INIT; + + strbuf_reset(&buf); + strbuf_addstr(&buf, c_meta); + strbuf_addstr(&buf, head); + quote_two_c_style(&buf, prefix, path, 0); + strbuf_addstr(&buf, c_reset); + puts(buf.buf); } static void show_patch_diff(struct combine_diff_path *elem, int num_parent, @@ -793,7 +800,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent, if (rev->loginfo && !rev->no_commit_id) show_log(rev, opt->msg_sep); dump_quoted_path(dense ? "diff --cc " : "diff --combined ", - elem->path, c_meta, c_reset); + "", elem->path, c_meta, c_reset); printf("%sindex ", c_meta); for (i = 0; i < num_parent; i++) { abb = find_unique_abbrev(elem->parent[i].sha1, @@ -829,14 +836,19 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent, printf("%s\n", c_reset); } if (added) - dump_quoted_path("--- /dev/", "null", c_meta, c_reset); + dump_quoted_path("--- ", "", "/dev/null", + c_meta, c_reset); else - dump_quoted_path("--- a/", elem->path, c_meta, c_reset); + dump_quoted_path("--- ", opt->a_prefix, elem->path, + c_meta, c_reset); if (deleted) - dump_quoted_path("+++ /dev/", "null", c_meta, c_reset); + dump_quoted_path("+++ ", "", "/dev/null", + c_meta, c_reset); else - dump_quoted_path("+++ b/", elem->path, c_meta, c_reset); - dump_sline(sline, cnt, num_parent, DIFF_OPT_TST(opt, COLOR_DIFF)); + dump_quoted_path("+++ ", opt->b_prefix, elem->path, + c_meta, c_reset); + dump_sline(sline, cnt, num_parent, + DIFF_OPT_TST(opt, COLOR_DIFF)); } free(result); diff --git a/config.c b/config.c index 9a5c5470cc..80db92990a 100644 --- a/config.c +++ b/config.c @@ -234,17 +234,23 @@ static int git_parse_file(config_fn_t fn) die("bad config file line %d in %s", config_linenr, config_file_name); } -static unsigned long get_unit_factor(const char *end) +static int parse_unit_factor(const char *end, unsigned long *val) { if (!*end) return 1; - else if (!strcasecmp(end, "k")) - return 1024; - else if (!strcasecmp(end, "m")) - return 1024 * 1024; - else if (!strcasecmp(end, "g")) - return 1024 * 1024 * 1024; - die("unknown unit: '%s'", end); + else if (!strcasecmp(end, "k")) { + *val *= 1024; + return 1; + } + else if (!strcasecmp(end, "m")) { + *val *= 1024 * 1024; + return 1; + } + else if (!strcasecmp(end, "g")) { + *val *= 1024 * 1024 * 1024; + return 1; + } + return 0; } int git_parse_long(const char *value, long *ret) @@ -252,7 +258,10 @@ int git_parse_long(const char *value, long *ret) if (value && *value) { char *end; long val = strtol(value, &end, 0); - *ret = val * get_unit_factor(end); + unsigned long factor = 1; + if (!parse_unit_factor(end, &factor)) + return 0; + *ret = val * factor; return 1; } return 0; @@ -263,7 +272,9 @@ int git_parse_ulong(const char *value, unsigned long *ret) if (value && *value) { char *end; unsigned long val = strtoul(value, &end, 0); - *ret = val * get_unit_factor(end); + if (!parse_unit_factor(end, &val)) + return 0; + *ret = val; return 1; } return 0; diff --git a/contrib/examples/git-revert.sh b/contrib/examples/git-revert.sh new file mode 100755 index 0000000000..49f00321b2 --- /dev/null +++ b/contrib/examples/git-revert.sh @@ -0,0 +1,197 @@ +#!/bin/sh +# +# Copyright (c) 2005 Linus Torvalds +# Copyright (c) 2005 Junio C Hamano +# + +case "$0" in +*-revert* ) + test -t 0 && edit=-e + replay= + me=revert + USAGE='[--edit | --no-edit] [-n] ' ;; +*-cherry-pick* ) + replay=t + edit= + me=cherry-pick + USAGE='[--edit] [-n] [-r] [-x] ' ;; +* ) + echo >&2 "What are you talking about?" + exit 1 ;; +esac + +SUBDIRECTORY_OK=Yes ;# we will cd up +. git-sh-setup +require_work_tree +cd_to_toplevel + +no_commit= +while case "$#" in 0) break ;; esac +do + case "$1" in + -n|--n|--no|--no-|--no-c|--no-co|--no-com|--no-comm|\ + --no-commi|--no-commit) + no_commit=t + ;; + -e|--e|--ed|--edi|--edit) + edit=-e + ;; + --n|--no|--no-|--no-e|--no-ed|--no-edi|--no-edit) + edit= + ;; + -r) + : no-op ;; + -x|--i-really-want-to-expose-my-private-commit-object-name) + replay= + ;; + -*) + usage + ;; + *) + break + ;; + esac + shift +done + +set_reflog_action "$me" + +test "$me,$replay" = "revert,t" && usage + +case "$no_commit" in +t) + # We do not intend to commit immediately. We just want to + # merge the differences in. + head=$(git-write-tree) || + die "Your index file is unmerged." + ;; +*) + head=$(git-rev-parse --verify HEAD) || + die "You do not have a valid HEAD" + files=$(git-diff-index --cached --name-only $head) || exit + if [ "$files" ]; then + die "Dirty index: cannot $me (dirty: $files)" + fi + ;; +esac + +rev=$(git-rev-parse --verify "$@") && +commit=$(git-rev-parse --verify "$rev^0") || + die "Not a single commit $@" +prev=$(git-rev-parse --verify "$commit^1" 2>/dev/null) || + die "Cannot run $me a root commit" +git-rev-parse --verify "$commit^2" >/dev/null 2>&1 && + die "Cannot run $me a multi-parent commit." + +encoding=$(git config i18n.commitencoding || echo UTF-8) + +# "commit" is an existing commit. We would want to apply +# the difference it introduces since its first parent "prev" +# on top of the current HEAD if we are cherry-pick. Or the +# reverse of it if we are revert. + +case "$me" in +revert) + git show -s --pretty=oneline --encoding="$encoding" $commit | + sed -e ' + s/^[^ ]* /Revert "/ + s/$/"/ + ' + echo + echo "This reverts commit $commit." + test "$rev" = "$commit" || + echo "(original 'git revert' arguments: $@)" + base=$commit next=$prev + ;; + +cherry-pick) + pick_author_script=' + /^author /{ + s/'\''/'\''\\'\'\''/g + h + s/^author \([^<]*\) <[^>]*> .*$/\1/ + s/'\''/'\''\'\'\''/g + s/.*/GIT_AUTHOR_NAME='\''&'\''/p + + g + s/^author [^<]* <\([^>]*\)> .*$/\1/ + s/'\''/'\''\'\'\''/g + s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p + + g + s/^author [^<]* <[^>]*> \(.*\)$/\1/ + s/'\''/'\''\'\'\''/g + s/.*/GIT_AUTHOR_DATE='\''&'\''/p + + q + }' + + logmsg=`git show -s --pretty=raw --encoding="$encoding" "$commit"` + set_author_env=`echo "$logmsg" | + LANG=C LC_ALL=C sed -ne "$pick_author_script"` + eval "$set_author_env" + export GIT_AUTHOR_NAME + export GIT_AUTHOR_EMAIL + export GIT_AUTHOR_DATE + + echo "$logmsg" | + sed -e '1,/^$/d' -e 's/^ //' + case "$replay" in + '') + echo "(cherry picked from commit $commit)" + test "$rev" = "$commit" || + echo "(original 'git cherry-pick' arguments: $@)" + ;; + esac + base=$prev next=$commit + ;; + +esac >.msg + +eval GITHEAD_$head=HEAD +eval GITHEAD_$next='`git show -s \ + --pretty=oneline --encoding="$encoding" "$commit" | + sed -e "s/^[^ ]* //"`' +export GITHEAD_$head GITHEAD_$next + +# This three way merge is an interesting one. We are at +# $head, and would want to apply the change between $commit +# and $prev on top of us (when reverting), or the change between +# $prev and $commit on top of us (when cherry-picking or replaying). + +git-merge-recursive $base -- $head $next && +result=$(git-write-tree 2>/dev/null) || { + mv -f .msg "$GIT_DIR/MERGE_MSG" + { + echo ' +Conflicts: +' + git ls-files --unmerged | + sed -e 's/^[^ ]* / /' | + uniq + } >>"$GIT_DIR/MERGE_MSG" + echo >&2 "Automatic $me failed. After resolving the conflicts," + echo >&2 "mark the corrected paths with 'git-add '" + echo >&2 "and commit the result." + case "$me" in + cherry-pick) + echo >&2 "You may choose to use the following when making" + echo >&2 "the commit:" + echo >&2 "$set_author_env" + esac + exit 1 +} +echo >&2 "Finished one $me." + +# If we are cherry-pick, and if the merge did not result in +# hand-editing, we will hit this commit and inherit the original +# author date and name. +# If we are revert, or if our cherry-pick results in a hand merge, +# we had better say that the current user is responsible for that. + +case "$no_commit" in +'') + git-commit -n -F .msg $edit + rm -f .msg + ;; +esac diff --git a/diff.c b/diff.c index e26584cdfc..5bdc111378 100644 --- a/diff.c +++ b/diff.c @@ -290,28 +290,35 @@ static void emit_rewrite_diff(const char *name_a, const char *name_b, struct diff_filespec *one, struct diff_filespec *two, - int color_diff) + struct diff_options *o) { int lc_a, lc_b; + int color_diff = DIFF_OPT_TST(o, COLOR_DIFF); const char *name_a_tab, *name_b_tab; const char *metainfo = diff_get_color(color_diff, DIFF_METAINFO); const char *fraginfo = diff_get_color(color_diff, DIFF_FRAGINFO); const char *old = diff_get_color(color_diff, DIFF_FILE_OLD); const char *new = diff_get_color(color_diff, DIFF_FILE_NEW); const char *reset = diff_get_color(color_diff, DIFF_RESET); + static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT; name_a += (*name_a == '/'); name_b += (*name_b == '/'); name_a_tab = strchr(name_a, ' ') ? "\t" : ""; name_b_tab = strchr(name_b, ' ') ? "\t" : ""; + strbuf_reset(&a_name); + strbuf_reset(&b_name); + quote_two_c_style(&a_name, o->a_prefix, name_a, 0); + quote_two_c_style(&b_name, o->b_prefix, name_b, 0); + diff_populate_filespec(one, 0); diff_populate_filespec(two, 0); lc_a = count_lines(one->data, one->size); lc_b = count_lines(two->data, two->size); - printf("%s--- a/%s%s%s\n%s+++ b/%s%s%s\n%s@@ -", - metainfo, name_a, name_a_tab, reset, - metainfo, name_b, name_b_tab, reset, fraginfo); + printf("%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -", + metainfo, a_name.buf, name_a_tab, reset, + metainfo, b_name.buf, name_b_tab, reset, fraginfo); print_line_count(lc_a); printf(" +"); print_line_count(lc_b); @@ -1212,8 +1219,8 @@ static void builtin_diff(const char *name_a, const char *set = diff_get_color_opt(o, DIFF_METAINFO); const char *reset = diff_get_color_opt(o, DIFF_RESET); - a_one = quote_two("a/", name_a + (*name_a == '/')); - b_two = quote_two("b/", name_b + (*name_b == '/')); + a_one = quote_two(o->a_prefix, name_a + (*name_a == '/')); + b_two = quote_two(o->b_prefix, name_b + (*name_b == '/')); lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null"; lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null"; printf("%sdiff --git %s %s%s\n", set, a_one, b_two, reset); @@ -1242,8 +1249,7 @@ static void builtin_diff(const char *name_a, if ((one->mode ^ two->mode) & S_IFMT) goto free_ab_and_return; if (complete_rewrite) { - emit_rewrite_diff(name_a, name_b, one, two, - DIFF_OPT_TST(o, COLOR_DIFF)); + emit_rewrite_diff(name_a, name_b, one, two, o); o->found_changes = 1; goto free_ab_and_return; } @@ -2020,6 +2026,9 @@ void diff_setup(struct diff_options *options) else DIFF_OPT_CLR(options, COLOR_DIFF); options->detect_rename = diff_detect_rename_default; + + options->a_prefix = "a/"; + options->b_prefix = "b/"; } int diff_setup_done(struct diff_options *options) @@ -2291,6 +2300,12 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (40 < options->abbrev) options->abbrev = 40; } + else if (!prefixcmp(arg, "--src-prefix=")) + options->a_prefix = arg + 13; + else if (!prefixcmp(arg, "--dst-prefix=")) + options->b_prefix = arg + 13; + else if (!strcmp(arg, "--no-prefix")) + options->a_prefix = options->b_prefix = ""; else return 0; return 1; diff --git a/diff.h b/diff.h index 7e8000a5d7..beccf85962 100644 --- a/diff.h +++ b/diff.h @@ -69,6 +69,7 @@ struct diff_options { const char *orderfile; const char *pickaxe; const char *single_follow; + const char *a_prefix, *b_prefix; unsigned flags; int context; int break_opt; diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 92648f40c9..6d8ff93f5f 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -632,6 +632,7 @@ unless ($opt_P) { print $cvspsfh $_; } close CVSPS; + $? == 0 or die "git-cvsimport: fatal: cvsps reported error\n"; close $cvspsfh; } else { $cvspsfile = $opt_P; @@ -848,7 +849,7 @@ while () { } if (!$opt_a && $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) { # skip if the commit is too recent - # that the cvsps default fuzz is 300s, we give ourselves another + # given that the cvsps default fuzz is 300s, we give ourselves another # 300s just in case -- this also prevents skipping commits # due to server clock drift print "skip patchset $patchset: $date too recent\n" if $opt_v; diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 47581ced5a..090c3e5143 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -372,8 +372,10 @@ do test ! -f "$DOTEST"/amend || git reset --soft HEAD^ } && export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE && - git commit --no-verify -F "$DOTEST"/message -e || + if ! git commit --no-verify -F "$DOTEST"/message -e + then die "Could not commit staged changes." + fi require_clean_work_tree do_rest diff --git a/git-send-email.perl b/git-send-email.perl index 248d035088..e47994afc4 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -400,7 +400,7 @@ EOT close(C); my $editor = $ENV{GIT_EDITOR} || $repo->config("core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi"; - system($editor, $compose_filename); + system('sh', '-c', '$0 $@', $editor, $compose_filename); open(C2,">",$compose_filename . ".final") or die "Failed to open $compose_filename.final : " . $!; diff --git a/git-sh-setup.sh b/git-sh-setup.sh index 55abe8ebee..92817959d0 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -18,7 +18,8 @@ die() { if test -n "$OPTIONS_SPEC"; then usage() { - exec "$0" -h + "$0" -h + exit 1 } parseopt_extra= diff --git a/git-stash.sh b/git-stash.sh index f16fd9c3c0..06cb177ec6 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -99,7 +99,7 @@ save_stash () { git update-ref -m "$stash_msg" $ref_stash $w_commit || die "Cannot save the current status" - printf >&2 'Saved "%s"\n' "$stash_msg" + printf >&2 'Saved working directory and index state "%s"\n' "$stash_msg" } have_stash () { @@ -228,7 +228,9 @@ create) *) if test $# -eq 0 then - save_stash && git-reset --hard + save_stash && + echo >&2 '(To restore them type "git stash apply")' && + git-reset --hard else usage fi diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 28bb8c3933..6256641ace 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1511,7 +1511,7 @@ sub config_to_int { sub config_to_multi { my $val = shift; - return ref($val) ? $val : (defined($val) ? [ $val ] : []); + return ref($val) ? $val : (defined($val) ? [ $val ] : []); } sub git_get_project_config { diff --git a/object.c b/object.c index 16793d9958..5a5ebe27b0 100644 --- a/object.c +++ b/object.c @@ -136,29 +136,38 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t struct object *obj; int eaten = 0; + obj = NULL; if (type == OBJ_BLOB) { struct blob *blob = lookup_blob(sha1); - parse_blob_buffer(blob, buffer, size); - obj = &blob->object; + if (blob) { + parse_blob_buffer(blob, buffer, size); + obj = &blob->object; + } } else if (type == OBJ_TREE) { struct tree *tree = lookup_tree(sha1); - obj = &tree->object; - if (!tree->object.parsed) { - parse_tree_buffer(tree, buffer, size); - eaten = 1; + if (tree) { + obj = &tree->object; + if (!tree->object.parsed) { + parse_tree_buffer(tree, buffer, size); + eaten = 1; + } } } else if (type == OBJ_COMMIT) { struct commit *commit = lookup_commit(sha1); - parse_commit_buffer(commit, buffer, size); - if (!commit->buffer) { - commit->buffer = buffer; - eaten = 1; + if (commit) { + parse_commit_buffer(commit, buffer, size); + if (!commit->buffer) { + commit->buffer = buffer; + eaten = 1; + } + obj = &commit->object; } - obj = &commit->object; } else if (type == OBJ_TAG) { struct tag *tag = lookup_tag(sha1); - parse_tag_buffer(tag, buffer, size); - obj = &tag->object; + if (tag) { + parse_tag_buffer(tag, buffer, size); + obj = &tag->object; + } } else { warning("object %s has unknown type id %d\n", sha1_to_hex(sha1), type); obj = NULL; diff --git a/parse-options.c b/parse-options.c index e12b428c0a..7a08a0c64f 100644 --- a/parse-options.c +++ b/parse-options.c @@ -89,7 +89,7 @@ static int get_value(struct optparse_t *p, *(const char **)opt->value = NULL; return 0; } - if (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-')) { + if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { *(const char **)opt->value = (const char *)opt->defval; return 0; } @@ -103,7 +103,7 @@ static int get_value(struct optparse_t *p, return (*opt->callback)(opt, NULL, 1); if (opt->flags & PARSE_OPT_NOARG) return (*opt->callback)(opt, NULL, 0); - if (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-')) + if (opt->flags & PARSE_OPT_OPTARG && !p->opt) return (*opt->callback)(opt, NULL, 0); if (!arg) return opterror(opt, "requires a value", flags); @@ -114,7 +114,7 @@ static int get_value(struct optparse_t *p, *(int *)opt->value = 0; return 0; } - if (opt->flags & PARSE_OPT_OPTARG && (!arg || !isdigit(*arg))) { + if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { *(int *)opt->value = opt->defval; return 0; } diff --git a/pretty.c b/pretty.c index 9db75b4e4f..5b1078beb6 100644 --- a/pretty.c +++ b/pretty.c @@ -412,7 +412,7 @@ static void parse_commit_header(struct format_commit_context *context) if (i == eol) { state++; /* strip empty lines */ - while (msg[eol + 1] == '\n') + while (msg[eol] == '\n' && msg[eol + 1] == '\n') eol++; } else if (!prefixcmp(msg + i, "author ")) { context->author.off = i + 7; @@ -425,6 +425,8 @@ static void parse_commit_header(struct format_commit_context *context) context->encoding.len = eol - i - 9; } i = eol; + if (!msg[i]) + break; } context->body_off = i; context->commit_header_parsed = 1; diff --git a/quote.c b/quote.c index 6986b4420f..d061626c34 100644 --- a/quote.c +++ b/quote.c @@ -213,6 +213,22 @@ size_t quote_c_style(const char *name, struct strbuf *sb, FILE *fp, int nodq) return quote_c_style_counted(name, -1, sb, fp, nodq); } +void quote_two_c_style(struct strbuf *sb, const char *prefix, const char *path, int nodq) +{ + if (quote_c_style(prefix, NULL, NULL, 0) || + quote_c_style(path, NULL, NULL, 0)) { + if (!nodq) + strbuf_addch(sb, '"'); + quote_c_style(prefix, sb, NULL, 1); + quote_c_style(path, sb, NULL, 1); + if (!nodq) + strbuf_addch(sb, '"'); + } else { + strbuf_addstr(sb, prefix); + strbuf_addstr(sb, path); + } +} + void write_name_quoted(const char *name, FILE *fp, int terminator) { if (terminator) { diff --git a/quote.h b/quote.h index ab7596f57b..4da110ec01 100644 --- a/quote.h +++ b/quote.h @@ -41,6 +41,7 @@ extern char *sq_dequote(char *); extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp); extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq); +extern void quote_two_c_style(struct strbuf *, const char *, const char *, int); extern void write_name_quoted(const char *name, FILE *, int terminator); extern void write_name_quotedpfx(const char *pfx, size_t pfxlen, diff --git a/revision.c b/revision.c index 7e2f4f1eb5..6e85aaa3fb 100644 --- a/revision.c +++ b/revision.c @@ -1290,8 +1290,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch if (revs->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT) revs->diff = 1; - /* Pickaxe and rename following needs diffs */ - if (revs->diffopt.pickaxe || DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES)) + /* Pickaxe, diff-filter and rename following need diffs */ + if (revs->diffopt.pickaxe || + revs->diffopt.filter || + DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES)) revs->diff = 1; if (revs->topo_order) diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index d175d6b074..a9d9d4f1b0 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -448,6 +448,23 @@ test_expect_success numbers ' test z1048576 = "z$m" ' +cat > expect <actual + then + echo config should have failed + false + fi && + cmp actual expect +' + cat > expect << EOF true false diff --git a/t/t4024-diff-optimize-common.sh b/t/t4024-diff-optimize-common.sh index 20fe87b7dd..3c66102f7a 100755 --- a/t/t4024-diff-optimize-common.sh +++ b/t/t4024-diff-optimize-common.sh @@ -10,58 +10,146 @@ z="$z$z$z$z$z$z$z$z" ;# 512 z="$z$z$z$z" ;# 2048 z2047=$(expr "$z" : '.\(.*\)') ; #2047 -test_expect_success setup ' +x=zzzzzzzzzz ;# 10 +y="$x$x$x$x$x$x$x$x$x$x" ;# 100 +z="$y$y$y$y$y$y$y$y$y$y" ;# 1000 +z1000=$z +z100=$y +z10=$x - echo "a$z2047" >file-a && - echo "b" >file-b && - echo "$z2047" >>file-b && - echo "c$z2047" | tr -d "\012" >file-c && - echo "d" >file-d && - echo "$z2047" | tr -d "\012" >>file-d && +zs() { + count="$1" + while test "$count" -ge 1000 + do + count=$(($count - 1000)) + printf "%s" $z1000 + done + while test "$count" -ge 100 + do + count=$(($count - 100)) + printf "%s" $z100 + done + while test "$count" -ge 10 + do + count=$(($count - 10)) + printf "%s" $z10 + done + while test "$count" -ge 1 + do + count=$(($count - 1)) + printf "z" + done +} - git add file-a file-b file-c file-d && +zc () { + sed -e "/^index/d" \ + -e "s/$z1000/Q/g" \ + -e "s/QQQQQQQQQ/Z9000/g" \ + -e "s/QQQQQQQQ/Z8000/g" \ + -e "s/QQQQQQQ/Z7000/g" \ + -e "s/QQQQQQ/Z6000/g" \ + -e "s/QQQQQ/Z5000/g" \ + -e "s/QQQQ/Z4000/g" \ + -e "s/QQQ/Z3000/g" \ + -e "s/QQ/Z2000/g" \ + -e "s/Q/Z1000/g" \ + -e "s/$z100/Q/g" \ + -e "s/QQQQQQQQQ/Z900/g" \ + -e "s/QQQQQQQQ/Z800/g" \ + -e "s/QQQQQQQ/Z700/g" \ + -e "s/QQQQQQ/Z600/g" \ + -e "s/QQQQQ/Z500/g" \ + -e "s/QQQQ/Z400/g" \ + -e "s/QQQ/Z300/g" \ + -e "s/QQ/Z200/g" \ + -e "s/Q/Z100/g" \ + -e "s/000Z//g" \ + -e "s/$z10/Q/g" \ + -e "s/QQQQQQQQQ/Z90/g" \ + -e "s/QQQQQQQQ/Z80/g" \ + -e "s/QQQQQQQ/Z70/g" \ + -e "s/QQQQQQ/Z60/g" \ + -e "s/QQQQQ/Z50/g" \ + -e "s/QQQQ/Z40/g" \ + -e "s/QQQ/Z30/g" \ + -e "s/QQ/Z20/g" \ + -e "s/Q/Z10/g" \ + -e "s/00Z//g" \ + -e "s/z/Q/g" \ + -e "s/QQQQQQQQQ/Z9/g" \ + -e "s/QQQQQQQQ/Z8/g" \ + -e "s/QQQQQQQ/Z7/g" \ + -e "s/QQQQQQ/Z6/g" \ + -e "s/QQQQQ/Z5/g" \ + -e "s/QQQQ/Z4/g" \ + -e "s/QQQ/Z3/g" \ + -e "s/QQ/Z2/g" \ + -e "s/Q/Z1/g" \ + -e "s/0Z//g" \ + ; +} - echo "A$z2047" >file-a && - echo "B" >file-b && - echo "$z2047" >>file-b && - echo "C$z2047" | tr -d "\012" >file-c && - echo "D" >file-d && - echo "$z2047" | tr -d "\012" >>file-d - -' - -cat >expect <<\EOF -diff --git a/file-a b/file-a ---- a/file-a -+++ b/file-a +expect_pattern () { + cnt="$1" + cat <file-a$n && + ( echo b; zs $n; echo ) >file-b$n && + ( printf c; zs $n ) >file-c$n && + ( echo d; zs $n ) >file-d$n && + + git add file-a$n file-b$n file-c$n file-d$n && + + ( zs $n ; echo A ) >file-a$n && + ( echo B; zs $n; echo ) >file-b$n && + ( printf C; zs $n ) >file-c$n && + ( echo D; zs $n ) >file-d$n && + + expect_pattern $n || break + + done >expect +' test_expect_success 'diff -U0' ' - git diff -U0 | sed -e "/^index/d" -e "s/$z2047/Z/g" >actual && + for n in $sample + do + git diff -U0 file-?$n + done | zc >actual && diff -u expect actual ' diff --git a/t/t4202-log.sh b/t/t4202-log.sh new file mode 100755 index 0000000000..b53645417b --- /dev/null +++ b/t/t4202-log.sh @@ -0,0 +1,74 @@ +#!/bin/sh + +test_description='git log' + +. ./test-lib.sh + +test_expect_success setup ' + + echo one >one && + git add one && + test_tick && + git commit -m initial && + + echo ichi >one && + git add one && + test_tick && + git commit -m second && + + mkdir a && + echo ni >a/two && + git add a/two && + test_tick && + git commit -m third && + + echo san >a/three && + git add a/three && + test_tick && + git commit -m fourth && + + git rm a/three && + test_tick && + git commit -m fifth + +' + +test_expect_success 'diff-filter=A' ' + + actual=$(git log --pretty="format:%s" --diff-filter=A HEAD) && + expect=$(echo fourth ; echo third ; echo initial) && + test "$actual" = "$expect" || { + echo Oops + echo "Actual: $actual" + false + } + +' + +test_expect_success 'diff-filter=M' ' + + actual=$(git log --pretty="format:%s" --diff-filter=M HEAD) && + expect=$(echo second) && + test "$actual" = "$expect" || { + echo Oops + echo "Actual: $actual" + false + } + +' + +test_expect_success 'diff-filter=D' ' + + actual=$(git log --pretty="format:%s" --diff-filter=D HEAD) && + expect=$(echo fifth) && + test "$actual" = "$expect" || { + echo Oops + echo "Actual: $actual" + false + } + +' + + + +test_done \ No newline at end of file diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index d94e5a39b2..22481f8521 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -492,25 +492,21 @@ test_expect_success \ echo "tag-one-line" >expect && git-tag -l | grep "^tag-one-line" >actual && git diff expect actual && - git-tag -n 0 -l | grep "^tag-one-line" >actual && + git-tag -n0 -l | grep "^tag-one-line" >actual && git diff expect actual && - git-tag -n 0 -l tag-one-line >actual && + git-tag -n0 -l tag-one-line >actual && git diff expect actual && echo "tag-one-line A msg" >expect && - git-tag -n xxx -l | grep "^tag-one-line" >actual && - git diff expect actual && - git-tag -n "" -l | grep "^tag-one-line" >actual && - git diff expect actual && - git-tag -n 1 -l | grep "^tag-one-line" >actual && + git-tag -n1 -l | grep "^tag-one-line" >actual && git diff expect actual && git-tag -n -l | grep "^tag-one-line" >actual && git diff expect actual && - git-tag -n 1 -l tag-one-line >actual && + git-tag -n1 -l tag-one-line >actual && git diff expect actual && - git-tag -n 2 -l tag-one-line >actual && + git-tag -n2 -l tag-one-line >actual && git diff expect actual && - git-tag -n 999 -l tag-one-line >actual && + git-tag -n999 -l tag-one-line >actual && git diff expect actual ' @@ -521,21 +517,21 @@ test_expect_success \ echo "tag-zero-lines" >expect && git-tag -l | grep "^tag-zero-lines" >actual && git diff expect actual && - git-tag -n 0 -l | grep "^tag-zero-lines" >actual && + git-tag -n0 -l | grep "^tag-zero-lines" >actual && git diff expect actual && - git-tag -n 0 -l tag-zero-lines >actual && + git-tag -n0 -l tag-zero-lines >actual && git diff expect actual && echo "tag-zero-lines " >expect && - git-tag -n 1 -l | grep "^tag-zero-lines" >actual && + git-tag -n1 -l | grep "^tag-zero-lines" >actual && git diff expect actual && git-tag -n -l | grep "^tag-zero-lines" >actual && git diff expect actual && - git-tag -n 1 -l tag-zero-lines >actual && + git-tag -n1 -l tag-zero-lines >actual && git diff expect actual && - git-tag -n 2 -l tag-zero-lines >actual && + git-tag -n2 -l tag-zero-lines >actual && git diff expect actual && - git-tag -n 999 -l tag-zero-lines >actual && + git-tag -n999 -l tag-zero-lines >actual && git diff expect actual ' @@ -549,37 +545,37 @@ test_expect_success \ echo "tag-lines" >expect && git-tag -l | grep "^tag-lines" >actual && git diff expect actual && - git-tag -n 0 -l | grep "^tag-lines" >actual && + git-tag -n0 -l | grep "^tag-lines" >actual && git diff expect actual && - git-tag -n 0 -l tag-lines >actual && + git-tag -n0 -l tag-lines >actual && git diff expect actual && echo "tag-lines tag line one" >expect && - git-tag -n 1 -l | grep "^tag-lines" >actual && + git-tag -n1 -l | grep "^tag-lines" >actual && git diff expect actual && git-tag -n -l | grep "^tag-lines" >actual && git diff expect actual && - git-tag -n 1 -l tag-lines >actual && + git-tag -n1 -l tag-lines >actual && git diff expect actual && echo " tag line two" >>expect && - git-tag -n 2 -l | grep "^ *tag.line" >actual && + git-tag -n2 -l | grep "^ *tag.line" >actual && git diff expect actual && - git-tag -n 2 -l tag-lines >actual && + git-tag -n2 -l tag-lines >actual && git diff expect actual && echo " tag line three" >>expect && - git-tag -n 3 -l | grep "^ *tag.line" >actual && + git-tag -n3 -l | grep "^ *tag.line" >actual && git diff expect actual && - git-tag -n 3 -l tag-lines >actual && + git-tag -n3 -l tag-lines >actual && git diff expect actual && - git-tag -n 4 -l | grep "^ *tag.line" >actual && + git-tag -n4 -l | grep "^ *tag.line" >actual && git diff expect actual && - git-tag -n 4 -l tag-lines >actual && + git-tag -n4 -l tag-lines >actual && git diff expect actual && - git-tag -n 99 -l | grep "^ *tag.line" >actual && + git-tag -n99 -l | grep "^ *tag.line" >actual && git diff expect actual && - git-tag -n 99 -l tag-lines >actual && + git-tag -n99 -l tag-lines >actual && git diff expect actual ' @@ -907,25 +903,21 @@ test_expect_success \ echo "stag-one-line" >expect && git-tag -l | grep "^stag-one-line" >actual && git diff expect actual && - git-tag -n 0 -l | grep "^stag-one-line" >actual && + git-tag -n0 -l | grep "^stag-one-line" >actual && git diff expect actual && - git-tag -n 0 -l stag-one-line >actual && + git-tag -n0 -l stag-one-line >actual && git diff expect actual && echo "stag-one-line A message line signed" >expect && - git-tag -n xxx -l | grep "^stag-one-line" >actual && - git diff expect actual && - git-tag -n "" -l | grep "^stag-one-line" >actual && - git diff expect actual && - git-tag -n 1 -l | grep "^stag-one-line" >actual && + git-tag -n1 -l | grep "^stag-one-line" >actual && git diff expect actual && git-tag -n -l | grep "^stag-one-line" >actual && git diff expect actual && - git-tag -n 1 -l stag-one-line >actual && + git-tag -n1 -l stag-one-line >actual && git diff expect actual && - git-tag -n 2 -l stag-one-line >actual && + git-tag -n2 -l stag-one-line >actual && git diff expect actual && - git-tag -n 999 -l stag-one-line >actual && + git-tag -n999 -l stag-one-line >actual && git diff expect actual ' @@ -936,21 +928,21 @@ test_expect_success \ echo "stag-zero-lines" >expect && git-tag -l | grep "^stag-zero-lines" >actual && git diff expect actual && - git-tag -n 0 -l | grep "^stag-zero-lines" >actual && + git-tag -n0 -l | grep "^stag-zero-lines" >actual && git diff expect actual && - git-tag -n 0 -l stag-zero-lines >actual && + git-tag -n0 -l stag-zero-lines >actual && git diff expect actual && echo "stag-zero-lines " >expect && - git-tag -n 1 -l | grep "^stag-zero-lines" >actual && + git-tag -n1 -l | grep "^stag-zero-lines" >actual && git diff expect actual && git-tag -n -l | grep "^stag-zero-lines" >actual && git diff expect actual && - git-tag -n 1 -l stag-zero-lines >actual && + git-tag -n1 -l stag-zero-lines >actual && git diff expect actual && - git-tag -n 2 -l stag-zero-lines >actual && + git-tag -n2 -l stag-zero-lines >actual && git diff expect actual && - git-tag -n 999 -l stag-zero-lines >actual && + git-tag -n999 -l stag-zero-lines >actual && git diff expect actual ' @@ -964,37 +956,37 @@ test_expect_success \ echo "stag-lines" >expect && git-tag -l | grep "^stag-lines" >actual && git diff expect actual && - git-tag -n 0 -l | grep "^stag-lines" >actual && + git-tag -n0 -l | grep "^stag-lines" >actual && git diff expect actual && - git-tag -n 0 -l stag-lines >actual && + git-tag -n0 -l stag-lines >actual && git diff expect actual && echo "stag-lines stag line one" >expect && - git-tag -n 1 -l | grep "^stag-lines" >actual && + git-tag -n1 -l | grep "^stag-lines" >actual && git diff expect actual && git-tag -n -l | grep "^stag-lines" >actual && git diff expect actual && - git-tag -n 1 -l stag-lines >actual && + git-tag -n1 -l stag-lines >actual && git diff expect actual && echo " stag line two" >>expect && - git-tag -n 2 -l | grep "^ *stag.line" >actual && + git-tag -n2 -l | grep "^ *stag.line" >actual && git diff expect actual && - git-tag -n 2 -l stag-lines >actual && + git-tag -n2 -l stag-lines >actual && git diff expect actual && echo " stag line three" >>expect && - git-tag -n 3 -l | grep "^ *stag.line" >actual && + git-tag -n3 -l | grep "^ *stag.line" >actual && git diff expect actual && - git-tag -n 3 -l stag-lines >actual && + git-tag -n3 -l stag-lines >actual && git diff expect actual && - git-tag -n 4 -l | grep "^ *stag.line" >actual && + git-tag -n4 -l | grep "^ *stag.line" >actual && git diff expect actual && - git-tag -n 4 -l stag-lines >actual && + git-tag -n4 -l stag-lines >actual && git diff expect actual && - git-tag -n 99 -l | grep "^ *stag.line" >actual && + git-tag -n99 -l | grep "^ *stag.line" >actual && git diff expect actual && - git-tag -n 99 -l stag-lines >actual && + git-tag -n99 -l stag-lines >actual && git diff expect actual ' diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh index 44228b5ac1..c1cec55306 100755 --- a/t/t7005-editor.sh +++ b/t/t7005-editor.sh @@ -37,7 +37,7 @@ test_expect_success 'dumb should error out when falling back on vi' ' if git commit --amend then echo "Oops?" - exit 1 + false else : happy fi diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh index 21ac785e3d..aaf497e6a5 100755 --- a/t/t7502-commit.sh +++ b/t/t7502-commit.sh @@ -89,4 +89,69 @@ test_expect_success 'verbose' ' ' +test_expect_success 'cleanup commit messages (verbatim,-t)' ' + + echo >>negative && + { echo;echo "# text";echo; } >expect && + git commit --cleanup=verbatim -t expect -a && + git cat-file -p HEAD |sed -e "1,/^\$/d" |head -n 3 >actual && + diff -u expect actual + +' + +test_expect_success 'cleanup commit messages (verbatim,-F)' ' + + echo >>negative && + git commit --cleanup=verbatim -F expect -a && + git cat-file -p HEAD |sed -e "1,/^\$/d">actual && + diff -u expect actual + +' + +test_expect_success 'cleanup commit messages (verbatim,-m)' ' + + echo >>negative && + git commit --cleanup=verbatim -m "$(cat expect)" -a && + git cat-file -p HEAD |sed -e "1,/^\$/d">actual && + diff -u expect actual + +' + +test_expect_success 'cleanup commit messages (whitespace,-F)' ' + + echo >>negative && + { echo;echo "# text";echo; } >text && + echo "# text" >expect && + git commit --cleanup=whitespace -F text -a && + git cat-file -p HEAD |sed -e "1,/^\$/d">actual && + diff -u expect actual + +' + +test_expect_success 'cleanup commit messages (strip,-F)' ' + + echo >>negative && + { echo;echo "# text";echo sample;echo; } >text && + echo sample >expect && + git commit --cleanup=strip -F text -a && + git cat-file -p HEAD |sed -e "1,/^\$/d">actual && + diff -u expect actual + +' + +echo "sample + +# Please enter the commit message for your changes. +# (Comment lines starting with '#' will not be included)" >expect + +test_expect_success 'cleanup commit messages (strip,-F,-e)' ' + + echo >>negative && + { echo;echo sample;echo; } >text && + git commit -e -F text -a && + head -n 4 .git/COMMIT_EDITMSG >actual && + diff -u expect actual + +' + test_done diff --git a/xdiff-interface.c b/xdiff-interface.c index 9ee877c6f4..4b8e5cca80 100644 --- a/xdiff-interface.c +++ b/xdiff-interface.c @@ -115,17 +115,20 @@ static void trim_common_tail(mmfile_t *a, mmfile_t *b, long ctx) char *bp = b->ptr + b->size; long smaller = (a->size < b->size) ? a->size : b->size; + if (ctx) + return; + while (blk + trimmed <= smaller && !memcmp(ap - blk, bp - blk, blk)) { trimmed += blk; ap -= blk; bp -= blk; } - while (recovered < trimmed && 0 <= ctx) + while (recovered < trimmed) if (ap[recovered++] == '\n') - ctx--; - a->size -= (trimmed - recovered); - b->size -= (trimmed - recovered); + break; + a->size -= trimmed - recovered; + b->size -= trimmed - recovered; } int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *xecb)