diff options
143 files changed, 2723 insertions, 1051 deletions
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines new file mode 100644 index 0000000000..3b042db624 --- /dev/null +++ b/Documentation/CodingGuidelines @@ -0,0 +1,112 @@ +Like other projects, we also have some guidelines to keep to the +code. For git in general, three rough rules are: + + - Most importantly, we never say "It's in POSIX; we'll happily + ignore your needs should your system not conform to it." + We live in the real world. + + - However, we often say "Let's stay away from that construct, + it's not even in POSIX". + + - In spite of the above two rules, we sometimes say "Although + this is not in POSIX, it (is so convenient | makes the code + much more readable | has other good characteristics) and + practically all the platforms we care about support it, so + let's use it". + + Again, we live in the real world, and it is sometimes a + judgement call, the decision based more on real world + constraints people face than what the paper standard says. + + +As for more concrete guidelines, just imitate the existing code +(this is a good guideline, no matter which project you are +contributing to). But if you must have a list of rules, +here they are. + +For shell scripts specifically (not exhaustive): + + - We prefer $( ... ) for command substitution; unlike ``, it + properly nests. It should have been the way Bourne spelled + it from day one, but unfortunately isn't. + + - We use ${parameter-word} and its [-=?+] siblings, and their + colon'ed "unset or null" form. + + - We use ${parameter#word} and its [#%] siblings, and their + doubled "longest matching" form. + + - We use Arithmetic Expansion $(( ... )). + + - No "Substring Expansion" ${parameter:offset:length}. + + - No shell arrays. + + - No strlen ${#parameter}. + + - No regexp ${parameter/pattern/string}. + + - We do not use Process Substitution <(list) or >(list). + + - We prefer "test" over "[ ... ]". + + - We do not write the noiseword "function" in front of shell + functions. + +For C programs: + + - We use tabs to indent, and interpret tabs as taking up to + 8 spaces. + + - We try to keep to at most 80 characters per line. + + - When declaring pointers, the star sides with the variable + name, i.e. "char *string", not "char* string" or + "char * string". This makes it easier to understand code + like "char *string, c;". + + - We avoid using braces unnecessarily. I.e. + + if (bla) { + x = 1; + } + + is frowned upon. A gray area is when the statement extends + over a few lines, and/or you have a lengthy comment atop of + it. Also, like in the Linux kernel, if there is a long list + of "else if" statements, it can make sense to add braces to + single line blocks. + + - Try to make your code understandable. You may put comments + in, but comments invariably tend to stale out when the code + they were describing changes. Often splitting a function + into two makes the intention of the code much clearer. + + - Double negation is often harder to understand than no negation + at all. + + - Some clever tricks, like using the !! operator with arithmetic + constructs, can be extremely confusing to others. Avoid them, + unless there is a compelling reason to use them. + + - Use the API. No, really. We have a strbuf (variable length + string), several arrays with the ALLOC_GROW() macro, a + path_list for sorted string lists, a hash map (mapping struct + objects) named "struct decorate", amongst other things. + + - When you come up with an API, document it. + + - The first #include in C files, except in platform specific + compat/ implementations, should be git-compat-util.h or another + header file that includes it, such as cache.h or builtin.h. + + - If you are planning a new command, consider writing it in shell + or perl first, so that changes in semantics can be easily + changed and discussed. Many git commands started out like + that, and a few are still scripts. + + - Avoid introducing a new dependency into git. This means you + usually should stay away from scripting languages not already + used in the git core command set (unless your command is clearly + separate from it, such as an importer to convert random-scm-X + repositories to git). diff --git a/Documentation/Makefile b/Documentation/Makefile index 39ec0ede02..d88664177d 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -37,9 +37,6 @@ man7dir=$(mandir)/man7 ASCIIDOC=asciidoc ASCIIDOC_EXTRA = -ifdef ASCIIDOC8 -ASCIIDOC_EXTRA += -a asciidoc7compatible -endif INSTALL?=install RM ?= rm -f DOC_REF = origin/man @@ -52,6 +49,13 @@ DOCBOOK2X_TEXI=docbook2x-texi -include ../config.mak.autogen -include ../config.mak +ifdef ASCIIDOC8 +ASCIIDOC_EXTRA += -a asciidoc7compatible +endif +ifdef DOCBOOK_XSL_172 +ASCIIDOC_EXTRA += -a docbook-xsl-172 +endif + # # Please note that there is a minor bug in asciidoc. # The version after 6.0.3 _will_ include the patch found here: diff --git a/Documentation/RelNotes-1.5.3.5.txt b/Documentation/RelNotes-1.5.3.5.txt index f99a2cd650..7ff1d5d0d1 100644 --- a/Documentation/RelNotes-1.5.3.5.txt +++ b/Documentation/RelNotes-1.5.3.5.txt @@ -90,5 +90,5 @@ Fixes since v1.5.3.4 * "git-send-pack $remote frotz" segfaulted when there is nothing named 'frotz' on the local end. - * "git-rebase -interactive" did not handle its "--strategy" option + * "git-rebase --interactive" did not handle its "--strategy" option properly. diff --git a/Documentation/RelNotes-1.5.3.6.txt b/Documentation/RelNotes-1.5.3.6.txt new file mode 100644 index 0000000000..c9f948ec0e --- /dev/null +++ b/Documentation/RelNotes-1.5.3.6.txt @@ -0,0 +1,52 @@ +GIT v1.5.3.6 Release Notes +========================== + +Fixes since v1.5.3.5 +-------------------- + + * git-cvsexportcommit handles root commits better; + + * git-svn dcommit used to clobber when sending a series of + patches; + + * git-svn dcommit failed after attempting to rebase when + started with a dirty index; now it stops upfront. + + * git-grep sometimes refused to work when your index was + unmerged; + + * git-hash-object did not honor configuration variables, such as + core.compression. + + * git-index-pack choked on a huge pack on 32-bit machines, even when + large file offsets are supported. + + * atom feeds from git-web said "10" for the month of November. + + * a memory leak in commit walker was plugged. + + * When git-send-email inserted the original author's From: + address in body, it did not mark the message with + Content-type: as needed. + + * git-revert and git-cherry-pick incorrectly refused to start + when the work tree was dirty. + + * git-clean did not honor core.excludesfile configuration. + + * git-add mishandled ".gitignore" files when applying them to + subdirectories. + + * While importing a too branchy history, git-fastimport did not + honor delta depth limit properly. + + * Support for zlib implementations that lack ZLIB_VERNUM and definition + of deflateBound() has been added. + + * Quite a lot of documentation clarifications. + +-- +exec >/var/tmp/1 +O=v1.5.3.5-57-gb574c8d +echo O=`git describe refs/heads/maint` +git shortlog --no-merges $O..refs/heads/maint diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes-1.5.4.txt index 133fa64d22..229d773423 100644 --- a/Documentation/RelNotes-1.5.4.txt +++ b/Documentation/RelNotes-1.5.4.txt @@ -6,7 +6,10 @@ Updates since v1.5.3 * Comes with much improved gitk. - * git-reset is now built-in. + * "progress display" from many commands are a lot nicer to the + eye. Transfer commands show throughput data. + + * git-reset is now built-in and its output can be squelched with -q. * git-send-email can optionally talk over ssmtp and use SMTP-AUTH. @@ -29,6 +32,10 @@ Updates since v1.5.3 (read: safer than the usual one) after the user accumulates too many loose objects. + * You need to explicitly set clean.requireForce to true to allow + git-clean to do any damage now (the configuration variable used + to default to false). + * git-push has been rewritten in C. * git-push learned --dry-run option to show what would happen @@ -46,14 +53,60 @@ Updates since v1.5.3 * Various Perforce importer updates. + * git-lost-found was deprecated in favor of git-fsck's --lost-found + option. + + * git-svnimport was removed in favor of git-svn. + + * git-bisect learned "skip" action to mark untestable commits. + + * git-format-patch learned "format.numbered" configuration variable + to automatically turn --numbered option on when more than one + commits are formatted. + + * git-ls-files learned "--exclude-standard" to use the canned + set of exclude files. + + * git-rebase now detaches head during its operation, so after a + successful "git rebase" operation, the reflog entry branch@{1} + for the current branch points at the commit before the rebase + was started. + + * "git-tag -a -f existing" begins the editor session using the + existing annotation message. + + * "git cvsexportcommit" learned -w option to specify and switch + to the CVS working directory. + + * Output processing for '--pretty=format:<user format>' has + been optimized. + + * Rename detection diff family, while detecting exact matches, + has been greatly optimized. + + * Example update and post-receive hooks have been improved. + + * In addition there are quite a few internal clean-ups. Notably + + - many fork/exec have been replaced with run-command API, + brought from the msysgit effort. + + - introduction and more use of the option parser API. + + - enhancement and more use of the strbuf API. + + Fixes since v1.5.3 ------------------ All of the fixes in v1.5.3 maintenance series are included in this release, unless otherwise noted. + * git-svn talking with the SVN over http will correctly quote branch + and project names. + -- exec >/var/tmp/1 -O=v1.5.3.4-450-g952a9e5 +O=v1.5.3.5-736-geb9d2b9 echo O=`git describe refs/heads/master` git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 61635bf04d..83bf54c7ac 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -20,9 +20,6 @@ Checklist (and a short version for the impatient): Patch: - use "git format-patch -M" to create the patch - - send your patch to <git@vger.kernel.org>. If you use - git-send-email(1), please test it first by sending - email to yourself. - do not PGP sign your patch - do not attach your patch, but read in the mail body, unless you cannot teach your mailer to @@ -31,13 +28,15 @@ Checklist (and a short version for the impatient): corrupt whitespaces. - provide additional information (which is unsuitable for the commit message) between the "---" and the diffstat - - send the patch to the list (git@vger.kernel.org) and the - maintainer (gitster@pobox.com). - if you change, add, or remove a command line option or make some other user interface change, the associated documentation should be updated as well. - if your name is not writable in ASCII, make sure that you send off a message in the correct encoding. + - send the patch to the list (git@vger.kernel.org) and the + maintainer (gitster@pobox.com). If you use + git-send-email(1), please test it first by sending + email to yourself. Long version: diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf index af5b1558a6..99d8874aa0 100644 --- a/Documentation/asciidoc.conf +++ b/Documentation/asciidoc.conf @@ -23,7 +23,9 @@ ifdef::backend-docbook[] endif::backend-docbook[] ifdef::backend-docbook[] +ifndef::docbook-xsl-172[] # "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this. +# v1.72 breaks with this because it replaces dots not in roff requests. [listingblock] <example><title>{title}</title> <literallayout> @@ -36,6 +38,7 @@ ifdef::doctype-manpage[] endif::doctype-manpage[] </literallayout> {title#}</example> +endif::docbook-xsl-172[] endif::backend-docbook[] ifdef::doctype-manpage[] diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl index 8d21d423e5..57a790df63 100755 --- a/Documentation/cmd-list.perl +++ b/Documentation/cmd-list.perl @@ -3,7 +3,8 @@ use File::Compare qw(compare); sub format_one { - my ($out, $name) = @_; + my ($out, $nameattr) = @_; + my ($name, $attr) = @$nameattr; my ($state, $description); $state = 0; open I, '<', "$name.txt" or die "No such file $name.txt"; @@ -26,8 +27,11 @@ sub format_one { die "No description found in $name.txt"; } if (my ($verify_name, $text) = ($description =~ /^($name) - (.*)/)) { - print $out "gitlink:$name\[1\]::\n"; - print $out "\t$text.\n\n"; + print $out "gitlink:$name\[1\]::\n\t"; + if ($attr) { + print $out "($attr) "; + } + print $out "$text.\n\n"; } else { die "Description does not match $name: $description"; @@ -39,8 +43,8 @@ while (<DATA>) { next if /^#/; chomp; - my ($name, $cat) = /^(\S+)\s+(.*)$/; - push @{$cmds{$cat}}, $name; + my ($name, $cat, $attr) = /^(\S+)\s+(.*?)(?:\s+(.*))?$/; + push @{$cmds{$cat}}, [$name, $attr]; } for my $cat (qw(ancillaryinterrogators @@ -124,9 +128,8 @@ git-index-pack plumbingmanipulators git-init mainporcelain git-instaweb ancillaryinterrogators gitk mainporcelain -git-local-fetch synchingrepositories git-log mainporcelain -git-lost-found ancillarymanipulators +git-lost-found ancillarymanipulators deprecated git-ls-files plumbinginterrogators git-ls-remote plumbinginterrogators git-ls-tree plumbinginterrogators @@ -178,8 +181,6 @@ git-show-branch ancillaryinterrogators git-show-index plumbinginterrogators git-show-ref plumbinginterrogators git-sh-setup purehelpers -git-ssh-fetch synchingrepositories -git-ssh-upload synchingrepositories git-stash mainporcelain git-status mainporcelain git-stripspace purehelpers @@ -187,7 +188,7 @@ git-submodule mainporcelain git-svn foreignscminterface git-symbolic-ref plumbingmanipulators git-tag mainporcelain -git-tar-tree plumbinginterrogators +git-tar-tree plumbinginterrogators deprecated git-unpack-file plumbinginterrogators git-unpack-objects plumbingmanipulators git-update-index plumbingmanipulators diff --git a/Documentation/config.txt b/Documentation/config.txt index 0df004ea26..6dc9f3ed02 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -345,8 +345,8 @@ branch.<name>.mergeoptions:: supported. clean.requireForce:: - A boolean to make git-clean do nothing unless given -f or -n. Defaults - to false. + A boolean to make git-clean do nothing unless given -f + or -n. Defaults to true. color.branch:: A boolean to enable/disable color in the output of @@ -432,6 +432,12 @@ fetch.unpackLimit:: pack from a push can make the push operation complete faster, especially on slow filesystems. +format.numbered:: + A boolean which can enable sequence numbers in patch subjects. + Seting this option to "auto" will enable it only if there is + more than one patch. See --numbered option in + gitlink:git-format-patch[1]. + format.headers:: Additional email headers to include in a patch to be submitted by mail. See gitlink:git-format-patch[1]. diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt index 99817c5337..bd6cd41245 100644 --- a/Documentation/core-tutorial.txt +++ b/Documentation/core-tutorial.txt @@ -931,12 +931,13 @@ Another useful tool, especially if you do not always work in X-Window environment, is `git show-branch`. ------------------------------------------------ -$ git show-branch --topo-order master mybranch +$ git-show-branch --topo-order --more=1 master mybranch * [master] Merge work in mybranch ! [mybranch] Some work. -- - [master] Merge work in mybranch *+ [mybranch] Some work. +* [master^] Some fun. ------------------------------------------------ The first two lines indicate that it is showing the two branches @@ -954,10 +955,22 @@ because `mybranch` has not been merged to incorporate these commits from the master branch. The string inside brackets before the commit log message is a short name you can use to name the commit. In the above example, 'master' and 'mybranch' -are branch heads. 'master~1' is the first parent of 'master' +are branch heads. 'master^' is the first parent of 'master' branch head. Please see 'git-rev-parse' documentation if you see more complex cases. +[NOTE] +Without the '--more=1' option, 'git-show-branch' would not output the +'[master^]' commit, as '[mybranch]' commit is a common ancestor of +both 'master' and 'mybranch' tips. Please see 'git-show-branch' +documentation for details. + +[NOTE] +If there were more commits on the 'master' branch after the merge, the +merge commit itself would not be shown by 'git-show-branch' by +default. You would need to provide '--sparse' option to make the +merge commit visible in this case. + Now, let's pretend you are the one who did all the work in `mybranch`, and the fruit of your hard work has finally been merged to the `master` branch. Let's go back to `mybranch`, and run @@ -1077,11 +1090,6 @@ server like git Native transport does. Any stock HTTP server that does not even support directory index would suffice. But you must prepare your repository with `git-update-server-info` to help dumb transport downloaders. -+ -There are (confusingly enough) `git-ssh-fetch` and `git-ssh-upload` -programs, which are 'commit walkers'; they outlived their -usefulness when git Native and SSH transports were introduced, -and are not used by `git pull` or `git push` scripts. Once you fetch from the remote repository, you `merge` that with your current branch. @@ -1144,7 +1152,7 @@ back to the earlier repository with "hello" and "example" file, and bring ourselves back to the pre-merge state: ------------ -$ git show-branch --more=3 master mybranch +$ git show-branch --more=2 master mybranch ! [master] Merge work in mybranch * [mybranch] Merge work in mybranch -- @@ -1207,7 +1215,7 @@ $ git-read-tree -m -u $mb HEAD mybranch This is the same `git-read-tree` command we have already seen, but it takes three trees, unlike previous examples. This reads the contents of each tree into different 'stage' in the index -file (the first tree goes to stage 1, the second stage 2, +file (the first tree goes to stage 1, the second to stage 2, etc.). After reading three trees into three stages, the paths that are the same in all three stages are 'collapsed' into stage 0. Also paths that are the same in two of three stages are diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index b1f528ae88..e4af393515 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -1,5 +1,25 @@ +// Please don't remove this comment as asciidoc behaves badly when +// the first non-empty line is ifdef/ifndef. The symptom is that +// without this comment the <git-diff-core> attribute conditionally +// defined below ends up being defined unconditionally. +// Last checked with asciidoc 7.0.2. + +ifndef::git-format-patch[] +ifndef::git-diff[] +:git-diff-core: 1 +endif::git-diff[] +endif::git-format-patch[] + +ifdef::git-format-patch[] -p:: - Generate patch (see section on generating patches) + Generate patches without diffstat. +endif::git-format-patch[] + +ifndef::git-format-patch[] +-p:: + Generate patch (see section on generating patches). + {git-diff? This is the default.} +endif::git-format-patch[] -u:: Synonym for "-p". @@ -13,6 +33,7 @@ --raw:: Generate the raw format. + {git-diff-core? This is the default.} --patch-with-raw:: Synonym for "-p --raw". @@ -41,6 +62,7 @@ --patch-with-stat:: Synonym for "-p --stat". + {git-format-patch? This is the default.} -z:: NUL-line termination on output. This affects the --raw diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 963e1ab1e2..63829d93cc 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -224,6 +224,7 @@ See Also -------- gitlink:git-status[1] gitlink:git-rm[1] +gitlink:git-reset[1] gitlink:git-mv[1] gitlink:git-commit[1] gitlink:git-update-index[1] diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 5e81aa4ee1..5ce905de86 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -105,7 +105,7 @@ OPTIONS '--track' were given. --no-track:: - When -b is given and a branch is created off a remote branch, + When a branch is created off a remote branch, set up configuration so that git-pull will not retrieve data from the remote branch, ignoring the branch.autosetupmerge configuration variable. diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 14e58f3866..c90bcece24 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -130,6 +130,7 @@ OPTIONS for "host.xz:foo/.git"). Cloning into an existing directory is not allowed. +:git-clone: 1 include::urls.txt[] Examples diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index e54fb12103..d4bfd49ce1 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -154,10 +154,13 @@ EXAMPLES -------- When recording your own work, the contents of modified files in your working tree are temporarily stored to a staging area -called the "index" with gitlink:git-add[1]. Removal -of a file is staged with gitlink:git-rm[1]. After building the -state to be committed incrementally with these commands, `git -commit` (without any pathname parameter) is used to record what +called the "index" with gitlink:git-add[1]. A file can be +reverted back, only in the index but not in the working tree, +to that of the last commit with `git-reset HEAD -- <file>`, +which effectively reverts `git-add` and prevents the changes to +this file from participating in the next commit. After building +the state to be committed incrementally with these commands, +`git commit` (without any pathname parameter) is used to record what has been staged so far. This is the most basic form of the command. An example: diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt index c3922f9238..3f9d2295d3 100644 --- a/Documentation/git-cvsexportcommit.txt +++ b/Documentation/git-cvsexportcommit.txt @@ -8,7 +8,7 @@ git-cvsexportcommit - Export a single commit to a CVS checkout SYNOPSIS -------- -'git-cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID +'git-cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-w cvsworkdir] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID DESCRIPTION @@ -16,8 +16,9 @@ DESCRIPTION Exports a commit from GIT to a CVS checkout, making it easier to merge patches from a git repository into a CVS repository. -Execute it from the root of the CVS working copy. GIT_DIR must be defined. -See examples below. +Specify the name of a CVS checkout using the -w switch or execute it +from the root of the CVS working copy. In the latter case GIT_DIR must +be defined. See examples below. It does its best to do the safe thing, it will check that the files are unchanged and up to date in the CVS checkout, and it will not autocommit @@ -61,6 +62,11 @@ OPTIONS -u:: Update affected files from CVS repository before attempting export. +-w:: + Specify the location of the CVS checkout to use for the export. This + option does not require GIT_DIR to be set before execution if the + current directory is within a git repository. + -v:: Verbose. @@ -76,6 +82,12 @@ $ git-cvsexportcommit -v <commit-sha1> $ cvs commit -F .msg <files> ------------ +Merge one patch into CVS (-c and -w options). The working directory is within the Git Repo:: ++ +------------ + $ git-cvsexportcommit -v -c -w ~/project_cvs_checkout <commit-sha1> +------------ + Merge pending patches into CVS automatically -- only if you really know what you are doing:: + ------------ @@ -86,11 +98,11 @@ $ git-cherry cvshead myhead | sed -n 's/^+ //p' | xargs -l1 git-cvsexportcommit Author ------ -Written by Martin Langhoff <martin@catalyst.net.nz> +Written by Martin Langhoff <martin@catalyst.net.nz> and others. Documentation -------------- -Documentation by Martin Langhoff <martin@catalyst.net.nz> +Documentation by Martin Langhoff <martin@catalyst.net.nz> and others. GIT --- diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt index 11c4216c4a..2808a5ec44 100644 --- a/Documentation/git-diff.txt +++ b/Documentation/git-diff.txt @@ -75,6 +75,7 @@ and the range notations ("<commit>..<commit>" and OPTIONS ------- +:git-diff: 1 include::diff-options.txt[] <path>...:: diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index f0617efa0a..6fb9429851 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -9,9 +9,10 @@ git-format-patch - Prepare patches for e-mail submission SYNOPSIS -------- [verse] -'git-format-patch' [-n | -k] [-o <dir> | --stdout] [--thread] +'git-format-patch' [-k] [-o <dir> | --stdout] [--thread] [--attach[=<boundary>] | --inline[=<boundary>]] [-s | --signoff] [<common diff options>] + [-n | --numbered | -N | --no-numbered] [--start-number <n>] [--numbered-files] [--in-reply-to=Message-Id] [--suffix=.<sfx>] [--ignore-if-in-upstream] @@ -65,6 +66,7 @@ reference. OPTIONS ------- +:git-format-patch: 1 include::diff-options.txt[] -<n>:: @@ -77,6 +79,9 @@ include::diff-options.txt[] -n|--numbered:: Name output in '[PATCH n/m]' format. +-N|--no-numbered:: + Name output in '[PATCH]' format. + --start-number <n>:: Start numbering the patches at <n> instead of 1. @@ -142,15 +147,16 @@ not add any suffix. CONFIGURATION ------------- -You can specify extra mail header lines to be added to each -message in the repository configuration. You can also specify -new defaults for the subject prefix and file suffix. +You can specify extra mail header lines to be added to each message +in the repository configuration, new defaults for the subject prefix +and file suffix, and number patches when outputting more than one. ------------ [format] headers = "Organization: git-foo\n" subjectprefix = CHANGE suffix = .txt + numbered = auto ------------ diff --git a/Documentation/git-get-tar-commit-id.txt b/Documentation/git-get-tar-commit-id.txt index 9b5f86fc30..76316bbc9e 100644 --- a/Documentation/git-get-tar-commit-id.txt +++ b/Documentation/git-get-tar-commit-id.txt @@ -14,12 +14,12 @@ SYNOPSIS DESCRIPTION ----------- Acts as a filter, extracting the commit ID stored in archives created by -git-tar-tree. It reads only the first 1024 bytes of input, thus its +gitlink:git-archive[1]. It reads only the first 1024 bytes of input, thus its runtime is not influenced by the size of <tarfile> very much. If no commit ID is found, git-get-tar-commit-id quietly exists with a return code of 1. This can happen if <tarfile> had not been created -using git-tar-tree or if the first parameter of git-tar-tree had been +using git-archive or if the <treeish> parameter of git-archive had been a tree ID instead of a commit ID or tag. diff --git a/Documentation/git-local-fetch.txt b/Documentation/git-local-fetch.txt deleted file mode 100644 index e830deeff3..0000000000 --- a/Documentation/git-local-fetch.txt +++ /dev/null @@ -1,66 +0,0 @@ -git-local-fetch(1) -================== - -NAME ----- -git-local-fetch - Duplicate another git repository on a local system - - -SYNOPSIS --------- -[verse] -'git-local-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [-l] [-s] [-n] - commit-id path - -DESCRIPTION ------------ -THIS COMMAND IS DEPRECATED. - -Duplicates another git repository on a local system. - -OPTIONS -------- --c:: - Get the commit objects. --t:: - Get trees associated with the commit objects. --a:: - Get all the objects. --v:: - Report what is downloaded. --s:: - Instead of regular file-to-file copying use symbolic links to the objects - in the remote repository. --l:: - Before attempting symlinks (if -s is specified) or file-to-file copying the - remote objects, try to hardlink the remote objects into the local - repository. --n:: - Never attempt to file-to-file copy remote objects. Only useful with - -s or -l command-line options. - --w <filename>:: - Writes the commit-id into the filename under $GIT_DIR/refs/<filename> on - the local end after the transfer is complete. - ---stdin:: - Instead of a commit id on the command line (which is not expected in this - case), 'git-local-fetch' expects lines on stdin in the format - - <commit-id>['\t'<filename-as-in--w>] - ---recover:: - Verify that everything reachable from target is fetched. Used after - an earlier fetch is interrupted. - -Author ------- -Written by Junio C Hamano <junkio@cox.net> - -Documentation --------------- -Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>. - -GIT ---- -Part of the gitlink:git[7] suite diff --git a/Documentation/git-lost-found.txt b/Documentation/git-lost-found.txt index bc739117be..7f808fcd76 100644 --- a/Documentation/git-lost-found.txt +++ b/Documentation/git-lost-found.txt @@ -11,6 +11,10 @@ SYNOPSIS DESCRIPTION ----------- + +*NOTE*: this command is deprecated. Use gitlink:git-fsck[1] with +the option '--lost-found' instead. + Finds dangling commits and tags from the object database, and creates refs to them in the .git/lost-found/ directory. Commits and tags that dereference to commits are stored in .git/lost-found/commit, diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index 9e454f0a4d..2ec0c0d270 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -15,6 +15,7 @@ SYNOPSIS [-x <pattern>|--exclude=<pattern>] [-X <file>|--exclude-from=<file>] [--exclude-per-directory=<file>] + [--exclude-standard] [--error-unmatch] [--with-tree=<tree-ish>] [--full-name] [--abbrev] [--] [<file>]\* @@ -77,6 +78,10 @@ OPTIONS read additional exclude patterns that apply only to the directory and its subdirectories in <file>. +--exclude-standard:: + Add the standard git exclusions: .git/info/exclude, .gitignore + in each directory, and the user's global exclusion file. + --error-unmatch:: If any <file> does not appear in the index, treat this as an error (return 1). diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index e5dd4c1066..4a68aaba34 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -10,7 +10,7 @@ SYNOPSIS -------- [verse] 'git-push' [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] - [--repo=all] [-f | --force] [-v] [<repository> <refspec>...] + [--repo=all] [-f | --force] [-v | --verbose] [<repository> <refspec>...] DESCRIPTION ----------- @@ -95,7 +95,7 @@ the remote repository. transfer spends extra cycles to minimize the number of objects to be sent and meant to be used on slower connection. --v:: +-v, \--verbose:: Run verbosely. include::urls-remotes.txt[] diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index 027ba11bdb..0da8704a25 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -79,7 +79,7 @@ caution. Fetch updates for a named set of remotes in the repository as defined by remotes.<group>. If a named group is not specified on the command line, the configuration parameter remotes.default will get used; if -remotes.default is not defined, all remotes which do not the +remotes.default is not defined, all remotes which do not have the configuration parameter remote.<name>.skipDefaultUpdate set to true will be updated. (See gitlink:git-config[1]). diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 87afa6f8da..050e4eadbb 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -8,8 +8,8 @@ git-reset - Reset current HEAD to the specified state SYNOPSIS -------- [verse] -'git-reset' [--mixed | --soft | --hard] [<commit>] -'git-reset' [--mixed] <commit> [--] <paths>... +'git-reset' [--mixed | --soft | --hard] [-q] [<commit>] +'git-reset' [--mixed] [-q] <commit> [--] <paths>... DESCRIPTION ----------- @@ -45,6 +45,9 @@ OPTIONS switched to. Any changes to tracked files in the working tree since <commit> are lost. +-q:: + Be quiet, only report errors. + <commit>:: Commit to make the current HEAD. diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index 485280423e..989fbf3562 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -20,6 +20,7 @@ SYNOPSIS [ \--not ] [ \--all ] [ \--stdin ] + [ \--quiet ] [ \--topo-order ] [ \--parents ] [ \--timestamp ] @@ -270,6 +271,14 @@ limiting may be applied. In addition to the '<commit>' listed on the command line, read them from the standard input. +--quiet:: + + Don't print anything to standard output. This form of + git-rev-list is primarly meant to allow the caller to + test the exit status to see if a range of objects is fully + connected (or not). It is faster than redirecting stdout + to /dev/null as the output does not have to be formatted. + --cherry-pick:: Omit any commit that introduces the same change as diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index e38b7021b4..659215ac72 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -113,8 +113,7 @@ The --cc option must be repeated for each user you want on the cc list. is not set, this will be prompted for. --suppress-from, --no-suppress-from:: - If this is set, do not add the From: address to the cc: list, if it - shows up in a From: line. + If this is set, do not add the From: address to the cc: list. Default is the value of 'sendemail.suppressfrom' configuration value; if that is unspecified, default to --no-suppress-from. diff --git a/Documentation/git-ssh-fetch.txt b/Documentation/git-ssh-fetch.txt deleted file mode 100644 index 8d3e2ffb2c..0000000000 --- a/Documentation/git-ssh-fetch.txt +++ /dev/null @@ -1,52 +0,0 @@ -git-ssh-fetch(1) -================ - -NAME ----- -git-ssh-fetch - Fetch from a remote repository over ssh connection - - - -SYNOPSIS --------- -'git-ssh-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] commit-id url - -DESCRIPTION ------------ -THIS COMMAND IS DEPRECATED. - -Pulls from a remote repository over ssh connection, invoking -git-ssh-upload on the other end. It functions identically to -git-ssh-upload, aside from which end you run it on. - - -OPTIONS -------- -commit-id:: - Either the hash or the filename under [URL]/refs/ to - pull. - --c:: - Get the commit objects. --t:: - Get trees associated with the commit objects. --a:: - Get all the objects. --v:: - Report what is downloaded. --w:: - Writes the commit-id into the filename under $GIT_DIR/refs/ on - the local end after the transfer is complete. - - -Author ------- -Written by Daniel Barkalow <barkalow@iabervon.org> - -Documentation --------------- -Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>. - -GIT ---- -Part of the gitlink:git[7] suite diff --git a/Documentation/git-ssh-upload.txt b/Documentation/git-ssh-upload.txt deleted file mode 100644 index 5e2ca8dccf..0000000000 --- a/Documentation/git-ssh-upload.txt +++ /dev/null @@ -1,48 +0,0 @@ -git-ssh-upload(1) -================= - -NAME ----- -git-ssh-upload - Push to a remote repository over ssh connection - - -SYNOPSIS --------- -'git-ssh-upload' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] commit-id url - -DESCRIPTION ------------ -THIS COMMAND IS DEPRECATED. - -Pushes from a remote repository over ssh connection, invoking -git-ssh-fetch on the other end. It functions identically to -git-ssh-fetch, aside from which end you run it on. - -OPTIONS -------- -commit-id:: - Id of commit to push. - --c:: - Get the commit objects. --t:: - Get tree associated with the requested commit object. --a:: - Get all the objects. --v:: - Report what is uploaded. --w:: - Writes the commit-id into the filename under [URL]/refs/ on - the remote end after the transfer is complete. - -Author ------- -Written by Daniel Barkalow <barkalow@iabervon.org> - -Documentation --------------- -Documentation by Daniel Barkalow - -GIT ---- -Part of the gitlink:git[7] suite diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 20cf8ff816..19bd25f299 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -148,22 +148,23 @@ with `$Id$` upon check-in. `filter` ^^^^^^^^ -A `filter` attribute can be set to a string value. This names +A `filter` attribute can be set to a string value that names a filter driver specified in the configuration. -A filter driver consists of `clean` command and `smudge` +A filter driver consists of a `clean` command and a `smudge` command, either of which can be left unspecified. Upon -checkout, when `smudge` command is specified, the command is fed -the blob object from its standard input, and its standard output -is used to update the worktree file. Similarly, `clean` command -is used to convert the contents of worktree file upon checkin. +checkout, when the `smudge` command is specified, the command is +fed the blob object from its standard input, and its standard +output is used to update the worktree file. Similarly, the +`clean` command is used to convert the contents of worktree file +upon checkin. -Missing filter driver definition in the config is not an error +A missing filter driver definition in the config is not an error but makes the filter a no-op passthru. The content filtering is done to massage the content into a shape that is more convenient for the platform, filesystem, and -the user to use. The keyword here is "more convenient" and not +the user to use. The key phrase here is "more convenient" and not "turning something unusable into usable". In other words, the intent is that if someone unsets the filter driver definition, or does not have the appropriate filter program, the project diff --git a/Documentation/howto/recover-corrupted-blob-object.txt b/Documentation/howto/recover-corrupted-blob-object.txt new file mode 100644 index 0000000000..323b513ed0 --- /dev/null +++ b/Documentation/howto/recover-corrupted-blob-object.txt @@ -0,0 +1,134 @@ +Date: Fri, 9 Nov 2007 08:28:38 -0800 (PST) +From: Linus Torvalds <torvalds@linux-foundation.org> +Subject: corrupt object on git-gc +Abstract: Some tricks to reconstruct blob objects in order to fix + a corrupted repository. + +On Fri, 9 Nov 2007, Yossi Leybovich wrote: +> +> Did not help still the repository look for this object? +> Any one know how can I track this object and understand which file is it + +So exactly *because* the SHA1 hash is cryptographically secure, the hash +itself doesn't actually tell you anything, in order to fix a corrupt +object you basically have to find the "original source" for it. + +The easiest way to do that is almost always to have backups, and find the +same object somewhere else. Backups really are a good idea, and git makes +it pretty easy (if nothing else, just clone the repository somewhere else, +and make sure that you do *not* use a hard-linked clone, and preferably +not the same disk/machine). + +But since you don't seem to have backups right now, the good news is that +especially with a single blob being corrupt, these things *are* somewhat +debuggable. + +First off, move the corrupt object away, and *save* it. The most common +cause of corruption so far has been memory corruption, but even so, there +are people who would be interested in seeing the corruption - but it's +basically impossible to judge the corruption until we can also see the +original object, so right now the corrupt object is useless, but it's very +interesting for the future, in the hope that you can re-create a +non-corrupt version. + +So: + +> ib]$ mv .git/objects/4b/9458b3786228369c63936db65827de3cc06200 ../ + +This is the right thing to do, although it's usually best to save it under +it's full SHA1 name (you just dropped the "4b" from the result ;). + +Let's see what that tells us: + +> ib]$ git-fsck --full +> broken link from tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8 +> to blob 4b9458b3786228369c63936db65827de3cc06200 +> missing blob 4b9458b3786228369c63936db65827de3cc06200 + +Ok, I removed the "dangling commit" messages, because they are just +messages about the fact that you probably have rebased etc, so they're not +at all interesting. But what remains is still very useful. In particular, +we now know which tree points to it! + +Now you can do + + git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8 + +which will show something like + + 100644 blob 8d14531846b95bfa3564b58ccfb7913a034323b8 .gitignore + 100644 blob ebf9bf84da0aab5ed944264a5db2a65fe3a3e883 .mailmap + 100644 blob ca442d313d86dc67e0a2e5d584b465bd382cbf5c COPYING + 100644 blob ee909f2cc49e54f0799a4739d24c4cb9151ae453 CREDITS + 040000 tree 0f5f709c17ad89e72bdbbef6ea221c69807009f6 Documentation + 100644 blob 1570d248ad9237e4fa6e4d079336b9da62d9ba32 Kbuild + 100644 blob 1c7c229a092665b11cd46a25dbd40feeb31661d9 MAINTAINERS + ... + +and you should now have a line that looks like + + 10064 blob 4b9458b3786228369c63936db65827de3cc06200 my-magic-file + +in the output. This already tells you a *lot* it tells you what file the +corrupt blob came from! + +Now, it doesn't tell you quite enough, though: it doesn't tell what +*version* of the file didn't get correctly written! You might be really +lucky, and it may be the version that you already have checked out in your +working tree, in which case fixing this problem is really simple, just do + + git hash-object -w my-magic-file + +again, and if it outputs the missing SHA1 (4b945..) you're now all done! + +But that's the really lucky case, so let's assume that it was some older +version that was broken. How do you tell which version it was? + +The easiest way to do it is to do + + git log --raw --all --full-history -- subdirectory/my-magic-file + +and that will show you the whole log for that file (please realize that +the tree you had may not be the top-level tree, so you need to figure out +which subdirectory it was in on your own), and because you're asking for +raw output, you'll now get something like + + commit abc + Author: + Date: + .. + :100644 100644 4b9458b... newsha... M somedirectory/my-magic-file + + + commit xyz + Author: + Date: + + .. + :100644 100644 oldsha... 4b9458b... M somedirectory/my-magic-file + +and this actually tells you what the *previous* and *subsequent* versions +of that file were! So now you can look at those ("oldsha" and "newsha" +respectively), and hopefully you have done commits often, and can +re-create the missing my-magic-file version by looking at those older and +newer versions! + +If you can do that, you can now recreate the missing object with + + git hash-object -w <recreated-file> + +and your repository is good again! + +(Btw, you could have ignored the fsck, and started with doing a + + git log --raw --all + +and just looked for the sha of the missing object (4b9458b..) in that +whole thing. It's up to you - git does *have* a lot of information, it is +just missing one particular blob version. + +Trying to recreate trees and especially commits is *much* harder. So you +were lucky that it's a blob. It's quite possible that you can recreate the +thing. + + Linus diff --git a/Documentation/urls.txt b/Documentation/urls.txt index e67f9140ab..4f667382ec 100644 --- a/Documentation/urls.txt +++ b/Documentation/urls.txt @@ -36,5 +36,11 @@ To sync with a local directory, you can use: - file:///path/to/repo.git/ =============================================================== +ifndef::git-clone[] They are mostly equivalent, except when cloning. See gitlink:git-clone[1] for details. +endif::git-clone[] + +ifdef::git-clone[] +They are equivalent, except the former implies --local option. +endif::git-clone[] diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index d99adc6f72..518b7b5c9e 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -475,7 +475,7 @@ Bisecting: 3537 revisions left to test after this If you run "git branch" at this point, you'll see that git has temporarily moved you to a new branch named "bisect". This branch points to a commit (with commit id 65934...) that is reachable from -v2.6.19 but not from v2.6.18. Compile and test it, and see whether +"master" but not from v2.6.18. Compile and test it, and see whether it crashes. Assume it does crash. Then: ------------------------------------------------- @@ -1367,7 +1367,7 @@ If you make a commit that you later wish you hadn't, there are two fundamentally different ways to fix the problem: 1. You can create a new commit that undoes whatever was done - by the previous commit. This is the correct thing if your + by the old commit. This is the correct thing if your mistake has already been made public. 2. You can go back and modify the old commit. You should @@ -1567,9 +1567,9 @@ old history using, for example, $ git log master@{1} ------------------------------------------------- -This lists the commits reachable from the previous version of the head. -This syntax can be used to with any git command that accepts a commit, -not just with git log. Some other examples: +This lists the commits reachable from the previous version of the +"master" branch head. This syntax can be used with any git command +that accepts a commit, not just with git log. Some other examples: ------------------------------------------------- $ git show master@{2} # See where the branch pointed 2, @@ -98,6 +98,8 @@ all:: # Define OLD_ICONV if your library has an old iconv(), where the second # (input buffer pointer) parameter is declared with type (const char **). # +# Define NO_DEFLATE_BOUND if your zlib does not have deflateBound. +# # Define NO_R_TO_GCC_LINKER if your gcc does not like "-R/path/lib" # that tells runtime paths to dynamic libraries; # "-Wl,-rpath=/path/lib" is used instead. @@ -113,6 +115,8 @@ all:: # # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8 # +# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72. +# # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's # MakeMaker (e.g. using ActiveState under Cygwin). # @@ -661,6 +665,10 @@ ifdef OLD_ICONV BASIC_CFLAGS += -DOLD_ICONV endif +ifdef NO_DEFLATE_BOUND + BASIC_CFLAGS += -DNO_DEFLATE_BOUND +endif + ifdef PPC_SHA1 SHA1_HEADER = "ppc/sha1.h" LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o @@ -916,6 +924,7 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS) $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) +builtin-revert.o builtin-runstatus.o wt-status.o: wt-status.h $(LIB_FILE): $(LIB_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) @@ -991,6 +1000,8 @@ test-date$X: date.o ctype.o test-delta$X: diff-delta.o patch-delta.o +test-parse-options$X: parse-options.o + .PRECIOUS: $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS)) test-%$X: test-%.o $(GITLIBS) @@ -1119,12 +1130,13 @@ endif ### Check documentation # check-docs:: - @for v in $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk; \ + @(for v in $(ALL_PROGRAMS) $(BUILT_INS) git gitk; \ do \ case "$$v" in \ git-merge-octopus | git-merge-ours | git-merge-recursive | \ - git-merge-resolve | git-merge-stupid | \ + git-merge-resolve | git-merge-stupid | git-merge-subtree | \ git-add--interactive | git-fsck-objects | git-init-db | \ + git-rebase--interactive | \ git-repo-config | git-fetch--tool ) continue ;; \ esac ; \ test -f "Documentation/$$v.txt" || \ @@ -1135,7 +1147,30 @@ check-docs:: git) ;; \ *) echo "no link: $$v";; \ esac ; \ - done | sort + done; \ + ( \ + sed -e '1,/^__DATA__/d' \ + -e 's/[ ].*//' \ + -e 's/^/listed /' Documentation/cmd-list.perl; \ + ls -1 Documentation/git*txt | \ + sed -e 's|Documentation/|documented |' \ + -e 's/\.txt//'; \ + ) | while read how cmd; \ + do \ + case "$$how,$$cmd" in \ + *,git-citool | \ + *,git-gui | \ + documented,gitattributes | \ + documented,gitignore | \ + documented,gitmodules | \ + documented,git-tools | \ + sentinel,not,matching,is,ok ) continue ;; \ + esac; \ + case " $(ALL_PROGRAMS) $(BUILT_INS) git gitk " in \ + *" $$cmd "*) ;; \ + *) echo "removed but $$how: $$cmd" ;; \ + esac; \ + done ) | sort ### Make sure built-ins do not have dups and listed in git.c # diff --git a/builtin-add.c b/builtin-add.c index 45b14e8a61..cf815a0b8e 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -21,7 +21,6 @@ static const char * const builtin_add_usage[] = { }; static int take_worktree_changes; -static const char *excludes_file; static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) { @@ -61,12 +60,7 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec, memset(dir, 0, sizeof(*dir)); if (!ignored_too) { dir->collect_ignored = 1; - dir->exclude_per_dir = ".gitignore"; - path = git_path("info/exclude"); - if (!access(path, R_OK)) - add_excludes_from_file(dir, path); - if (excludes_file != NULL && !access(excludes_file, R_OK)) - add_excludes_from_file(dir, excludes_file); + setup_standard_excludes(dir); } /* @@ -120,7 +114,7 @@ void add_files_to_cache(int verbose, const char *prefix, const char **files) rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = update_callback; rev.diffopt.format_callback_data = &verbose; - run_diff_files(&rev, 0); + run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); } static void refresh(int verbose, const char **pathspec) @@ -141,18 +135,6 @@ static void refresh(int verbose, const char **pathspec) free(seen); } -static int git_add_config(const char *var, const char *value) -{ - if (!strcmp(var, "core.excludesfile")) { - if (!value) - die("core.excludesfile without value"); - excludes_file = xstrdup(value); - return 0; - } - - return git_default_config(var, value); -} - int interactive_add(void) { const char *argv[2] = { "add--interactive", NULL }; @@ -193,7 +175,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) exit(interactive_add()); } - git_config(git_add_config); + git_config(git_default_config); newfd = hold_locked_index(&lock_file, 1); diff --git a/builtin-apply.c b/builtin-apply.c index 8411b38c79..91f8752ff7 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -683,7 +683,6 @@ static char *git_header_name(char *line, int llen) } } } - return NULL; } /* Verify that we recognize the lines following a git header */ @@ -1993,7 +1992,7 @@ static int verify_index_match(struct cache_entry *ce, struct stat *st) return -1; return 0; } - return ce_match_stat(ce, st, 1); + return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID); } static int check_patch(struct patch *patch, struct patch *prev_patch) diff --git a/builtin-blame.c b/builtin-blame.c index aedc294eac..ba80bf8942 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -2295,6 +2295,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) else if (i != argc - 1) usage(blame_usage); /* garbage at end */ + setup_work_tree(); if (!has_path_in_work_tree(path)) die("cannot stat path %s: %s", path, strerror(errno)); @@ -2342,6 +2343,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) * do not default to HEAD, but use the working tree * or "--contents". */ + setup_work_tree(); sb.final = fake_working_tree_commit(path, contents_from); add_pending_object(&revs, &(sb.final->object), ":"); } diff --git a/builtin-diff.c b/builtin-diff.c index f77352b40d..f557d21929 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -200,15 +200,11 @@ static void refresh_index_quietly(void) discard_cache(); read_cache(); refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED); - if (active_cache_changed) { - if (write_cache(fd, active_cache, active_nr) || - close(fd) || - commit_locked_index(lock_file)) - ; /* - * silently ignore it -- we haven't mucked - * with the real index. - */ - } + + if (active_cache_changed && + !write_cache(fd, active_cache, active_nr) && !close(fd)) + commit_locked_index(lock_file); + rollback_lock_file(lock_file); } diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c index 6a78517958..ed60847d9f 100644 --- a/builtin-fetch--tool.c +++ b/builtin-fetch--tool.c @@ -435,9 +435,7 @@ static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_resu cp++; if (!*cp) break; - np = strchr(cp, '\n'); - if (!np) - np = cp + strlen(cp); + np = strchrnul(cp, '\n'); if (pass) { lrr_list[i].line = cp; lrr_list[i].name = cp + 41; @@ -461,9 +459,7 @@ static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_resu rref++; if (!*rref) break; - next = strchr(rref, '\n'); - if (!next) - next = rref + strlen(rref); + next = strchrnul(rref, '\n'); rreflen = next - rref; for (i = 0; i < lrr_count; i++) { diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index 862652be92..bb1742f1a2 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -32,7 +32,7 @@ static const char fetch_pack_usage[] = #define MAX_IN_VAIN 256 static struct commit_list *rev_list; -static int non_common_revs, multi_ack, use_thin_pack, use_sideband; +static int non_common_revs, multi_ack, use_sideband; static void rev_list_push(struct commit *commit, int mark) { @@ -178,7 +178,7 @@ static int find_common(int fd[2], unsigned char *result_sha1, (multi_ack ? " multi_ack" : ""), (use_sideband == 2 ? " side-band-64k" : ""), (use_sideband == 1 ? " side-band" : ""), - (use_thin_pack ? " thin-pack" : ""), + (args.use_thin_pack ? " thin-pack" : ""), (args.no_progress ? " no-progress" : ""), " ofs-delta"); else diff --git a/builtin-fetch.c b/builtin-fetch.c index 5f5b59bfdb..be9e3ea2bc 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -8,10 +8,12 @@ #include "path-list.h" #include "remote.h" #include "transport.h" +#include "run-command.h" static const char fetch_usage[] = "git-fetch [-a | --append] [--upload-pack <upload-pack>] [-f | --force] [--no-tags] [-t | --tags] [-k | --keep] [-u | --update-head-ok] [--depth <depth>] [-v | --verbose] [<repository> <refspec>...]"; static int append, force, tags, no_tags, update_head_ok, verbose, quiet; +static const char *depth; static char *default_rla = NULL; static struct transport *transport; @@ -152,6 +154,7 @@ static int s_update_ref(const char *action, } #define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3) +#define REFCOL_WIDTH 10 static int update_local_ref(struct ref *ref, const char *remote, @@ -181,8 +184,9 @@ static int update_local_ref(struct ref *ref, if (!hashcmp(ref->old_sha1, ref->new_sha1)) { if (verbose) - sprintf(display, "= %-*s %s -> %s", SUMMARY_WIDTH, - "[up to date]", remote, pretty_ref); + sprintf(display, "= %-*s %-*s -> %s", SUMMARY_WIDTH, + "[up to date]", REFCOL_WIDTH, remote, + pretty_ref); return 0; } @@ -194,15 +198,17 @@ static int update_local_ref(struct ref *ref, * If this is the head, and it's not okay to update * the head, and the old value of the head isn't empty... */ - sprintf(display, "! %-*s %s -> %s (can't fetch in current branch)", - SUMMARY_WIDTH, "[rejected]", remote, pretty_ref); + sprintf(display, "! %-*s %-*s -> %s (can't fetch in current branch)", + SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote, + pretty_ref); return 1; } if (!is_null_sha1(ref->old_sha1) && !prefixcmp(ref->name, "refs/tags/")) { - sprintf(display, "- %-*s %s -> %s", - SUMMARY_WIDTH, "[tag update]", remote, pretty_ref); + sprintf(display, "- %-*s %-*s -> %s", + SUMMARY_WIDTH, "[tag update]", REFCOL_WIDTH, remote, + pretty_ref); return s_update_ref("updating tag", ref, 0); } @@ -220,8 +226,8 @@ static int update_local_ref(struct ref *ref, what = "[new branch]"; } - sprintf(display, "* %-*s %s -> %s", - SUMMARY_WIDTH, what, remote, pretty_ref); + sprintf(display, "* %-*s %-*s -> %s", SUMMARY_WIDTH, what, + REFCOL_WIDTH, remote, pretty_ref); return s_update_ref(msg, ref, 0); } @@ -230,20 +236,21 @@ static int update_local_ref(struct ref *ref, strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); strcat(quickref, ".."); strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); - sprintf(display, " %-*s %s -> %s (fast forward)", - SUMMARY_WIDTH, quickref, remote, pretty_ref); + sprintf(display, " %-*s %-*s -> %s", SUMMARY_WIDTH, quickref, + REFCOL_WIDTH, remote, pretty_ref); return s_update_ref("fast forward", ref, 1); } else if (force || ref->force) { char quickref[84]; strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); strcat(quickref, "..."); strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); - sprintf(display, "+ %-*s %s -> %s (forced update)", - SUMMARY_WIDTH, quickref, remote, pretty_ref); + sprintf(display, "+ %-*s %-*s -> %s (forced update)", + SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote, pretty_ref); return s_update_ref("forced-update", ref, 1); } else { - sprintf(display, "! %-*s %s -> %s (non fast forward)", - SUMMARY_WIDTH, "[rejected]", remote, pretty_ref); + sprintf(display, "! %-*s %-*s -> %s (non fast forward)", + SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote, + pretty_ref); return 1; } } @@ -330,9 +337,72 @@ static void store_updated_refs(const char *url, struct ref *ref_map) fclose(fp); } +/* + * We would want to bypass the object transfer altogether if + * everything we are going to fetch already exists and connected + * locally. + * + * The refs we are going to fetch are in to_fetch (nr_heads in + * total). If running + * + * $ git-rev-list --objects to_fetch[0] to_fetch[1] ... --not --all + * + * does not error out, that means everything reachable from the + * refs we are going to fetch exists and is connected to some of + * our existing refs. + */ +static int quickfetch(struct ref *ref_map) +{ + struct child_process revlist; + struct ref *ref; + char **argv; + int i, err; + + /* + * If we are deepening a shallow clone we already have these + * objects reachable. Running rev-list here will return with + * a good (0) exit status and we'll bypass the fetch that we + * really need to perform. Claiming failure now will ensure + * we perform the network exchange to deepen our history. + */ + if (depth) + return -1; + + for (i = 0, ref = ref_map; ref; ref = ref->next) + i++; + if (!i) + return 0; + + argv = xmalloc(sizeof(*argv) * (i + 6)); + i = 0; + argv[i++] = xstrdup("rev-list"); + argv[i++] = xstrdup("--quiet"); + argv[i++] = xstrdup("--objects"); + for (ref = ref_map; ref; ref = ref->next) + argv[i++] = xstrdup(sha1_to_hex(ref->old_sha1)); + argv[i++] = xstrdup("--not"); + argv[i++] = xstrdup("--all"); + argv[i++] = NULL; + + memset(&revlist, 0, sizeof(revlist)); + revlist.argv = (const char**)argv; + revlist.git_cmd = 1; + revlist.no_stdin = 1; + revlist.no_stdout = 1; + revlist.no_stderr = 1; + err = run_command(&revlist); + + for (i = 0; argv[i]; i++) + free(argv[i]); + free(argv); + return err; +} + static int fetch_refs(struct transport *transport, struct ref *ref_map) { - int ret = transport_fetch_refs(transport, ref_map); + int ret = quickfetch(ref_map); + if (ret) + ret = transport_fetch_refs(transport, ref_map); if (!ret) store_updated_refs(transport->url, ref_map); transport_unlock_pack(transport); @@ -384,7 +454,7 @@ static struct ref *find_non_local_tags(struct transport *transport, if (!path_list_has_path(&existing_refs, ref_name) && !path_list_has_path(&new_refs, ref_name) && - lookup_object(ref->old_sha1)) { + has_sha1_file(ref->old_sha1)) { path_list_insert(ref_name, &new_refs); rm = alloc_ref(strlen(ref_name) + 1); @@ -468,7 +538,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) static const char **refs = NULL; int ref_nr = 0; int cmd_len = 0; - const char *depth = NULL, *upload_pack = NULL; + const char *upload_pack = NULL; int keep = 0; for (i = 1; i < argc; i++) { diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index da8c7948e6..bfde2e2bbe 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -304,7 +304,7 @@ static const char *find_wholine(const char *who, int wholen, const char *buf, un if (!eol) return ""; eol++; - if (eol[1] == '\n') + if (*eol == '\n') return ""; /* end of header */ buf = eol; } @@ -847,7 +847,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) OPT_GROUP(""), OPT_INTEGER( 0 , "count", &maxcount, "show only <n> matched refs"), OPT_STRING( 0 , "format", &format, "format", "format to use for the output"), - OPT_CALLBACK(0 , "sort", &sort_tail, "key", + OPT_CALLBACK(0 , "sort", sort_tail, "key", "field name to sort on", &opt_parse_sort), OPT_END(), }; diff --git a/builtin-log.c b/builtin-log.c index 8b2bf632c5..c5230106a0 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -273,6 +273,8 @@ static int istitlechar(char c) static char *extra_headers = NULL; static int extra_headers_size = 0; static const char *fmt_patch_suffix = ".patch"; +static int numbered = 0; +static int auto_number = 0; static int git_format_config(const char *var, const char *value) { @@ -297,6 +299,15 @@ static int git_format_config(const char *var, const char *value) if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) { return 0; } + if (!strcmp(var, "format.numbered")) { + if (!strcasecmp(value, "auto")) { + auto_number = 1; + return 0; + } + + numbered = git_config_bool(var, value); + return 0; + } return git_log_config(var, value); } @@ -466,7 +477,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) struct rev_info rev; int nr = 0, total, i, j; int use_stdout = 0; - int numbered = 0; int start_number = -1; int keep_subject = 0; int numbered_files = 0; /* _just_ numbers */ @@ -503,6 +513,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--numbered")) numbered = 1; + else if (!strcmp(argv[i], "-N") || + !strcmp(argv[i], "--no-numbered")) { + numbered = 0; + auto_number = 0; + } else if (!prefixcmp(argv[i], "--start-number=")) start_number = strtol(argv[i] + 15, NULL, 10); else if (!strcmp(argv[i], "--numbered-files")) @@ -642,6 +657,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) list[nr - 1] = commit; } total = nr; + if (!keep_subject && auto_number && total > 1) + numbered = 1; if (numbered) rev.total = total + start_number - 1; rev.add_signoff = add_signoff; diff --git a/builtin-ls-files.c b/builtin-ls-files.c index b70da1863b..7f60709830 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -387,8 +387,8 @@ static void overlay_tree(const char *tree_name, const char *prefix) static const char ls_files_usage[] = "git-ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* " "[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] " - "[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] " - "[--] [<file>]*"; + "[ --exclude-per-directory=<filename> ] [--exclude-standard] " + "[--full-name] [--abbrev] [--] [<file>]*"; int cmd_ls_files(int argc, const char **argv, const char *prefix) { @@ -496,6 +496,11 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) dir.exclude_per_dir = arg + 24; continue; } + if (!strcmp(arg, "--exclude-standard")) { + exc_given = 1; + setup_standard_excludes(&dir); + continue; + } if (!strcmp(arg, "--full-name")) { prefix_offset = 0; continue; @@ -525,11 +530,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) break; } - if (require_work_tree && !is_inside_work_tree()) { - const char *work_tree = get_git_work_tree(); - if (!work_tree || chdir(work_tree)) - die("This operation must be run in a work tree"); - } + if (require_work_tree && !is_inside_work_tree()) + setup_work_tree(); pathspec = get_pathspec(prefix, argv + i); diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c index 74b04706f3..46b27cdaea 100644 --- a/builtin-mailsplit.c +++ b/builtin-mailsplit.c @@ -101,20 +101,29 @@ static int populate_maildir_list(struct path_list *list, const char *path) { DIR *dir; struct dirent *dent; + char name[PATH_MAX]; + char *subs[] = { "cur", "new", NULL }; + char **sub; + + for (sub = subs; *sub; ++sub) { + snprintf(name, sizeof(name), "%s/%s", path, *sub); + if ((dir = opendir(name)) == NULL) { + if (errno == ENOENT) + continue; + error("cannot opendir %s (%s)", name, strerror(errno)); + return -1; + } - if ((dir = opendir(path)) == NULL) { - error("cannot opendir %s (%s)", path, strerror(errno)); - return -1; - } + while ((dent = readdir(dir)) != NULL) { + if (dent->d_name[0] == '.') + continue; + snprintf(name, sizeof(name), "%s/%s", *sub, dent->d_name); + path_list_insert(name, list); + } - while ((dent = readdir(dir)) != NULL) { - if (dent->d_name[0] == '.') - continue; - path_list_insert(dent->d_name, list); + closedir(dir); } - closedir(dir); - return 0; } @@ -122,19 +131,17 @@ static int split_maildir(const char *maildir, const char *dir, int nr_prec, int skip) { char file[PATH_MAX]; - char curdir[PATH_MAX]; char name[PATH_MAX]; int ret = -1; int i; struct path_list list = {NULL, 0, 0, 1}; - snprintf(curdir, sizeof(curdir), "%s/cur", maildir); - if (populate_maildir_list(&list, curdir) < 0) + if (populate_maildir_list(&list, maildir) < 0) goto out; for (i = 0; i < list.nr; i++) { FILE *f; - snprintf(file, sizeof(file), "%s/%s", curdir, list.items[i].path); + snprintf(file, sizeof(file), "%s/%s", maildir, list.items[i].path); f = fopen(file, "r"); if (!f) { error("cannot open mail %s (%s)", file, strerror(errno)); @@ -152,10 +159,9 @@ static int split_maildir(const char *maildir, const char *dir, fclose(f); } - path_list_clear(&list, 1); - ret = skip; out: + path_list_clear(&list, 1); return ret; } diff --git a/builtin-push.c b/builtin-push.c index 2c561953fc..6d1da07c46 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -115,6 +115,8 @@ int cmd_push(int argc, const char **argv, const char *prefix) flags |= TRANSPORT_PUSH_FORCE; if (dry_run) flags |= TRANSPORT_PUSH_DRY_RUN; + if (verbose) + flags |= TRANSPORT_PUSH_VERBOSE; if (tags) add_refspec("refs/tags/*"); if (all) diff --git a/builtin-reset.c b/builtin-reset.c index 5467e36c73..4c61025aae 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -18,7 +18,7 @@ #include "tree.h" static const char builtin_reset_usage[] = -"git-reset [--mixed | --soft | --hard] [<commit-ish>] [ [--] <paths>...]"; +"git-reset [--mixed | --soft | --hard] [-q] [<commit-ish>] [ [--] <paths>...]"; static char *args_to_str(const char **argv) { @@ -46,26 +46,14 @@ static inline int is_merge(void) static int unmerged_files(void) { - char b; - ssize_t len; - struct child_process cmd; - const char *argv_ls_files[] = {"ls-files", "--unmerged", NULL}; - - memset(&cmd, 0, sizeof(cmd)); - cmd.argv = argv_ls_files; - cmd.git_cmd = 1; - cmd.out = -1; - - if (start_command(&cmd)) - die("Could not run sub-command: git ls-files"); - - len = xread(cmd.out, &b, 1); - if (len < 0) - die("Could not read output from git ls-files: %s", - strerror(errno)); - finish_command(&cmd); - - return len; + int i; + read_cache(); + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + if (ce_stage(ce)) + return 1; + } + return 0; } static int reset_index_file(const unsigned char *sha1, int is_hard_reset) @@ -107,26 +95,34 @@ static void print_new_head_line(struct commit *commit) printf("\n"); } -static int update_index_refresh(void) +static int update_index_refresh(int fd, struct lock_file *index_lock) { - const char *argv_update_index[] = {"update-index", "--refresh", NULL}; - return run_command_v_opt(argv_update_index, RUN_GIT_CMD); -} + int result; -struct update_cb_data { - int index_fd; - struct lock_file *lock; - int exit_code; -}; + if (!index_lock) { + index_lock = xcalloc(1, sizeof(struct lock_file)); + fd = hold_locked_index(index_lock, 1); + } + + if (read_cache() < 0) + return error("Could not read index"); + result = refresh_cache(0) ? 1 : 0; + if (write_cache(fd, active_cache, active_nr) || + close(fd) || + commit_locked_index(index_lock)) + return error ("Could not refresh index"); + return result; +} static void update_index_from_diff(struct diff_queue_struct *q, struct diff_options *opt, void *data) { int i; - struct update_cb_data *cb = data; + int *discard_flag = data; /* do_diff_cache() mangled the index */ discard_cache(); + *discard_flag = 1; read_cache(); for (i = 0; i < q->nr; i++) { @@ -140,34 +136,33 @@ static void update_index_from_diff(struct diff_queue_struct *q, } else remove_file_from_cache(one->path); } - - cb->exit_code = write_cache(cb->index_fd, active_cache, active_nr) || - close(cb->index_fd) || - commit_locked_index(cb->lock); } static int read_from_tree(const char *prefix, const char **argv, unsigned char *tree_sha1) { + struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); + int index_fd, index_was_discarded = 0; struct diff_options opt; - struct update_cb_data cb; memset(&opt, 0, sizeof(opt)); diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt); opt.output_format = DIFF_FORMAT_CALLBACK; opt.format_callback = update_index_from_diff; - opt.format_callback_data = &cb; + opt.format_callback_data = &index_was_discarded; - cb.lock = xcalloc(1, sizeof(struct lock_file)); - cb.index_fd = hold_locked_index(cb.lock, 1); - cb.exit_code = 0; + index_fd = hold_locked_index(lock, 1); + index_was_discarded = 0; read_cache(); if (do_diff_cache(tree_sha1, &opt)) return 1; diffcore_std(&opt); diff_flush(&opt); - return cb.exit_code; + if (!index_was_discarded) + /* The index is still clobbered from do_diff_cache() */ + discard_cache(); + return update_index_refresh(index_fd, lock); } static void prepend_reflog_action(const char *action, char *buf, size_t size) @@ -185,7 +180,7 @@ static const char *reset_type_names[] = { "mixed", "soft", "hard", NULL }; int cmd_reset(int argc, const char **argv, const char *prefix) { - int i = 1, reset_type = NONE, update_ref_status = 0; + int i = 1, reset_type = NONE, update_ref_status = 0, quiet = 0; const char *rev = "HEAD"; unsigned char sha1[20], *orig = NULL, sha1_orig[20], *old_orig = NULL, sha1_old_orig[20]; @@ -197,7 +192,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) reflog_action = args_to_str(argv); setenv("GIT_REFLOG_ACTION", reflog_action, 0); - if (i < argc) { + while (i < argc) { if (!strcmp(argv[i], "--mixed")) { reset_type = MIXED; i++; @@ -210,6 +205,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix) reset_type = HARD; i++; } + else if (!strcmp(argv[i], "-q")) { + quiet = 1; + i++; + } + else + break; } if (i < argc && argv[i][0] != '-') @@ -237,9 +238,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) else if (reset_type != NONE) die("Cannot do %s reset with paths.", reset_type_names[reset_type]); - if (read_from_tree(prefix, argv + i, sha1)) - return 1; - return update_index_refresh() ? 1 : 0; + return read_from_tree(prefix, argv + i, sha1); } if (reset_type == NONE) reset_type = MIXED; /* by default */ @@ -270,13 +269,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix) switch (reset_type) { case HARD: - if (!update_ref_status) + if (!update_ref_status && !quiet) print_new_head_line(commit); break; case SOFT: /* Nothing else to do. */ break; case MIXED: /* Report what has not been updated. */ - update_index_refresh(); + update_index_refresh(0, NULL); break; } diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 697046723f..f5149e59b7 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -26,6 +26,7 @@ static const char rev_list_usage[] = " --remove-empty\n" " --all\n" " --stdin\n" +" --quiet\n" " ordering output:\n" " --topo-order\n" " --date-order\n" @@ -50,6 +51,7 @@ static int show_timestamp; static int hdr_termination; static const char *header_prefix; +static void finish_commit(struct commit *commit); static void show_commit(struct commit *commit) { if (show_timestamp) @@ -93,6 +95,11 @@ static void show_commit(struct commit *commit) strbuf_release(&buf); } maybe_flush_or_die(stdout, "stdout"); + finish_commit(commit); +} + +static void finish_commit(struct commit *commit) +{ if (commit->parents) { free_commit_list(commit->parents); commit->parents = NULL; @@ -101,6 +108,12 @@ static void show_commit(struct commit *commit) commit->buffer = NULL; } +static void finish_object(struct object_array_entry *p) +{ + if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1)) + die("missing blob object '%s'", sha1_to_hex(p->item->sha1)); +} + static void show_object(struct object_array_entry *p) { /* An object with name "foo\n0000000..." can be used to @@ -108,9 +121,7 @@ static void show_object(struct object_array_entry *p) */ const char *ep = strchr(p->name, '\n'); - if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1)) - die("missing blob object '%s'", sha1_to_hex(p->item->sha1)); - + finish_object(p); if (ep) { printf("%s %.*s\n", sha1_to_hex(p->item->sha1), (int) (ep - p->name), @@ -527,6 +538,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) int read_from_stdin = 0; int bisect_show_vars = 0; int bisect_find_all = 0; + int quiet = 0; git_config(git_default_config); init_revisions(&revs, prefix); @@ -565,6 +577,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) read_revisions_from_stdin(&revs); continue; } + if (!strcmp(arg, "--quiet")) { + quiet = 1; + continue; + } usage(rev_list_usage); } @@ -640,7 +656,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) } } - traverse_commit_list(&revs, show_commit, show_object); + traverse_commit_list(&revs, + quiet ? finish_commit : show_commit, + quiet ? finish_object : show_object); return 0; } diff --git a/builtin-revert.c b/builtin-revert.c index 62ab1fa1f4..365b330f9e 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -246,7 +246,9 @@ static int revert_or_cherry_pick(int argc, const char **argv) if (no_commit) { /* * We do not intend to commit immediately. We just want to - * merge the differences in. + * merge the differences in, so let's compute the tree + * that represents the "current" state for merge-recursive + * to work on. */ if (write_tree(head, 0, NULL)) die ("Your index file is unmerged."); @@ -256,7 +258,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) if (get_sha1("HEAD", head)) die ("You do not have a valid HEAD"); wt_status_prepare(&s); - if (s.commitable || s.workdir_dirty) + if (s.commitable) die ("Dirty index: cannot %s", me); discard_cache(); } diff --git a/builtin-rm.c b/builtin-rm.c index bca2bd9703..a3d25e6a57 100644 --- a/builtin-rm.c +++ b/builtin-rm.c @@ -155,6 +155,9 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (!argc) usage_with_options(builtin_rm_usage, builtin_rm_options); + if (!index_only) + setup_work_tree(); + pathspec = get_pathspec(prefix, argv); seen = NULL; for (i = 0; pathspec[i] ; i++) diff --git a/builtin-tag.c b/builtin-tag.c index 66e5a58307..cbb0f04e85 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -81,17 +81,16 @@ static int show_reference(const char *refname, const unsigned char *sha1, } printf("%-15s ", refname); - sp = buf = read_sha1_file(sha1, &type, &size); - if (!buf) + buf = read_sha1_file(sha1, &type, &size); + if (!buf || !size) return 0; - if (!size) { + + /* skip header */ + sp = strstr(buf, "\n\n"); + if (!sp) { free(buf); return 0; } - /* skip header */ - while (sp + 1 < buf + size && - !(sp[0] == '\n' && sp[1] == '\n')) - sp++; /* only take up to "lines" lines, and strip the signature */ for (i = 0, sp += 2; i < filter->lines && sp < buf + size && @@ -247,9 +246,37 @@ static int git_tag_config(const char *var, const char *value) return git_default_config(var, value); } +static void write_tag_body(int fd, const unsigned char *sha1) +{ + unsigned long size; + enum object_type type; + char *buf, *sp, *eob; + size_t len; + + buf = read_sha1_file(sha1, &type, &size); + if (!buf) + return; + /* skip header */ + sp = strstr(buf, "\n\n"); + + if (!sp || !size || type != OBJ_TAG) { + free(buf); + return; + } + sp += 2; /* skip the 2 LFs */ + eob = strstr(sp, "\n" PGP_SIGNATURE "\n"); + if (eob) + len = eob - sp; + else + len = buf + size - sp; + write_or_die(fd, sp, len); + + free(buf); +} + static void create_tag(const unsigned char *object, const char *tag, struct strbuf *buf, int message, int sign, - unsigned char *result) + unsigned char *prev, unsigned char *result) { enum object_type type; char header_buf[1024]; @@ -282,7 +309,11 @@ static void create_tag(const unsigned char *object, const char *tag, if (fd < 0) die("could not create file '%s': %s", path, strerror(errno)); - write_or_die(fd, tag_template, strlen(tag_template)); + + if (!is_null_sha1(prev)) + write_tag_body(fd, prev); + else + write_or_die(fd, tag_template, strlen(tag_template)); close(fd); launch_editor(path, buf); @@ -419,7 +450,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) die("tag '%s' already exists", tag); if (annotate) - create_tag(object, tag, &buf, message, sign, object); + create_tag(object, tag, &buf, message, sign, prev, object); lock = lock_any_ref_for_update(ref, prev, 0); if (!lock) @@ -23,7 +23,8 @@ static void add_to_ref_list(const unsigned char *sha1, const char *name, } /* returns an fd */ -int read_bundle_header(const char *path, struct bundle_header *header) { +int read_bundle_header(const char *path, struct bundle_header *header) +{ char buffer[1024]; int fd; long fpos; @@ -7,7 +7,7 @@ #include SHA1_HEADER #include <zlib.h> -#if ZLIB_VERNUM < 0x1200 +#if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200 #define deflateBound(c,s) ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11) #endif @@ -175,8 +175,8 @@ extern struct index_state the_index; #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path)) #define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose)) #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL) -#define ce_match_stat(ce, st, really) ie_match_stat(&the_index, (ce), (st), (really)) -#define ce_modified(ce, st, really) ie_modified(&the_index, (ce), (st), (really)) +#define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options)) +#define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options)) #endif enum object_type { @@ -222,6 +222,7 @@ extern const char *get_git_work_tree(void); #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES" extern const char **get_pathspec(const char *prefix, const char **pathspec); +extern void setup_work_tree(void); extern const char *setup_git_directory_gently(int *); extern const char *setup_git_directory(void); extern const char *prefix_path(const char *prefix, int len, const char *path); @@ -267,8 +268,14 @@ extern int remove_file_from_index(struct index_state *, const char *path); extern int add_file_to_index(struct index_state *, const char *path, int verbose); extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh); extern int ce_same_name(struct cache_entry *a, struct cache_entry *b); -extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, int); -extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int); + +/* do stat comparison even if CE_VALID is true */ +#define CE_MATCH_IGNORE_VALID 01 +/* do not check the contents but report dirty on racily-clean entries */ +#define CE_MATCH_RACY_IS_DIRTY 02 +extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, unsigned int); +extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, unsigned int); + extern int ce_path_match(const struct cache_entry *ce, const char **pathspec); extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path); extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object); @@ -574,6 +581,7 @@ extern int pager_in_use; extern int pager_use_color; extern char *editor_program; +extern char *excludes_file; /* base85 */ int decode_85(char *dst, const char *line, int linelen); diff --git a/check-racy.c b/check-racy.c index d6a08b4a55..00d92a1663 100644 --- a/check-racy.c +++ b/check-racy.c @@ -18,7 +18,7 @@ int main(int ac, char **av) if (ce_match_stat(ce, &st, 0)) dirty++; - else if (ce_match_stat(ce, &st, 2)) + else if (ce_match_stat(ce, &st, CE_MATCH_RACY_IS_DIRTY)) racy++; else clean++; @@ -431,6 +431,13 @@ int git_default_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.excludesfile")) { + if (!value) + die("core.excludesfile without value"); + excludes_file = xstrdup(value); + return 0; + } + /* Add other config variables here and to Documentation/config.txt. */ return 0; } diff --git a/config.mak.in b/config.mak.in index a3032e389f..776b805659 100644 --- a/config.mak.in +++ b/config.mak.in @@ -38,3 +38,4 @@ NO_STRCASESTR=@NO_STRCASESTR@ NO_STRLCPY=@NO_STRLCPY@ NO_SETENV=@NO_SETENV@ NO_ICONV=@NO_ICONV@ +NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@ diff --git a/configure.ac b/configure.ac index ed7cc895d2..53e9a17c73 100644 --- a/configure.ac +++ b/configure.ac @@ -73,7 +73,7 @@ fi \ AC_ARG_WITH([lib], [AS_HELP_STRING([--with-lib=ARG], [ARG specifies alternative name for lib directory])], - [if test "$withval" = "no" -o "$withval" = "yes"; then \ + [if test "$withval" = "no" || test "$withval" = "yes"; then \ AC_MSG_WARN([You should provide name for --with-lib=ARG]); \ else \ GIT_CONF_APPEND_LINE(lib=$withval); \ @@ -182,6 +182,26 @@ AC_SUBST(NEEDS_LIBICONV) AC_SUBST(NO_ICONV) test -n "$NEEDS_LIBICONV" && LIBS="$LIBS -liconv" # +# Define NO_DEFLATE_BOUND if deflateBound is missing from zlib. +AC_DEFUN([ZLIBTEST_SRC], [ +#include <zlib.h> + +int main(void) +{ + deflateBound(0, 0); + return 0; +} +]) +AC_MSG_CHECKING([for deflateBound in -lz]) +old_LIBS="$LIBS" +LIBS="$LIBS -lz" +AC_LINK_IFELSE(ZLIBTEST_SRC, + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + NO_DEFLATE_BOUND=yes]) +LIBS="$old_LIBS" +AC_SUBST(NO_DEFLATE_BOUND) +# # Define NEEDS_SOCKET if linking with libc is not enough (SunOS, # Patrick Mauritz). AC_CHECK_LIB([c], [socket], @@ -245,9 +265,9 @@ AC_RUN_IFELSE( [AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT], [[char buf[64]; if (sprintf(buf, "%lld%hhd%jd%zd%td", (long long int)1, (char)2, (intmax_t)3, (size_t)4, (ptrdiff_t)5) != 5) - exit(1); + return 1; else if (strcmp(buf, "12345")) - exit(2);]])], + return 2;]])], [ac_cv_c_c99_format=yes], [ac_cv_c_c99_format=no]) ]) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index bf33f74b70..c148b5ab7d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -71,6 +71,79 @@ def isP4Exec(kind): a plus sign, it is also executable""" return (re.search(r"(^[cku]?x)|\+.*x", kind) != None) +def setP4ExecBit(file, mode): + # Reopens an already open file and changes the execute bit to match + # the execute bit setting in the passed in mode. + + p4Type = "+x" + + if not isModeExec(mode): + p4Type = getP4OpenedType(file) + p4Type = re.sub('^([cku]?)x(.*)', '\\1\\2', p4Type) + p4Type = re.sub('(.*?\+.*?)x(.*?)', '\\1\\2', p4Type) + if p4Type[-1] == "+": + p4Type = p4Type[0:-1] + + system("p4 reopen -t %s %s" % (p4Type, file)) + +def getP4OpenedType(file): + # Returns the perforce file type for the given file. + + result = read_pipe("p4 opened %s" % file) + match = re.match(".*\((.+)\)$", result) + if match: + return match.group(1) + else: + die("Could not determine file type for %s" % file) + +def diffTreePattern(): + # This is a simple generator for the diff tree regex pattern. This could be + # a class variable if this and parseDiffTreeEntry were a part of a class. + pattern = re.compile(':(\d+) (\d+) (\w+) (\w+) ([A-Z])(\d+)?\t(.*?)((\t(.*))|$)') + while True: + yield pattern + +def parseDiffTreeEntry(entry): + """Parses a single diff tree entry into its component elements. + + See git-diff-tree(1) manpage for details about the format of the diff + output. This method returns a dictionary with the following elements: + + src_mode - The mode of the source file + dst_mode - The mode of the destination file + src_sha1 - The sha1 for the source file + dst_sha1 - The sha1 fr the destination file + status - The one letter status of the diff (i.e. 'A', 'M', 'D', etc) + status_score - The score for the status (applicable for 'C' and 'R' + statuses). This is None if there is no score. + src - The path for the source file. + dst - The path for the destination file. This is only present for + copy or renames. If it is not present, this is None. + + If the pattern is not matched, None is returned.""" + + match = diffTreePattern().next().match(entry) + if match: + return { + 'src_mode': match.group(1), + 'dst_mode': match.group(2), + 'src_sha1': match.group(3), + 'dst_sha1': match.group(4), + 'status': match.group(5), + 'status_score': match.group(6), + 'src': match.group(7), + 'dst': match.group(10) + } + return None + +def isModeExec(mode): + # Returns True if the given git mode represents an executable file, + # otherwise False. + return mode[-3:] == "755" + +def isModeExecChanged(src_mode, dst_mode): + return isModeExec(src_mode) != isModeExec(dst_mode) + def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): cmd = "p4 -G %s" % cmd if verbose: @@ -494,18 +567,23 @@ class P4Submit(Command): else: print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id)) diffOpts = ("", "-M")[self.detectRename] - diff = read_pipe_lines("git diff-tree -r --name-status %s \"%s^\" \"%s\"" % (diffOpts, id, id)) + diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id)) filesToAdd = set() filesToDelete = set() editedFiles = set() + filesToChangeExecBit = {} for line in diff: - modifier = line[0] - path = line[1:].strip() + diff = parseDiffTreeEntry(line) + modifier = diff['status'] + path = diff['src'] if modifier == "M": system("p4 edit \"%s\"" % path) + if isModeExecChanged(diff['src_mode'], diff['dst_mode']): + filesToChangeExecBit[path] = diff['dst_mode'] editedFiles.add(path) elif modifier == "A": filesToAdd.add(path) + filesToChangeExecBit[path] = diff['dst_mode'] if path in filesToDelete: filesToDelete.remove(path) elif modifier == "D": @@ -513,9 +591,11 @@ class P4Submit(Command): if path in filesToAdd: filesToAdd.remove(path) elif modifier == "R": - src, dest = line.strip().split("\t")[1:3] + src, dest = diff['src'], diff['dst'] system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest)) system("p4 edit \"%s\"" % (dest)) + if isModeExecChanged(diff['src_mode'], diff['dst_mode']): + filesToChangeExecBit[dest] = diff['dst_mode'] os.unlink(dest) editedFiles.add(dest) filesToDelete.add(src) @@ -568,6 +648,11 @@ class P4Submit(Command): system("p4 revert \"%s\"" % f) system("p4 delete \"%s\"" % f) + # Set/clear executable bits + for f in filesToChangeExecBit.keys(): + mode = filesToChangeExecBit[f] + setP4ExecBit(f, mode) + logMessage = "" if not self.directSubmit: logMessage = extractLogMessageFromGitCommit(id) diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 2aa9bb501c..7511ea0797 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -2,24 +2,26 @@ # # Copyright (c) 2007 Andy Parkins # -# An example hook script to mail out commit update information. This hook sends emails -# listing new revisions to the repository introduced by the change being reported. The -# rule is that (for branch updates) each commit will appear on one email and one email -# only. +# An example hook script to mail out commit update information. This hook +# sends emails listing new revisions to the repository introduced by the +# change being reported. The rule is that (for branch updates) each commit +# will appear on one email and one email only. # -# This hook is stored in the contrib/hooks directory. Your distribution will have put -# this somewhere standard. You should make this script executable then link to it in -# the repository you would like to use it in. For example, on debian the hook is stored -# in /usr/share/doc/git-core/contrib/hooks/post-receive-email: +# This hook is stored in the contrib/hooks directory. Your distribution +# will have put this somewhere standard. You should make this script +# executable then link to it in the repository you would like to use it in. +# For example, on debian the hook is stored in +# /usr/share/doc/git-core/contrib/hooks/post-receive-email: # # chmod a+x post-receive-email # cd /path/to/your/repository.git # ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive # -# This hook script assumes it is enabled on the central repository of a project, with -# all users pushing only to it and not between each other. It will still work if you -# don't operate in that style, but it would become possible for the email to be from -# someone other than the person doing the push. +# This hook script assumes it is enabled on the central repository of a +# project, with all users pushing only to it and not between each other. It +# will still work if you don't operate in that style, but it would become +# possible for the email to be from someone other than the person doing the +# push. # # Config # ------ @@ -28,15 +30,17 @@ # emails for every ref update. # hooks.announcelist # This is the list that all pushes of annotated tags will go to. Leave it -# blank to default to the mailinglist field. The announce emails lists the -# short log summary of the changes since the last annotated tag. -# hook.envelopesender -# If set then the -f option is passed to sendmail to allow the envelope sender -# address to be set +# blank to default to the mailinglist field. The announce emails lists +# the short log summary of the changes since the last annotated tag. +# hooks.envelopesender +# If set then the -f option is passed to sendmail to allow the envelope +# sender address to be set +# hooks.emailprefix +# All emails have their subjects prefixed with this prefix, or "[SCM]" +# if emailprefix is unset, to aid filtering # # Notes # ----- -# All emails have their subjects prefixed with "[SCM]" to aid filtering. # All emails include the headers "X-Git-Refname", "X-Git-Oldrev", # "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and # give information for debugging. @@ -49,8 +53,8 @@ # this is and calls the appropriate body-generation routine after outputting # the common header # -# Note this function doesn't actually generate any email output, that is taken -# care of by the functions it calls: +# Note this function doesn't actually generate any email output, that is +# taken care of by the functions it calls: # - generate_email_header # - generate_create_XXXX_email # - generate_update_XXXX_email @@ -152,10 +156,6 @@ generate_email() fi # Email parameters - # The committer will be obtained from the latest existing rev; so - # for a deletion it will be the oldrev, for the others, then newrev - committer=$(git show --pretty=full -s $rev | sed -ne "s/^Commit: //p" | - sed -ne 's/\(.*\) </"\1" </p') # The email subject will contain the best description of the ref # that we can build from the parameters describe=$(git describe $rev 2>/dev/null) @@ -186,7 +186,7 @@ generate_email_header() # Generate header cat <<-EOF To: $recipients - Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe + Subject: ${emailprefix}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe X-Git-Refname: $refname X-Git-Reftype: $refname_type X-Git-Oldrev: $oldrev @@ -225,8 +225,9 @@ generate_create_branch_email() echo $LOGBEGIN # This shows all log entries that are not already covered by # another ref - i.e. commits that are now accessible from this - # ref that were previously not accessible (see generate_update_branch_email - # for the explanation of this command) + # ref that were previously not accessible + # (see generate_update_branch_email for the explanation of this + # command) git rev-parse --not --branches | grep -v $(git rev-parse $refname) | git rev-list --pretty --stdin $newrev echo $LOGEND @@ -254,9 +255,10 @@ generate_update_branch_email() # # git-rev-list N ^O ^X ^N # - # So, we need to build up the list more carefully. git-rev-parse will - # generate a list of revs that may be fed into git-rev-list. We can get - # it to make the "--not --all" part and then filter out the "^N" with: + # So, we need to build up the list more carefully. git-rev-parse + # will generate a list of revs that may be fed into git-rev-list. + # We can get it to make the "--not --all" part and then filter out + # the "^N" with: # # git-rev-parse --not --all | grep -v N # @@ -266,16 +268,17 @@ generate_update_branch_email() # git-rev-list N ^O ^X # # This leaves a problem when someone else updates the repository - # while this script is running. Their new value of the ref we're working - # on would be included in the "--not --all" output; and as our $newrev - # would be an ancestor of that commit, it would exclude all of our - # commits. What we really want is to exclude the current value of - # $refname from the --not list, rather than N itself. So: + # while this script is running. Their new value of the ref we're + # working on would be included in the "--not --all" output; and as + # our $newrev would be an ancestor of that commit, it would exclude + # all of our commits. What we really want is to exclude the current + # value of $refname from the --not list, rather than N itself. So: # # git-rev-parse --not --all | grep -v $(git-rev-parse $refname) # - # Get's us to something pretty safe (apart from the small time between - # refname being read, and git-rev-parse running - for that, I give up) + # Get's us to something pretty safe (apart from the small time + # between refname being read, and git-rev-parse running - for that, + # I give up) # # # Next problem, consider this: @@ -283,18 +286,18 @@ generate_update_branch_email() # \ # * --- X --- * --- N ($newrev) # - # That is to say, there is no guarantee that oldrev is a strict subset of - # newrev (it would have required a --force, but that's allowed). So, we - # can't simply say rev-list $oldrev..$newrev. Instead we find the common - # base of the two revs and list from there. + # That is to say, there is no guarantee that oldrev is a strict + # subset of newrev (it would have required a --force, but that's + # allowed). So, we can't simply say rev-list $oldrev..$newrev. + # Instead we find the common base of the two revs and list from + # there. # - # As above, we need to take into account the presence of X; if another - # branch is already in the repository and points at some of the revisions - # that we are about to output - we don't want them. The solution is as - # before: git-rev-parse output filtered. + # As above, we need to take into account the presence of X; if + # another branch is already in the repository and points at some of + # the revisions that we are about to output - we don't want them. + # The solution is as before: git-rev-parse output filtered. # - # Finally, tags: - # 1 --- 2 --- O --- T --- 3 --- 4 --- N + # Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N # # Tags pushed into the repository generate nice shortlog emails that # summarise the commits between them and the previous tag. However, @@ -302,13 +305,14 @@ generate_update_branch_email() # for a branch update. Therefore we still want to output revisions # that have been output on a tag email. # - # Luckily, git-rev-parse includes just the tool. Instead of using "--all" - # we use "--branches"; this has the added benefit that "remotes/" will - # be ignored as well. - - # List all of the revisions that were removed by this update, in a fast forward - # update, this list will be empty, because rev-list O ^N is empty. For a non - # fast forward, O ^N is the list of removed revisions + # Luckily, git-rev-parse includes just the tool. Instead of using + # "--all" we use "--branches"; this has the added benefit that + # "remotes/" will be ignored as well. + + # List all of the revisions that were removed by this update, in a + # fast forward update, this list will be empty, because rev-list O + # ^N is empty. For a non fast forward, O ^N is the list of removed + # revisions fast_forward="" rev="" for rev in $(git rev-list $newrev..$oldrev) @@ -321,10 +325,10 @@ generate_update_branch_email() fi # List all the revisions from baserev to newrev in a kind of - # "table-of-contents"; note this list can include revisions that have - # already had notification emails and is present to show the full detail - # of the change from rolling back the old revision to the base revision and - # then forward to the new revision + # "table-of-contents"; note this list can include revisions that + # have already had notification emails and is present to show the + # full detail of the change from rolling back the old revision to + # the base revision and then forward to the new revision for rev in $(git rev-list $oldrev..$newrev) do revtype=$(git cat-file -t "$rev") @@ -334,19 +338,20 @@ generate_update_branch_email() if [ "$fast_forward" ]; then echo " from $oldrev ($oldrev_type)" else - # 1. Existing revisions were removed. In this case newrev is a - # subset of oldrev - this is the reverse of a fast-forward, - # a rewind - # 2. New revisions were added on top of an old revision, this is - # a rewind and addition. + # 1. Existing revisions were removed. In this case newrev + # is a subset of oldrev - this is the reverse of a + # fast-forward, a rewind + # 2. New revisions were added on top of an old revision, + # this is a rewind and addition. - # (1) certainly happened, (2) possibly. When (2) hasn't happened, - # we set a flag to indicate that no log printout is required. + # (1) certainly happened, (2) possibly. When (2) hasn't + # happened, we set a flag to indicate that no log printout + # is required. echo "" - # Find the common ancestor of the old and new revisions and compare - # it with newrev + # Find the common ancestor of the old and new revisions and + # compare it with newrev baserev=$(git merge-base $oldrev $newrev) rewind_only="" if [ "$baserev" = "$newrev" ]; then @@ -387,21 +392,22 @@ generate_update_branch_email() git rev-parse --not --branches | grep -v $(git rev-parse $refname) | git rev-list --pretty --stdin $oldrev..$newrev - # XXX: Need a way of detecting whether git rev-list actually outputted - # anything, so that we can issue a "no new revisions added by this - # update" message + # XXX: Need a way of detecting whether git rev-list actually + # outputted anything, so that we can issue a "no new + # revisions added by this update" message echo $LOGEND else echo "No new revisions were added by this update." fi - # The diffstat is shown from the old revision to the new revision. This - # is to show the truth of what happened in this change. There's no point - # showing the stat from the base to the new revision because the base - # is effectively a random revision at this point - the user will be - # interested in what this revision changed - including the undoing of - # previous revisions in the case of non-fast forward updates. + # The diffstat is shown from the old revision to the new revision. + # This is to show the truth of what happened in this change. + # There's no point showing the stat from the base to the new + # revision because the base is effectively a random revision at this + # point - the user will be interested in what this revision changed + # - including the undoing of previous revisions in the case of + # non-fast forward updates. echo "" echo "Summary of changes:" git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev @@ -448,7 +454,8 @@ generate_update_atag_email() # generate_atag_email() { - # Use git-for-each-ref to pull out the individual fields from the tag + # Use git-for-each-ref to pull out the individual fields from the + # tag eval $(git for-each-ref --shell --format=' tagobject=%(*objectname) tagtype=%(*objecttype) @@ -459,8 +466,10 @@ generate_atag_email() echo " tagging $tagobject ($tagtype)" case "$tagtype" in commit) + # If the tagged object is a commit, then we assume this is a - # release, and so we calculate which tag this tag is replacing + # release, and so we calculate which tag this tag is + # replacing prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null) if [ -n "$prevtag" ]; then @@ -477,25 +486,27 @@ generate_atag_email() echo "" echo $LOGBEGIN - # Show the content of the tag message; this might contain a change log - # or release notes so is worth displaying. + # Show the content of the tag message; this might contain a change + # log or release notes so is worth displaying. git cat-file tag $newrev | sed -e '1,/^$/d' echo "" case "$tagtype" in commit) - # Only commit tags make sense to have rev-list operations performed - # on them + # Only commit tags make sense to have rev-list operations + # performed on them if [ -n "$prevtag" ]; then # Show changes since the previous release git rev-list --pretty=short "$prevtag..$newrev" | git shortlog else - # No previous tag, show all the changes since time began + # No previous tag, show all the changes since time + # began git rev-list --pretty=short $newrev | git shortlog fi ;; *) - # XXX: Is there anything useful we can do for non-commit objects? + # XXX: Is there anything useful we can do for non-commit + # objects? ;; esac @@ -544,13 +555,14 @@ generate_update_general_email() # generate_general_email() { - # Unannotated tags are more about marking a point than releasing a version; - # therefore we don't do the shortlog summary that we do for annotated tags - # above - we simply show that the point has been marked, and print the log - # message for the marked point for reference purposes + # Unannotated tags are more about marking a point than releasing a + # version; therefore we don't do the shortlog summary that we do for + # annotated tags above - we simply show that the point has been + # marked, and print the log message for the marked point for + # reference purposes # - # Note this section also catches any other reference type (although there - # aren't any) and deals with them in the same way. + # Note this section also catches any other reference type (although + # there aren't any) and deals with them in the same way. echo "" if [ "$newrev_type" = "commit" ]; then @@ -558,10 +570,10 @@ generate_general_email() git show --no-color --root -s $newrev echo $LOGEND else - # What can we do here? The tag marks an object that is not a commit, - # so there is no log for us to display. It's probably not wise to - # output git-cat-file as it could be a binary blob. We'll just say how - # big it is + # What can we do here? The tag marks an object that is not + # a commit, so there is no log for us to display. It's + # probably not wise to output git-cat-file as it could be a + # binary blob. We'll just say how big it is echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long." fi } @@ -590,7 +602,6 @@ send_mail() # ---------------------------- main() # --- Constants -EMAILPREFIX="[SCM] " LOGBEGIN="- Log -----------------------------------------------------------------" LOGEND="-----------------------------------------------------------------------" @@ -604,8 +615,8 @@ if [ -z "$GIT_DIR" ]; then fi projectdesc=$(sed -ne '1p' "$GIT_DIR/description") -# Check if the description is unchanged from it's default, and shorten it to a -# more manageable length if it is +# Check if the description is unchanged from it's default, and shorten it to +# a more manageable length if it is if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null then projectdesc="UNNAMED PROJECT" @@ -614,13 +625,15 @@ fi recipients=$(git repo-config hooks.mailinglist) announcerecipients=$(git repo-config hooks.announcelist) envelopesender=$(git-repo-config hooks.envelopesender) +emailprefix=$(git-repo-config hooks.emailprefix || echo '[SCM] ') # --- Main loop -# Allow dual mode: run from the command line just like the update hook, or if -# no arguments are given then run as a hook script +# Allow dual mode: run from the command line just like the update hook, or +# if no arguments are given then run as a hook script if [ -n "$1" -a -n "$2" -a -n "$3" ]; then # Output to the terminal in command line mode - if someone wanted to - # resend an email; they could redirect the output to sendmail themselves + # resend an email; they could redirect the output to sendmail + # themselves PAGER= generate_email $2 $3 $1 else while read oldrev newrev refname diff --git a/csum-file.c b/csum-file.c index 3729e73e19..9728a99541 100644 --- a/csum-file.c +++ b/csum-file.c @@ -18,7 +18,8 @@ static void sha1flush(struct sha1file *f, unsigned int count) for (;;) { int ret = xwrite(f->fd, buf, count); if (ret > 0) { - display_throughput(f->tp, ret); + f->total += ret; + display_throughput(f->tp, f->total); buf = (char *) buf + ret; count -= ret; if (count) @@ -87,21 +88,12 @@ struct sha1file *sha1fd(int fd, const char *name) struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp) { - struct sha1file *f; - unsigned len; - - f = xmalloc(sizeof(*f)); - - len = strlen(name); - if (len >= PATH_MAX) - die("you wascally wabbit, you"); - f->namelen = len; - memcpy(f->name, name, len+1); - + struct sha1file *f = xmalloc(sizeof(*f)); f->fd = fd; - f->error = 0; f->offset = 0; + f->total = 0; f->tp = tp; + f->name = name; f->do_crc = 0; SHA1_Init(&f->ctx); return f; diff --git a/csum-file.h b/csum-file.h index 4d1b231292..1af76562f3 100644 --- a/csum-file.h +++ b/csum-file.h @@ -5,11 +5,12 @@ struct progress; /* A SHA1-protected file */ struct sha1file { - int fd, error; - unsigned int offset, namelen; + int fd; + unsigned int offset; SHA_CTX ctx; + off_t total; struct progress *tp; - char name[PATH_MAX]; + const char *name; int do_crc; uint32_t crc32; unsigned char buffer[8192]; @@ -406,7 +406,8 @@ static struct daemon_service daemon_service[] = { { "receive-pack", "receivepack", receive_pack, 0, 1 }, }; -static void enable_service(const char *name, int ena) { +static void enable_service(const char *name, int ena) +{ int i; for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { if (!strcmp(daemon_service[i].name, name)) { @@ -417,7 +418,8 @@ static void enable_service(const char *name, int ena) { die("No such service %s", name); } -static void make_service_overridable(const char *name, int ena) { +static void make_service_overridable(const char *name, int ena) +{ int i; for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { if (!strcmp(daemon_service[i].name, name)) { diff --git a/diff-lib.c b/diff-lib.c index da5571302d..ec1b5e3d44 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -173,9 +173,10 @@ static int is_in_index(const char *path) } static int handle_diff_files_args(struct rev_info *revs, - int argc, const char **argv, int *silent) + int argc, const char **argv, + unsigned int *options) { - *silent = 0; + *options = 0; /* revs->max_count == -2 means --no-index */ while (1 < argc && argv[1][0] == '-') { @@ -192,7 +193,7 @@ static int handle_diff_files_args(struct rev_info *revs, revs->diffopt.no_index = 1; } else if (!strcmp(argv[1], "-q")) - *silent = 1; + *options |= DIFF_SILENT_ON_REMOVED; else return error("invalid option: %s", argv[1]); argv++; argc--; @@ -305,9 +306,9 @@ int setup_diff_no_index(struct rev_info *revs, int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv) { - int silent_on_removed; + unsigned int options; - if (handle_diff_files_args(revs, argc, argv, &silent_on_removed)) + if (handle_diff_files_args(revs, argc, argv, &options)) return -1; if (revs->diffopt.no_index) { @@ -329,13 +330,16 @@ int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv) perror("read_cache"); return -1; } - return run_diff_files(revs, silent_on_removed); + return run_diff_files(revs, options); } -int run_diff_files(struct rev_info *revs, int silent_on_removed) +int run_diff_files(struct rev_info *revs, unsigned int option) { int entries, i; int diff_unmerged_stage = revs->max_count; + int silent_on_removed = option & DIFF_SILENT_ON_REMOVED; + unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED) + ? CE_MATCH_RACY_IS_DIRTY : 0); if (diff_unmerged_stage < 0) diff_unmerged_stage = 2; @@ -441,7 +445,7 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed) ce->sha1, ce->name, NULL); continue; } - changed = ce_match_stat(ce, &st, 0); + changed = ce_match_stat(ce, &st, ce_option); if (!changed && !revs->diffopt.find_copies_harder) continue; oldmode = ntohl(ce->ce_mode); @@ -224,7 +224,11 @@ extern void diff_flush(struct diff_options*); extern const char *diff_unique_abbrev(const unsigned char *, int); -extern int run_diff_files(struct rev_info *revs, int silent_on_removed); +/* do not report anything on removed paths */ +#define DIFF_SILENT_ON_REMOVED 01 +/* report racily-clean paths as modified */ +#define DIFF_RACY_IS_MODIFIED 02 +extern int run_diff_files(struct rev_info *revs, unsigned int option); extern int setup_diff_no_index(struct rev_info *revs, int argc, const char ** argv, int nongit, const char *prefix); extern int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv); @@ -298,7 +298,8 @@ int excluded(struct dir_struct *dir, const char *pathname) return 0; } -static struct dir_entry *dir_entry_new(const char *pathname, int len) { +static struct dir_entry *dir_entry_new(const char *pathname, int len) +{ struct dir_entry *ent; ent = xmalloc(sizeof(*ent) + len + 1); @@ -653,6 +654,7 @@ static void free_simplify(struct path_simplify *simplify) int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec) { struct path_simplify *simplify = create_simplify(pathspec); + char *pp = NULL; /* * Make sure to do the per-directory exclude for all the @@ -660,7 +662,8 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i */ if (baselen) { if (dir->exclude_per_dir) { - char *p, *pp = xmalloc(baselen+1); + char *p; + pp = xmalloc(baselen+1); memcpy(pp, base, baselen+1); p = pp; while (1) { @@ -676,12 +679,12 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i else p = pp + baselen; } - free(pp); } } read_directory_recursive(dir, path, base, baselen, 0, simplify); free_simplify(simplify); + free(pp); qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name); qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name); return dir->nr; @@ -777,3 +780,15 @@ int remove_dir_recursively(struct strbuf *path, int only_empty) ret = rmdir(path->buf); return ret; } + +void setup_standard_excludes(struct dir_struct *dir) +{ + const char *path; + + dir->exclude_per_dir = ".gitignore"; + path = git_path("info/exclude"); + if (!access(path, R_OK)) + add_excludes_from_file(dir, path); + if (excludes_file && !access(excludes_file, R_OK)) + add_excludes_from_file(dir, excludes_file); +} @@ -71,6 +71,7 @@ extern struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathna extern char *get_relative_cwd(char *buffer, int size, const char *dir); extern int is_inside_dir(const char *dir); +extern void setup_standard_excludes(struct dir_struct *dir); extern int remove_dir_recursively(struct strbuf *path, int only_empty); #endif @@ -203,7 +203,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t strcpy(path + len, ce->name); if (!lstat(path, &st)) { - unsigned changed = ce_match_stat(ce, &st, 1); + unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID); if (!changed) return 0; if (!state->force) { diff --git a/environment.c b/environment.c index b5a6c69f7c..1dab72ec15 100644 --- a/environment.c +++ b/environment.c @@ -34,6 +34,7 @@ char *pager_program; int pager_in_use; int pager_use_color = 1; char *editor_program; +char *excludes_file; int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */ /* This is set by setup_git_dir_gently() and/or git_default_config() */ diff --git a/fast-import.c b/fast-import.c index f93d7d6c9b..98c2bd5359 100644 --- a/fast-import.c +++ b/fast-import.c @@ -153,13 +153,16 @@ Format of STDIN stream: #define PACK_ID_BITS 16 #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1) +#define DEPTH_BITS 13 +#define MAX_DEPTH ((1<<DEPTH_BITS)-1) struct object_entry { struct object_entry *next; uint32_t offset; - unsigned type : TYPE_BITS; - unsigned pack_id : PACK_ID_BITS; + uint32_t type : TYPE_BITS, + pack_id : PACK_ID_BITS, + depth : DEPTH_BITS; unsigned char sha1[20]; }; @@ -1083,7 +1086,7 @@ static int store_object( unsigned pos = sizeof(hdr) - 1; delta_count_by_type[type]++; - last->depth++; + e->depth = last->depth + 1; hdrlen = encode_header(OBJ_OFS_DELTA, deltalen, hdr); write_or_die(pack_data->pack_fd, hdr, hdrlen); @@ -1095,8 +1098,7 @@ static int store_object( write_or_die(pack_data->pack_fd, hdr + pos, sizeof(hdr) - pos); pack_size += sizeof(hdr) - pos; } else { - if (last) - last->depth = 0; + e->depth = 0; hdrlen = encode_header(type, dat->len, hdr); write_or_die(pack_data->pack_fd, hdr, hdrlen); pack_size += hdrlen; @@ -1114,6 +1116,7 @@ static int store_object( strbuf_swap(&last->data, dat); } last->offset = e->offset; + last->depth = e->depth; } return 0; } @@ -1160,7 +1163,7 @@ static void load_tree(struct tree_entry *root) if (myoe && myoe->pack_id != MAX_PACK_ID) { if (myoe->type != OBJ_TREE) die("Not a tree: %s", sha1_to_hex(sha1)); - t->delta_depth = 0; + t->delta_depth = myoe->depth; buf = gfi_unpack_entry(myoe, &size); } else { enum object_type type; @@ -2289,8 +2292,11 @@ int main(int argc, const char **argv) } else if (!prefixcmp(a, "--max-pack-size=")) max_packsize = strtoumax(a + 16, NULL, 0) * 1024 * 1024; - else if (!prefixcmp(a, "--depth=")) + else if (!prefixcmp(a, "--depth=")) { max_depth = strtoul(a + 8, NULL, 0); + if (max_depth > MAX_DEPTH) + die("--depth cannot exceed %u", MAX_DEPTH); + } else if (!prefixcmp(a, "--active-branches=")) max_active_branches = strtoul(a + 18, NULL, 0); else if (!prefixcmp(a, "--import-marks=")) diff --git a/git-add--interactive.perl b/git-add--interactive.perl index ac598f88e6..0317ad9127 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -37,10 +37,7 @@ sub list_untracked { chomp $_; $_; } - run_cmd_pipe(qw(git ls-files --others - --exclude-per-directory=.gitignore), - "--exclude-from=$GIT_DIR/info/exclude", - '--', @_); + run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @_); } my $status_fmt = '%12s %12s %s'; diff --git a/git-bisect.sh b/git-bisect.sh index c18bd32bf4..3aac8164c6 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -276,7 +276,8 @@ exit_if_skipped_commits () { if expr "$_tried" : ".*[|].*" > /dev/null ; then echo "There are only 'skip'ped commit left to test." echo "The first bad commit could be any of:" - echo "$_tried" | sed -e 's/[|]/\n/g' + echo "$_tried" | sed -e 's/[|]/\ +/g' echo "We cannot bisect more!" exit 2 fi diff --git a/git-clean.sh b/git-clean.sh index 35a5142c56..01c95e9fe8 100755 --- a/git-clean.sh +++ b/git-clean.sh @@ -26,12 +26,13 @@ require_work_tree ignored= ignoredonly= cleandir= -disabled="`git config --bool clean.requireForce`" rmf="rm -f --" rmrf="rm -rf --" rm_refuse="echo Not removing" echo1="echo" +disabled=$(git config --bool clean.requireForce) + while test $# != 0 do case "$1" in @@ -39,10 +40,10 @@ do cleandir=1 ;; -f) - disabled= + disabled=false ;; -n) - disabled= + disabled=false rmf="echo Would remove" rmrf="echo Would remove" rm_refuse="echo Would not remove" @@ -68,9 +69,17 @@ do shift done -if [ "$disabled" = true ]; then +# requireForce used to default to false but now it defaults to true. +# IOW, lack of explicit "clean.requireForce = false" is taken as +# "clean.requireForce = true". +case "$disabled" in +"") + die "clean.requireForce not set and -n or -f not given; refusing to clean" + ;; +"true") die "clean.requireForce set and -n or -f not given; refusing to clean" -fi + ;; +esac if [ "$ignored,$ignoredonly" = "1,1" ]; then die "-x and -X cannot be set together" @@ -78,15 +87,22 @@ fi if [ -z "$ignored" ]; then excl="--exclude-per-directory=.gitignore" + excl_info= excludes_file= if [ -f "$GIT_DIR/info/exclude" ]; then excl_info="--exclude-from=$GIT_DIR/info/exclude" fi + if cfg_excl=$(git config core.excludesfile) && test -f "$cfg_excl" + then + excludes_file="--exclude-from=$cfg_excl" + fi if [ "$ignoredonly" ]; then excl="$excl --ignored" fi fi -git ls-files --others --directory $excl ${excl_info:+"$excl_info"} -- "$@" | +git ls-files --others --directory \ + $excl ${excl_info:+"$excl_info"} ${excludes_file:+"$excludes_file"} \ + -- "$@" | while read -r file; do if [ -d "$file" -a ! -L "$file" ]; then if [ -z "$cleandir" ]; then diff --git a/git-commit.sh b/git-commit.sh index 9a636e55cc..485339754c 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -27,7 +27,7 @@ refuse_partial () { } TMP_INDEX= -THIS_INDEX="$GIT_DIR/index" +THIS_INDEX="${GIT_INDEX_FILE:-$GIT_DIR/index}" NEXT_INDEX="$GIT_DIR/next-index$$" rm -f "$NEXT_INDEX" save_index () { @@ -283,9 +283,9 @@ unset only case "$all,$interactive,$also,$#" in *t,*t,*) die "Cannot use -a, --interactive or -i at the same time." ;; -t,,[1-9]*) +t,,,[1-9]*) die "Paths with -a does not make sense." ;; -,t,[1-9]*) +,t,,[1-9]*) die "Paths with --interactive does not make sense." ;; ,,t,0) die "No paths with -i does not make sense." ;; diff --git a/git-compat-util.h b/git-compat-util.h index 7b29d1b905..ede9408bbd 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -183,6 +183,22 @@ void *gitmemmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen); #endif +#ifdef __GLIBC_PREREQ +#if __GLIBC_PREREQ(2, 1) +#define HAVE_STRCHRNUL +#endif +#endif + +#ifndef HAVE_STRCHRNUL +#define strchrnul gitstrchrnul +static inline char *gitstrchrnul(const char *s, int c) +{ + while (*s && *s != c) + s++; + return (char *)s; +} +#endif + extern void release_pack_memory(size_t, int); static inline char* xstrdup(const char *str) diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index 26844af439..92e41620fd 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -1,28 +1,42 @@ #!/usr/bin/perl -w -# Known limitations: -# - does not propagate permissions -# - error handling has not been extensively tested -# - use strict; use Getopt::Std; use File::Temp qw(tempdir); use Data::Dumper; use File::Basename qw(basename dirname); -unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){ - die "GIT_DIR is not defined or is unreadable"; -} - -our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u); +our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u, $opt_w); -getopts('uhPpvcfam:d:'); +getopts('uhPpvcfam:d:w:'); $opt_h && usage(); die "Need at least one commit identifier!" unless @ARGV; +if ($opt_w) { + unless ($ENV{GIT_DIR}) { + # Remember where our GIT_DIR is before changing to CVS checkout + my $gd =`git-rev-parse --git-dir`; + chomp($gd); + if ($gd eq '.git') { + my $wd = `pwd`; + chomp($wd); + $gd = $wd."/.git" ; + } + $ENV{GIT_DIR} = $gd; + } + + if (! -d $opt_w."/CVS" ) { + die "$opt_w is not a CVS checkout"; + } + chdir $opt_w or die "Cannot change to CVS checkout at $opt_w"; +} +unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){ + die "GIT_DIR is not defined or is unreadable"; +} + + my @cvs; if ($opt_d) { @cvs = ('cvs', '-d', $opt_d); @@ -274,6 +288,7 @@ if ($dirtypatch) { print "You'll need to apply the patch in .cvsexportcommit.diff manually\n"; print "using a patch program. After applying the patch and resolving the\n"; print "problems you may commit using:"; + print "\n cd \"$opt_w\"" if $opt_w; print "\n $cmd\n\n"; exit(1); } @@ -301,7 +316,7 @@ sleep(1); sub usage { print STDERR <<END; -Usage: GIT_DIR=/path/to/.git ${\basename $0} [-h] [-p] [-v] [-c] [-f] [-m msgprefix] [ parent ] commit +Usage: GIT_DIR=/path/to/.git ${\basename $0} [-h] [-p] [-v] [-c] [-f] [-u] [-w cvsworkdir] [-m msgprefix] [ parent ] commit END exit(1); } diff --git a/git-cvsimport.perl b/git-cvsimport.perl index e4bc2b54f6..efa6a0c41a 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -223,7 +223,8 @@ sub conn { } } - $user="anonymous" unless defined $user; + # if username is not explicit in CVSROOT, then use current user, as cvs would + $user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || "anonymous") unless $user; my $rr2 = "-"; unless ($port) { $rr2 = ":pserver:$user\@$serv:$repo"; diff --git a/git-instaweb.sh b/git-instaweb.sh index f05884c1fa..8503ae4030 100755 --- a/git-instaweb.sh +++ b/git-instaweb.sh @@ -27,7 +27,7 @@ browser="`git config --get instaweb.browser`" port=`git config --get instaweb.port` module_path="`git config --get instaweb.modulepath`" -conf=$GIT_DIR/gitweb/httpd.conf +conf="$GIT_DIR/gitweb/httpd.conf" # Defaults: @@ -44,7 +44,7 @@ start_httpd () { httpd_only="`echo $httpd | cut -f1 -d' '`" if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null;; esac then - $httpd $fqgitdir/gitweb/httpd.conf + $httpd "$fqgitdir/gitweb/httpd.conf" else # many httpds are installed in /usr/sbin or /usr/local/sbin # these days and those are not in most users $PATHs @@ -171,14 +171,14 @@ server.pid-file = "$fqgitdir/pid" cgi.assign = ( ".cgi" => "" ) mimetype.assign = ( ".css" => "text/css" ) EOF - test "$local" = true && echo 'server.bind = "127.0.0.1"' >> "$conf" + test x"$local" = xtrue && echo 'server.bind = "127.0.0.1"' >> "$conf" } apache2_conf () { test -z "$module_path" && module_path=/usr/lib/apache2/modules mkdir -p "$GIT_DIR/gitweb/logs" bind= - test "$local" = true && bind='127.0.0.1:' + test x"$local" = xtrue && bind='127.0.0.1:' echo 'text/css css' > $fqgitdir/mime.types cat > "$conf" <<EOF ServerName "git-instaweb" @@ -231,7 +231,7 @@ EOF } script=' -s#^\(my\|our\) $projectroot =.*#\1 $projectroot = "'`dirname $fqgitdir`'";# +s#^\(my\|our\) $projectroot =.*#\1 $projectroot = "'$(dirname "$fqgitdir")'";# s#\(my\|our\) $gitbin =.*#\1 $gitbin = "'$GIT_EXEC_PATH'";# s#\(my\|our\) $projects_list =.*#\1 $projects_list = $projectroot;# s#\(my\|our\) $git_temp =.*#\1 $git_temp = "'$fqgitdir/gitweb/tmp'";#' @@ -251,8 +251,8 @@ gitweb_css () { EOFGITWEB } -gitweb_cgi $GIT_DIR/gitweb/gitweb.cgi -gitweb_css $GIT_DIR/gitweb/gitweb.css +gitweb_cgi "$GIT_DIR/gitweb/gitweb.cgi" +gitweb_css "$GIT_DIR/gitweb/gitweb.css" case "$httpd" in *lighttpd*) @@ -271,6 +271,5 @@ webrick) esac start_httpd -test -z "$browser" && browser=echo url=http://127.0.0.1:$port -$browser $url || echo $url +"$browser" $url || echo $url diff --git a/git-lost-found.sh b/git-lost-found.sh index a5a32e7c70..9cedaf80ce 100755 --- a/git-lost-found.sh +++ b/git-lost-found.sh @@ -5,6 +5,8 @@ SUBDIRECTORY_OK='Yes' OPTIONS_SPEC= . git-sh-setup +echo "WARNING: '$0' is deprecated in favor of 'git fsck --lost-found'" >&2 + if [ "$#" != "0" ] then usage diff --git a/git-merge.sh b/git-merge.sh index 173d2325ed..1c123a37e6 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -40,20 +40,19 @@ allow_trivial_merge=t dropsave() { rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \ - "$GIT_DIR/MERGE_SAVE" || exit 1 + "$GIT_DIR/MERGE_STASH" || exit 1 } savestate() { # Stash away any local modifications. - git diff-index -z --name-only $head | - cpio -0 -o >"$GIT_DIR/MERGE_SAVE" + git stash create >"$GIT_DIR/MERGE_STASH" } restorestate() { - if test -f "$GIT_DIR/MERGE_SAVE" + if test -f "$GIT_DIR/MERGE_STASH" then git reset --hard $head >/dev/null - cpio -iuv <"$GIT_DIR/MERGE_SAVE" + git stash apply $(cat "$GIT_DIR/MERGE_STASH") git update-index --refresh >/dev/null fi } @@ -419,7 +418,7 @@ case "$use_strategies" in single_strategy=no ;; *) - rm -f "$GIT_DIR/MERGE_SAVE" + rm -f "$GIT_DIR/MERGE_STASH" single_strategy=yes ;; esac diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 6d14092889..66c80d4e16 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -392,7 +392,7 @@ do -s|--strategy) case "$#,$1" in *,*=*) - STRATEGY="-s `expr "z$1" : 'z-[^=]*=\(.*\)'`" ;; + STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;; 1,*) usage ;; *) diff --git a/git-rebase.sh b/git-rebase.sh index e3ad7dedc4..df5fd65d56 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -88,7 +88,7 @@ call_merge () { cmt="$(cat "$dotest/cmt.$1")" echo "$cmt" > "$dotest/current" hd=$(git rev-parse --verify HEAD) - cmt_name=$(git symbolic-ref HEAD) + cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD) msgnum=$(cat "$dotest/msgnum") end=$(cat "$dotest/end") eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"' @@ -116,7 +116,24 @@ call_merge () { esac } +move_to_original_branch () { + test -z "$head_name" && + head_name="$(cat "$dotest"/head-name)" && + onto="$(cat "$dotest"/onto)" && + orig_head="$(cat "$dotest"/orig-head)" + case "$head_name" in + refs/*) + message="rebase finished: $head_name onto $onto" + git update-ref -m "$message" \ + $head_name $(git rev-parse HEAD) $orig_head && + git symbolic-ref HEAD $head_name || + die "Could not move back to $head_name" + ;; + esac +} + finish_rb_merge () { + move_to_original_branch rm -r "$dotest" echo "All done." } @@ -154,7 +171,11 @@ do finish_rb_merge exit fi - git am --resolved --3way --resolvemsg="$RESOLVEMSG" + head_name=$(cat .dotest/head-name) && + onto=$(cat .dotest/onto) && + orig_head=$(cat .dotest/orig-head) && + git am --resolved --3way --resolvemsg="$RESOLVEMSG" && + move_to_original_branch exit ;; --skip) @@ -174,16 +195,23 @@ do finish_rb_merge exit fi - git am -3 --skip --resolvemsg="$RESOLVEMSG" + head_name=$(cat .dotest/head-name) && + onto=$(cat .dotest/onto) && + orig_head=$(cat .dotest/orig-head) && + git am -3 --skip --resolvemsg="$RESOLVEMSG" && + move_to_original_branch exit ;; --abort) git rerere clear if test -d "$dotest" then + move_to_original_branch rm -r "$dotest" elif test -d .dotest then + dotest=.dotest + move_to_original_branch rm -r .dotest else die "No rebase in progress?" @@ -319,6 +347,19 @@ then GIT_PAGER='' git diff --stat --summary "$mb" "$onto" fi +# move to a detached HEAD +orig_head=$(git rev-parse HEAD^0) +head_name=$(git symbolic-ref HEAD 2> /dev/null) +case "$head_name" in +'') + head_name="detached HEAD" + ;; +*) + git checkout "$orig_head" > /dev/null 2>&1 || + die "could not detach HEAD" + ;; +esac + # Rewind the head to "$onto"; this saves our current head in ORIG_HEAD. echo "First, rewinding head to replay your work on top of it..." git-reset --hard "$onto" @@ -328,14 +369,21 @@ git-reset --hard "$onto" if test "$mb" = "$branch" then echo >&2 "Fast-forwarded $branch_name to $onto_name." + move_to_original_branch exit 0 fi if test -z "$do_merge" then git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD | - git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG" - exit $? + git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG" && + move_to_original_branch + ret=$? + test 0 != $ret -a -d .dotest && + echo $head_name > .dotest/head-name && + echo $onto > .dotest/onto && + echo $orig_head > .dotest/orig-head + exit $ret fi # start doing a rebase with git-merge @@ -344,8 +392,10 @@ fi mkdir -p "$dotest" echo "$onto" > "$dotest/onto" echo "$onto_name" > "$dotest/onto_name" -prev_head=`git rev-parse HEAD^0` +prev_head=$orig_head echo "$prev_head" > "$dotest/prev_head" +echo "$orig_head" > "$dotest/orig-head" +echo "$head_name" > "$dotest/head-name" msgnum=0 for cmt in `git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD` diff --git a/git-request-pull.sh b/git-request-pull.sh index 90d969c625..068f5e0fc7 100755 --- a/git-request-pull.sh +++ b/git-request-pull.sh @@ -25,13 +25,13 @@ headrev=`git rev-parse --verify "$head"^0` || exit merge_base=`git merge-base $baserev $headrev` || die "fatal: No commits in common between $base and $head" -url="`get_remote_url "$url"`" -branch=`git peek-remote "$url" \ +url=$(get_remote_url "$url") +branch=$(git peek-remote "$url" \ | sed -n -e "/^$headrev refs.heads./{ s/^.* refs.heads.// p q - }"` + }") if [ -z "$branch" ]; then echo "warn: No branch of $url is at:" >&2 git log --max-count=1 --pretty='format:warn: %h: %s' $headrev >&2 diff --git a/git-send-email.perl b/git-send-email.perl index 96051bc01e..fd0a4ad0c2 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -88,8 +88,7 @@ Options: --smtp-ssl If set, connects to the SMTP server using SSL. - --suppress-from Suppress sending emails to yourself if your address - appears in a From: line. Defaults to off. + --suppress-from Suppress sending emails to yourself. Defaults to off. --thread Specify that the "In-Reply-To:" header should be set on all emails. Defaults to on. @@ -353,7 +352,7 @@ sub expand_aliases { if (!defined $initial_subject && $compose) { do { - $_ = $term->readline("What subject should the emails start with? ", + $_ = $term->readline("What subject should the initial email start with? ", $initial_subject); } while (!defined $_); $initial_subject = $_; @@ -515,11 +514,13 @@ $time = time - scalar $#files; sub unquote_rfc2047 { local ($_) = @_; - if (s/=\?utf-8\?q\?(.*)\?=/$1/g) { + my $encoding; + if (s/=\?([^?]+)\?q\?(.*)\?=/$2/g) { + $encoding = $1; s/_/ /g; s/=([0-9A-F]{2})/chr(hex($1))/eg; } - return "$_"; + return wantarray ? ($_, $encoding) : $_; } # use the simplest quoting being able to handle the recipient @@ -668,6 +669,9 @@ foreach my $t (@files) { open(F,"<",$t) or die "can't open file $t"; my $author = undef; + my $author_encoding; + my $has_content_type; + my $body_encoding; @cc = @initial_cc; @xh = (); my $input_format = undef; @@ -693,12 +697,20 @@ foreach my $t (@files) { next if ($suppress_from); } elsif ($1 eq 'From') { - $author = unquote_rfc2047($2); + ($author, $author_encoding) + = unquote_rfc2047($2); } printf("(mbox) Adding cc: %s from line '%s'\n", $2, $_) unless $quiet; push @cc, $2; } + elsif (/^Content-type:/i) { + $has_content_type = 1; + if (/charset="?[^ "]+/) { + $body_encoding = $1; + } + push @xh, $_; + } elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) { push @xh, $_; } @@ -730,6 +742,7 @@ foreach my $t (@files) { if (/^(Signed-off-by|Cc): (.*)$/i && $signed_off_cc) { my $c = $2; chomp $c; + next if ($c eq $sender and $suppress_from); push @cc, $c; printf("(sob) Adding cc: %s from line '%s'\n", $c, $_) unless $quiet; @@ -745,6 +758,7 @@ foreach my $t (@files) { my $c = $_; $c =~ s/^\s*//g; $c =~ s/\n$//g; + next if ($c eq $sender and $suppress_from); push @cc, $c; printf("(cc-cmd) Adding cc: %s from: '%s'\n", $c, $cc_cmd) unless $quiet; @@ -755,6 +769,21 @@ foreach my $t (@files) { if (defined $author) { $message = "From: $author\n\n$message"; + if (defined $author_encoding) { + if ($has_content_type) { + if ($body_encoding eq $author_encoding) { + # ok, we already have the right encoding + } + else { + # uh oh, we should re-encode + } + } + else { + push @xh, + 'MIME-Version: 1.0', + "Content-Type: text/plain; charset=$author_encoding"; + } + } } send_message(); diff --git a/git-stash.sh b/git-stash.sh index 9cabd5dc2c..534eb168ab 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -22,23 +22,17 @@ no_changes () { clear_stash () { if current=$(git rev-parse --verify $ref_stash 2>/dev/null) then - git update-ref -d refs/stash $current + git update-ref -d $ref_stash $current fi } -save_stash () { +create_stash () { stash_msg="$1" if no_changes then - echo >&2 'No local changes to save' exit 0 fi - test -f "$GIT_DIR/logs/$ref_stash" || - clear_stash || die "Cannot initialize stash" - - # Make sure the reflog for stash is kept. - : >>"$GIT_DIR/logs/$ref_stash" # state of the base commit if b_commit=$(git rev-parse --verify HEAD) @@ -85,6 +79,23 @@ save_stash () { w_commit=$(printf '%s\n' "$stash_msg" | git commit-tree $w_tree -p $b_commit -p $i_commit) || die "Cannot record working tree state" +} + +save_stash () { + stash_msg="$1" + + if no_changes + then + echo >&2 'No local changes to save' + exit 0 + fi + test -f "$GIT_DIR/logs/$ref_stash" || + clear_stash || die "Cannot initialize stash" + + create_stash "$stash_msg" + + # Make sure the reflog for stash is kept. + : >>"$GIT_DIR/logs/$ref_stash" git update-ref -m "$stash_msg" $ref_stash $w_commit || die "Cannot save the current status" @@ -203,6 +214,13 @@ apply) clear) clear_stash ;; +create) + if test $# -gt 0 && test "$1" = create + then + shift + fi + create_stash "$*" && echo "$w_commit" + ;; help | usage) usage ;; diff --git a/git-submodule.sh b/git-submodule.sh index 1c656be1cf..82ac28fa27 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -74,7 +74,7 @@ resolve_relative_url () module_name() { # Do we have "submodule.<something>.path = $1" defined in .gitmodules file? - re=$(printf '%s' "$1" | sed -e 's/\([^a-zA-Z0-9_]\)/\\\1/g') + re=$(printf '%s' "$1" | sed -e 's/[].[^$\\*]/\\&/g') name=$( GIT_CONFIG=.gitmodules \ git config --get-regexp '^submodule\..*\.path$' | sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' ) diff --git a/git-svn.perl b/git-svn.perl index dd93e320a7..5b1deeab94 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -390,6 +390,9 @@ sub cmd_set_tree { sub cmd_dcommit { my $head = shift; + git_cmd_try { command_oneline(qw/diff-index --quiet HEAD/) } + 'Cannot dcommit with a dirty index. Commit your changes first, ' + . "or stash them with `git stash'.\n"; $head ||= 'HEAD'; my @refs; my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs); @@ -422,6 +425,9 @@ sub cmd_dcommit { my %ed_opts = ( r => $last_rev, log => get_commit_entry($d)->{log}, ra => Git::SVN::Ra->new($gs->full_url), + config => SVN::Core::config_get_config( + $Git::SVN::Ra::config_dir + ), tree_a => "$d~1", tree_b => $d, editor_cb => sub { @@ -2393,10 +2399,15 @@ sub rev_db_get { $ret; } +# Finds the first svn revision that exists on (if $eq_ok is true) or +# before $rev for the current branch. It will not search any lower +# than $min_rev. Returns the git commit hash and svn revision number +# if found, else (undef, undef). sub find_rev_before { - my ($self, $rev, $eq_ok) = @_; + my ($self, $rev, $eq_ok, $min_rev) = @_; --$rev unless $eq_ok; - while ($rev > 0) { + $min_rev ||= 1; + while ($rev >= $min_rev) { if (my $c = $self->rev_db_get($rev)) { return ($rev, $c); } @@ -2405,6 +2416,23 @@ sub find_rev_before { return (undef, undef); } +# Finds the first svn revision that exists on (if $eq_ok is true) or +# after $rev for the current branch. It will not search any higher +# than $max_rev. Returns the git commit hash and svn revision number +# if found, else (undef, undef). +sub find_rev_after { + my ($self, $rev, $eq_ok, $max_rev) = @_; + ++$rev unless $eq_ok; + $max_rev ||= $self->rev_db_max(); + while ($rev <= $max_rev) { + if (my $c = $self->rev_db_get($rev)) { + return ($rev, $c); + } + ++$rev; + } + return (undef, undef); +} + sub _new { my ($class, $repo_id, $ref_id, $path) = @_; unless (defined $repo_id && length $repo_id) { @@ -3220,6 +3248,25 @@ sub _auth_providers () { ] } +sub escape_uri_only { + my ($uri) = @_; + my @tmp; + foreach (split m{/}, $uri) { + s/([^\w.-])/sprintf("%%%02X",ord($1))/eg; + push @tmp, $_; + } + join('/', @tmp); +} + +sub escape_url { + my ($url) = @_; + if ($url =~ m#^(https?)://([^/]+)(.*)$#) { + my ($scheme, $domain, $uri) = ($1, $2, escape_uri_only($3)); + $url = "$scheme://$domain$uri"; + } + $url; +} + sub new { my ($class, $url) = @_; $url =~ s!/+$!!; @@ -3252,10 +3299,11 @@ sub new { $Git::SVN::Prompt::_no_auth_cache = 1; } } # no warnings 'once' - my $self = SVN::Ra->new(url => $url, auth => $baton, + my $self = SVN::Ra->new(url => escape_url($url), auth => $baton, config => $config, pool => SVN::Pool->new, auth_provider_callbacks => $callbacks); + $self->{url} = $url; $self->{svn_path} = $url; $self->{repos_root} = $self->get_repos_root; $self->{svn_path} =~ s#^\Q$self->{repos_root}\E(/|$)##; @@ -3381,7 +3429,7 @@ sub gs_do_switch { my $full_url = $self->{url}; my $old_url = $full_url; - $full_url .= "/$path" if length $path; + $full_url .= '/' . escape_uri_only($path) if length $path; my ($ra, $reparented); if ($old_url ne $full_url) { if ($old_url !~ m#^svn(\+ssh)?://#) { @@ -3675,6 +3723,7 @@ package Git::SVN::Log; use strict; use warnings; use POSIX qw/strftime/; +use constant commit_log_separator => ('-' x 72) . "\n"; use vars qw/$TZ $limit $color $pager $non_recursive $verbose $oneline %rusers $show_commit $incremental/; my $l_fmt; @@ -3768,19 +3817,19 @@ sub git_svn_log_cmd { push @cmd, $c; } } elsif (defined $r_max) { - my ($c_min, $c_max); - $c_max = $gs->rev_db_get($r_max); - $c_min = $gs->rev_db_get($r_min); - if (defined $c_min && defined $c_max) { - if ($r_max > $r_max) { - push @cmd, "$c_min..$c_max"; - } else { - push @cmd, "$c_max..$c_min"; - } - } elsif ($r_max > $r_min) { - push @cmd, $c_max; + if ($r_max < $r_min) { + ($r_min, $r_max) = ($r_max, $r_min); + } + my (undef, $c_max) = $gs->find_rev_before($r_max, 1, $r_min); + my (undef, $c_min) = $gs->find_rev_after($r_min, 1, $r_max); + # If there are no commits in the range, both $c_max and $c_min + # will be undefined. If there is at least 1 commit in the + # range, both will be defined. + return () if !defined $c_min || !defined $c_max; + if ($c_min eq $c_max) { + push @cmd, '--max-count=1', $c_min; } else { - push @cmd, $c_min; + push @cmd, '--boundary', "$c_min..$c_max"; } } return (@cmd, @files); @@ -3888,7 +3937,7 @@ sub show_commit_changed_paths { sub show_commit_normal { my ($c) = @_; - print '-' x72, "\nr$c->{r} | "; + print commit_log_separator, "r$c->{r} | "; print "$c->{c} | " if $show_commit; print "$c->{a} | ", strftime("%Y-%m-%d %H:%M:%S %z (%a, %d %b %Y)", localtime($c->{t_utc})), ' | '; @@ -3949,12 +3998,16 @@ sub cmd_show_log { config_pager(); @args = git_svn_log_cmd($r_min, $r_max, @args); + if (!@args) { + print commit_log_separator unless $incremental || $oneline; + return; + } my $log = command_output_pipe(@args); run_pager(); my (@k, $c, $d, $stat); my $esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/; while (<$log>) { - if (/^${esc_color}commit ($::sha1_short)/o) { + if (/^${esc_color}commit -?($::sha1_short)/o) { my $cmt = $1; if ($c && cmt_showable($c) && $c->{r} != $r_last) { $r_last = $c->{r}; @@ -3997,14 +4050,12 @@ sub cmd_show_log { process_commit($c, $r_min, $r_max, \@k); } if (@k) { - my $swap = $r_max; - $r_max = $r_min; - $r_min = $swap; + ($r_min, $r_max) = ($r_max, $r_min); process_commit($_, $r_min, $r_max) foreach reverse @k; } out: close $log; - print '-' x72,"\n" unless $incremental || $oneline; + print commit_log_separator unless $incremental || $oneline; } package Git::SVN::Migration; @@ -249,14 +249,9 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv) prefix = setup_git_directory(); if (p->option & USE_PAGER) setup_pager(); - if (p->option & NEED_WORK_TREE) { - const char *work_tree = get_git_work_tree(); - const char *git_dir = get_git_dir(); - if (!is_absolute_path(git_dir)) - set_git_dir(make_absolute_path(git_dir)); - if (!work_tree || chdir(work_tree)) - die("%s must be run in a work tree", p->cmd); - } + if (p->option & NEED_WORK_TREE) + setup_work_tree(); + trace_argv_printf(argv, argc, "trace: built-in: git"); status = p->fn(argc, argv, prefix); @@ -345,7 +340,7 @@ static void handle_internal_command(int argc, const char **argv) { "rev-list", cmd_rev_list, RUN_SETUP }, { "rev-parse", cmd_rev_parse }, { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE }, - { "rm", cmd_rm, RUN_SETUP | NEED_WORK_TREE }, + { "rm", cmd_rm, RUN_SETUP }, { "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE }, { "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER }, { "show-branch", cmd_show_branch, RUN_SETUP }, diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 759dff1cce..e788ef90c9 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1856,7 +1856,7 @@ sub parse_date { $date{'mday-time'} = sprintf "%d %s %02d:%02d", $mday, $months[$mon], $hour ,$min; $date{'iso-8601'} = sprintf "%04d-%02d-%02dT%02d:%02d:%02dZ", - 1900+$year, $mon, $mday, $hour ,$min, $sec; + 1900+$year, 1+$mon, $mday, $hour ,$min, $sec; $tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/; my $local = $epoch + ((int $1 + ($2/60)) * 3600); diff --git a/hash-object.c b/hash-object.c index 18f5017f51..0a58f3f126 100644 --- a/hash-object.c +++ b/hash-object.c @@ -42,6 +42,8 @@ int main(int argc, char **argv) int prefix_length = -1; int no_more_flags = 0; + git_config(git_default_config); + for (i = 1 ; i < argc; i++) { if (!no_more_flags && argv[i][0] == '-') { if (!strcmp(argv[i], "-t")) { @@ -79,7 +79,8 @@ static void uniq(struct cmdnames *cmds) cmds->cnt = j; } -static void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) { +static void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) +{ int ci, cj, ei; int cmp; diff --git a/index-pack.c b/index-pack.c index 715a5bb7a6..9fd6982a97 100644 --- a/index-pack.c +++ b/index-pack.c @@ -87,9 +87,9 @@ static void *fill(int min) die("early EOF"); die("read error on input: %s", strerror(errno)); } - if (from_stdin) - display_throughput(progress, ret); input_len += ret; + if (from_stdin) + display_throughput(progress, consumed_bytes + input_len); } while (input_len < min); return input_buffer; } @@ -256,7 +256,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_ static void *get_data_from_pack(struct object_entry *obj) { - unsigned long from = obj[0].idx.offset + obj[0].hdr_size; + off_t from = obj[0].idx.offset + obj[0].hdr_size; unsigned long len = obj[1].idx.offset - from; unsigned long rdy = 0; unsigned char *src, *data; @@ -792,6 +792,7 @@ int main(int argc, char **argv) flush(); } else { if (fix_thin_pack) { + char msg[48]; int nr_unresolved = nr_deltas - nr_resolved_deltas; int nr_objects_initial = nr_objects; if (nr_unresolved <= 0) @@ -800,12 +801,11 @@ int main(int argc, char **argv) (nr_objects + nr_unresolved + 1) * sizeof(*objects)); fix_unresolved_deltas(nr_unresolved); - stop_progress(&progress); - if (verbose) - fprintf(stderr, "%d objects were added to complete this thin pack.\n", - nr_objects - nr_objects_initial); + sprintf(msg, "completed with %d local objects", + nr_objects - nr_objects_initial); + stop_progress_msg(&progress, msg); fixup_pack_header_footer(output_fd, sha1, - curr_pack, nr_objects); + curr_pack, nr_objects); } if (nr_deltas != nr_resolved_deltas) die("pack has %d unresolved deltas", diff --git a/list-objects.c b/list-objects.c index e5c88c278f..4ef58e7ec0 100644 --- a/list-objects.c +++ b/list-objects.c @@ -170,4 +170,11 @@ void traverse_commit_list(struct rev_info *revs, } for (i = 0; i < objects.nr; i++) show_object(&objects.objects[i]); + free(objects.objects); + if (revs->pending.nr) { + free(revs->pending.objects); + revs->pending.nr = 0; + revs->pending.alloc = 0; + revs->pending.objects = NULL; + } } diff --git a/parse-options.c b/parse-options.c index cc09c98ec3..15b32f741b 100644 --- a/parse-options.c +++ b/parse-options.c @@ -119,8 +119,8 @@ static int parse_long_opt(struct optparse_t *p, const char *arg, const struct option *options) { const char *arg_end = strchr(arg, '='); - const struct option *abbrev_option = NULL; - int abbrev_flags = 0; + const struct option *abbrev_option = NULL, *ambiguous_option = NULL; + int abbrev_flags = 0, ambiguous_flags = 0; if (!arg_end) arg_end = arg + strlen(arg); @@ -137,16 +137,16 @@ static int parse_long_opt(struct optparse_t *p, const char *arg, /* abbreviated? */ if (!strncmp(options->long_name, arg, arg_end - arg)) { is_abbreviated: - if (abbrev_option) - return error("Ambiguous option: %s " - "(could be --%s%s or --%s%s)", - arg, - (flags & OPT_UNSET) ? - "no-" : "", - options->long_name, - (abbrev_flags & OPT_UNSET) ? - "no-" : "", - abbrev_option->long_name); + if (abbrev_option) { + /* + * If this is abbreviated, it is + * ambiguous. So when there is no + * exact match later, we need to + * error out. + */ + ambiguous_option = abbrev_option; + ambiguous_flags = abbrev_flags; + } if (!(flags & OPT_UNSET) && *arg_end) p->opt = arg_end + 1; abbrev_option = options; @@ -176,6 +176,15 @@ is_abbreviated: } return get_value(p, options, flags); } + + if (ambiguous_option) + return error("Ambiguous option: %s " + "(could be --%s%s or --%s%s)", + arg, + (ambiguous_flags & OPT_UNSET) ? "no-" : "", + ambiguous_option->long_name, + (abbrev_flags & OPT_UNSET) ? "no-" : "", + abbrev_option->long_name); if (abbrev_option) return get_value(p, abbrev_option, abbrev_flags); return error("unknown option `%s'", arg); diff --git a/perl/Git.pm b/perl/Git.pm index 3f4080cbf8..dca92c8adb 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -812,7 +812,7 @@ sub _cmd_exec { $self->wc_subdir() and chdir($self->wc_subdir()); } _execv_git_cmd(@args); - die "exec failed: $!"; + die qq[exec "@args" failed: $!]; } # Execute the given Git command ($_[0]) with arguments ($_[1..]) @@ -1,6 +1,5 @@ #include "cache.h" #include "commit.h" -#include "interpolate.h" #include "utf8.h" #include "diff.h" #include "revision.h" @@ -283,7 +282,8 @@ static char *logmsg_reencode(const struct commit *commit, return out; } -static void fill_person(struct interp *table, const char *msg, int len) +static void format_person_part(struct strbuf *sb, char part, + const char *msg, int len) { int start, end, tz = 0; unsigned long date; @@ -295,7 +295,10 @@ static void fill_person(struct interp *table, const char *msg, int len) start = end + 1; while (end > 0 && isspace(msg[end - 1])) end--; - table[0].value = xmemdupz(msg, end); + if (part == 'n') { /* name */ + strbuf_add(sb, msg, end); + return; + } if (start >= len) return; @@ -307,7 +310,10 @@ static void fill_person(struct interp *table, const char *msg, int len) if (end >= len) return; - table[1].value = xmemdupz(msg + start, end - start); + if (part == 'e') { /* email */ + strbuf_add(sb, msg + start, end - start); + return; + } /* parse date */ for (start = end + 1; start < len && isspace(msg[start]); start++) @@ -318,7 +324,10 @@ static void fill_person(struct interp *table, const char *msg, int len) if (msg + start == ep) return; - table[5].value = xmemdupz(msg + start, ep - (msg + start)); + if (part == 't') { /* date, UNIX timestamp */ + strbuf_add(sb, msg + start, ep - (msg + start)); + return; + } /* parse tz */ for (start = ep - msg + 1; start < len && isspace(msg[start]); start++) @@ -329,115 +338,66 @@ static void fill_person(struct interp *table, const char *msg, int len) tz = -tz; } - interp_set_entry(table, 2, show_date(date, tz, DATE_NORMAL)); - interp_set_entry(table, 3, show_date(date, tz, DATE_RFC2822)); - interp_set_entry(table, 4, show_date(date, tz, DATE_RELATIVE)); - interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601)); + switch (part) { + case 'd': /* date */ + strbuf_addstr(sb, show_date(date, tz, DATE_NORMAL)); + return; + case 'D': /* date, RFC2822 style */ + strbuf_addstr(sb, show_date(date, tz, DATE_RFC2822)); + return; + case 'r': /* date, relative */ + strbuf_addstr(sb, show_date(date, tz, DATE_RELATIVE)); + return; + case 'i': /* date, ISO 8601 */ + strbuf_addstr(sb, show_date(date, tz, DATE_ISO8601)); + return; + } } -void format_commit_message(const struct commit *commit, - const void *format, struct strbuf *sb) -{ - struct interp table[] = { - { "%H" }, /* commit hash */ - { "%h" }, /* abbreviated commit hash */ - { "%T" }, /* tree hash */ - { "%t" }, /* abbreviated tree hash */ - { "%P" }, /* parent hashes */ - { "%p" }, /* abbreviated parent hashes */ - { "%an" }, /* author name */ - { "%ae" }, /* author email */ - { "%ad" }, /* author date */ - { "%aD" }, /* author date, RFC2822 style */ - { "%ar" }, /* author date, relative */ - { "%at" }, /* author date, UNIX timestamp */ - { "%ai" }, /* author date, ISO 8601 */ - { "%cn" }, /* committer name */ - { "%ce" }, /* committer email */ - { "%cd" }, /* committer date */ - { "%cD" }, /* committer date, RFC2822 style */ - { "%cr" }, /* committer date, relative */ - { "%ct" }, /* committer date, UNIX timestamp */ - { "%ci" }, /* committer date, ISO 8601 */ - { "%e" }, /* encoding */ - { "%s" }, /* subject */ - { "%b" }, /* body */ - { "%Cred" }, /* red */ - { "%Cgreen" }, /* green */ - { "%Cblue" }, /* blue */ - { "%Creset" }, /* reset color */ - { "%n" }, /* newline */ - { "%m" }, /* left/right/bottom */ - }; - enum interp_index { - IHASH = 0, IHASH_ABBREV, - ITREE, ITREE_ABBREV, - IPARENTS, IPARENTS_ABBREV, - IAUTHOR_NAME, IAUTHOR_EMAIL, - IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE, - IAUTHOR_TIMESTAMP, IAUTHOR_ISO8601, - ICOMMITTER_NAME, ICOMMITTER_EMAIL, - ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822, - ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP, - ICOMMITTER_ISO8601, - IENCODING, - ISUBJECT, - IBODY, - IRED, IGREEN, IBLUE, IRESET_COLOR, - INEWLINE, - ILEFT_RIGHT, - }; - struct commit_list *p; - char parents[1024]; - unsigned long len; - int i; - enum { HEADER, SUBJECT, BODY } state; - const char *msg = commit->buffer; +struct chunk { + size_t off; + size_t len; +}; - if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table)) - die("invalid interp table!"); +struct format_commit_context { + const struct commit *commit; + + /* These offsets are relative to the start of the commit message. */ + int commit_header_parsed; + struct chunk subject; + struct chunk author; + struct chunk committer; + struct chunk encoding; + size_t body_off; + + /* The following ones are relative to the result struct strbuf. */ + struct chunk abbrev_commit_hash; + struct chunk abbrev_tree_hash; + struct chunk abbrev_parent_hashes; +}; - /* these are independent of the commit */ - interp_set_entry(table, IRED, "\033[31m"); - interp_set_entry(table, IGREEN, "\033[32m"); - interp_set_entry(table, IBLUE, "\033[34m"); - interp_set_entry(table, IRESET_COLOR, "\033[m"); - interp_set_entry(table, INEWLINE, "\n"); +static int add_again(struct strbuf *sb, struct chunk *chunk) +{ + if (chunk->len) { + strbuf_adddup(sb, chunk->off, chunk->len); + return 1; + } - /* these depend on the commit */ - if (!commit->object.parsed) - parse_object(commit->object.sha1); - interp_set_entry(table, IHASH, sha1_to_hex(commit->object.sha1)); - interp_set_entry(table, IHASH_ABBREV, - find_unique_abbrev(commit->object.sha1, - DEFAULT_ABBREV)); - interp_set_entry(table, ITREE, sha1_to_hex(commit->tree->object.sha1)); - interp_set_entry(table, ITREE_ABBREV, - find_unique_abbrev(commit->tree->object.sha1, - DEFAULT_ABBREV)); - interp_set_entry(table, ILEFT_RIGHT, - (commit->object.flags & BOUNDARY) - ? "-" - : (commit->object.flags & SYMMETRIC_LEFT) - ? "<" - : ">"); - - parents[1] = 0; - for (i = 0, p = commit->parents; - p && i < sizeof(parents) - 1; - p = p->next) - i += snprintf(parents + i, sizeof(parents) - i - 1, " %s", - sha1_to_hex(p->item->object.sha1)); - interp_set_entry(table, IPARENTS, parents + 1); - - parents[1] = 0; - for (i = 0, p = commit->parents; - p && i < sizeof(parents) - 1; - p = p->next) - i += snprintf(parents + i, sizeof(parents) - i - 1, " %s", - find_unique_abbrev(p->item->object.sha1, - DEFAULT_ABBREV)); - interp_set_entry(table, IPARENTS_ABBREV, parents + 1); + /* + * We haven't seen this chunk before. Our caller is surely + * going to add it the hard way now. Remember the most likely + * start of the to-be-added chunk: the current end of the + * struct strbuf. + */ + chunk->off = sb->len; + return 0; +} + +static void parse_commit_header(struct format_commit_context *context) +{ + const char *msg = context->commit->buffer; + int i; + enum { HEADER, SUBJECT, BODY } state; for (i = 0, state = HEADER; msg[i] && state < BODY; i++) { int eol; @@ -445,7 +405,8 @@ void format_commit_message(const struct commit *commit, ; /* do nothing */ if (state == SUBJECT) { - table[ISUBJECT].value = xmemdupz(msg + i, eol - i); + context->subject.off = i; + context->subject.len = eol - i; i = eol; } if (i == eol) { @@ -453,29 +414,170 @@ void format_commit_message(const struct commit *commit, /* strip empty lines */ while (msg[eol + 1] == '\n') eol++; - } else if (!prefixcmp(msg + i, "author ")) - fill_person(table + IAUTHOR_NAME, - msg + i + 7, eol - i - 7); - else if (!prefixcmp(msg + i, "committer ")) - fill_person(table + ICOMMITTER_NAME, - msg + i + 10, eol - i - 10); - else if (!prefixcmp(msg + i, "encoding ")) - table[IENCODING].value = - xmemdupz(msg + i + 9, eol - i - 9); + } else if (!prefixcmp(msg + i, "author ")) { + context->author.off = i + 7; + context->author.len = eol - i - 7; + } else if (!prefixcmp(msg + i, "committer ")) { + context->committer.off = i + 10; + context->committer.len = eol - i - 10; + } else if (!prefixcmp(msg + i, "encoding ")) { + context->encoding.off = i + 9; + context->encoding.len = eol - i - 9; + } i = eol; } - if (msg[i]) - table[IBODY].value = xstrdup(msg + i); + context->body_off = i; + context->commit_header_parsed = 1; +} + +static void format_commit_item(struct strbuf *sb, const char *placeholder, + void *context) +{ + struct format_commit_context *c = context; + const struct commit *commit = c->commit; + const char *msg = commit->buffer; + struct commit_list *p; + + /* these are independent of the commit */ + switch (placeholder[0]) { + case 'C': + switch (placeholder[3]) { + case 'd': /* red */ + strbuf_addstr(sb, "\033[31m"); + return; + case 'e': /* green */ + strbuf_addstr(sb, "\033[32m"); + return; + case 'u': /* blue */ + strbuf_addstr(sb, "\033[34m"); + return; + case 's': /* reset color */ + strbuf_addstr(sb, "\033[m"); + return; + } + case 'n': /* newline */ + strbuf_addch(sb, '\n'); + return; + } - len = interpolate(sb->buf + sb->len, strbuf_avail(sb), - format, table, ARRAY_SIZE(table)); - if (len > strbuf_avail(sb)) { - strbuf_grow(sb, len); - interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1, - format, table, ARRAY_SIZE(table)); + /* these depend on the commit */ + if (!commit->object.parsed) + parse_object(commit->object.sha1); + + switch (placeholder[0]) { + case 'H': /* commit hash */ + strbuf_addstr(sb, sha1_to_hex(commit->object.sha1)); + return; + case 'h': /* abbreviated commit hash */ + if (add_again(sb, &c->abbrev_commit_hash)) + return; + strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1, + DEFAULT_ABBREV)); + c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off; + return; + case 'T': /* tree hash */ + strbuf_addstr(sb, sha1_to_hex(commit->tree->object.sha1)); + return; + case 't': /* abbreviated tree hash */ + if (add_again(sb, &c->abbrev_tree_hash)) + return; + strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1, + DEFAULT_ABBREV)); + c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off; + return; + case 'P': /* parent hashes */ + for (p = commit->parents; p; p = p->next) { + if (p != commit->parents) + strbuf_addch(sb, ' '); + strbuf_addstr(sb, sha1_to_hex(p->item->object.sha1)); + } + return; + case 'p': /* abbreviated parent hashes */ + if (add_again(sb, &c->abbrev_parent_hashes)) + return; + for (p = commit->parents; p; p = p->next) { + if (p != commit->parents) + strbuf_addch(sb, ' '); + strbuf_addstr(sb, find_unique_abbrev( + p->item->object.sha1, DEFAULT_ABBREV)); + } + c->abbrev_parent_hashes.len = sb->len - + c->abbrev_parent_hashes.off; + return; + case 'm': /* left/right/bottom */ + strbuf_addch(sb, (commit->object.flags & BOUNDARY) + ? '-' + : (commit->object.flags & SYMMETRIC_LEFT) + ? '<' + : '>'); + return; + } + + /* For the rest we have to parse the commit header. */ + if (!c->commit_header_parsed) + parse_commit_header(c); + + switch (placeholder[0]) { + case 's': + strbuf_add(sb, msg + c->subject.off, c->subject.len); + return; + case 'a': + format_person_part(sb, placeholder[1], + msg + c->author.off, c->author.len); + return; + case 'c': + format_person_part(sb, placeholder[1], + msg + c->committer.off, c->committer.len); + return; + case 'e': + strbuf_add(sb, msg + c->encoding.off, c->encoding.len); + return; + case 'b': + strbuf_addstr(sb, msg + c->body_off); + return; } - strbuf_setlen(sb, sb->len + len); - interp_clear_table(table, ARRAY_SIZE(table)); +} + +void format_commit_message(const struct commit *commit, + const void *format, struct strbuf *sb) +{ + const char *placeholders[] = { + "H", /* commit hash */ + "h", /* abbreviated commit hash */ + "T", /* tree hash */ + "t", /* abbreviated tree hash */ + "P", /* parent hashes */ + "p", /* abbreviated parent hashes */ + "an", /* author name */ + "ae", /* author email */ + "ad", /* author date */ + "aD", /* author date, RFC2822 style */ + "ar", /* author date, relative */ + "at", /* author date, UNIX timestamp */ + "ai", /* author date, ISO 8601 */ + "cn", /* committer name */ + "ce", /* committer email */ + "cd", /* committer date */ + "cD", /* committer date, RFC2822 style */ + "cr", /* committer date, relative */ + "ct", /* committer date, UNIX timestamp */ + "ci", /* committer date, ISO 8601 */ + "e", /* encoding */ + "s", /* subject */ + "b", /* body */ + "Cred", /* red */ + "Cgreen", /* green */ + "Cblue", /* blue */ + "Creset", /* reset color */ + "n", /* newline */ + "m", /* left/right/bottom */ + NULL + }; + struct format_commit_context context; + + memset(&context, 0, sizeof(context)); + context.commit = commit; + strbuf_expand(sb, format, placeholders, format_commit_item, &context); } static void pp_header(enum cmit_fmt fmt, diff --git a/progress.c b/progress.c index 3f6a602a53..4bd650f9ba 100644 --- a/progress.c +++ b/progress.c @@ -14,12 +14,12 @@ #define TP_IDX_MAX 8 struct throughput { + off_t curr_total; + off_t prev_total; struct timeval prev_tv; - off_t total; - unsigned long count; - unsigned long avg_bytes; - unsigned long last_bytes[TP_IDX_MAX]; + unsigned int avg_bytes; unsigned int avg_misecs; + unsigned int last_bytes[TP_IDX_MAX]; unsigned int last_misecs[TP_IDX_MAX]; unsigned int idx; char display[32]; @@ -69,9 +69,9 @@ static void clear_progress_signal(void) progress_update = 0; } -static int display(struct progress *progress, unsigned n, int done) +static int display(struct progress *progress, unsigned n, const char *done) { - char *eol, *tp; + const char *eol, *tp; if (progress->delay) { if (!progress_update || --progress->delay) @@ -90,7 +90,7 @@ static int display(struct progress *progress, unsigned n, int done) progress->last_value = n; tp = (progress->throughput) ? progress->throughput->display : ""; - eol = done ? ", done. \n" : " \r"; + eol = done ? done : " \r"; if (progress->total) { unsigned percent = n * 100 / progress->total; if (percent != progress->last_percent || progress_update) { @@ -110,7 +110,31 @@ static int display(struct progress *progress, unsigned n, int done) return 0; } -void display_throughput(struct progress *progress, unsigned long n) +static void throughput_string(struct throughput *tp, off_t total, + unsigned int rate) +{ + int l = sizeof(tp->display); + if (total > 1 << 30) { + l -= snprintf(tp->display, l, ", %u.%2.2u GiB", + (int)(total >> 30), + (int)(total & ((1 << 30) - 1)) / 10737419); + } else if (total > 1 << 20) { + l -= snprintf(tp->display, l, ", %u.%2.2u MiB", + (int)(total >> 20), + ((int)(total & ((1 << 20) - 1)) * 100) >> 20); + } else if (total > 1 << 10) { + l -= snprintf(tp->display, l, ", %u.%2.2u KiB", + (int)(total >> 10), + ((int)(total & ((1 << 10) - 1)) * 100) >> 10); + } else { + l -= snprintf(tp->display, l, ", %u bytes", (int)total); + } + if (rate) + snprintf(tp->display + sizeof(tp->display) - l, l, + " | %u KiB/s", rate); +} + +void display_throughput(struct progress *progress, off_t total) { struct throughput *tp; struct timeval tv; @@ -124,13 +148,13 @@ void display_throughput(struct progress *progress, unsigned long n) if (!tp) { progress->throughput = tp = calloc(1, sizeof(*tp)); - if (tp) + if (tp) { + tp->prev_total = tp->curr_total = total; tp->prev_tv = tv; + } return; } - - tp->total += n; - tp->count += n; + tp->curr_total = total; /* * We have x = bytes and y = microsecs. We want z = KiB/s: @@ -151,47 +175,29 @@ void display_throughput(struct progress *progress, unsigned long n) misecs += (int)(tv.tv_usec - tp->prev_tv.tv_usec) / 977; if (misecs > 512) { - int l = sizeof(tp->display); + unsigned int count, rate; + + count = total - tp->prev_total; + tp->prev_total = total; tp->prev_tv = tv; - tp->avg_bytes += tp->count; + tp->avg_bytes += count; tp->avg_misecs += misecs; - - if (tp->total > 1 << 30) { - l -= snprintf(tp->display, l, ", %u.%2.2u GiB", - (int)(tp->total >> 30), - (int)(tp->total & ((1 << 30) - 1)) / 10737419); - } else if (tp->total > 1 << 20) { - l -= snprintf(tp->display, l, ", %u.%2.2u MiB", - (int)(tp->total >> 20), - ((int)(tp->total & ((1 << 20) - 1)) - * 100) >> 20); - } else if (tp->total > 1 << 10) { - l -= snprintf(tp->display, l, ", %u.%2.2u KiB", - (int)(tp->total >> 10), - ((int)(tp->total & ((1 << 10) - 1)) - * 100) >> 10); - } else { - l -= snprintf(tp->display, l, ", %u bytes", - (int)tp->total); - } - snprintf(tp->display + sizeof(tp->display) - l, l, - " | %lu KiB/s", tp->avg_bytes / tp->avg_misecs); - + rate = tp->avg_bytes / tp->avg_misecs; tp->avg_bytes -= tp->last_bytes[tp->idx]; tp->avg_misecs -= tp->last_misecs[tp->idx]; - tp->last_bytes[tp->idx] = tp->count; + tp->last_bytes[tp->idx] = count; tp->last_misecs[tp->idx] = misecs; tp->idx = (tp->idx + 1) % TP_IDX_MAX; - tp->count = 0; + throughput_string(tp, total, rate); if (progress->last_value != -1 && progress_update) - display(progress, progress->last_value, 0); + display(progress, progress->last_value, NULL); } } int display_progress(struct progress *progress, unsigned n) { - return progress ? display(progress, n, 0) : 0; + return progress ? display(progress, n, NULL) : 0; } struct progress *start_progress_delay(const char *title, unsigned total, @@ -221,14 +227,27 @@ struct progress *start_progress(const char *title, unsigned total) void stop_progress(struct progress **p_progress) { + stop_progress_msg(p_progress, "done"); +} + +void stop_progress_msg(struct progress **p_progress, const char *msg) +{ struct progress *progress = *p_progress; if (!progress) return; *p_progress = NULL; if (progress->last_value != -1) { /* Force the last update */ + char buf[strlen(msg) + 5]; + struct throughput *tp = progress->throughput; + if (tp) { + unsigned int rate = !tp->avg_misecs ? 0 : + tp->avg_bytes / tp->avg_misecs; + throughput_string(tp, tp->curr_total, rate); + } progress_update = 1; - display(progress, progress->last_value, 1); + sprintf(buf, ", %s.\n", msg); + display(progress, progress->last_value, buf); } clear_progress_signal(); free(progress->throughput); diff --git a/progress.h b/progress.h index 61cb68dfa5..611e4c4d42 100644 --- a/progress.h +++ b/progress.h @@ -3,11 +3,12 @@ struct progress; -void display_throughput(struct progress *progress, unsigned long n); +void display_throughput(struct progress *progress, off_t total); int display_progress(struct progress *progress, unsigned n); struct progress *start_progress(const char *title, unsigned total); struct progress *start_progress_delay(const char *title, unsigned total, unsigned percent_treshold, unsigned delay); void stop_progress(struct progress **progress); +void stop_progress_msg(struct progress **progress, const char *msg); #endif @@ -131,7 +131,8 @@ static signed char const sq_lookup[256] = { /* 0x80 */ /* set to 0 */ }; -static inline int sq_must_quote(char c) { +static inline int sq_must_quote(char c) +{ return sq_lookup[(unsigned char)c] + quote_path_fully > 0; } diff --git a/read-cache.c b/read-cache.c index 056b322fb0..7db55883d6 100644 --- a/read-cache.c +++ b/read-cache.c @@ -194,11 +194,12 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) } int ie_match_stat(struct index_state *istate, - struct cache_entry *ce, struct stat *st, int options) + struct cache_entry *ce, struct stat *st, + unsigned int options) { unsigned int changed; - int ignore_valid = options & 01; - int assume_racy_is_modified = options & 02; + int ignore_valid = options & CE_MATCH_IGNORE_VALID; + int assume_racy_is_modified = options & CE_MATCH_RACY_IS_DIRTY; /* * If it's marked as always valid in the index, it's @@ -238,10 +239,11 @@ int ie_match_stat(struct index_state *istate, } int ie_modified(struct index_state *istate, - struct cache_entry *ce, struct stat *st, int really) + struct cache_entry *ce, struct stat *st, unsigned int options) { int changed, changed_fs; - changed = ie_match_stat(istate, ce, st, really); + + changed = ie_match_stat(istate, ce, st, options); if (!changed) return 0; /* @@ -387,6 +389,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose) int size, namelen, pos; struct stat st; struct cache_entry *ce; + unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY; if (lstat(path, &st)) die("%s: unable to stat (%s)", path, strerror(errno)); @@ -421,7 +424,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose) pos = index_name_pos(istate, ce->name, namelen); if (0 <= pos && !ce_stage(istate->cache[pos]) && - !ie_modified(istate, istate->cache[pos], &st, 1)) { + !ie_match_stat(istate, istate->cache[pos], &st, ce_option)) { /* Nothing changed, really */ free(ce); return 0; @@ -783,11 +786,13 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti * to link up the stat cache details with the proper files. */ static struct cache_entry *refresh_cache_ent(struct index_state *istate, - struct cache_entry *ce, int really, int *err) + struct cache_entry *ce, + unsigned int options, int *err) { struct stat st; struct cache_entry *updated; int changed, size; + int ignore_valid = options & CE_MATCH_IGNORE_VALID; if (lstat(ce->name, &st) < 0) { if (err) @@ -795,16 +800,23 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate, return NULL; } - changed = ie_match_stat(istate, ce, &st, really); + changed = ie_match_stat(istate, ce, &st, options); if (!changed) { - if (really && assume_unchanged && + /* + * The path is unchanged. If we were told to ignore + * valid bit, then we did the actual stat check and + * found that the entry is unmodified. If the entry + * is not marked VALID, this is the place to mark it + * valid again, under "assume unchanged" mode. + */ + if (ignore_valid && assume_unchanged && !(ce->ce_flags & htons(CE_VALID))) ; /* mark this one VALID again */ else return ce; } - if (ie_modified(istate, ce, &st, really)) { + if (ie_modified(istate, ce, &st, options)) { if (err) *err = EINVAL; return NULL; @@ -815,13 +827,14 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate, memcpy(updated, ce, size); fill_stat_cache_info(updated, &st); - /* In this case, if really is not set, we should leave - * CE_VALID bit alone. Otherwise, paths marked with - * --no-assume-unchanged (i.e. things to be edited) will - * reacquire CE_VALID bit automatically, which is not - * really what we want. + /* + * If ignore_valid is not set, we should leave CE_VALID bit + * alone. Otherwise, paths marked with --no-assume-unchanged + * (i.e. things to be edited) will reacquire CE_VALID bit + * automatically, which is not really what we want. */ - if (!really && assume_unchanged && !(ce->ce_flags & htons(CE_VALID))) + if (!ignore_valid && assume_unchanged && + !(ce->ce_flags & htons(CE_VALID))) updated->ce_flags &= ~htons(CE_VALID); return updated; @@ -835,6 +848,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p int allow_unmerged = (flags & REFRESH_UNMERGED) != 0; int quiet = (flags & REFRESH_QUIET) != 0; int not_new = (flags & REFRESH_IGNORE_MISSING) != 0; + unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0; for (i = 0; i < istate->cache_nr; i++) { struct cache_entry *ce, *new; @@ -856,7 +870,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p if (pathspec && !match_pathspec(pathspec, ce->name, strlen(ce->name), 0, seen)) continue; - new = refresh_cache_ent(istate, ce, really, &cache_errno); + new = refresh_cache_ent(istate, ce, options, &cache_errno); if (new == ce) continue; if (!new) { @@ -580,18 +580,6 @@ int for_each_remote_ref(each_ref_fn fn, void *cb_data) return do_for_each_ref("refs/remotes/", fn, 13, cb_data); } -/* NEEDSWORK: This is only used by ssh-upload and it should go; the - * caller should do resolve_ref or read_ref like everybody else. Or - * maybe everybody else should use get_ref_sha1() instead of doing - * read_ref(). - */ -int get_ref_sha1(const char *ref, unsigned char *sha1) -{ - if (check_ref_format(ref)) - return -1; - return read_ref(mkpath("refs/%s", ref), sha1); -} - /* * Make sure "ref" is something reasonable to have under ".git/refs/"; * We do not like it if: @@ -26,9 +26,6 @@ extern int for_each_remote_ref(each_ref_fn, void *); extern int peel_ref(const char *, unsigned char *); -/** Reads the refs file specified into sha1 **/ -extern int get_ref_sha1(const char *ref, unsigned char *sha1); - /** Locks a "refs/" ref returning the lock on success and NULL on failure. **/ extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1); diff --git a/run-command.c b/run-command.c index d99a6c4ea7..476d00c218 100644 --- a/run-command.c +++ b/run-command.c @@ -41,7 +41,7 @@ int start_command(struct child_process *cmd) cmd->close_out = 1; } - need_err = cmd->err < 0; + need_err = !cmd->no_stderr && cmd->err < 0; if (need_err) { if (pipe(fderr) < 0) { if (need_in) @@ -87,7 +87,9 @@ int start_command(struct child_process *cmd) close(cmd->out); } - if (need_err) { + if (cmd->no_stderr) + dup_devnull(2); + else if (need_err) { dup2(fderr[1], 2); close_pair(fderr); } diff --git a/run-command.h b/run-command.h index 94e1e9d516..1fc781d766 100644 --- a/run-command.h +++ b/run-command.h @@ -23,6 +23,7 @@ struct child_process { unsigned close_out:1; unsigned no_stdin:1; unsigned no_stdout:1; + unsigned no_stderr:1; unsigned git_cmd:1; /* if this is to be git sub-command */ unsigned stdout_to_stderr:1; }; @@ -206,6 +206,22 @@ static const char *set_work_tree(const char *dir) return NULL; } +void setup_work_tree(void) +{ + const char *work_tree, *git_dir; + static int initialized = 0; + + if (initialized) + return; + work_tree = get_git_work_tree(); + git_dir = get_git_dir(); + if (!is_absolute_path(git_dir)) + set_git_dir(make_absolute_path(git_dir)); + if (!work_tree || chdir(work_tree)) + die("This operation must be run in a work tree"); + initialized = 1; +} + /* * We cannot decide in this function whether we are in the work tree or * not, since the config can only be read _after_ this function was called. diff --git a/sideband.c b/sideband.c index ab8a1e990d..756bbc28d7 100644 --- a/sideband.c +++ b/sideband.c @@ -11,13 +11,19 @@ * stream, aka "verbose"). A message over band #3 is a signal that * the remote died unexpectedly. A flush() concludes the stream. */ + +#define PREFIX "remote:" +#define SUFFIX "\033[K" /* change to " " if ANSI sequences don't work */ + int recv_sideband(const char *me, int in_stream, int out, int err) { - char buf[7 + LARGE_PACKET_MAX + 1]; - strcpy(buf, "remote:"); + unsigned pf = strlen(PREFIX); + unsigned sf = strlen(SUFFIX); + char buf[pf + LARGE_PACKET_MAX + sf + 1]; + memcpy(buf, PREFIX, pf); while (1) { int band, len; - len = packet_read_line(in_stream, buf+7, LARGE_PACKET_MAX); + len = packet_read_line(in_stream, buf + pf, LARGE_PACKET_MAX); if (len == 0) break; if (len < 1) { @@ -25,35 +31,52 @@ int recv_sideband(const char *me, int in_stream, int out, int err) safe_write(err, buf, len); return SIDEBAND_PROTOCOL_ERROR; } - band = buf[7] & 0xff; + band = buf[pf] & 0xff; len--; switch (band) { case 3: - buf[7] = ' '; - buf[8+len] = '\n'; - safe_write(err, buf, 8+len+1); + buf[pf] = ' '; + buf[pf+1+len] = '\n'; + safe_write(err, buf, pf+1+len+1); return SIDEBAND_REMOTE_ERROR; case 2: - buf[7] = ' '; - len += 8; + buf[pf] = ' '; + len += pf+1; while (1) { - int brk = 8; + int brk = pf+1; + + /* Break the buffer into separate lines. */ while (brk < len) { brk++; if (buf[brk-1] == '\n' || buf[brk-1] == '\r') break; } - safe_write(err, buf, brk); + + /* + * Let's insert a suffix to clear the end + * of the screen line, but only if current + * line data actually contains something. + */ + if (brk > pf+1 + 1) { + char save[sf]; + memcpy(save, buf + brk, sf); + buf[brk + sf - 1] = buf[brk - 1]; + memcpy(buf + brk - 1, SUFFIX, sf); + safe_write(err, buf, brk + sf); + memcpy(buf + brk, save, sf); + } else + safe_write(err, buf, brk); + if (brk < len) { - memmove(buf + 8, buf + brk, len - brk); - len = len - brk + 8; + memmove(buf + pf+1, buf + brk, len - brk); + len = len - brk + pf+1; } else break; } continue; case 1: - safe_write(out, buf+8, len); + safe_write(out, buf + pf+1, len); continue; default: len = sprintf(buf, @@ -106,17 +106,25 @@ void strbuf_add(struct strbuf *sb, const void *data, size_t len) strbuf_setlen(sb, sb->len + len); } +void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len) +{ + strbuf_grow(sb, len); + memcpy(sb->buf + sb->len, sb->buf + pos, len); + strbuf_setlen(sb, sb->len + len); +} + void strbuf_addf(struct strbuf *sb, const char *fmt, ...) { int len; va_list ap; + if (!strbuf_avail(sb)) + strbuf_grow(sb, 64); va_start(ap, fmt); len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); va_end(ap); - if (len < 0) { - len = 0; - } + if (len < 0) + die("your vsnprintf is broken"); if (len > strbuf_avail(sb)) { strbuf_grow(sb, len); va_start(ap, fmt); @@ -129,6 +137,30 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...) strbuf_setlen(sb, sb->len + len); } +void strbuf_expand(struct strbuf *sb, const char *format, + const char **placeholders, expand_fn_t fn, void *context) +{ + for (;;) { + const char *percent, **p; + + percent = strchrnul(format, '%'); + strbuf_add(sb, format, percent - format); + if (!*percent) + break; + format = percent + 1; + + for (p = placeholders; *p; p++) { + if (!prefixcmp(format, *p)) + break; + } + if (*p) { + fn(sb, *p, context); + format += strlen(*p); + } else + strbuf_addch(sb, '%'); + } +} + size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f) { size_t res; @@ -101,6 +101,10 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s) { static inline void strbuf_addbuf(struct strbuf *sb, struct strbuf *sb2) { strbuf_add(sb, sb2->buf, sb2->len); } +extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len); + +typedef void (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context); +extern void strbuf_expand(struct strbuf *sb, const char *format, const char **placeholders, expand_fn_t fn, void *context); __attribute__((format(printf,2,3))) extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index ae49424aa0..462fdf262f 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -18,6 +18,7 @@ string options -s, --string <string> get a string --string2 <str> get another string + --st <st> get another string (pervert ordering) EOF @@ -90,4 +91,16 @@ test_expect_failure 'ambiguously abbreviated option' ' test $? != 129 ' +cat > expect << EOF +boolean: 0 +integer: 0 +string: 123 +EOF + +test_expect_success 'non ambiguous option (after two options it abbreviates)' ' + test-parse-options --st 123 > output 2> output.err && + test ! -s output.err && + git diff expect output +' + test_done diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index ce045b2a57..a90824ba8a 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -205,7 +205,7 @@ test_expect_success \ echo $h_TEST >.git/MERGE_HEAD && GIT_AUTHOR_DATE="2005-05-26 23:45" \ GIT_COMMITTER_DATE="2005-05-26 23:45" git-commit -F M && - h_MERGED=$(git rev-parse --verify HEAD) + h_MERGED=$(git rev-parse --verify HEAD) && rm -f M' cat >expect <<EOF diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh index eb1ced3c37..24f892f793 100755 --- a/t/t2200-add-update.sh +++ b/t/t2200-add-update.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='git add -u with path limiting +test_description='git add -u This test creates a working tree state with three files: @@ -9,7 +9,10 @@ This test creates a working tree state with three files: dir/other (untracked) and issues a git add -u with path limiting on "dir" to add -only the updates to dir/sub.' +only the updates to dir/sub. + +Also tested are "git add -u" without limiting, and "git add -u" +without contents changes.' . ./test-lib.sh @@ -85,4 +88,27 @@ test_expect_success 'replace a file with a symlink' ' ' +test_expect_success 'add everything changed' ' + + git add -u && + test -z "$(git diff-files)" + +' + +test_expect_success 'touch and then add -u' ' + + touch check && + git add -u && + test -z "$(git diff-files)" + +' + +test_expect_success 'touch and then add explicitly' ' + + touch check && + git add check && + test -z "$(git diff-files)" + +' + test_done diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh index 0779aaa9ab..7b7d07269a 100755 --- a/t/t3402-rebase-merge.sh +++ b/t/t3402-rebase-merge.sh @@ -48,9 +48,14 @@ test_expect_success 'reference merge' ' git merge -s recursive "reference merge" HEAD master ' +PRE_REBASE=$(git rev-parse test-rebase) test_expect_success rebase ' git checkout test-rebase && - git rebase --merge master + GIT_TRACE=1 git rebase --merge master +' + +test_expect_success 'test-rebase@{1} is pre rebase' ' + test $PRE_REBASE = $(git rev-parse test-rebase@{1}) ' test_expect_success 'merge and rebase should match' ' diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh index eab053c3e0..2ee5a00ea7 100755 --- a/t/t3403-rebase-skip.sh +++ b/t/t3403-rebase-skip.sh @@ -39,6 +39,19 @@ test_expect_success 'rebase --skip with am -3' ' git reset --hard HEAD && git rebase --skip ' + +test_expect_success 'rebase moves back to skip-reference' ' + test refs/heads/skip-reference = $(git symbolic-ref HEAD) && + git branch post-rebase && + git reset --hard pre-rebase && + ! git rebase master && + echo "hello" > hello && + git add hello && + git rebase --continue && + test refs/heads/skip-reference = $(git symbolic-ref HEAD) && + git reset --hard post-rebase +' + test_expect_success 'checkout skip-merge' 'git checkout -f skip-merge' test_expect_failure 'rebase with --merge' 'git rebase --merge master' @@ -51,6 +64,10 @@ test_expect_success 'rebase --skip with --merge' ' test_expect_success 'merge and reference trees equal' \ 'test -z "`git diff-tree skip-merge skip-reference`"' +test_expect_success 'moved back to branch correctly' ' + test refs/heads/skip-merge = $(git symbolic-ref HEAD) +' + test_debug 'gitk --all & sleep 1' test_done diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 11139048fe..f1039d1a21 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -149,7 +149,7 @@ test_expect_success 'stop on conflicting pick' ' diff -u expect .git/.dotest-merge/patch && diff -u expect2 file1 && test 4 = $(grep -v "^#" < .git/.dotest-merge/done | wc -l) && - test 0 = $(grep -v "^#" < .git/.dotest-merge/todo | wc -l) + test 0 = $(grep -v "^#" < .git/.dotest-merge/git-rebase-todo | wc -l) ' test_expect_success 'abort' ' diff --git a/t/t3700-add.sh b/t/t3700-add.sh index a328bf57eb..287e058e37 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -104,9 +104,33 @@ test_expect_success 'add ignored ones with -f' ' git ls-files --error-unmatch d.ig/d.if d.ig/d.ig ' +test_expect_success 'add ignored ones with -f' ' + rm -f .git/index && + git add -f d.?? && + git ls-files --error-unmatch d.ig/d.if d.ig/d.ig +' + +test_expect_success '.gitignore with subdirectory' ' + + rm -f .git/index && + mkdir -p sub/dir && + echo "!dir/a.*" >sub/.gitignore && + >sub/a.ig && + >sub/dir/a.ig && + git add sub/dir && + git ls-files --error-unmatch sub/dir/a.ig && + rm -f .git/index && + ( + cd sub/dir && + git add . + ) && + git ls-files --error-unmatch sub/dir/a.ig +' + mkdir 1 1/2 1/3 touch 1/2/a 1/3/b 1/2/c test_expect_success 'check correct prefix detection' ' + rm -f .git/index && git add 1/2/a 1/3/b 1/2/c ' diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh new file mode 100755 index 0000000000..43d64bbd82 --- /dev/null +++ b/t/t4021-format-patch-numbered.sh @@ -0,0 +1,106 @@ +#!/bin/sh +# +# Copyright (c) 2006 Brian C Gernhardt +# + +test_description='Format-patch numbering options' + +. ./test-lib.sh + +test_expect_success setup ' + + echo A > file && + git add file && + git commit -m First && + + echo B >> file && + git commit -a -m Second && + + echo C >> file && + git commit -a -m Third + +' + +# Each of these gets used multiple times. + +test_num_no_numbered() { + cnt=$(grep "^Subject: \[PATCH\]" $1 | wc -l) && + test $cnt = $2 +} + +test_single_no_numbered() { + test_num_no_numbered $1 1 +} + +test_no_numbered() { + test_num_no_numbered $1 2 +} + +test_single_numbered() { + grep "^Subject: \[PATCH 1/1\]" $1 +} + +test_numbered() { + grep "^Subject: \[PATCH 1/2\]" $1 && + grep "^Subject: \[PATCH 2/2\]" $1 +} + +test_expect_success 'Default: no numbered' ' + + git format-patch --stdout HEAD~2 >patch0 && + test_no_numbered patch0 + +' + +test_expect_success 'Use --numbered' ' + + git format-patch --numbered --stdout HEAD~2 >patch1 && + test_numbered patch1 + +' + +test_expect_success 'format.numbered = true' ' + + git config format.numbered true && + git format-patch --stdout HEAD~2 >patch2 && + test_numbered patch2 + +' + +test_expect_success 'format.numbered && single patch' ' + + git format-patch --stdout HEAD^ > patch3 && + test_single_numbered patch3 + +' + +test_expect_success 'format.numbered && --no-numbered' ' + + git format-patch --no-numbered --stdout HEAD~2 >patch4 && + test_no_numbered patch4 + +' + +test_expect_success 'format.numbered = auto' ' + + git config format.numbered auto + git format-patch --stdout HEAD~2 > patch5 && + test_numbered patch5 + +' + +test_expect_success 'format.numbered = auto && single patch' ' + + git format-patch --stdout HEAD^ > patch6 && + test_single_no_numbered patch6 + +' + +test_expect_success 'format.numbered = auto && --no-numbered' ' + + git format-patch --no-numbered --stdout HEAD~2 > patch7 && + test_no_numbered patch7 + +' + +test_done diff --git a/t/t5502-quickfetch.sh b/t/t5502-quickfetch.sh index b4760f2dc0..16eadd6b68 100755 --- a/t/t5502-quickfetch.sh +++ b/t/t5502-quickfetch.sh @@ -86,4 +86,37 @@ test_expect_success 'quickfetch should not leave a corrupted repository' ' ' +test_expect_success 'quickfetch should not copy from alternate' ' + + ( + mkdir quickclone && + cd quickclone && + git init-db && + (cd ../.git/objects && pwd) >.git/objects/info/alternates && + git remote add origin .. && + git fetch -k -k + ) && + obj_cnt=$( ( + cd quickclone && + git count-objects | sed -e "s/ *objects,.*//" + ) ) && + pck_cnt=$( ( + cd quickclone && + git count-objects -v | sed -n -e "/packs:/{ + s/packs:// + p + q + }" + ) ) && + origin_master=$( ( + cd quickclone && + git rev-parse origin/master + ) ) && + echo "loose objects: $obj_cnt, packfiles: $pck_cnt" && + test $obj_cnt -eq 0 && + test $pck_cnt -eq 0 && + test z$origin_master = z$(git rev-parse master) + +' + test_done diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh new file mode 100755 index 0000000000..cc8949e3ef --- /dev/null +++ b/t/t5530-upload-pack-error.sh @@ -0,0 +1,75 @@ +#!/bin/sh + +test_description='errors in upload-pack' + +. ./test-lib.sh + +D=`pwd` + +corrupt_repo () { + object_sha1=$(git rev-parse "$1") && + ob=$(expr "$object_sha1" : "\(..\)") && + ject=$(expr "$object_sha1" : "..\(..*\)") && + rm -f ".git/objects/$ob/$ject" +} + +test_expect_success 'setup and corrupt repository' ' + + echo file >file && + git add file && + git rev-parse :file && + git commit -a -m original && + test_tick && + echo changed >file && + git commit -a -m changed && + corrupt_repo HEAD:file + +' + +test_expect_failure 'fsck fails' ' + + git fsck +' + +test_expect_success 'upload-pack fails due to error in pack-objects' ' + + ! echo "0032want $(git rev-parse HEAD) +00000009done +0000" | git-upload-pack . > /dev/null 2> output.err && + grep "pack-objects died" output.err +' + +test_expect_success 'corrupt repo differently' ' + + git hash-object -w file && + corrupt_repo HEAD^^{tree} + +' + +test_expect_failure 'fsck fails' ' + + git fsck +' +test_expect_success 'upload-pack fails due to error in rev-list' ' + + ! echo "0032want $(git rev-parse HEAD) +00000009done +0000" | git-upload-pack . > /dev/null 2> output.err && + grep "waitpid (async) failed" output.err +' + +test_expect_success 'create empty repository' ' + + mkdir foo && + cd foo && + git init + +' + +test_expect_failure 'fetch fails' ' + + git fetch .. master + +' + +test_done diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index d0809eb651..c722635050 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -148,4 +148,26 @@ test_expect_success 'Check format "rfc2822" date fields output' ' git diff expected actual ' +cat >expected <<\EOF +refs/heads/master +refs/tags/testtag +EOF + +test_expect_success 'Verify ascending sort' ' + git-for-each-ref --format="%(refname)" --sort=refname >actual && + git diff expected actual +' + + +cat >expected <<\EOF +refs/tags/testtag +refs/heads/master +EOF + +test_expect_success 'Verify descending sort' ' + git-for-each-ref --format="%(refname)" --sort=-refname >actual && + git diff expected actual +' + + test_done diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 0d07bc39c7..5f7e388d7a 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -1004,4 +1004,30 @@ test_expect_failure \ 'verify signed tag fails when public key is not present' \ 'git-tag -v signed-tag' +test_expect_failure \ + 'git-tag -a fails if tag annotation is empty' ' + GIT_EDITOR=cat git tag -a initial-comment +' + +test_expect_success \ + 'message in editor has initial comment' ' + GIT_EDITOR=cat git tag -a initial-comment > actual + # check the first line --- should be empty + first=$(sed -e 1q <actual) && + test -z "$first" && + # remove commented lines from the remainder -- should be empty + rest=$(sed -e 1d -e '/^#/d' <actual) && + test -z "$rest" +' + +get_tag_header reuse $commit commit $time >expect +echo "An annotation to be reused" >> expect +test_expect_success \ + 'overwriting an annoted tag should use its previous body' ' + git tag -a -m "An annotation to be reused" reuse && + GIT_EDITOR=true git tag -f -a reuse && + get_tag_msg reuse >actual && + git diff expect actual +' + test_done diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh index 01cc0c02b1..44228b5ac1 100755 --- a/t/t7005-editor.sh +++ b/t/t7005-editor.sh @@ -15,7 +15,6 @@ do done unset vi mv e-vi.sh vi -PATH=".:$PATH" unset EDITOR VISUAL GIT_EDITOR test_expect_success setup ' @@ -61,7 +60,7 @@ do ;; esac test_expect_success "Using $i" ' - git commit --amend && + git --exec-path=. commit --amend && git show -s --pretty=oneline | sed -e "s/^[0-9a-f]* //" >actual && diff actual expect @@ -83,7 +82,7 @@ do ;; esac test_expect_success "Using $i (override)" ' - git commit --amend && + git --exec-path=. commit --amend && git show -s --pretty=oneline | sed -e "s/^[0-9a-f]* //" >actual && diff actual expect diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index cea9afb764..e5c9f30c73 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -59,6 +59,15 @@ test_expect_success 'giving a non existing revision should fail' ' check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc ' +test_expect_success 'reset --soft with unmerged index should fail' ' + touch .git/MERGE_HEAD && + echo "100644 44c5b5884550c17758737edcced463447b91d42b 1 un" | + git update-index --index-info && + ! git reset --soft HEAD && + rm .git/MERGE_HEAD && + git rm --cached -- un +' + test_expect_success \ 'giving paths with options different than --mixed should fail' ' ! git reset --soft -- first && @@ -409,4 +418,14 @@ test_expect_success 'resetting an unmodified path is a no-op' ' git diff-index --cached --exit-code HEAD ' +cat > expect << EOF +file2: needs update +EOF + +test_expect_success '--mixed refreshes the index' ' + echo 123 >> file2 && + git reset --mixed HEAD > output && + git diff --exit-code expect output +' + test_done diff --git a/t/t7201-co.sh b/t/t7201-co.sh index ed2e9ee3c6..55558aba8b 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -77,7 +77,7 @@ test_expect_success "checkout with dirty tree without -m" ' test_expect_success "checkout -m with dirty tree" ' git checkout -f master && - git clean && + git clean -f && fill 0 1 2 3 4 5 6 7 8 >one && git checkout -m side && @@ -99,7 +99,7 @@ test_expect_success "checkout -m with dirty tree" ' test_expect_success "checkout -m with dirty tree, renamed" ' - git checkout -f master && git clean && + git checkout -f master && git clean -f && fill 1 2 3 4 5 7 8 >one && if git checkout renamer @@ -121,7 +121,7 @@ test_expect_success "checkout -m with dirty tree, renamed" ' test_expect_success 'checkout -m with merge conflict' ' - git checkout -f master && git clean && + git checkout -f master && git clean -f && fill 1 T 3 4 5 6 S 8 >one && if git checkout renamer @@ -144,7 +144,7 @@ test_expect_success 'checkout -m with merge conflict' ' test_expect_success 'checkout to detach HEAD' ' - git checkout -f renamer && git clean && + git checkout -f renamer && git clean -f && git checkout renamer^ && H=$(git rev-parse --verify HEAD) && M=$(git show-ref -s --verify refs/heads/master) && @@ -160,7 +160,7 @@ test_expect_success 'checkout to detach HEAD' ' test_expect_success 'checkout to detach HEAD with branchname^' ' - git checkout -f master && git clean && + git checkout -f master && git clean -f && git checkout renamer^ && H=$(git rev-parse --verify HEAD) && M=$(git show-ref -s --verify refs/heads/master) && @@ -176,7 +176,7 @@ test_expect_success 'checkout to detach HEAD with branchname^' ' test_expect_success 'checkout to detach HEAD with HEAD^0' ' - git checkout -f master && git clean && + git checkout -f master && git clean -f && git checkout HEAD^0 && H=$(git rev-parse --verify HEAD) && M=$(git show-ref -s --verify refs/heads/master) && diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index 2327436187..f013c176ed 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -7,6 +7,8 @@ test_description='git-clean basic tests' . ./test-lib.sh +git config clean.requireForce no + test_expect_success 'setup' ' mkdir -p src && @@ -244,6 +246,13 @@ test_expect_success 'git-clean -d -X' ' ' +test_expect_success 'clean.requireForce defaults to true' ' + + git config --unset clean.requireForce && + ! git-clean + +' + test_expect_success 'clean.requireForce' ' git config clean.requireForce true && @@ -282,4 +291,15 @@ test_expect_success 'clean.requireForce and -f' ' ' +test_expect_success 'core.excludesfile' ' + + echo excludes >excludes && + echo included >included && + git config core.excludesfile excludes && + output=$(git clean -n excludes included 2>&1) && + expr "$output" : ".*included" >/dev/null && + ! expr "$output" : ".*excludes" >/dev/null + +' + test_done diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh index abbf54ba63..cf389b81da 100755 --- a/t/t7500-commit.sh +++ b/t/t7500-commit.sh @@ -93,4 +93,36 @@ test_expect_success 'commit message from file should override template' ' commit_msg_is "standard input msg" ' +test_expect_success 'using alternate GIT_INDEX_FILE (1)' ' + + cp .git/index saved-index && + ( + echo some new content >file && + GIT_INDEX_FILE=.git/another_index && + export GIT_INDEX_FILE && + git add file && + git commit -m "commit using another index" && + git diff-index --exit-code HEAD && + git diff-files --exit-code + ) && + cmp .git/index saved-index >/dev/null + +' + +test_expect_success 'using alternate GIT_INDEX_FILE (2)' ' + + cp .git/index saved-index && + ( + rm -f .git/no-such-index && + GIT_INDEX_FILE=.git/no-such-index && + export GIT_INDEX_FILE && + git commit -m "commit using nonexistent index" && + test -z "$(git ls-files)" && + test -z "$(git ls-tree HEAD)" + + ) && + cmp .git/index saved-index >/dev/null + +' + test_done diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index b151b51a34..9dba104b1f 100644 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -34,6 +34,16 @@ test_expect_failure \ "git-commit -C HEAD -m illegal" test_expect_failure \ + "using paths with -a" \ + "echo King of the bongo >file && + git-commit -m foo -a file" + +test_expect_failure \ + "using paths with --interactive" \ + "echo bong-o-bong >file && + echo 7 | git-commit -m foo --interactive file" + +test_expect_failure \ "using invalid commit with -C" \ "git-commit -C bogus" @@ -163,4 +173,73 @@ test_expect_success 'partial commit that involves removal (3)' ' ' +author="The Real Author <someguy@his.email.org>" +test_expect_success 'amend commit to fix author' ' + + oldtick=$GIT_AUTHOR_DATE && + test_tick && + git reset --hard && + git cat-file -p HEAD | + sed -e "s/author.*/author $author $oldtick/" \ + -e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" > \ + expected && + git commit --amend --author="$author" && + git cat-file -p HEAD > current && + diff expected current + +' + +test_expect_success 'sign off (1)' ' + + echo 1 >positive && + git add positive && + git commit -s -m "thank you" && + git cat-file commit HEAD | sed -e "1,/^\$/d" >actual && + ( + echo thank you + echo + git var GIT_COMMITTER_IDENT | + sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /" + ) >expected && + diff -u expected actual + +' + +test_expect_success 'sign off (2)' ' + + echo 2 >positive && + git add positive && + existing="Signed-off-by: Watch This <watchthis@example.com>" && + git commit -s -m "thank you + +$existing" && + git cat-file commit HEAD | sed -e "1,/^\$/d" >actual && + ( + echo thank you + echo + echo $existing + git var GIT_COMMITTER_IDENT | + sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /" + ) >expected && + diff -u expected actual + +' + +test_expect_success 'multiple -m' ' + + >negative && + git add negative && + git commit -m "one" -m "two" -m "three" && + git cat-file commit HEAD | sed -e "1,/^\$/d" >actual && + ( + echo one + echo + echo two + echo + echo three + ) >expected && + diff -u expected actual + +' + test_done diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh index 3c83127a0e..d7a704754e 100755 --- a/t/t9101-git-svn-props.sh +++ b/t/t9101-git-svn-props.sh @@ -48,7 +48,7 @@ EOF printf "\r\n" > empty_crlf a_empty_crlf=`git-hash-object -w empty_crlf` - svn import -m 'import for git-svn' . "$svnrepo" >/dev/null + svn import --no-auto-props -m 'import for git-svn' . "$svnrepo" >/dev/null cd .. rm -rf import diff --git a/t/t9106-git-svn-dcommit-clobber-series.sh b/t/t9106-git-svn-dcommit-clobber-series.sh index 7eff4cdc05..d59acc8d1a 100755 --- a/t/t9106-git-svn-dcommit-clobber-series.sh +++ b/t/t9106-git-svn-dcommit-clobber-series.sh @@ -53,4 +53,10 @@ test_expect_success 'change file but in unrelated area' " test x\"\`sed -n -e 61p < file\`\" = x6611 " +test_expect_failure 'attempt to dcommit with a dirty index' ' + echo foo >>file && + git add file && + git svn dcommit +' + test_done diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh index 0d4e6b3f04..902ed4145d 100755 --- a/t/t9116-git-svn-log.sh +++ b/t/t9116-git-svn-log.sh @@ -30,6 +30,12 @@ test_expect_success 'setup repository and import' " git reset --hard trunk && echo aye >> README && git commit -a -m aye && + git svn dcommit && + git reset --hard b && + echo spy >> README && + git commit -a -m spy && + echo try >> README && + git commit -a -m try && git svn dcommit " @@ -45,4 +51,78 @@ test_expect_success 'run log against a from trunk' " git svn log -r3 a | grep ^r3 " +printf 'r1 \nr2 \nr4 \n' > expected-range-r1-r2-r4 + +test_expect_success 'test ascending revision range' " + git reset --hard trunk && + git svn log -r 1:4 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r1-r2-r4 - + " + +printf 'r4 \nr2 \nr1 \n' > expected-range-r4-r2-r1 + +test_expect_success 'test descending revision range' " + git reset --hard trunk && + git svn log -r 4:1 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r4-r2-r1 - + " + +printf 'r1 \nr2 \n' > expected-range-r1-r2 + +test_expect_success 'test ascending revision range with unreachable revision' " + git reset --hard trunk && + git svn log -r 1:3 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r1-r2 - + " + +printf 'r2 \nr1 \n' > expected-range-r2-r1 + +test_expect_success 'test descending revision range with unreachable revision' " + git reset --hard trunk && + git svn log -r 3:1 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r2-r1 - + " + +printf 'r2 \n' > expected-range-r2 + +test_expect_success 'test ascending revision range with unreachable upper boundary revision and 1 commit' " + git reset --hard trunk && + git svn log -r 2:3 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r2 - + " + +test_expect_success 'test descending revision range with unreachable upper boundary revision and 1 commit' " + git reset --hard trunk && + git svn log -r 3:2 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r2 - + " + +printf 'r4 \n' > expected-range-r4 + +test_expect_success 'test ascending revision range with unreachable lower boundary revision and 1 commit' " + git reset --hard trunk && + git svn log -r 3:4 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r4 - + " + +test_expect_success 'test descending revision range with unreachable lower boundary revision and 1 commit' " + git reset --hard trunk && + git svn log -r 4:3 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r4 - + " + +printf -- '------------------------------------------------------------------------\n' > expected-separator + +test_expect_success 'test ascending revision range with unreachable boundary revisions and no commits' " + git reset --hard trunk && + git svn log -r 5:6 | diff -u expected-separator - + " + +test_expect_success 'test descending revision range with unreachable boundary revisions and no commits' " + git reset --hard trunk && + git svn log -r 6:5 | diff -u expected-separator - + " + +test_expect_success 'test ascending revision range with unreachable boundary revisions and 1 commit' " + git reset --hard trunk && + git svn log -r 3:5 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r4 - + " + +test_expect_success 'test descending revision range with unreachable boundary revisions and 1 commit' " + git reset --hard trunk && + git svn log -r 5:3 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r4 - + " + test_done diff --git a/t/t9117-git-svn-init-clone.sh b/t/t9117-git-svn-init-clone.sh new file mode 100755 index 0000000000..d482b407f2 --- /dev/null +++ b/t/t9117-git-svn-init-clone.sh @@ -0,0 +1,55 @@ +#!/bin/sh +# +# Copyright (c) 2007 Eric Wong +# + +test_description='git-svn init/clone tests' + +. ./lib-git-svn.sh + +# setup, run inside tmp so we don't have any conflicts with $svnrepo +set -e +rm -r .git +mkdir tmp +cd tmp + +test_expect_success 'setup svnrepo' " + mkdir project project/trunk project/branches project/tags && + echo foo > project/trunk/foo && + svn import -m '$test_description' project $svnrepo/project && + rm -rf project + " + +test_expect_success 'basic clone' " + test ! -d trunk && + git svn clone $svnrepo/project/trunk && + test -d trunk/.git/svn && + test -e trunk/foo && + rm -rf trunk + " + +test_expect_success 'clone to target directory' " + test ! -d target && + git svn clone $svnrepo/project/trunk target && + test -d target/.git/svn && + test -e target/foo && + rm -rf target + " + +test_expect_success 'clone with --stdlayout' " + test ! -d project && + git svn clone -s $svnrepo/project && + test -d project/.git/svn && + test -e project/foo && + rm -rf project + " + +test_expect_success 'clone to target directory with --stdlayout' " + test ! -d target && + git svn clone -s $svnrepo/project target && + test -d target/.git/svn && + test -e target/foo && + rm -rf target + " + +test_done diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh new file mode 100755 index 0000000000..640bb066f3 --- /dev/null +++ b/t/t9118-git-svn-funky-branch-names.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# +# Copyright (c) 2007 Eric Wong +# + +test_description='git-svn funky branch names' +. ./lib-git-svn.sh + +test_expect_success 'setup svnrepo' " + mkdir project project/trunk project/branches project/tags && + echo foo > project/trunk/foo && + svn import -m '$test_description' project \"$svnrepo/pr ject\" && + rm -rf project && + svn cp -m 'fun' \"$svnrepo/pr ject/trunk\" \ + \"$svnrepo/pr ject/branches/fun plugin\" && + svn cp -m 'more fun!' \"$svnrepo/pr ject/branches/fun plugin\" \ + \"$svnrepo/pr ject/branches/more fun plugin!\" && + start_httpd + " + +test_expect_success 'test clone with funky branch names' " + git svn clone -s \"$svnrepo/pr ject\" project && + cd project && + git rev-parse 'refs/remotes/fun%20plugin' && + git rev-parse 'refs/remotes/more%20fun%20plugin!' && + cd .. + " + +test_expect_success 'test dcommit to funky branch' " + cd project && + git reset --hard 'refs/remotes/more%20fun%20plugin!' && + echo hello >> foo && + git commit -m 'hello' -- foo && + git svn dcommit && + cd .. + " + +stop_httpd + +test_done diff --git a/t/test-lib.sh b/t/test-lib.sh index 603a8cd5e7..90b6844d00 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -66,9 +66,6 @@ esac tput sgr0 >/dev/null 2>&1 && color=t -test "${test_description}" != "" || -error "Test script did not set test_description." - while test "$#" -ne 0 do case "$1" in @@ -77,8 +74,7 @@ do -i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate) immediate=t; shift ;; -h|--h|--he|--hel|--help) - echo "$test_description" - exit 0 ;; + help=t; shift ;; -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) verbose=t; shift ;; -q|--q|--qu|--qui|--quie|--quiet) @@ -124,6 +120,15 @@ say () { say_color info "$*" } +test "${test_description}" != "" || +error "Test script did not set test_description." + +if test "$help" = "t" +then + echo "$test_description" + exit 0 +fi + exec 5>&1 if test "$verbose" = "t" then diff --git a/templates/hooks--update b/templates/hooks--update index d8c76264be..bd93dd1977 100644 --- a/templates/hooks--update +++ b/templates/hooks--update @@ -10,6 +10,12 @@ # hooks.allowunannotated # This boolean sets whether unannotated tags will be allowed into the # repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. # # --- Command line @@ -32,18 +38,20 @@ fi # --- Config allowunannotated=$(git-repo-config --bool hooks.allowunannotated) +allowdeletebranch=$(git-repo-config --bool hooks.allowdeletebranch) +allowdeletetag=$(git-repo-config --bool hooks.allowdeletetag) # check for no description -projectdesc=$(sed -e '1p' "$GIT_DIR/description") -if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb" ]; then +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb." ]; then echo "*** Project description file hasn't been set" >&2 exit 1 fi # --- Check types -# if $newrev is 0000...0000, it's a commit to delete a branch +# if $newrev is 0000...0000, it's a commit to delete a ref. if [ "$newrev" = "0000000000000000000000000000000000000000" ]; then - newrev_type=commit + newrev_type=delete else newrev_type=$(git-cat-file -t $newrev) fi @@ -58,15 +66,36 @@ case "$refname","$newrev_type" in exit 1 fi ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; refs/tags/*,tag) # annotated tag ;; refs/heads/*,commit) # branch ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; refs/remotes/*,commit) # tracking branch ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; *) # Anything else (is there anything else?) echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 diff --git a/test-parse-options.c b/test-parse-options.c index 277cfe4d6d..4d3e2ec39e 100644 --- a/test-parse-options.c +++ b/test-parse-options.c @@ -18,6 +18,7 @@ int main(int argc, const char **argv) OPT_GROUP("string options"), OPT_STRING('s', "string", &string, "string", "get a string"), OPT_STRING(0, "string2", &string, "str", "get another string"), + OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"), OPT_END(), }; int i; @@ -72,7 +72,7 @@ void trace_printf(const char *fmt, ...) if (!fd) return; - strbuf_init(&buf, 0); + strbuf_init(&buf, 64); va_start(ap, fmt); len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap); va_end(ap); @@ -103,7 +103,7 @@ void trace_argv_printf(const char **argv, int count, const char *fmt, ...) if (!fd) return; - strbuf_init(&buf, 0); + strbuf_init(&buf, 64); va_start(ap, fmt); len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap); va_end(ap); diff --git a/transport.c b/transport.c index d44fe7cee7..e8a2608372 100644 --- a/transport.c +++ b/transport.c @@ -380,12 +380,13 @@ static int disconnect_walker(struct transport *transport) } #ifndef NO_CURL -static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) { +static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) +{ const char **argv; int argc; int err; - argv = xmalloc((refspec_nr + 11) * sizeof(char *)); + argv = xmalloc((refspec_nr + 12) * sizeof(char *)); argv[0] = "http-push"; argc = 1; if (flags & TRANSPORT_PUSH_ALL) @@ -394,6 +395,8 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons argv[argc++] = "--force"; if (flags & TRANSPORT_PUSH_DRY_RUN) argv[argc++] = "--dry-run"; + if (flags & TRANSPORT_PUSH_VERBOSE) + argv[argc++] = "--verbose"; argv[argc++] = transport->url; while (refspec_nr--) argv[argc++] = *refspec++; @@ -646,14 +649,15 @@ static int fetch_refs_via_pack(struct transport *transport, return 0; } -static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) { +static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) +{ struct git_transport_data *data = transport->data; const char **argv; char *rem; int argc; int err; - argv = xmalloc((refspec_nr + 11) * sizeof(char *)); + argv = xmalloc((refspec_nr + 12) * sizeof(char *)); argv[0] = "send-pack"; argc = 1; if (flags & TRANSPORT_PUSH_ALL) @@ -662,6 +666,8 @@ static int git_transport_push(struct transport *transport, int refspec_nr, const argv[argc++] = "--force"; if (flags & TRANSPORT_PUSH_DRY_RUN) argv[argc++] = "--dry-run"; + if (flags & TRANSPORT_PUSH_VERBOSE) + argv[argc++] = "--verbose"; if (data->receivepack) { char *rp = xmalloc(strlen(data->receivepack) + 16); sprintf(rp, "--receive-pack=%s", data->receivepack); diff --git a/transport.h b/transport.h index df12ea7424..2f80ab4b03 100644 --- a/transport.h +++ b/transport.h @@ -30,6 +30,7 @@ struct transport { #define TRANSPORT_PUSH_ALL 1 #define TRANSPORT_PUSH_FORCE 2 #define TRANSPORT_PUSH_DRY_RUN 4 +#define TRANSPORT_PUSH_VERBOSE 8 /* Returns a transport suitable for the url */ struct transport *transport_get(struct remote *, const char *); diff --git a/unpack-trees.c b/unpack-trees.c index c527d7d049..aea16adde8 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -404,7 +404,7 @@ static void verify_uptodate(struct cache_entry *ce, return; if (!lstat(ce->name, &st)) { - unsigned changed = ce_match_stat(ce, &st, 1); + unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID); if (!changed) return; /* @@ -925,7 +925,7 @@ int oneway_merge(struct cache_entry **src, if (o->reset) { struct stat st; if (lstat(old->name, &st) || - ce_match_stat(old, &st, 1)) + ce_match_stat(old, &st, CE_MATCH_IGNORE_VALID)) old->ce_flags |= htons(CE_UPDATE); } return keep_entry(old, o); diff --git a/upload-pack.c b/upload-pack.c index 67994680f2..7e04311027 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -144,6 +144,7 @@ static void create_pack_file(void) char abort_msg[] = "aborting due to possible repository " "corruption on the remote side."; int buffered = -1; + ssize_t sz; const char *argv[10]; int arg = 0; @@ -168,22 +169,15 @@ static void create_pack_file(void) pack_objects.git_cmd = 1; pack_objects.argv = argv; - if (start_command(&pack_objects)) { - /* daemon sets things up to ignore TERM */ - kill(rev_list.pid, SIGKILL); + if (start_command(&pack_objects)) die("git-upload-pack: unable to fork git-pack-objects"); - } /* We read from pack_objects.err to capture stderr output for * progress bar, and pack_objects.out to capture the pack data. */ while (1) { - const char *who; struct pollfd pfd[2]; - pid_t pid; - int status; - ssize_t sz; int pe, pu, pollsize; reset_timeout(); @@ -204,123 +198,91 @@ static void create_pack_file(void) pollsize++; } - if (pollsize) { - if (poll(pfd, pollsize, -1) < 0) { - if (errno != EINTR) { - error("poll failed, resuming: %s", - strerror(errno)); - sleep(1); - } - continue; - } - if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) { - /* Data ready; we keep the last byte - * to ourselves in case we detect - * broken rev-list, so that we can - * leave the stream corrupted. This - * is unfortunate -- unpack-objects - * would happily accept a valid pack - * data with trailing garbage, so - * appending garbage after we pass all - * the pack data is not good enough to - * signal breakage to downstream. - */ - char *cp = data; - ssize_t outsz = 0; - if (0 <= buffered) { - *cp++ = buffered; - outsz++; - } - sz = xread(pack_objects.out, cp, - sizeof(data) - outsz); - if (0 < sz) - ; - else if (sz == 0) { - close(pack_objects.out); - pack_objects.out = -1; - } - else - goto fail; - sz += outsz; - if (1 < sz) { - buffered = data[sz-1] & 0xFF; - sz--; - } - else - buffered = -1; - sz = send_client_data(1, data, sz); - if (sz < 0) - goto fail; - } - if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) { - /* Status ready; we ship that in the side-band - * or dump to the standard error. - */ - sz = xread(pack_objects.err, progress, - sizeof(progress)); - if (0 < sz) - send_client_data(2, progress, sz); - else if (sz == 0) { - close(pack_objects.err); - pack_objects.err = -1; - } - else - goto fail; + if (!pollsize) + break; + + if (poll(pfd, pollsize, -1) < 0) { + if (errno != EINTR) { + error("poll failed, resuming: %s", + strerror(errno)); + sleep(1); } + continue; } - - /* See if the children are still there */ - if (rev_list.pid || pack_objects.pid) { - pid = waitpid(-1, &status, WNOHANG); - if (!pid) - continue; - who = ((pid == rev_list.pid) ? "git-rev-list" : - (pid == pack_objects.pid) ? "git-pack-objects" : - NULL); - if (!who) { - if (pid < 0) { - error("git-upload-pack: %s", - strerror(errno)); - goto fail; - } - error("git-upload-pack: we weren't " - "waiting for %d", pid); - continue; + if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) { + /* Data ready; we keep the last byte to ourselves + * in case we detect broken rev-list, so that we + * can leave the stream corrupted. This is + * unfortunate -- unpack-objects would happily + * accept a valid packdata with trailing garbage, + * so appending garbage after we pass all the + * pack data is not good enough to signal + * breakage to downstream. + */ + char *cp = data; + ssize_t outsz = 0; + if (0 <= buffered) { + *cp++ = buffered; + outsz++; + } + sz = xread(pack_objects.out, cp, + sizeof(data) - outsz); + if (0 < sz) + ; + else if (sz == 0) { + close(pack_objects.out); + pack_objects.out = -1; } - if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) { - error("git-upload-pack: %s died with error.", - who); + else goto fail; + sz += outsz; + if (1 < sz) { + buffered = data[sz-1] & 0xFF; + sz--; } - if (pid == rev_list.pid) - rev_list.pid = 0; - if (pid == pack_objects.pid) - pack_objects.pid = 0; - if (rev_list.pid || pack_objects.pid) - continue; - } - - /* both died happily */ - if (pollsize) - continue; - - /* flush the data */ - if (0 <= buffered) { - data[0] = buffered; - sz = send_client_data(1, data, 1); + else + buffered = -1; + sz = send_client_data(1, data, sz); if (sz < 0) goto fail; - fprintf(stderr, "flushed.\n"); } - if (use_sideband) - packet_flush(1); - return; + if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) { + /* Status ready; we ship that in the side-band + * or dump to the standard error. + */ + sz = xread(pack_objects.err, progress, + sizeof(progress)); + if (0 < sz) + send_client_data(2, progress, sz); + else if (sz == 0) { + close(pack_objects.err); + pack_objects.err = -1; + } + else + goto fail; + } + } + + if (finish_command(&pack_objects)) { + error("git-upload-pack: git-pack-objects died with error."); + goto fail; + } + if (finish_async(&rev_list)) + goto fail; /* error was already reported */ + + /* flush the data */ + if (0 <= buffered) { + data[0] = buffered; + sz = send_client_data(1, data, 1); + if (sz < 0) + goto fail; + fprintf(stderr, "flushed.\n"); } + if (use_sideband) + packet_flush(1); + return; + fail: - if (pack_objects.pid) - kill(pack_objects.pid, SIGKILL); - if (rev_list.pid) - kill(rev_list.pid, SIGKILL); send_client_data(3, abort_msg, sizeof(abort_msg)); die("git-upload-pack: %s", abort_msg); } @@ -7,9 +7,9 @@ static void report(const char *prefix, const char *err, va_list params) { - fputs(prefix, stderr); - vfprintf(stderr, err, params); - fputs("\n", stderr); + char msg[256]; + vsnprintf(msg, sizeof(msg), err, params); + fprintf(stderr, "%s%s\n", prefix, msg); } static NORETURN void usage_builtin(const char *err) @@ -11,7 +11,8 @@ struct interval { }; /* auxiliary function for binary search in interval table */ -static int bisearch(ucs_char_t ucs, const struct interval *table, int max) { +static int bisearch(ucs_char_t ucs, const struct interval *table, int max) +{ int min = 0; int mid; @@ -283,7 +284,6 @@ int print_wrapped_text(const char *text, int indent, int indent2, int width) text++; } } - return w; } int is_encoding_utf8(const char *name) diff --git a/wt-status.c b/wt-status.c index 03b5ec4488..9a6ef4a89a 100644 --- a/wt-status.c +++ b/wt-status.c @@ -22,7 +22,6 @@ static const char use_add_rm_msg[] = "use \"git add/rm <file>...\" to update what will be committed"; static const char use_add_to_include_msg[] = "use \"git add <file>...\" to include in what will be committed"; -static const char *excludes_file; static int parse_status_slot(const char *var, int offset) { @@ -251,22 +250,16 @@ static void wt_status_print_changed(struct wt_status *s) static void wt_status_print_untracked(struct wt_status *s) { struct dir_struct dir; - const char *x; int i; int shown_header = 0; memset(&dir, 0, sizeof(dir)); - dir.exclude_per_dir = ".gitignore"; if (!s->untracked) { dir.show_other_directories = 1; dir.hide_empty_directories = 1; } - x = git_path("info/exclude"); - if (file_exists(x)) - add_excludes_from_file(&dir, x); - if (excludes_file && file_exists(excludes_file)) - add_excludes_from_file(&dir, excludes_file); + setup_standard_excludes(&dir); read_directory(&dir, ".", "", 0, NULL); for(i = 0; i < dir.nr; i++) { @@ -364,11 +357,5 @@ int git_status_config(const char *k, const char *v) int slot = parse_status_slot(k, 13); color_parse(v, k, wt_status_colors[slot]); } - if (!strcmp(k, "core.excludesfile")) { - if (!v) - die("core.excludesfile without value"); - excludes_file = xstrdup(v); - return 0; - } return git_default_config(k, v); } diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c index 5cb7171a8f..1bad8462fb 100644 --- a/xdiff/xdiffi.c +++ b/xdiff/xdiffi.c @@ -257,8 +257,6 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, return ec; } } - - return -1; } diff --git a/xdiff/xutils.c b/xdiff/xutils.c index 2ade97b257..d7974d1a3e 100644 --- a/xdiff/xutils.c +++ b/xdiff/xutils.c @@ -232,8 +232,6 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags) return i1 >= s1 && i2 >= s2; } else return s1 == s2 && !memcmp(l1, l2, s1); - - return 0; } static unsigned long xdl_hash_record_with_whitespace(char const **data, |