diff options
151 files changed, 2938 insertions, 1863 deletions
diff --git a/.gitignore b/.gitignore index a213e8e25..bbaf9de0b 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ git-gc git-get-tar-commit-id git-grep git-hash-object +git-help git-http-fetch git-http-push git-imap-send diff --git a/Documentation/RelNotes-1.6.1.txt b/Documentation/RelNotes-1.6.1.txt new file mode 100644 index 000000000..d37da039f --- /dev/null +++ b/Documentation/RelNotes-1.6.1.txt @@ -0,0 +1,58 @@ +GIT v1.6.1 Release Notes +======================== + +Updates since v1.6.0 +-------------------- + +When some commands (e.g. "git log", "git diff") spawn pager internally, we +used to make the pager the parent process of the git command that produces +output. This meant that the exit status of the whole thing comes from the +pager, not the underlying git command. We swapped the order of the +processes around and you will see the exit code from the command from now +on. + +(subsystems) + +* ... + +(portability) + +* ... + +(documentation) + +* ... + +(performance) + +* The underlying diff machinery to produce textual output has been + optimized, which would result in faster "git blame" processing. + +(usability, bells and whistles) + +* "git checkout --track origin/hack" used to be a syntax error. It now + DWIMs to create a corresponding local branch "hack", i.e. acts as if you + said "git checkout --track -b hack origin/hack". + +* "git diff" learned to mimick --suppress-blank-empty from GNU diff via a + configuration option. + +* "git imap-send" can optionally talk SSL. + +(internal) + +* "git hash-object" learned to lie about the path being hashed, so that + correct gitattributes processing can be done while hashing contents + stored in a temporary file. + +Fixes since v1.6.0 +------------------ + +All of the fixes in v1.6.0.X maintenance series are included in this +release, unless otherwise noted. + +-- +exec >/var/tmp/1 +O=v1.6.0-48-ge28a867 +echo O=$(git describe master) +git shortlog --no-merges $O..master ^maint diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 841bead9d..a1e9100f9 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -71,7 +71,7 @@ run git diff --check on your changes before you commit. (1a) Try to be nice to older C compilers -We try to support wide range of C compilers to compile +We try to support a wide range of C compilers to compile git with. That means that you should not use C99 initializers, even if a lot of compilers grok it. diff --git a/Documentation/config.txt b/Documentation/config.txt index 81f981509..ed3285f89 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -572,6 +572,10 @@ diff.autorefreshindex:: affects only 'git-diff' Porcelain, and not lower level 'diff' commands, such as 'git-diff-files'. +diff.suppress-blank-empty:: + A boolean to inhibit the standard behavior of printing a space + before each empty output line. Defaults to false. + diff.external:: If this config variable is set, diff generation is not performed using the internal diff machinery, but using the @@ -693,7 +697,7 @@ gitcvs.logfile:: Path to a log file where the CVS server interface well... logs various stuff. See linkgit:git-cvsserver[1]. -gitcvs.usecrlfattr +gitcvs.usecrlfattr:: If true, the server will look up the `crlf` attribute for files to determine the '-k' modes to use. If `crlf` is set, the '-k' mode will be left blank, so cvs clients will diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index cba90fd27..6e268326d 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -59,12 +59,11 @@ endif::git-format-patch[] lines. --dirstat[=limit]:: - Output only the sub-directories that are impacted by a diff, - and to what degree they are impacted. You can override the - default cut-off in percent (3) by "--dirstat=limit". If you - want to enable "cumulative" directory statistics, you can use - the "--cumulative" flag, which adds up percentages recursively - even when they have been already reported for a sub-directory. + Output the distribution of relative amount of changes (number of lines added or + removed) for each sub-directory. Directories with changes below + a cut-off percent (3% by default) are not shown. The cut-off percent + can be set with "--dirstat=limit". Changes in a child directory is not + counted for the parent directory, unless "--cumulative" is used. --summary:: Output a condensed summary of extended header information @@ -107,9 +106,9 @@ endif::git-format-patch[] --exit-code. --full-index:: - Instead of the first handful characters, show full - object name of pre- and post-image blob on the "index" - line when generating a patch format output. + Instead of the first handful of characters, show the full + pre- and post-image blob object names on the "index" + line when generating patch format output. --binary:: In addition to --full-index, output "binary diff" that diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 5aa69c0e1..be54a0299 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -8,7 +8,7 @@ git-checkout - Checkout a branch or paths to the working tree SYNOPSIS -------- [verse] -'git checkout' [-q] [-f] [[--track | --no-track] -b <new_branch> [-l]] [-m] [<branch>] +'git checkout' [-q] [-f] [--track | --no-track] [-b <new_branch> [-l]] [-m] [<branch>] 'git checkout' [<tree-ish>] [--] <paths>... DESCRIPTION @@ -21,6 +21,10 @@ specified, <new_branch>. Using -b will cause <new_branch> to be created; in this case you can use the --track or --no-track options, which will be passed to `git branch`. +As a convenience, --track will default to create a branch whose +name is constructed from the specified branch name by stripping +the first namespace level. + When <paths> are given, this command does *not* switch branches. It updates the named paths in the working tree from the index file (i.e. it runs `git checkout-index -f -u`), or @@ -59,6 +63,17 @@ OPTIONS 'git-checkout' and 'git-branch' to always behave as if '--no-track' were given. Set it to `always` if you want this behavior when the start-point is either a local or remote branch. ++ +If no '-b' option was given, the name of the new branch will be +derived from the remote branch, by attempting to guess the name +of the branch on remote system. If "remotes/" or "refs/remotes/" +are prefixed, it is stripped away, and then the part up to the +next slash (which would be the nickname of the remote) is removed. +This would tell us to use "hack" as the local branch when branching +off of "origin/hack" (or "remotes/origin/hack", or even +"refs/remotes/origin/hack"). If the given name has no slash, or the above +guessing results in an empty name, the guessing is aborted. You can +exlicitly give a name with '-b' in such a case. --no-track:: Ignore the branch.autosetupmerge configuration variable. diff --git a/Documentation/git-count-objects.txt b/Documentation/git-count-objects.txt index 75a8da1ca..6bc1c21e6 100644 --- a/Documentation/git-count-objects.txt +++ b/Documentation/git-count-objects.txt @@ -21,8 +21,9 @@ OPTIONS --verbose:: In addition to the number of loose objects and disk space consumed, it reports the number of in-pack - objects, number of packs, and number of objects that can be - removed by running `git prune-packed`. + objects, number of packs, disk space consumed by those packs, + and number of objects that can be removed by running + `git prune-packed`. Author diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt index 4ba4b75c1..b08a08cd9 100644 --- a/Documentation/git-daemon.txt +++ b/Documentation/git-daemon.txt @@ -9,8 +9,9 @@ SYNOPSIS -------- [verse] 'git daemon' [--verbose] [--syslog] [--export-all] - [--timeout=n] [--init-timeout=n] [--strict-paths] - [--base-path=path] [--user-path | --user-path=path] + [--timeout=n] [--init-timeout=n] [--max-connections=n] + [--strict-paths] [--base-path=path] [--base-path-relaxed] + [--user-path | --user-path=path] [--interpolated-path=pathtemplate] [--reuseaddr] [--detach] [--pid-file=file] [--enable=service] [--disable=service] @@ -99,6 +100,10 @@ OPTIONS it takes for the server to process the sub-request and time spent waiting for next client's request. +--max-connections:: + Maximum number of concurrent clients, defaults to 32. Set it to + zero for no limit. + --syslog:: Log to syslog instead of stderr. Note that this option does not imply --verbose, thus by default only error conditions will be logged. diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt index 1fdf20dcc..5d48664e6 100644 --- a/Documentation/git-diff-tree.txt +++ b/Documentation/git-diff-tree.txt @@ -49,13 +49,22 @@ include::diff-options.txt[] --stdin:: When '--stdin' is specified, the command does not take <tree-ish> arguments from the command line. Instead, it - reads either one <commit> or a list of <commit> - separated with a single space from its standard input. + reads lines containing either two <tree>, one <commit>, or a + list of <commit> from its standard input. (Use a single space + as separator.) + -When a single commit is given on one line of such input, it compares -the commit with its parents. The following flags further affects its -behavior. The remaining commits, when given, are used as if they are +When two trees are given, it compares the first tree with the second. +When a single commit is given, it compares the commit with its +parents. The remaining commits, when given, are used as if they are parents of the first commit. ++ +When comparing two trees, the ID of both trees (separated by a space +and terminated by a newline) is printed before the difference. When +comparing commits, the ID of the first (or only) commit, followed by a +newline, is printed. ++ +The following flags further affects the behavior when comparing +commits (but not trees). -m:: By default, 'git-diff-tree --stdin' does not show diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index eae6c0e7b..ebd7c5fbb 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -16,7 +16,7 @@ DESCRIPTION Iterate over all refs that match `<pattern>` and show them according to the given `<format>`, after sorting them according -to the given set of `<key>`. If `<max>` is given, stop after +to the given set of `<key>`. If `<count>` is given, stop after showing that many refs. The interpolated values in `<format>` can optionally be quoted as string literals in the specified host language allowing their direct evaluation in that language. diff --git a/Documentation/git-hash-object.txt b/Documentation/git-hash-object.txt index ac928e198..0af40cfb8 100644 --- a/Documentation/git-hash-object.txt +++ b/Documentation/git-hash-object.txt @@ -8,7 +8,9 @@ git-hash-object - Compute object ID and optionally creates a blob from a file SYNOPSIS -------- -'git hash-object' [-t <type>] [-w] [--stdin | --stdin-paths] [--] <file>... +[verse] +'git hash-object' [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>... +'git hash-object' [-t <type>] [-w] --stdin-paths < <list-of-paths> DESCRIPTION ----------- @@ -35,6 +37,22 @@ OPTIONS --stdin-paths:: Read file names from stdin instead of from the command-line. +--path:: + Hash object as it were located at the given path. The location of + file does not directly influence on the hash value, but path is + used to determine what git filters should be applied to the object + before it can be placed to the object database, and, as result of + applying filters, the actual blob put into the object database may + differ from the given file. This option is mainly useful for hashing + temporary files located outside of the working directory or files + read from stdin. + +--no-filters:: + Hash the contents as is, ignoring any input filter that would + have been chosen by the attributes mechanism, including crlf + conversion. If the file is read from standard input then this + is always implied, unless the --path option is given. + Author ------ Written by Junio C Hamano <gitster@pobox.com> diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt index b3d8da33e..bd49a0aee 100644 --- a/Documentation/git-imap-send.txt +++ b/Documentation/git-imap-send.txt @@ -3,7 +3,7 @@ git-imap-send(1) NAME ---- -git-imap-send - Dump a mailbox from stdin into an imap folder +git-imap-send - Send a collection of patches from stdin to an IMAP folder SYNOPSIS @@ -13,9 +13,9 @@ SYNOPSIS DESCRIPTION ----------- -This command uploads a mailbox generated with git-format-patch -into an imap drafts folder. This allows patches to be sent as -other email is sent with mail clients that cannot read mailbox +This command uploads a mailbox generated with 'git-format-patch' +into an IMAP drafts folder. This allows patches to be sent as +other email is when using mail clients that cannot read mailbox files directly. Typical usage is something like: @@ -26,21 +26,75 @@ git format-patch --signoff --stdout --attach origin | git imap-send CONFIGURATION ------------- -'git-imap-send' requires the following values in the repository -configuration file (shown with examples): +To use the tool, imap.folder and either imap.tunnel or imap.host must be set +to appropriate values. + +Variables +~~~~~~~~~ + +imap.folder:: + The folder to drop the mails into, which is typically the Drafts + folder. For example: "INBOX.Drafts", "INBOX/Drafts" or + "[Gmail]/Drafts". Required to use imap-send. + +imap.tunnel:: + Command used to setup a tunnel to the IMAP server through which + commands will be piped instead of using a direct network connection + to the server. Required when imap.host is not set to use imap-send. + +imap.host:: + A URL identifying the server. Use a `imap://` prefix for non-secure + connections and a `imaps://` prefix for secure connections. + Ignored when imap.tunnel is set, but required to use imap-send + otherwise. + +imap.user:: + The username to use when logging in to the server. + +imap.password:: + The password to use when logging in to the server. + +imap.port:: + An integer port number to connect to on the server. + Defaults to 143 for imap:// hosts and 993 for imaps:// hosts. + Ignored when imap.tunnel is set. + +imap.sslverify:: + A boolean to enable/disable verification of the server certificate + used by the SSL/TLS connection. Default is `true`. Ignored when + imap.tunnel is set. + +Examples +~~~~~~~~ + +Using tunnel mode: .......................... [imap] - Folder = "INBOX.Drafts" + folder = "INBOX.Drafts" + tunnel = "ssh -q -C user@example.com /usr/bin/imapd ./Maildir 2> /dev/null" +.......................... +Using direct mode: + +......................... [imap] - Tunnel = "ssh -q user@server.com /usr/bin/imapd ./Maildir 2> /dev/null" + folder = "INBOX.Drafts" + host = imap://imap.example.com + user = bob + pass = p4ssw0rd +.......................... + +Using direct mode with SSL: +......................... [imap] - Host = imap.server.com - User = bob - Pass = pwd - Port = 143 + folder = "INBOX.Drafts" + host = imaps://imap.example.com + user = bob + pass = p4ssw0rd + port = 123 + sslverify = false .......................... diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt index 1a7ecbf8f..2f0c5259e 100644 --- a/Documentation/git-merge-base.txt +++ b/Documentation/git-merge-base.txt @@ -8,26 +8,81 @@ git-merge-base - Find as good common ancestors as possible for a merge SYNOPSIS -------- -'git merge-base' [--all] <commit> <commit> +'git merge-base' [--all] <commit> <commit>... DESCRIPTION ----------- -'git-merge-base' finds as good a common ancestor as possible between -the two commits. That is, given two commits A and B, `git merge-base A -B` will output a commit which is reachable from both A and B through -the parent relationship. +'git-merge-base' finds best common ancestor(s) between two commits to use +in a three-way merge. One common ancestor is 'better' than another common +ancestor if the latter is an ancestor of the former. A common ancestor +that does not have any better common ancestor than it is a 'best common +ancestor', i.e. a 'merge base'. Note that there can be more than one +merge bases between two commits. -Given a selection of equally good common ancestors it should not be -relied on to decide in any particular way. - -The 'git-merge-base' algorithm is still in flux - use the source... +Among the two commits to compute their merge bases, one is specified by +the first commit argument on the command line; the other commit is a +(possibly hypothetical) commit that is a merge across all the remaining +commits on the command line. As the most common special case, giving only +two commits from the command line means computing the merge base between +the given two commits. OPTIONS ------- --all:: - Output all common ancestors for the two commits instead of - just one. + Output all merge bases for the commits, instead of just one. + +DISCUSSION +---------- + +Given two commits 'A' and 'B', `git merge-base A B` will output a commit +which is reachable from both 'A' and 'B' through the parent relationship. + +For example, with this topology: + + o---o---o---B + / + ---o---1---o---o---o---A + +the merge base between 'A' and 'B' is '1'. + +Given three commits 'A', 'B' and 'C', `git merge-base A B C` will compute the +merge base between 'A' and an hypothetical commit 'M', which is a merge +between 'B' and 'C'. For example, with this topology: + + o---o---o---o---C + / + / o---o---o---B + / / + ---2---1---o---o---o---A + +the result of `git merge-base A B C` is '1'. This is because the +equivalent topology with a merge commit 'M' between 'B' and 'C' is: + + + o---o---o---o---o + / \ + / o---o---o---o---M + / / + ---2---1---o---o---o---A + +and the result of `git merge-base A M` is '1'. Commit '2' is also a +common ancestor between 'A' and 'M', but '1' is a better common ancestor, +because '2' is an ancestor of '1'. Hence, '2' is not a merge base. + +When the history involves criss-cross merges, there can be more than one +'best' common ancestors between two commits. For example, with this +topology: + + ---1---o---A + \ / + X + / \ + ---2---o---o---B + +both '1' and '2' are merge-base of A and B. Neither one is better than +the other (both are 'best' merge base). When `--all` option is not given, +it is unspecified which best one is output. Author ------ diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index 17a15acb0..685e1fed5 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -126,13 +126,25 @@ After seeing a conflict, you can do two things: up working tree changes made by 2. and 3.; 'git-reset --hard' can be used for this. - * Resolve the conflicts. `git diff` would report only the - conflicting paths because of the above 2. and 3. - Edit the working tree files into a desirable shape - ('git mergetool' can ease this task), 'git-add' or 'git-rm' - them, to make the index file contain what the merge result - should be, and run 'git-commit' to commit the result. + * Resolve the conflicts. Git will mark the conflicts in + the working tree. Edit the files into shape and + 'git-add' to the index. 'git-commit' to seal the deal. +You can work through the conflict with a number of tools: + + * Use a mergetool. 'git mergetool' to launch a graphical + mergetool which will work you through the merge. + + * Look at the diffs. 'git diff' will show a three-way diff, + highlighting changes from both the HEAD and remote versions. + + * Look at the diffs on their own. 'git log --merge -p <path>' + will show diffs first for the HEAD version and then the + remote version. + + * Look at the originals. 'git show :1:filename' shows the + common ancestor, 'git show :2:filename' shows the HEAD + version and 'git show :3:filename' shows the remote version. SEE ALSO -------- diff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt index abd2237e5..7ca8a7b48 100644 --- a/Documentation/git-name-rev.txt +++ b/Documentation/git-name-rev.txt @@ -59,7 +59,7 @@ Enter 'git-name-rev': ------------ % git name-rev 33db5f4d9027a10e477ccf054b2c1ab94f74c85a -33db5f4d9027a10e477ccf054b2c1ab94f74c85a tags/v0.99^0~940 +33db5f4d9027a10e477ccf054b2c1ab94f74c85a tags/v0.99~940 ------------ Now you are wiser, because you know that it happened 940 revisions before v0.99. diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index e2437f30c..3c3e1b0e7 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -179,6 +179,9 @@ user is prompted for a password while the input is masked for privacy. This is useful if your default address is not the address that is subscribed to a list. If you use the sendmail binary, you must have suitable privileges for the -f parameter. + Default is the value of the 'sendemail.envelopesender' configuration + variable; if that is unspecified, choosing the envelope sender is left + to your MTA. --to:: Specify the primary recipient of the emails generated. diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 49e2296a2..051f94d26 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -159,7 +159,7 @@ perform a pull, and then unstash, like this: + ---------------------------------------------------------------- $ git pull -... + ... file foobar not up to date, cannot merge. $ git stash $ git pull @@ -174,7 +174,7 @@ make a commit to a temporary branch to store your changes away, and return to your original branch to make the emergency fix, like this: + ---------------------------------------------------------------- -... hack hack hack ... +# ... hack hack hack ... $ git checkout -b my_wip $ git commit -a -m "WIP" $ git checkout master @@ -182,18 +182,18 @@ $ edit emergency fix $ git commit -a -m "Fix in a hurry" $ git checkout my_wip $ git reset --soft HEAD^ -... continue hacking ... +# ... continue hacking ... ---------------------------------------------------------------- + You can use 'git-stash' to simplify the above, like this: + ---------------------------------------------------------------- -... hack hack hack ... +# ... hack hack hack ... $ git stash $ edit emergency fix $ git commit -a -m "Fix in a hurry" $ git stash apply -... continue hacking ... +# ... continue hacking ... ---------------------------------------------------------------- Testing partial commits:: @@ -203,13 +203,13 @@ more commits out of the changes in the work tree, and you want to test each change before committing: + ---------------------------------------------------------------- -... hack hack hack ... +# ... hack hack hack ... $ git add --patch foo # add just first part to the index $ git stash save --keep-index # save all other changes to the stash $ edit/build/test first part -$ git commit foo -m 'First part' # commit fully tested change +$ git commit -m 'First part' # commit fully tested change $ git stash pop # prepare to work on all other changes -... repeat above five steps until one commit remains ... +# ... repeat above five steps until one commit remains ... $ edit/build/test remaining parts $ git commit foo -m 'Remaining parts' ---------------------------------------------------------------- diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index bf33b0cba..babaa9bc4 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -14,6 +14,8 @@ SYNOPSIS 'git submodule' [--quiet] init [--] [<path>...] 'git submodule' [--quiet] update [--init] [--] [<path>...] 'git submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...] +'git submodule' [--quiet] foreach <command> +'git submodule' [--quiet] sync [--] [<path>...] DESCRIPTION @@ -123,6 +125,30 @@ summary:: in the submodule between the given super project commit and the index or working tree (switched by --cached) are shown. +foreach:: + Evaluates an arbitrary shell command in each checked out submodule. + The command has access to the variables $path and $sha1: + $path is the name of the submodule directory relative to the + superproject, and $sha1 is the commit as recorded in the superproject. + Any submodules defined in the superproject but not checked out are + ignored by this command. Unless given --quiet, foreach prints the name + of each submodule before evaluating the command. + A non-zero return from the command in any submodule causes + the processing to terminate. This can be overridden by adding '|| :' + to the end of the command. ++ +As an example, "git submodule foreach 'echo $path `git rev-parse HEAD`' will +show the path and currently checked out commit for each submodule. + +sync:: + Synchronizes submodules' remote URL configuration setting + to the value specified in .gitmodules. This is useful when + submodule URLs change upstream and you need to update your local + repositories accordingly. ++ +"git submodule sync" synchronizes all submodules while +"git submodule sync -- A" synchronizes submodule "A" only. + OPTIONS ------- -q:: diff --git a/Documentation/git.txt b/Documentation/git.txt index 363a78545..e178fb581 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,9 +43,10 @@ unreleased) version of git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v1.6.0/git.html[documentation for release 1.6.0] +* link:v1.6.1/git.html[documentation for release 1.6.1] * release notes for + link:RelNotes-1.6.1.txt[1.6.1], link:RelNotes-1.6.0.txt[1.6.0]. * link:v1.5.6.5/git.html[documentation for release 1.5.6.5] diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index db16b0ca5..5fb500741 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -7,7 +7,7 @@ gitattributes - defining attributes per path SYNOPSIS -------- -$GIT_DIR/info/attributes, gitattributes +$GIT_DIR/info/attributes, .gitattributes DESCRIPTION @@ -105,9 +105,8 @@ Set:: Unset:: - Unsetting the `crlf` attribute on a path is meant to - mark the path as a "binary" file. The path never goes - through line endings conversion upon checkin/checkout. + Unsetting the `crlf` attribute on a path tells git not to + attempt any end-of-line conversion upon checkin or checkout. Unspecified:: @@ -316,10 +315,14 @@ patterns are available: - `pascal` suitable for source code in the Pascal/Delphi language. +- `python` suitable for source code in the Python language. + - `ruby` suitable for source code in the Ruby language. - `tex` suitable for source code for LaTeX documents. +- `html` suitable for HTML/XHTML documents. + Performing a three-way merge ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -482,6 +485,41 @@ in the file. E.g. the string `$Format:%H$` will be replaced by the commit hash. +USING ATTRIBUTE MACROS +---------------------- + +You do not want any end-of-line conversions applied to, nor textual diffs +produced for, any binary file you track. You would need to specify e.g. + +------------ +*.jpg -crlf -diff +------------ + +but that may become cumbersome, when you have many attributes. Using +attribute macros, you can specify groups of attributes set or unset at +the same time. The system knows a built-in attribute macro, `binary`: + +------------ +*.jpg binary +------------ + +which is equivalent to the above. Note that the attribute macros can only +be "Set" (see the above example that sets "binary" macro as if it were an +ordinary attribute --- setting it in turn unsets "crlf" and "diff"). + + +DEFINING ATTRIBUTE MACROS +------------------------- + +Custom attribute macros can be defined only in the `.gitattributes` file +at the toplevel (i.e. not in any subdirectory). The built-in attribute +macro "binary" is equivalent to: + +------------ +[attr]binary -diff -crlf +------------ + + EXAMPLE ------- diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt index f8d122a8b..d1a17e262 100644 --- a/Documentation/gitmodules.txt +++ b/Documentation/gitmodules.txt @@ -7,7 +7,7 @@ gitmodules - defining submodule properties SYNOPSIS -------- -gitmodules +$GIT_WORK_DIR/.gitmodules DESCRIPTION @@ -124,6 +124,9 @@ all:: # Define USE_STDEV below if you want git to care about the underlying device # change being considered an inode change from the update-index perspective. # +# Define NO_ST_BLOCKS_IN_STRUCT_STAT if your platform does not have st_blocks +# field that counts the on-disk footprint in 512-byte blocks. +# # 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. @@ -333,7 +336,6 @@ endif export PERL_PATH LIB_FILE=libgit.a -COMPAT_LIB = compat/lib.a XDIFF_LIB=xdiff/lib.a LIB_H += archive.h @@ -355,10 +357,12 @@ LIB_H += git-compat-util.h LIB_H += graph.h LIB_H += grep.h LIB_H += hash.h +LIB_H += help.h LIB_H += list-objects.h LIB_H += ll-merge.h LIB_H += log-tree.h LIB_H += mailmap.h +LIB_H += merge-recursive.h LIB_H += object.h LIB_H += pack.h LIB_H += pack-refs.h @@ -518,6 +522,7 @@ BUILTIN_OBJS += builtin-for-each-ref.o BUILTIN_OBJS += builtin-fsck.o BUILTIN_OBJS += builtin-gc.o BUILTIN_OBJS += builtin-grep.o +BUILTIN_OBJS += builtin-help.o BUILTIN_OBJS += builtin-init-db.o BUILTIN_OBJS += builtin-log.o BUILTIN_OBJS += builtin-ls-files.o @@ -575,9 +580,11 @@ EXTLIBS = ifeq ($(uname_S),Linux) NO_STRLCPY = YesPlease + THREADED_DELTA_SEARCH = YesPlease endif ifeq ($(uname_S),GNU/kFreeBSD) NO_STRLCPY = YesPlease + THREADED_DELTA_SEARCH = YesPlease endif ifeq ($(uname_S),UnixWare) CC = cc @@ -675,6 +682,7 @@ ifeq ($(uname_S),FreeBSD) BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease + THREADED_DELTA_SEARCH = YesPlease endif ifeq ($(uname_S),OpenBSD) NO_STRCASESTR = YesPlease @@ -682,6 +690,7 @@ ifeq ($(uname_S),OpenBSD) NEEDS_LIBICONV = YesPlease BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib + THREADED_DELTA_SEARCH = YesPlease endif ifeq ($(uname_S),NetBSD) ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2) @@ -690,6 +699,7 @@ ifeq ($(uname_S),NetBSD) BASIC_CFLAGS += -I/usr/pkg/include BASIC_LDFLAGS += -L/usr/pkg/lib ALL_LDFLAGS += -Wl,-rpath,/usr/pkg/lib + THREADED_DELTA_SEARCH = YesPlease endif ifeq ($(uname_S),AIX) NO_STRCASESTR=YesPlease @@ -750,6 +760,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) NO_SVN_TESTS = YesPlease NO_PERL_MAKEMAKER = YesPlease NO_POSIX_ONLY_PROGRAMS = YesPlease + NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1 COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" @@ -864,6 +875,9 @@ endif ifdef NO_D_INO_IN_DIRENT BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT endif +ifdef NO_ST_BLOCKS_IN_STRUCT_STAT + BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT +endif ifdef NO_C99_FORMAT BASIC_CFLAGS += -DNO_C99_FORMAT endif @@ -1092,14 +1106,17 @@ git$X: git.o $(BUILTIN_OBJS) $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \ $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS) -help.o: help.c common-cmds.h GIT-CFLAGS +builtin-help.o: builtin-help.c common-cmds.h GIT-CFLAGS $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \ '-DGIT_HTML_PATH="$(htmldir_SQ)"' \ '-DGIT_MAN_PATH="$(mandir_SQ)"' \ '-DGIT_INFO_PATH="$(infodir_SQ)"' $< $(BUILT_INS): git$X - $(QUIET_BUILT_IN)$(RM) $@ && ln git$X $@ + $(QUIET_BUILT_IN)$(RM) $@ && \ + ln git$X $@ 2>/dev/null || \ + ln -s git$X $@ 2>/dev/null || \ + cp git$X $@ common-cmds.h: ./generate-cmdlist.sh command-list.txt @@ -1216,7 +1233,9 @@ endif git-%$X: %.o $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) -git-imap-send$X: imap-send.o $(LIB_FILE) +git-imap-send$X: imap-send.o $(GITLIBS) + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ + $(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL) http.o http-walker.o http-push.o transport.o: http.h @@ -1224,12 +1243,6 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) -$(COMPAT_LIB): $(COMPAT_OBJS) - $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(COMPAT_OBJS) - -git-shell$X: abspath.o ctype.o exec_cmd.o quote.o strbuf.o usage.o wrapper.o shell.o $(COMPAT_LIB) - $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(COMPAT_LIB) - $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) builtin-revert.o wt-status.o: wt-status.h @@ -1352,7 +1365,7 @@ install: all $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' - $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X '$(DESTDIR_SQ)$(bindir_SQ)' + $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X '$(DESTDIR_SQ)$(bindir_SQ)' $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install ifndef NO_TCLTK @@ -1364,16 +1377,13 @@ ifneq (,$X) endif bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \ execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \ - if test "z$$bindir" != "z$$execdir"; \ - then \ - ln -f "$$bindir/git$X" "$$execdir/git$X" || \ - cp "$$bindir/git$X" "$$execdir/git$X"; \ - fi && \ - { $(foreach p,$(BUILT_INS), $(RM) "$$execdir/$p" && ln "$$execdir/git$X" "$$execdir/$p" ;) } && \ - if test "z$$bindir" != "z$$execdir"; \ - then \ - $(RM) "$$execdir/git$X"; \ - fi && \ + { $(RM) "$$execdir/git-add$X" && \ + ln git-add$X "$$execdir/git-add$X" 2>/dev/null || \ + cp git-add$X "$$execdir/git-add$X"; } && \ + { $(foreach p,$(filter-out git-add$X,$(BUILT_INS)), $(RM) "$$execdir/$p" && \ + ln "$$execdir/git-add$X" "$$execdir/$p" 2>/dev/null || \ + ln -s "git-add$X" "$$execdir/$p" 2>/dev/null || \ + cp "$$execdir/git-add$X" "$$execdir/$p" || exit;) } && \ ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X" install-doc: @@ -1442,7 +1452,7 @@ distclean: clean clean: $(RM) *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \ - $(LIB_FILE) $(XDIFF_LIB) $(COMPAT_LIB) + $(LIB_FILE) $(XDIFF_LIB) $(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X $(RM) $(TEST_PROGRAMS) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope* @@ -1 +1 @@ -Documentation/RelNotes-1.6.0.1.txt
\ No newline at end of file +Documentation/RelNotes-1.6.1.txt
\ No newline at end of file diff --git a/builtin-add.c b/builtin-add.c index fc3f96eae..7c874e311 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -8,10 +8,6 @@ #include "dir.h" #include "exec_cmd.h" #include "cache-tree.h" -#include "diff.h" -#include "diffcore.h" -#include "commit.h" -#include "revision.h" #include "run-command.h" #include "parse-options.h" @@ -22,6 +18,27 @@ static const char * const builtin_add_usage[] = { static int patch_interactive = 0, add_interactive = 0; static int take_worktree_changes; +static void fill_pathspec_matches(const char **pathspec, char *seen, int specs) +{ + int num_unmatched = 0, i; + + /* + * Since we are walking the index as if we are warlking the directory, + * we have to mark the matched pathspec as seen; otherwise we will + * mistakenly think that the user gave a pathspec that did not match + * anything. + */ + for (i = 0; i < specs; i++) + if (!seen[i]) + num_unmatched++; + if (!num_unmatched) + return; + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen); + } +} + static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) { char *seen; @@ -41,6 +58,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p *dst++ = entry; } dir->nr = dst - dir->entries; + fill_pathspec_matches(pathspec, seen, specs); for (i = 0; i < specs; i++) { if (!seen[i] && !file_exists(pathspec[i])) @@ -79,59 +97,6 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec, prune_directory(dir, pathspec, baselen); } -struct update_callback_data -{ - int flags; - int add_errors; -}; - -static void update_callback(struct diff_queue_struct *q, - struct diff_options *opt, void *cbdata) -{ - int i; - struct update_callback_data *data = cbdata; - - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - const char *path = p->one->path; - switch (p->status) { - default: - die("unexpected diff status %c", p->status); - case DIFF_STATUS_UNMERGED: - case DIFF_STATUS_MODIFIED: - case DIFF_STATUS_TYPE_CHANGED: - if (add_file_to_cache(path, data->flags)) { - if (!(data->flags & ADD_CACHE_IGNORE_ERRORS)) - die("updating files failed"); - data->add_errors++; - } - break; - case DIFF_STATUS_DELETED: - if (!(data->flags & ADD_CACHE_PRETEND)) - remove_file_from_cache(path); - if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE)) - printf("remove '%s'\n", path); - break; - } - } -} - -int add_files_to_cache(const char *prefix, const char **pathspec, int flags) -{ - struct update_callback_data data; - struct rev_info rev; - init_revisions(&rev, prefix); - setup_revisions(0, NULL, &rev, NULL); - rev.prune_data = pathspec; - rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; - rev.diffopt.format_callback = update_callback; - data.flags = flags; - data.add_errors = 0; - rev.diffopt.format_callback_data = &data; - run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); - return !!data.add_errors; -} - static void refresh(int verbose, const char **pathspec) { char *seen; @@ -153,6 +118,16 @@ static const char **validate_pathspec(int argc, const char **argv, const char *p { const char **pathspec = get_pathspec(prefix, argv); + if (pathspec) { + const char **p; + for (p = pathspec; *p; p++) { + if (has_symlink_leading_path(strlen(*p), *p)) { + int len = prefix ? strlen(prefix) : 0; + die("'%s' is beyond a symbolic link", *p + len); + } + } + } + return pathspec; } @@ -258,7 +233,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (addremove && take_worktree_changes) die("-A and -u are mutually incompatible"); - if (addremove && !argc) { + if ((addremove || take_worktree_changes) && !argc) { static const char *here[2] = { ".", NULL }; argc = 1; argv = here; @@ -271,33 +246,30 @@ int cmd_add(int argc, const char **argv, const char *prefix) flags = ((verbose ? ADD_CACHE_VERBOSE : 0) | (show_only ? ADD_CACHE_PRETEND : 0) | - (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0)); + (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) | + (!(addremove || take_worktree_changes) + ? ADD_CACHE_IGNORE_REMOVAL : 0)); if (require_pathspec && argc == 0) { fprintf(stderr, "Nothing specified, nothing added.\n"); fprintf(stderr, "Maybe you wanted to say 'git add .'?\n"); return 0; } - pathspec = get_pathspec(prefix, argv); - - /* - * If we are adding new files, we need to scan the working - * tree to find the ones that match pathspecs; this needs - * to be done before we read the index. - */ - if (add_new_files) - fill_directory(&dir, pathspec, ignored_too); + pathspec = validate_pathspec(argc, argv, prefix); if (read_cache() < 0) die("index file corrupt"); + if (add_new_files) + /* This picks up the paths that are not tracked */ + fill_directory(&dir, pathspec, ignored_too); + if (refresh_only) { refresh(verbose, pathspec); goto finish; } - if (take_worktree_changes || addremove) - exit_status |= add_files_to_cache(prefix, pathspec, flags); + exit_status |= add_files_to_cache(prefix, pathspec, flags); if (add_new_files) exit_status |= add_files(&dir, flags); diff --git a/builtin-apply.c b/builtin-apply.c index 2216a0bf7..47261e10f 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -1996,6 +1996,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, /* * A hunk to change lines at the beginning would begin with * @@ -1,L +N,M @@ + * but we need to be careful. -U0 that inserts before the second + * line also has this pattern. * * And a hunk to add to an empty file would begin with * @@ -0,0 +N,M @@ @@ -2003,7 +2005,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, * In other words, a hunk that is (frag->oldpos <= 1) with or * without leading context must match at the beginning. */ - match_beginning = frag->oldpos <= 1; + match_beginning = (!frag->oldpos || + (frag->oldpos == 1 && !unidiff_zero)); /* * A hunk without trailing lines must match at the end. diff --git a/builtin-blame.c b/builtin-blame.c index 4ea343189..e4d12de8a 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -465,7 +465,6 @@ struct patch { }; struct blame_diff_state { - struct xdiff_emit_state xm; struct patch *ret; unsigned hunk_post_context; unsigned hunk_in_pre_context : 1; @@ -528,15 +527,12 @@ static struct patch *compare_buffer(mmfile_t *file_p, mmfile_t *file_o, xpp.flags = xdl_opts; memset(&xecfg, 0, sizeof(xecfg)); xecfg.ctxlen = context; - ecb.outf = xdiff_outf; - ecb.priv = &state; memset(&state, 0, sizeof(state)); - state.xm.consume = process_u_diff; state.ret = xmalloc(sizeof(struct patch)); state.ret->chunks = NULL; state.ret->num = 0; - xdi_diff(file_p, file_o, &xpp, &xecfg, &ecb); + xdi_diff_outf(file_p, file_o, process_u_diff, &state, &xpp, &xecfg, &ecb); if (state.ret->num) { struct chunk *chunk; diff --git a/builtin-checkout.c b/builtin-checkout.c index 411cc513c..b380ad6e8 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -157,7 +157,7 @@ struct checkout_opts { int force; int writeout_error; - char *new_branch; + const char *new_branch; int new_branch_log; enum branch_track track; }; @@ -437,13 +437,28 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) git_config(git_default_config, NULL); - opts.track = git_branch_track; + opts.track = BRANCH_TRACK_UNSPECIFIED; argc = parse_options(argc, argv, options, checkout_usage, PARSE_OPT_KEEP_DASHDASH); - if (!opts.new_branch && (opts.track != git_branch_track)) - die("git checkout: --track and --no-track require -b"); + /* --track without -b should DWIM */ + if (0 < opts.track && !opts.new_branch) { + const char *argv0 = argv[0]; + if (!argc || !strcmp(argv0, "--")) + die ("--track needs a branch name"); + if (!prefixcmp(argv0, "refs/")) + argv0 += 5; + if (!prefixcmp(argv0, "remotes/")) + argv0 += 8; + argv0 = strchr(argv0, '/'); + if (!argv0 || !argv0[1]) + die ("Missing branch name; try -b"); + opts.new_branch = argv0 + 1; + } + + if (opts.track == BRANCH_TRACK_UNSPECIFIED) + opts.track = git_branch_track; if (opts.force && opts.merge) die("git checkout: -f and -m are incompatible"); diff --git a/builtin-clone.c b/builtin-clone.c index c0e308643..f44eceab3 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -387,7 +387,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) path = get_repo_path(repo_name, &is_bundle); if (path) - repo = path; + repo = xstrdup(make_nonrelative_path(repo_name)); else if (!strchr(repo_name, ':')) repo = xstrdup(make_absolute_path(repo_name)); else diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c index 7a9a309be..f773db596 100644 --- a/builtin-commit-tree.c +++ b/builtin-commit-tree.c @@ -48,6 +48,7 @@ static const char commit_utf8_warn[] = int commit_tree(const char *msg, unsigned char *tree, struct commit_list *parents, unsigned char *ret) { + int result; int encoding_is_utf8; struct strbuf buffer; @@ -86,7 +87,9 @@ int commit_tree(const char *msg, unsigned char *tree, if (encoding_is_utf8 && !is_utf8(buffer.buf)) fprintf(stderr, commit_utf8_warn); - return write_sha1_file(buffer.buf, buffer.len, commit_type, ret); + result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret); + strbuf_release(&buffer); + return result; } int cmd_commit_tree(int argc, const char **argv, const char *prefix) diff --git a/builtin-commit.c b/builtin-commit.c index 4182686b9..8165bb3d3 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -320,7 +320,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix) die("unable to write new_index file"); fd = hold_lock_file_for_update(&false_lock, - git_path("next-index-%d", getpid()), 1); + git_path("next-index-%"PRIuMAX, (uintmax_t) getpid()), 1); create_base_index(); add_remove_files(&partial); diff --git a/builtin-count-objects.c b/builtin-count-objects.c index 91b548747..6e80fe7c0 100644 --- a/builtin-count-objects.c +++ b/builtin-count-objects.c @@ -43,7 +43,7 @@ static void count_objects(DIR *d, char *path, int len, int verbose, if (lstat(path, &st) || !S_ISREG(st.st_mode)) bad = 1; else - (*loose_size) += xsize_t(st.st_blocks); + (*loose_size) += xsize_t(on_disk_bytes(st)); } if (bad) { if (verbose) { @@ -104,6 +104,7 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix) if (verbose) { struct packed_git *p; unsigned long num_pack = 0; + unsigned long size_pack = 0; if (!packed_git) prepare_packed_git(); for (p = packed_git; p; p = p->next) { @@ -112,12 +113,14 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix) if (open_pack_index(p)) continue; packed += p->num_objects; + size_pack += p->pack_size + p->index_size; num_pack++; } printf("count: %lu\n", loose); - printf("size: %lu\n", loose_size / 2); + printf("size: %lu\n", loose_size / 1024); printf("in-pack: %lu\n", packed); printf("packs: %lu\n", num_pack); + printf("size-pack: %lu\n", size_pack / 1024); printf("prune-packable: %lu\n", packed_loose); printf("garbage: %lu\n", garbage); } diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c index 415cb1612..1138c2da7 100644 --- a/builtin-diff-tree.c +++ b/builtin-diff-tree.c @@ -14,20 +14,10 @@ static int diff_tree_commit_sha1(const unsigned char *sha1) return log_tree_commit(&log_tree_opt, commit); } -static int diff_tree_stdin(char *line) +/* Diff one or more commits. */ +static int stdin_diff_commit(struct commit *commit, char *line, int len) { - int len = strlen(line); unsigned char sha1[20]; - struct commit *commit; - - if (!len || line[len-1] != '\n') - return -1; - line[len-1] = 0; - if (get_sha1_hex(line, sha1)) - return -1; - commit = lookup_commit(sha1); - if (!commit || parse_commit(commit)) - return -1; if (isspace(line[40]) && !get_sha1_hex(line+41, sha1)) { /* Graft the fake parents locally to the commit */ int pos = 41; @@ -52,6 +42,48 @@ static int diff_tree_stdin(char *line) return log_tree_commit(&log_tree_opt, commit); } +/* Diff two trees. */ +static int stdin_diff_trees(struct tree *tree1, char *line, int len) +{ + unsigned char sha1[20]; + struct tree *tree2; + if (len != 82 || !isspace(line[40]) || get_sha1_hex(line + 41, sha1)) + return error("Need exactly two trees, separated by a space"); + tree2 = lookup_tree(sha1); + if (!tree2 || parse_tree(tree2)) + return -1; + printf("%s %s\n", sha1_to_hex(tree1->object.sha1), + sha1_to_hex(tree2->object.sha1)); + diff_tree_sha1(tree1->object.sha1, tree2->object.sha1, + "", &log_tree_opt.diffopt); + log_tree_diff_flush(&log_tree_opt); + return 0; +} + +static int diff_tree_stdin(char *line) +{ + int len = strlen(line); + unsigned char sha1[20]; + struct object *obj; + + if (!len || line[len-1] != '\n') + return -1; + line[len-1] = 0; + if (get_sha1_hex(line, sha1)) + return -1; + obj = lookup_object(sha1); + obj = obj ? obj : parse_object(sha1); + if (!obj) + return -1; + if (obj->type == OBJ_COMMIT) + return stdin_diff_commit((struct commit *)obj, line, len); + if (obj->type == OBJ_TREE) + return stdin_diff_trees((struct tree *)obj, line, len); + error("Object %s is a %s, not a commit or tree", + sha1_to_hex(sha1), typename(obj->type)); + return -1; +} + static const char diff_tree_usage[] = "git diff-tree [--stdin] [-m] [-c] [--cc] [-s] [-v] [--pretty] [-t] [-r] [--root] " "[<common diff options>] <tree-ish> [<tree-ish>] [<path>...]\n" diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index 273239af3..17a5a422c 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -540,7 +540,7 @@ static int get_pack(int xd[2], char **pack_lockfile) *av++ = "--fix-thin"; if (args.lock_pack || unpack_limit) { int s = sprintf(keep_arg, - "--keep=fetch-pack %d on ", getpid()); + "--keep=fetch-pack %"PRIuMAX " on ", (uintmax_t) getpid()); if (gethostname(keep_arg + s, sizeof(keep_arg) - s)) strcpy(keep_arg + s, "localhost"); *av++ = keep_arg; diff --git a/builtin-help.c b/builtin-help.c new file mode 100644 index 000000000..721038e4f --- /dev/null +++ b/builtin-help.c @@ -0,0 +1,462 @@ +/* + * builtin-help.c + * + * Builtin help command + */ +#include "cache.h" +#include "builtin.h" +#include "exec_cmd.h" +#include "common-cmds.h" +#include "parse-options.h" +#include "run-command.h" +#include "help.h" + +static struct man_viewer_list { + struct man_viewer_list *next; + char name[FLEX_ARRAY]; +} *man_viewer_list; + +static struct man_viewer_info_list { + struct man_viewer_info_list *next; + const char *info; + char name[FLEX_ARRAY]; +} *man_viewer_info_list; + +enum help_format { + HELP_FORMAT_MAN, + HELP_FORMAT_INFO, + HELP_FORMAT_WEB, +}; + +static int show_all = 0; +static enum help_format help_format = HELP_FORMAT_MAN; +static struct option builtin_help_options[] = { + OPT_BOOLEAN('a', "all", &show_all, "print all available commands"), + OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN), + OPT_SET_INT('w', "web", &help_format, "show manual in web browser", + HELP_FORMAT_WEB), + OPT_SET_INT('i', "info", &help_format, "show info page", + HELP_FORMAT_INFO), + OPT_END(), +}; + +static const char * const builtin_help_usage[] = { + "git help [--all] [--man|--web|--info] [command]", + NULL +}; + +static enum help_format parse_help_format(const char *format) +{ + if (!strcmp(format, "man")) + return HELP_FORMAT_MAN; + if (!strcmp(format, "info")) + return HELP_FORMAT_INFO; + if (!strcmp(format, "web") || !strcmp(format, "html")) + return HELP_FORMAT_WEB; + die("unrecognized help format '%s'", format); +} + +static const char *get_man_viewer_info(const char *name) +{ + struct man_viewer_info_list *viewer; + + for (viewer = man_viewer_info_list; viewer; viewer = viewer->next) + { + if (!strcasecmp(name, viewer->name)) + return viewer->info; + } + return NULL; +} + +static int check_emacsclient_version(void) +{ + struct strbuf buffer = STRBUF_INIT; + struct child_process ec_process; + const char *argv_ec[] = { "emacsclient", "--version", NULL }; + int version; + + /* emacsclient prints its version number on stderr */ + memset(&ec_process, 0, sizeof(ec_process)); + ec_process.argv = argv_ec; + ec_process.err = -1; + ec_process.stdout_to_stderr = 1; + if (start_command(&ec_process)) { + fprintf(stderr, "Failed to start emacsclient.\n"); + return -1; + } + strbuf_read(&buffer, ec_process.err, 20); + close(ec_process.err); + + /* + * Don't bother checking return value, because "emacsclient --version" + * seems to always exits with code 1. + */ + finish_command(&ec_process); + + if (prefixcmp(buffer.buf, "emacsclient")) { + fprintf(stderr, "Failed to parse emacsclient version.\n"); + strbuf_release(&buffer); + return -1; + } + + strbuf_remove(&buffer, 0, strlen("emacsclient")); + version = atoi(buffer.buf); + + if (version < 22) { + fprintf(stderr, + "emacsclient version '%d' too old (< 22).\n", + version); + strbuf_release(&buffer); + return -1; + } + + strbuf_release(&buffer); + return 0; +} + +static void exec_woman_emacs(const char* path, const char *page) +{ + if (!check_emacsclient_version()) { + /* This works only with emacsclient version >= 22. */ + struct strbuf man_page = STRBUF_INIT; + + if (!path) + path = "emacsclient"; + strbuf_addf(&man_page, "(woman \"%s\")", page); + execlp(path, "emacsclient", "-e", man_page.buf, NULL); + warning("failed to exec '%s': %s", path, strerror(errno)); + } +} + +static void exec_man_konqueror(const char* path, const char *page) +{ + const char *display = getenv("DISPLAY"); + if (display && *display) { + struct strbuf man_page = STRBUF_INIT; + const char *filename = "kfmclient"; + + /* It's simpler to launch konqueror using kfmclient. */ + if (path) { + const char *file = strrchr(path, '/'); + if (file && !strcmp(file + 1, "konqueror")) { + char *new = xstrdup(path); + char *dest = strrchr(new, '/'); + + /* strlen("konqueror") == strlen("kfmclient") */ + strcpy(dest + 1, "kfmclient"); + path = new; + } + if (file) + filename = file; + } else + path = "kfmclient"; + strbuf_addf(&man_page, "man:%s(1)", page); + execlp(path, filename, "newTab", man_page.buf, NULL); + warning("failed to exec '%s': %s", path, strerror(errno)); + } +} + +static void exec_man_man(const char* path, const char *page) +{ + if (!path) + path = "man"; + execlp(path, "man", page, NULL); + warning("failed to exec '%s': %s", path, strerror(errno)); +} + +static void exec_man_cmd(const char *cmd, const char *page) +{ + struct strbuf shell_cmd = STRBUF_INIT; + strbuf_addf(&shell_cmd, "%s %s", cmd, page); + execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL); + warning("failed to exec '%s': %s", cmd, strerror(errno)); +} + +static void add_man_viewer(const char *name) +{ + struct man_viewer_list **p = &man_viewer_list; + size_t len = strlen(name); + + while (*p) + p = &((*p)->next); + *p = xcalloc(1, (sizeof(**p) + len + 1)); + strncpy((*p)->name, name, len); +} + +static int supported_man_viewer(const char *name, size_t len) +{ + return (!strncasecmp("man", name, len) || + !strncasecmp("woman", name, len) || + !strncasecmp("konqueror", name, len)); +} + +static void do_add_man_viewer_info(const char *name, + size_t len, + const char *value) +{ + struct man_viewer_info_list *new = xcalloc(1, sizeof(*new) + len + 1); + + strncpy(new->name, name, len); + new->info = xstrdup(value); + new->next = man_viewer_info_list; + man_viewer_info_list = new; +} + +static int add_man_viewer_path(const char *name, + size_t len, + const char *value) +{ + if (supported_man_viewer(name, len)) + do_add_man_viewer_info(name, len, value); + else + warning("'%s': path for unsupported man viewer.\n" + "Please consider using 'man.<tool>.cmd' instead.", + name); + + return 0; +} + +static int add_man_viewer_cmd(const char *name, + size_t len, + const char *value) +{ + if (supported_man_viewer(name, len)) + warning("'%s': cmd for supported man viewer.\n" + "Please consider using 'man.<tool>.path' instead.", + name); + else + do_add_man_viewer_info(name, len, value); + + return 0; +} + +static int add_man_viewer_info(const char *var, const char *value) +{ + const char *name = var + 4; + const char *subkey = strrchr(name, '.'); + + if (!subkey) + return error("Config with no key for man viewer: %s", name); + + if (!strcmp(subkey, ".path")) { + if (!value) + return config_error_nonbool(var); + return add_man_viewer_path(name, subkey - name, value); + } + if (!strcmp(subkey, ".cmd")) { + if (!value) + return config_error_nonbool(var); + return add_man_viewer_cmd(name, subkey - name, value); + } + + warning("'%s': unsupported man viewer sub key.", subkey); + return 0; +} + +static int git_help_config(const char *var, const char *value, void *cb) +{ + if (!strcmp(var, "help.format")) { + if (!value) + return config_error_nonbool(var); + help_format = parse_help_format(value); + return 0; + } + if (!strcmp(var, "man.viewer")) { + if (!value) + return config_error_nonbool(var); + add_man_viewer(value); + return 0; + } + if (!prefixcmp(var, "man.")) + return add_man_viewer_info(var, value); + + return git_default_config(var, value, cb); +} + +static struct cmdnames main_cmds, other_cmds; + +void list_common_cmds_help(void) +{ + int i, longest = 0; + + for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { + if (longest < strlen(common_cmds[i].name)) + longest = strlen(common_cmds[i].name); + } + + puts("The most commonly used git commands are:"); + for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { + printf(" %s ", common_cmds[i].name); + mput_char(' ', longest - strlen(common_cmds[i].name)); + puts(common_cmds[i].help); + } +} + +static int is_git_command(const char *s) +{ + return is_in_cmdlist(&main_cmds, s) || + is_in_cmdlist(&other_cmds, s); +} + +static const char *prepend(const char *prefix, const char *cmd) +{ + size_t pre_len = strlen(prefix); + size_t cmd_len = strlen(cmd); + char *p = xmalloc(pre_len + cmd_len + 1); + memcpy(p, prefix, pre_len); + strcpy(p + pre_len, cmd); + return p; +} + +static const char *cmd_to_page(const char *git_cmd) +{ + if (!git_cmd) + return "git"; + else if (!prefixcmp(git_cmd, "git")) + return git_cmd; + else if (is_git_command(git_cmd)) + return prepend("git-", git_cmd); + else + return prepend("git", git_cmd); +} + +static void setup_man_path(void) +{ + struct strbuf new_path; + const char *old_path = getenv("MANPATH"); + + strbuf_init(&new_path, 0); + + /* We should always put ':' after our path. If there is no + * old_path, the ':' at the end will let 'man' to try + * system-wide paths after ours to find the manual page. If + * there is old_path, we need ':' as delimiter. */ + strbuf_addstr(&new_path, GIT_MAN_PATH); + strbuf_addch(&new_path, ':'); + if (old_path) + strbuf_addstr(&new_path, old_path); + + setenv("MANPATH", new_path.buf, 1); + + strbuf_release(&new_path); +} + +static void exec_viewer(const char *name, const char *page) +{ + const char *info = get_man_viewer_info(name); + + if (!strcasecmp(name, "man")) + exec_man_man(info, page); + else if (!strcasecmp(name, "woman")) + exec_woman_emacs(info, page); + else if (!strcasecmp(name, "konqueror")) + exec_man_konqueror(info, page); + else if (info) + exec_man_cmd(info, page); + else + warning("'%s': unknown man viewer.", name); +} + +static void show_man_page(const char *git_cmd) +{ + struct man_viewer_list *viewer; + const char *page = cmd_to_page(git_cmd); + + setup_man_path(); + for (viewer = man_viewer_list; viewer; viewer = viewer->next) + { + exec_viewer(viewer->name, page); /* will return when unable */ + } + exec_viewer("man", page); + die("no man viewer handled the request"); +} + +static void show_info_page(const char *git_cmd) +{ + const char *page = cmd_to_page(git_cmd); + setenv("INFOPATH", GIT_INFO_PATH, 1); + execlp("info", "info", "gitman", page, NULL); +} + +static void get_html_page_path(struct strbuf *page_path, const char *page) +{ + struct stat st; + const char *html_path = system_path(GIT_HTML_PATH); + + /* Check that we have a git documentation directory. */ + if (stat(mkpath("%s/git.html", html_path), &st) + || !S_ISREG(st.st_mode)) + die("'%s': not a documentation directory.", html_path); + + strbuf_init(page_path, 0); + strbuf_addf(page_path, "%s/%s.html", html_path, page); +} + +/* + * If open_html is not defined in a platform-specific way (see for + * example compat/mingw.h), we use the script web--browse to display + * HTML. + */ +#ifndef open_html +void open_html(const char *path) +{ + execl_git_cmd("web--browse", "-c", "help.browser", path, NULL); +} +#endif + +static void show_html_page(const char *git_cmd) +{ + const char *page = cmd_to_page(git_cmd); + struct strbuf page_path; /* it leaks but we exec bellow */ + + get_html_page_path(&page_path, page); + + open_html(page_path.buf); +} + +int cmd_help(int argc, const char **argv, const char *prefix) +{ + int nongit; + const char *alias; + load_command_list("git-", &main_cmds, &other_cmds); + + setup_git_directory_gently(&nongit); + git_config(git_help_config, NULL); + + argc = parse_options(argc, argv, builtin_help_options, + builtin_help_usage, 0); + + if (show_all) { + printf("usage: %s\n\n", git_usage_string); + list_commands("git commands", &main_cmds, &other_cmds); + printf("%s\n", git_more_info_string); + return 0; + } + + if (!argv[0]) { + printf("usage: %s\n\n", git_usage_string); + list_common_cmds_help(); + printf("\n%s\n", git_more_info_string); + return 0; + } + + alias = alias_lookup(argv[0]); + if (alias && !is_git_command(argv[0])) { + printf("`git %s' is aliased to `%s'\n", argv[0], alias); + return 0; + } + + switch (help_format) { + case HELP_FORMAT_MAN: + show_man_page(argv[0]); + break; + case HELP_FORMAT_INFO: + show_info_page(argv[0]); + break; + case HELP_FORMAT_WEB: + show_html_page(argv[0]); + break; + } + + return 0; +} diff --git a/builtin-log.c b/builtin-log.c index 911fd6599..1d3c5cbf5 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -217,6 +217,11 @@ static int cmd_log_walk(struct rev_info *rev) if (rev->early_output) finish_early_output(rev); + /* + * For --check and --exit-code, the exit code is based on CHECK_FAILED + * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to + * retain that state information if replacing rev->diffopt in this loop + */ while ((commit = get_revision(rev)) != NULL) { log_tree_commit(rev, commit); if (!rev->reflog_info) { @@ -227,7 +232,11 @@ static int cmd_log_walk(struct rev_info *rev) free_commit_list(commit->parents); commit->parents = NULL; } - return 0; + if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF && + DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) { + return 02; + } + return diff_result_code(&rev->diffopt, 0); } static int git_log_config(const char *var, const char *value, void *cb) @@ -923,7 +932,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (argc > 1) die ("unrecognized argument: %s", argv[1]); - if (!rev.diffopt.output_format) + if (!rev.diffopt.output_format + || rev.diffopt.output_format == DIFF_FORMAT_PATCH) rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH; if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff) diff --git a/builtin-merge-base.c b/builtin-merge-base.c index 3382b1382..b08da516e 100644 --- a/builtin-merge-base.c +++ b/builtin-merge-base.c @@ -2,9 +2,11 @@ #include "cache.h" #include "commit.h" -static int show_merge_base(struct commit *rev1, struct commit *rev2, int show_all) +static int show_merge_base(struct commit **rev, int rev_nr, int show_all) { - struct commit_list *result = get_merge_bases(rev1, rev2, 0); + struct commit_list *result; + + result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1, 0); if (!result) return 1; @@ -20,7 +22,7 @@ static int show_merge_base(struct commit *rev1, struct commit *rev2, int show_al } static const char merge_base_usage[] = -"git merge-base [--all] <commit-id> <commit-id>"; +"git merge-base [--all] <commit-id> <commit-id>..."; static struct commit *get_commit_reference(const char *arg) { @@ -38,7 +40,8 @@ static struct commit *get_commit_reference(const char *arg) int cmd_merge_base(int argc, const char **argv, const char *prefix) { - struct commit *rev1, *rev2; + struct commit **rev; + int rev_nr = 0; int show_all = 0; git_config(git_default_config, NULL); @@ -51,10 +54,15 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix) usage(merge_base_usage); argc--; argv++; } - if (argc != 3) + if (argc < 3) usage(merge_base_usage); - rev1 = get_commit_reference(argv[1]); - rev2 = get_commit_reference(argv[2]); - return show_merge_base(rev1, rev2, show_all); + rev = xmalloc((argc - 1) * sizeof(*rev)); + + do { + rev[rev_nr++] = get_commit_reference(argv[1]); + argc--; argv++; + } while (argc > 1); + + return show_merge_base(rev, rev_nr, show_all); } diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c index 43e55bf90..dfb363ee2 100644 --- a/builtin-merge-recursive.c +++ b/builtin-merge-recursive.c @@ -729,13 +729,13 @@ static void conflict_rename_rename(struct rename *ren1, const char *dst_name2 = ren2_dst; if (string_list_has_string(¤t_directory_set, ren1_dst)) { dst_name1 = del[delp++] = unique_path(ren1_dst, branch1); - output(1, "%s is a directory in %s added as %s instead", + output(1, "%s is a directory in %s adding as %s instead", ren1_dst, branch2, dst_name1); remove_file(0, ren1_dst, 0); } if (string_list_has_string(¤t_directory_set, ren2_dst)) { dst_name2 = del[delp++] = unique_path(ren2_dst, branch2); - output(1, "%s is a directory in %s added as %s instead", + output(1, "%s is a directory in %s adding as %s instead", ren2_dst, branch1, dst_name2); remove_file(0, ren2_dst, 0); } @@ -760,7 +760,7 @@ static void conflict_rename_dir(struct rename *ren1, const char *branch1) { char *new_path = unique_path(ren1->pair->two->path, branch1); - output(1, "Renamed %s to %s instead", ren1->pair->one->path, new_path); + output(1, "Renaming %s to %s instead", ren1->pair->one->path, new_path); remove_file(0, ren1->pair->two->path, 0); update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path); free(new_path); @@ -773,7 +773,7 @@ static void conflict_rename_rename_2(struct rename *ren1, { char *new_path1 = unique_path(ren1->pair->two->path, branch1); char *new_path2 = unique_path(ren2->pair->two->path, branch2); - output(1, "Renamed %s to %s and %s to %s instead", + output(1, "Renaming %s to %s and %s to %s instead", ren1->pair->one->path, new_path1, ren2->pair->one->path, new_path2); remove_file(0, ren1->pair->two->path, 0); @@ -887,10 +887,10 @@ static int process_renames(struct string_list *a_renames, branch1, branch2); if (mfi.merge || !mfi.clean) - output(1, "Renamed %s->%s", src, ren1_dst); + output(1, "Renaming %s->%s", src, ren1_dst); if (mfi.merge) - output(2, "Auto-merged %s", ren1_dst); + output(2, "Auto-merging %s", ren1_dst); if (!mfi.clean) { output(1, "CONFLICT (content): merge conflict in %s", @@ -924,14 +924,14 @@ static int process_renames(struct string_list *a_renames, if (string_list_has_string(¤t_directory_set, ren1_dst)) { clean_merge = 0; - output(1, "CONFLICT (rename/directory): Renamed %s->%s in %s " + output(1, "CONFLICT (rename/directory): Rename %s->%s in %s " " directory %s added in %s", ren1_src, ren1_dst, branch1, ren1_dst, branch2); conflict_rename_dir(ren1, branch1); } else if (sha_eq(src_other.sha1, null_sha1)) { clean_merge = 0; - output(1, "CONFLICT (rename/delete): Renamed %s->%s in %s " + output(1, "CONFLICT (rename/delete): Rename %s->%s in %s " "and deleted in %s", ren1_src, ren1_dst, branch1, branch2); @@ -940,19 +940,19 @@ static int process_renames(struct string_list *a_renames, const char *new_path; clean_merge = 0; try_merge = 1; - output(1, "CONFLICT (rename/add): Renamed %s->%s in %s. " + output(1, "CONFLICT (rename/add): Rename %s->%s in %s. " "%s added in %s", ren1_src, ren1_dst, branch1, ren1_dst, branch2); new_path = unique_path(ren1_dst, branch2); - output(1, "Added as %s instead", new_path); + output(1, "Adding as %s instead", new_path); update_file(0, dst_other.sha1, dst_other.mode, new_path); } else if ((item = string_list_lookup(ren1_dst, renames2Dst))) { ren2 = item->util; clean_merge = 0; ren2->processed = 1; - output(1, "CONFLICT (rename/rename): Renamed %s->%s in %s. " - "Renamed %s->%s in %s", + output(1, "CONFLICT (rename/rename): Rename %s->%s in %s. " + "Rename %s->%s in %s", ren1_src, ren1_dst, branch1, ren2->pair->one->path, ren2->pair->two->path, branch2); conflict_rename_rename_2(ren1, branch1, ren2, branch2); @@ -986,9 +986,9 @@ static int process_renames(struct string_list *a_renames, output(3, "Skipped %s (merged same as existing)", ren1_dst); else { if (mfi.merge || !mfi.clean) - output(1, "Renamed %s => %s", ren1_src, ren1_dst); + output(1, "Renaming %s => %s", ren1_src, ren1_dst); if (mfi.merge) - output(2, "Auto-merged %s", ren1_dst); + output(2, "Auto-merging %s", ren1_dst); if (!mfi.clean) { output(1, "CONFLICT (rename/modify): Merge conflict in %s", ren1_dst); @@ -1039,7 +1039,7 @@ static int process_entry(const char *path, struct stage_data *entry, /* Deleted in both or deleted in one and * unchanged in the other */ if (a_sha) - output(2, "Removed %s", path); + output(2, "Removing %s", path); /* do not touch working file if it did not exist */ remove_file(1, path, !a_sha); } else { @@ -1086,12 +1086,12 @@ static int process_entry(const char *path, struct stage_data *entry, const char *new_path = unique_path(path, add_branch); clean_merge = 0; output(1, "CONFLICT (%s): There is a directory with name %s in %s. " - "Added %s as %s", + "Adding %s as %s", conf, path, other_branch, path, new_path); remove_file(0, path, 0); update_file(0, sha, mode, new_path); } else { - output(2, "Added %s", path); + output(2, "Adding %s", path); update_file(1, sha, mode, path); } } else if (a_sha && b_sha) { @@ -1105,7 +1105,7 @@ static int process_entry(const char *path, struct stage_data *entry, reason = "add/add"; o_sha = (unsigned char *)null_sha1; } - output(2, "Auto-merged %s", path); + output(2, "Auto-merging %s", path); o.path = a.path = b.path = (char *)path; hashcpy(o.sha1, o_sha); o.mode = o_mode; diff --git a/builtin-merge.c b/builtin-merge.c index b280444e1..9ad979106 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -22,6 +22,7 @@ #include "log-tree.h" #include "color.h" #include "rerere.h" +#include "help.h" #define DEFAULT_TWOHEAD (1<<0) #define DEFAULT_OCTOPUS (1<<1) @@ -77,7 +78,9 @@ static int option_parse_message(const struct option *opt, static struct strategy *get_strategy(const char *name) { int i; - struct strbuf err; + struct strategy *ret; + static struct cmdnames main_cmds, other_cmds; + static int loaded; if (!name) return NULL; @@ -86,12 +89,43 @@ static struct strategy *get_strategy(const char *name) if (!strcmp(name, all_strategy[i].name)) return &all_strategy[i]; - strbuf_init(&err, 0); - for (i = 0; i < ARRAY_SIZE(all_strategy); i++) - strbuf_addf(&err, " %s", all_strategy[i].name); - fprintf(stderr, "Could not find merge strategy '%s'.\n", name); - fprintf(stderr, "Available strategies are:%s.\n", err.buf); - exit(1); + if (!loaded) { + struct cmdnames not_strategies; + loaded = 1; + + memset(¬_strategies, 0, sizeof(struct cmdnames)); + load_command_list("git-merge-", &main_cmds, &other_cmds); + for (i = 0; i < main_cmds.cnt; i++) { + int j, found = 0; + struct cmdname *ent = main_cmds.names[i]; + for (j = 0; j < ARRAY_SIZE(all_strategy); j++) + if (!strncmp(ent->name, all_strategy[j].name, ent->len) + && !all_strategy[j].name[ent->len]) + found = 1; + if (!found) + add_cmdname(¬_strategies, ent->name, ent->len); + exclude_cmds(&main_cmds, ¬_strategies); + } + } + if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) { + fprintf(stderr, "Could not find merge strategy '%s'.\n", name); + fprintf(stderr, "Available strategies are:"); + for (i = 0; i < main_cmds.cnt; i++) + fprintf(stderr, " %s", main_cmds.names[i]->name); + fprintf(stderr, ".\n"); + if (other_cmds.cnt) { + fprintf(stderr, "Available custom strategies are:"); + for (i = 0; i < other_cmds.cnt; i++) + fprintf(stderr, " %s", other_cmds.names[i]->name); + fprintf(stderr, ".\n"); + } + exit(1); + } + + ret = xmalloc(sizeof(struct strategy)); + memset(ret, 0, sizeof(struct strategy)); + ret->name = xstrdup(name); + return ret; } static void append_strategy(struct strategy *s) @@ -834,6 +868,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (argc != 1) die("Can merge only exactly one commit into " "empty head"); + if (squash) + die("Squash commit into empty head not supported yet"); + if (!allow_fast_forward) + die("Non-fast-forward commit does not make sense into " + "an empty head"); remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT); if (!remote_head) die("%s - not something we can merge", argv[0]); diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index d394c494a..ef3befe57 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1095,9 +1095,12 @@ static void check_object(struct object_entry *entry) } entry->type = sha1_object_info(entry->idx.sha1, &entry->size); - if (entry->type < 0) - die("unable to get type of object %s", - sha1_to_hex(entry->idx.sha1)); + /* + * The error condition is checked in prepare_pack(). This is + * to permit a missing preferred base object to be ignored + * as a preferred base. Doing so can result in a larger + * pack file, but the transfer will still take place. + */ } static int pack_offset_sort(const void *_a, const void *_b) @@ -1721,8 +1724,12 @@ static void prepare_pack(int window, int depth) if (entry->no_try_delta) continue; - if (!entry->preferred_base) + if (!entry->preferred_base) { nr_deltas++; + if (entry->type < 0) + die("unable to get type of object %s", + sha1_to_hex(entry->idx.sha1)); + } delta_list[n++] = entry; } diff --git a/builtin-push.c b/builtin-push.c index c1ed68d93..cc6666f75 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -59,8 +59,17 @@ static int do_push(const char *repo, int flags) if (remote->mirror) flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE); - if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) && refspec) - return -1; + if ((flags & TRANSPORT_PUSH_ALL) && refspec) { + if (!strcmp(*refspec, "refs/tags/*")) + return error("--all and --tags are incompatible"); + return error("--all can't be combined with refspecs"); + } + + if ((flags & TRANSPORT_PUSH_MIRROR) && refspec) { + if (!strcmp(*refspec, "refs/tags/*")) + return error("--mirror and --tags are incompatible"); + return error("--mirror can't be combined with refspecs"); + } if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) == (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) { diff --git a/builtin-reflog.c b/builtin-reflog.c index 196fa03b7..6b3667ef0 100644 --- a/builtin-reflog.c +++ b/builtin-reflog.c @@ -540,11 +540,11 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) free(collected.e); } - while (i < argc) { - const char *ref = argv[i++]; + for (; i < argc; i++) { + char *ref; unsigned char sha1[20]; - if (!resolve_ref(ref, sha1, 1, NULL)) { - status |= error("%s points nowhere!", ref); + if (!dwim_log(argv[i], strlen(argv[i]), sha1, &ref)) { + status |= error("%s points nowhere!", argv[i]); continue; } set_reflog_expiry_param(&cb, explicit_expiry, ref); diff --git a/builtin-revert.c b/builtin-revert.c index 27881e949..36677053f 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -11,6 +11,7 @@ #include "cache-tree.h" #include "diff.h" #include "revision.h" +#include "rerere.h" /* * This implements the builtins revert and cherry-pick. @@ -395,6 +396,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) die ("Error wrapping up %s", defmsg); fprintf(stderr, "Automatic %s failed.%s\n", me, help_msg(commit->object.sha1)); + rerere(); exit(1); } if (commit_lock_file(&msg_file) < 0) diff --git a/builtin-update-index.c b/builtin-update-index.c index 38eb53ccb..434cb8e4a 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -194,6 +194,10 @@ static int process_path(const char *path) int len; struct stat st; + len = strlen(path); + if (has_symlink_leading_path(len, path)) + return error("'%s' is beyond a symbolic link", path); + /* * First things first: get the stat information, to decide * what to do about the pathname! @@ -201,7 +205,6 @@ static int process_path(const char *path) if (lstat(path, &st) < 0) return process_lstat_error(path, errno); - len = strlen(path); if (S_ISDIR(st.st_mode)) return process_directory(path, len, &st); diff --git a/builtin-verify-pack.c b/builtin-verify-pack.c index f4ac59569..25a29f11a 100644 --- a/builtin-verify-pack.c +++ b/builtin-verify-pack.c @@ -1,7 +1,7 @@ #include "builtin.h" #include "cache.h" #include "pack.h" - +#include "pack-revindex.h" #define MAX_CHAIN 50 @@ -129,6 +129,7 @@ int cmd_verify_pack(int argc, const char **argv, const char *prefix) else { if (verify_one_pack(argv[1], verbose)) err = 1; + discard_revindex(); nothing_done = 0; } argc--; argv++; @@ -126,6 +126,7 @@ struct cache_entry { #define CE_NAMEMASK (0x0fff) #define CE_STAGEMASK (0x3000) +#define CE_EXTENDED (0x4000) #define CE_VALID (0x8000) #define CE_STAGESHIFT 12 @@ -378,6 +379,7 @@ extern int remove_file_from_index(struct index_state *, const char *path); #define ADD_CACHE_VERBOSE 1 #define ADD_CACHE_PRETEND 2 #define ADD_CACHE_IGNORE_ERRORS 4 +#define ADD_CACHE_IGNORE_REMOVAL 8 extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags); extern int add_file_to_index(struct index_state *, const char *path, int flags); extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh); @@ -392,7 +394,6 @@ extern int ie_modified(const struct index_state *, struct cache_entry *, struct 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); extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object); extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); @@ -452,6 +453,7 @@ enum safe_crlf { extern enum safe_crlf safe_crlf; enum branch_track { + BRANCH_TRACK_UNSPECIFIED = -1, BRANCH_TRACK_NEVER = 0, BRANCH_TRACK_REMOTE, BRANCH_TRACK_ALWAYS, diff --git a/combine-diff.c b/combine-diff.c index 4dfc33086..0cf2a830b 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -143,8 +143,6 @@ static void append_lost(struct sline *sline, int n, const char *line, int len) } struct combine_diff_state { - struct xdiff_emit_state xm; - unsigned int lno; int ob, on, nb, nn; unsigned long nmask; @@ -217,17 +215,15 @@ static void combine_diff(const unsigned char *parent, mmfile_t *result_file, parent_file.size = sz; xpp.flags = XDF_NEED_MINIMAL; memset(&xecfg, 0, sizeof(xecfg)); - ecb.outf = xdiff_outf; - ecb.priv = &state; memset(&state, 0, sizeof(state)); - state.xm.consume = consume_line; state.nmask = nmask; state.sline = sline; state.lno = 1; state.num_parent = num_parent; state.n = n; - xdi_diff(&parent_file, result_file, &xpp, &xecfg, &ecb); + xdi_diff_outf(&parent_file, result_file, consume_line, &state, + &xpp, &xecfg, &ecb); free(parent_file.ptr); /* Assign line numbers for this parent. @@ -122,6 +122,7 @@ int read_graft_file(const char *graft_file); struct commit_graft *lookup_commit_graft(const unsigned char *sha1); extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup); +extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup); extern struct commit_list *get_octopus_merge_bases(struct commit_list *in); extern int register_shallow(const unsigned char *sha1); diff --git a/compat/mingw.c b/compat/mingw.c index 772cad510..ccfa2a0a3 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -31,11 +31,6 @@ static inline time_t filetime_to_time_t(const FILETIME *ft) return (time_t)winTime; } -static inline size_t size_to_blocks(size_t s) -{ - return (s+511)/512; -} - extern int _getdrive( void ); /* We keep the do_lstat code in a separate function to avoid recursion. * When a path ends with a slash, the stat will fail with ENOENT. In @@ -57,10 +52,10 @@ static int do_lstat(const char *file_name, struct stat *buf) buf->st_ino = 0; buf->st_gid = 0; buf->st_uid = 0; + buf->st_nlink = 1; buf->st_mode = fMode; buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */ - buf->st_blocks = size_to_blocks(buf->st_size); - buf->st_dev = _getdrive() - 1; + buf->st_dev = buf->st_rdev = (_getdrive() - 1); buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); @@ -94,7 +89,7 @@ static int do_lstat(const char *file_name, struct stat *buf) * complete. Note that Git stat()s are redirected to mingw_lstat() * too, since Windows doesn't really handle symlinks that well. */ -int mingw_lstat(const char *file_name, struct mingw_stat *buf) +int mingw_lstat(const char *file_name, struct stat *buf) { int namelen; static char alt_name[PATH_MAX]; @@ -122,8 +117,7 @@ int mingw_lstat(const char *file_name, struct mingw_stat *buf) } #undef fstat -#undef stat -int mingw_fstat(int fd, struct mingw_stat *buf) +int mingw_fstat(int fd, struct stat *buf) { HANDLE fh = (HANDLE)_get_osfhandle(fd); BY_HANDLE_FILE_INFORMATION fdata; @@ -133,22 +127,8 @@ int mingw_fstat(int fd, struct mingw_stat *buf) return -1; } /* direct non-file handles to MS's fstat() */ - if (GetFileType(fh) != FILE_TYPE_DISK) { - struct stat st; - if (fstat(fd, &st)) - return -1; - buf->st_ino = st.st_ino; - buf->st_gid = st.st_gid; - buf->st_uid = st.st_uid; - buf->st_mode = st.st_mode; - buf->st_size = st.st_size; - buf->st_blocks = size_to_blocks(buf->st_size); - buf->st_dev = st.st_dev; - buf->st_atime = st.st_atime; - buf->st_mtime = st.st_mtime; - buf->st_ctime = st.st_ctime; - return 0; - } + if (GetFileType(fh) != FILE_TYPE_DISK) + return fstat(fd, buf); if (GetFileInformationByHandle(fh, &fdata)) { int fMode = S_IREAD; @@ -162,10 +142,10 @@ int mingw_fstat(int fd, struct mingw_stat *buf) buf->st_ino = 0; buf->st_gid = 0; buf->st_uid = 0; + buf->st_nlink = 1; buf->st_mode = fMode; buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */ - buf->st_blocks = size_to_blocks(buf->st_size); - buf->st_dev = _getdrive() - 1; + buf->st_dev = buf->st_rdev = (_getdrive() - 1); buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); diff --git a/compat/mingw.h b/compat/mingw.h index a52e657c5..4f275cb8e 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -162,22 +162,12 @@ int mingw_rename(const char*, const char*); /* Use mingw_lstat() instead of lstat()/stat() and * mingw_fstat() instead of fstat() on Windows. - * struct stat is redefined because it lacks the st_blocks member. */ -struct mingw_stat { - unsigned st_mode; - time_t st_mtime, st_atime, st_ctime; - unsigned st_dev, st_ino, st_uid, st_gid; - size_t st_size; - size_t st_blocks; -}; -int mingw_lstat(const char *file_name, struct mingw_stat *buf); -int mingw_fstat(int fd, struct mingw_stat *buf); +int mingw_lstat(const char *file_name, struct stat *buf); +int mingw_fstat(int fd, struct stat *buf); #define fstat mingw_fstat #define lstat mingw_lstat -#define stat mingw_stat -static inline int mingw_stat(const char *file_name, struct mingw_stat *buf) -{ return mingw_lstat(file_name, buf); } +#define stat(x,y) mingw_lstat(x,y) int mingw_utime(const char *file_name, const struct utimbuf *times); #define utime mingw_utime diff --git a/config.mak.in b/config.mak.in index b77614953..f67d673d0 100644 --- a/config.mak.in +++ b/config.mak.in @@ -3,6 +3,7 @@ CC = @CC@ CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ AR = @AR@ TAR = @TAR@ #INSTALL = @INSTALL@ # needs install-sh or install.sh in sources diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 89858c237..4f64f8ab7 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1483,7 +1483,7 @@ _git_submodule () { __git_has_doubledash && return - local subcommands="add status init update" + local subcommands="add status init update summary foreach sync" if [ -z "$(__git_find_subcommand "$subcommands")" ]; then local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 46136d49b..2216cacba 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -708,6 +708,7 @@ class P4Submit(Command): newdiff = newdiff.replace("\n", "\r\n") tmpFile.write(submitTemplate + separatorLine + diff + newdiff) tmpFile.close() + mtime = os.stat(fileName).st_mtime defaultEditor = "vi" if platform.system() == "Windows": defaultEditor = "notepad" @@ -716,15 +717,29 @@ class P4Submit(Command): else: editor = os.environ.get("EDITOR", defaultEditor); system(editor + " " + fileName) - tmpFile = open(fileName, "rb") - message = tmpFile.read() - tmpFile.close() - os.remove(fileName) - submitTemplate = message[:message.index(separatorLine)] - if self.isWindows: - submitTemplate = submitTemplate.replace("\r\n", "\n") - p4_write_pipe("submit -i", submitTemplate) + response = "y" + if os.stat(fileName).st_mtime <= mtime: + response = "x" + while response != "y" and response != "n": + response = raw_input("Submit template unchanged. Submit anyway? [y]es, [n]o (skip this patch) ") + + if response == "y": + tmpFile = open(fileName, "rb") + message = tmpFile.read() + tmpFile.close() + submitTemplate = message[:message.index(separatorLine)] + if self.isWindows: + submitTemplate = submitTemplate.replace("\r\n", "\n") + p4_write_pipe("submit -i", submitTemplate) + else: + for f in editedFiles: + p4_system("revert \"%s\"" % f); + for f in filesToAdd: + p4_system("revert \"%s\"" % f); + system("rm %s" %f) + + os.remove(fileName) else: fileName = "submit.txt" file = open(fileName, "w+") @@ -1733,8 +1748,12 @@ class P4Clone(P4Sync): if not P4Sync.run(self, depotPaths): return False if self.branch != "master": - if gitBranchExists("refs/remotes/p4/master"): - system("git branch master refs/remotes/p4/master") + if self.importIntoRemotes: + masterbranch = "refs/remotes/p4/master" + else: + masterbranch = "refs/heads/p4/master" + if gitBranchExists(masterbranch): + system("git branch master %s" % masterbranch) system("git checkout -f") else: print "Could not detect main branch. No checkout/master branch created." @@ -9,18 +9,20 @@ #undef SS #undef AA #undef DD +#undef GS #define SS GIT_SPACE #define AA GIT_ALPHA #define DD GIT_DIGIT +#define GS GIT_SPECIAL /* \0, *, ?, [, \\ */ unsigned char sane_ctype[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, SS, SS, 0, 0, SS, 0, 0, /* 0-15 */ + GS, 0, 0, 0, 0, 0, 0, 0, 0, SS, SS, 0, 0, SS, 0, 0, /* 0-15 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-15 */ - SS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32-15 */ - DD, DD, DD, DD, DD, DD, DD, DD, DD, DD, 0, 0, 0, 0, 0, 0, /* 48-15 */ + SS, 0, 0, 0, 0, 0, 0, 0, 0, 0, GS, 0, 0, 0, 0, 0, /* 32-15 */ + DD, DD, DD, DD, DD, DD, DD, DD, DD, DD, 0, 0, 0, 0, 0, GS, /* 48-15 */ 0, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, /* 64-15 */ - AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, 0, 0, 0, 0, 0, /* 80-15 */ + AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, GS, GS, 0, 0, 0, /* 80-15 */ 0, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, /* 96-15 */ AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, 0, 0, 0, 0, 0, /* 112-15 */ /* Nothing in the 128.. range */ @@ -16,12 +16,11 @@ static int log_syslog; static int verbose; static int reuseaddr; -static int child_handler_pipe[2]; static const char daemon_usage[] = "git daemon [--verbose] [--syslog] [--export-all]\n" -" [--timeout=n] [--init-timeout=n] [--strict-paths]\n" -" [--base-path=path] [--base-path-relaxed]\n" +" [--timeout=n] [--init-timeout=n] [--max-connections=n]\n" +" [--strict-paths] [--base-path=path] [--base-path-relaxed]\n" " [--user-path | --user-path=path]\n" " [--interpolated-path=path]\n" " [--reuseaddr] [--detach] [--pid-file=file]\n" @@ -78,38 +77,19 @@ static struct interp interp_table[] = { static void logreport(int priority, const char *err, va_list params) { - /* We should do a single write so that it is atomic and output - * of several processes do not get intermingled. */ - char buf[1024]; - int buflen; - int maxlen, msglen; - - /* sizeof(buf) should be big enough for "[pid] \n" */ - buflen = snprintf(buf, sizeof(buf), "[%ld] ", (long) getpid()); - - maxlen = sizeof(buf) - buflen - 1; /* -1 for our own LF */ - msglen = vsnprintf(buf + buflen, maxlen, err, params); - if (log_syslog) { + char buf[1024]; + vsnprintf(buf, sizeof(buf), err, params); syslog(priority, "%s", buf); - return; + } else { + /* + * Since stderr is set to linebuffered mode, the + * logging of different processes will not overlap + */ + fprintf(stderr, "[%"PRIuMAX"] ", (uintmax_t)getpid()); + vfprintf(stderr, err, params); + fputc('\n', stderr); } - - /* maxlen counted our own LF but also counts space given to - * vsnprintf for the terminating NUL. We want to make sure that - * we have space for our own LF and NUL after the "meat" of the - * message, so truncate it at maxlen - 1. - */ - if (msglen > maxlen - 1) - msglen = maxlen - 1; - else if (msglen < 0) - msglen = 0; /* Protect against weird return values. */ - buflen += msglen; - - buf[buflen++] = '\n'; - buf[buflen] = '\0'; - - write_in_full(2, buf, buflen); } static void logerror(const char *err, ...) @@ -604,169 +584,107 @@ static int execute(struct sockaddr *addr) return -1; } +static int max_connections = 32; -/* - * We count spawned/reaped separately, just to avoid any - * races when updating them from signals. The SIGCHLD handler - * will only update children_reaped, and the fork logic will - * only update children_spawned. - * - * MAX_CHILDREN should be a power-of-two to make the modulus - * operation cheap. It should also be at least twice - * the maximum number of connections we will ever allow. - */ -#define MAX_CHILDREN 128 - -static int max_connections = 25; - -/* These are updated by the signal handler */ -static volatile unsigned int children_reaped; -static pid_t dead_child[MAX_CHILDREN]; - -/* These are updated by the main loop */ -static unsigned int children_spawned; -static unsigned int children_deleted; +static unsigned int live_children; static struct child { + struct child *next; pid_t pid; - int addrlen; struct sockaddr_storage address; -} live_child[MAX_CHILDREN]; +} *firstborn; -static void add_child(int idx, pid_t pid, struct sockaddr *addr, int addrlen) +static void add_child(pid_t pid, struct sockaddr *addr, int addrlen) { - live_child[idx].pid = pid; - live_child[idx].addrlen = addrlen; - memcpy(&live_child[idx].address, addr, addrlen); + struct child *newborn, **cradle; + + /* + * This must be xcalloc() -- we'll compare the whole sockaddr_storage + * but individual address may be shorter. + */ + newborn = xcalloc(1, sizeof(*newborn)); + live_children++; + newborn->pid = pid; + memcpy(&newborn->address, addr, addrlen); + for (cradle = &firstborn; *cradle; cradle = &(*cradle)->next) + if (!memcmp(&(*cradle)->address, &newborn->address, + sizeof(newborn->address))) + break; + newborn->next = *cradle; + *cradle = newborn; } -/* - * Walk from "deleted" to "spawned", and remove child "pid". - * - * We move everything up by one, since the new "deleted" will - * be one higher. - */ -static void remove_child(pid_t pid, unsigned deleted, unsigned spawned) +static void remove_child(pid_t pid) { - struct child n; + struct child **cradle, *blanket; - deleted %= MAX_CHILDREN; - spawned %= MAX_CHILDREN; - if (live_child[deleted].pid == pid) { - live_child[deleted].pid = -1; - return; - } - n = live_child[deleted]; - for (;;) { - struct child m; - deleted = (deleted + 1) % MAX_CHILDREN; - if (deleted == spawned) - die("could not find dead child %d\n", pid); - m = live_child[deleted]; - live_child[deleted] = n; - if (m.pid == pid) - return; - n = m; - } + for (cradle = &firstborn; (blanket = *cradle); cradle = &blanket->next) + if (blanket->pid == pid) { + *cradle = blanket->next; + live_children--; + free(blanket); + break; + } } /* * This gets called if the number of connections grows * past "max_connections". * - * We _should_ start off by searching for connections - * from the same IP, and if there is some address wth - * multiple connections, we should kill that first. - * - * As it is, we just "randomly" kill 25% of the connections, - * and our pseudo-random generator sucks too. I have no - * shame. - * - * Really, this is just a place-holder for a _real_ algorithm. + * We kill the newest connection from a duplicate IP. */ -static void kill_some_children(int signo, unsigned start, unsigned stop) +static void kill_some_child(void) { - start %= MAX_CHILDREN; - stop %= MAX_CHILDREN; - while (start != stop) { - if (!(start & 3)) - kill(live_child[start].pid, signo); - start = (start + 1) % MAX_CHILDREN; - } -} - -static void check_dead_children(void) -{ - unsigned spawned, reaped, deleted; - - spawned = children_spawned; - reaped = children_reaped; - deleted = children_deleted; + const struct child *blanket, *next; - while (deleted < reaped) { - pid_t pid = dead_child[deleted % MAX_CHILDREN]; - const char *dead = pid < 0 ? " (with error)" : ""; - - if (pid < 0) - pid = -pid; + if (!(blanket = firstborn)) + return; - /* XXX: Custom logging, since we don't wanna getpid() */ - if (verbose) { - if (log_syslog) - syslog(LOG_INFO, "[%d] Disconnected%s", - pid, dead); - else - fprintf(stderr, "[%d] Disconnected%s\n", - pid, dead); + for (; (next = blanket->next); blanket = next) + if (!memcmp(&blanket->address, &next->address, + sizeof(next->address))) { + kill(blanket->pid, SIGTERM); + break; } - remove_child(pid, deleted, spawned); - deleted++; - } - children_deleted = deleted; } -static void check_max_connections(void) +static void check_dead_children(void) { - for (;;) { - int active; - unsigned spawned, deleted; - - check_dead_children(); - - spawned = children_spawned; - deleted = children_deleted; - - active = spawned - deleted; - if (active <= max_connections) - break; - - /* Kill some unstarted connections with SIGTERM */ - kill_some_children(SIGTERM, deleted, spawned); - if (active <= max_connections << 1) - break; + int status; + pid_t pid; - /* If the SIGTERM thing isn't helping use SIGKILL */ - kill_some_children(SIGKILL, deleted, spawned); - sleep(1); + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + const char *dead = ""; + remove_child(pid); + if (!WIFEXITED(status) || (WEXITSTATUS(status) > 0)) + dead = " (with error)"; + loginfo("[%"PRIuMAX"] Disconnected%s", (uintmax_t)pid, dead); } } static void handle(int incoming, struct sockaddr *addr, int addrlen) { - pid_t pid = fork(); + pid_t pid; - if (pid) { - unsigned idx; + if (max_connections && live_children >= max_connections) { + kill_some_child(); + sleep(1); /* give it some time to die */ + check_dead_children(); + if (live_children >= max_connections) { + close(incoming); + logerror("Too many children, dropping connection"); + return; + } + } + if ((pid = fork())) { close(incoming); - if (pid < 0) + if (pid < 0) { + logerror("Couldn't fork %s", strerror(errno)); return; + } - idx = children_spawned % MAX_CHILDREN; - children_spawned++; - add_child(idx, pid, addr, addrlen); - - check_max_connections(); + add_child(pid, addr, addrlen); return; } @@ -779,21 +697,11 @@ static void handle(int incoming, struct sockaddr *addr, int addrlen) static void child_handler(int signo) { - for (;;) { - int status; - pid_t pid = waitpid(-1, &status, WNOHANG); - - if (pid > 0) { - unsigned reaped = children_reaped; - if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) - pid = -pid; - dead_child[reaped % MAX_CHILDREN] = pid; - children_reaped = reaped + 1; - write(child_handler_pipe[1], &status, 1); - continue; - } - break; - } + /* + * Otherwise empty handler because systemcalls will get interrupted + * upon signal receipt + * SysV needs the handler to be rearmed + */ signal(SIGCHLD, child_handler); } @@ -836,7 +744,7 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p) if (sockfd < 0) continue; if (sockfd >= FD_SETSIZE) { - error("too large socket descriptor."); + logerror("Socket descriptor too large"); close(sockfd); continue; } @@ -936,35 +844,28 @@ static int service_loop(int socknum, int *socklist) struct pollfd *pfd; int i; - if (pipe(child_handler_pipe) < 0) - die ("Could not set up pipe for child handler"); - - pfd = xcalloc(socknum + 1, sizeof(struct pollfd)); + pfd = xcalloc(socknum, sizeof(struct pollfd)); for (i = 0; i < socknum; i++) { pfd[i].fd = socklist[i]; pfd[i].events = POLLIN; } - pfd[socknum].fd = child_handler_pipe[0]; - pfd[socknum].events = POLLIN; signal(SIGCHLD, child_handler); for (;;) { int i; - if (poll(pfd, socknum + 1, -1) < 0) { + check_dead_children(); + + if (poll(pfd, socknum, -1) < 0) { if (errno != EINTR) { - error("poll failed, resuming: %s", + logerror("Poll failed, resuming: %s", strerror(errno)); sleep(1); } continue; } - if (pfd[socknum].revents & POLLIN) { - read(child_handler_pipe[0], &i, 1); - check_dead_children(); - } for (i = 0; i < socknum; i++) { if (pfd[i].revents & POLLIN) { @@ -1022,7 +923,7 @@ static void store_pid(const char *path) FILE *f = fopen(path, "w"); if (!f) die("cannot open pid file %s: %s", path, strerror(errno)); - if (fprintf(f, "%d\n", getpid()) < 0 || fclose(f) != 0) + if (fprintf(f, "%"PRIuMAX"\n", (uintmax_t) getpid()) < 0 || fclose(f) != 0) die("failed to write pid file %s: %s", path, strerror(errno)); } @@ -1055,11 +956,6 @@ int main(int argc, char **argv) gid_t gid = 0; int i; - /* Without this we cannot rely on waitpid() to tell - * what happened to our children. - */ - signal(SIGCHLD, SIG_DFL); - for (i = 1; i < argc; i++) { char *arg = argv[i]; @@ -1105,6 +1001,12 @@ int main(int argc, char **argv) init_timeout = atoi(arg+15); continue; } + if (!prefixcmp(arg, "--max-connections=")) { + max_connections = atoi(arg+18); + if (max_connections < 0) + max_connections = 0; /* unlimited */ + continue; + } if (!strcmp(arg, "--strict-paths")) { strict_paths = 1; continue; @@ -1178,9 +1080,10 @@ int main(int argc, char **argv) } if (log_syslog) { - openlog("git-daemon", 0, LOG_DAEMON); + openlog("git-daemon", LOG_PID, LOG_DAEMON); set_die_routine(daemon_die); - } + } else + setlinebuf(stderr); /* avoid splitting a message in the middle */ if (inetd_mode && (group_name || user_name)) die("--user and --group are incompatible with --inetd"); @@ -1233,8 +1136,10 @@ int main(int argc, char **argv) return execute(peer); } - if (detach) + if (detach) { daemonize(); + loginfo("Ready to rumble"); + } else sanitize_stdfds(); @@ -20,6 +20,7 @@ static int diff_detect_rename_default; static int diff_rename_limit_default = 200; +static int diff_suppress_blank_empty; int diff_use_color_default = -1; static const char *external_diff_cmd_cfg; int diff_auto_refresh_index = 1; @@ -176,6 +177,12 @@ int git_diff_basic_config(const char *var, const char *value, void *cb) return 0; } + /* like GNU diff's --suppress-blank-empty option */ + if (!strcmp(var, "diff.suppress-blank-empty")) { + diff_suppress_blank_empty = git_config_bool(var, value); + return 0; + } + if (!prefixcmp(var, "diff.")) { const char *ep = strrchr(var, '.'); if (ep != var + 4) { @@ -369,7 +376,6 @@ static void diff_words_append(char *line, unsigned long len, } struct diff_words_data { - struct xdiff_emit_state xm; struct diff_words_buffer minus, plus; FILE *file; }; @@ -459,11 +465,8 @@ static void diff_words_show(struct diff_words_data *diff_words) xpp.flags = XDF_NEED_MINIMAL; xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc; - ecb.outf = xdiff_outf; - ecb.priv = diff_words; - diff_words->xm.consume = fn_out_diff_words_aux; - xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb); - + xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words, + &xpp, &xecfg, &ecb); free(minus.ptr); free(plus.ptr); diff_words->minus.text.size = diff_words->plus.text.size = 0; @@ -477,7 +480,6 @@ static void diff_words_show(struct diff_words_data *diff_words) typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len); struct emit_callback { - struct xdiff_emit_state xm; int nparents, color_diff; unsigned ws_rule; sane_truncate_fn truncate; @@ -580,6 +582,12 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) ecbdata->label_path[0] = ecbdata->label_path[1] = NULL; } + if (diff_suppress_blank_empty + && len == 2 && line[0] == ' ' && line[1] == '\n') { + line[0] = '\n'; + len = 1; + } + /* This is not really necessary for now because * this codepath only deals with two-way diffs. */ @@ -708,8 +716,6 @@ static char *pprint_rename(const char *a, const char *b) } struct diffstat_t { - struct xdiff_emit_state xm; - int nr; int alloc; struct diffstat_file { @@ -1139,7 +1145,6 @@ static void free_diffstat_info(struct diffstat_t *diffstat) } struct checkdiff_t { - struct xdiff_emit_state xm; const char *filename; int lineno; struct diff_options *o; @@ -1384,6 +1389,8 @@ static struct builtin_funcname_pattern { const char *name; const char *pattern; } builtin_funcname_pattern[] = { + { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$" }, + { "html", "^\\s*\\(<[Hh][1-6]\\s.*>.*\\)$" }, { "java", "!^[ ]*\\(catch\\|do\\|for\\|if\\|instanceof\\|" "new\\|return\\|switch\\|throw\\|while\\)\n" "^[ ]*\\(\\([ ]*" @@ -1395,9 +1402,9 @@ static struct builtin_funcname_pattern { "\\|" "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$" }, - { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$" }, - { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" }, + { "python", "^\\s*\\(\\(class\\|def\\)\\s.*\\)$" }, { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$" }, + { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" }, }; static const char *diff_funcname_pattern(struct diff_filespec *one) @@ -1529,15 +1536,13 @@ static void builtin_diff(const char *name_a, xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10); else if (!prefixcmp(diffopts, "-u")) xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10); - ecb.outf = xdiff_outf; - ecb.priv = &ecbdata; - ecbdata.xm.consume = fn_out_consume; if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) { ecbdata.diff_words = xcalloc(1, sizeof(struct diff_words_data)); ecbdata.diff_words->file = o->file; } - xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata, + &xpp, &xecfg, &ecb); if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) free_diff_words_data(&ecbdata); } @@ -1588,9 +1593,8 @@ static void builtin_diffstat(const char *name_a, const char *name_b, memset(&xecfg, 0, sizeof(xecfg)); xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; - ecb.outf = xdiff_outf; - ecb.priv = diffstat; - xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat, + &xpp, &xecfg, &ecb); } free_and_return: @@ -1611,7 +1615,6 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, return; memset(&data, 0, sizeof(data)); - data.xm.consume = checkdiff_consume; data.filename = name_b ? name_b : name_a; data.lineno = 0; data.o = o; @@ -1637,9 +1640,8 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, memset(&xecfg, 0, sizeof(xecfg)); xecfg.ctxlen = 1; /* at least one context line */ xpp.flags = XDF_NEED_MINIMAL; - ecb.outf = xdiff_outf; - ecb.priv = &data; - xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data, + &xpp, &xecfg, &ecb); if ((data.ws_rule & WS_TRAILING_SPACE) && data.trailing_blanks_start) { @@ -3027,7 +3029,6 @@ static void diff_summary(FILE *file, struct diff_filepair *p) } struct patch_id_t { - struct xdiff_emit_state xm; SHA_CTX *ctx; int patchlen; }; @@ -3072,7 +3073,6 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) SHA1_Init(&ctx); memset(&data, 0, sizeof(struct patch_id_t)); data.ctx = &ctx; - data.xm.consume = patch_id_consume; for (i = 0; i < q->nr; i++) { xpparam_t xpp; @@ -3137,9 +3137,8 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) xpp.flags = XDF_NEED_MINIMAL; xecfg.ctxlen = 3; xecfg.flags = XDL_EMIT_FUNCNAMES; - ecb.outf = xdiff_outf; - ecb.priv = &data; - xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data, + &xpp, &xecfg, &ecb); } SHA1_Final(sha1, &ctx); @@ -3216,7 +3215,6 @@ void diff_flush(struct diff_options *options) struct diffstat_t diffstat; memset(&diffstat, 0, sizeof(struct diffstat_t)); - diffstat.xm.consume = diffstat_consume; for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; if (check_pair_status(p)) @@ -52,11 +52,6 @@ int common_prefix(const char **pathspec) return prefix; } -static inline int special_char(unsigned char c1) -{ - return !c1 || c1 == '*' || c1 == '[' || c1 == '?' || c1 == '\\'; -} - /* * Does 'match' matches the given name? * A match is found if @@ -80,7 +75,7 @@ static int match_one(const char *match, const char *name, int namelen) for (;;) { unsigned char c1 = *match; unsigned char c2 = *name; - if (special_char(c1)) + if (isspecial(c1)) break; if (c1 != c2) return 0; @@ -680,17 +675,12 @@ static int cmp_name(const void *p1, const void *p2) */ static int simple_length(const char *match) { - const char special[256] = { - [0] = 1, ['?'] = 1, - ['\\'] = 1, ['*'] = 1, - ['['] = 1 - }; int len = -1; for (;;) { unsigned char c = *match++; len++; - if (special[c]) + if (isspecial(c)) return len; } } @@ -727,8 +717,12 @@ 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); + struct path_simplify *simplify; + + if (has_symlink_leading_path(strlen(path), path)) + return dir->nr; + simplify = create_simplify(pathspec); read_directory_recursive(dir, path, base, baselen, 0, simplify); free_simplify(simplify); qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name); diff --git a/fast-import.c b/fast-import.c index 7089e6f9e..acb8e2e36 100644 --- a/fast-import.c +++ b/fast-import.c @@ -376,7 +376,7 @@ static void dump_marks_helper(FILE *, uintmax_t, struct mark_set *); static void write_crash_report(const char *err) { - char *loc = git_path("fast_import_crash_%d", getpid()); + char *loc = git_path("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid()); FILE *rpt = fopen(loc, "w"); struct branch *b; unsigned long lu; @@ -390,8 +390,8 @@ static void write_crash_report(const char *err) fprintf(stderr, "fast-import: dumping crash report to %s\n", loc); fprintf(rpt, "fast-import crash report:\n"); - fprintf(rpt, " fast-import process: %d\n", getpid()); - fprintf(rpt, " parent process : %d\n", getppid()); + fprintf(rpt, " fast-import process: %"PRIuMAX"\n", (uintmax_t) getpid()); + fprintf(rpt, " parent process : %"PRIuMAX"\n", (uintmax_t) getppid()); fprintf(rpt, " at %s\n", show_date(time(NULL), 0, DATE_LOCAL)); fputc('\n', rpt); diff --git a/git-compat-util.h b/git-compat-util.h index cf89cdf45..db2836fbd 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -99,6 +99,11 @@ #include <iconv.h> #endif +#ifndef NO_OPENSSL +#include <openssl/ssl.h> +#include <openssl/err.h> +#endif + /* On most systems <limits.h> would have given us this, but * not on some systems (e.g. GNU/Hurd). */ @@ -192,6 +197,12 @@ extern int git_munmap(void *start, size_t length); #endif /* NO_MMAP */ +#ifdef NO_ST_BLOCKS_IN_STRUCT_STAT +#define on_disk_bytes(st) ((st).st_size) +#else +#define on_disk_bytes(st) ((st).st_blocks * 512) +#endif + #define DEFAULT_PACKED_GIT_LIMIT \ ((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256)) @@ -318,11 +329,13 @@ extern unsigned char sane_ctype[256]; #define GIT_SPACE 0x01 #define GIT_DIGIT 0x02 #define GIT_ALPHA 0x04 +#define GIT_SPECIAL 0x08 #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0) #define isspace(x) sane_istest(x,GIT_SPACE) #define isdigit(x) sane_istest(x,GIT_DIGIT) #define isalpha(x) sane_istest(x,GIT_ALPHA) #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) +#define isspecial(x) sane_istest(x,GIT_SPECIAL) #define tolower(x) sane_case((unsigned char)(x), 0x20) #define toupper(x) sane_case((unsigned char)(x), 0) diff --git a/git-filter-branch.sh b/git-filter-branch.sh index a324cf059..2871a59e3 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -416,15 +416,17 @@ if [ "$filter_tag_name" ]; then echo "$ref -> $new_ref ($sha1 -> $new_sha1)" if [ "$type" = "tag" ]; then - new_sha1=$(git cat-file tag "$ref" | + new_sha1=$( ( printf 'object %s\ntype commit\ntag %s\n' \ + "$new_sha1" "$new_ref" + git cat-file tag "$ref" | sed -n \ -e "1,/^$/{ - s/^object .*/object $new_sha1/ - s/^type .*/type commit/ - s/^tag .*/tag $new_ref/ + /^object /d + /^type /d + /^tag /d }" \ -e '/^-----BEGIN PGP SIGNATURE-----/q' \ - -e 'p' | + -e 'p' ) | git mktag) || die "Could not create new tag object for $ref" if git cat-file tag "$ref" | \ diff --git a/git-merge-octopus.sh b/git-merge-octopus.sh index 645e1147d..1dadbb496 100755 --- a/git-merge-octopus.sh +++ b/git-merge-octopus.sh @@ -61,7 +61,7 @@ do exit 2 esac - common=$(git merge-base --all $MRC $SHA1) || + common=$(git merge-base --all $SHA1 $MRC) || die "Unable to find common commit with $SHA1" case "$LF$common$LF" in @@ -100,14 +100,7 @@ do next=$(git write-tree 2>/dev/null) fi - # We have merged the other branch successfully. Ideally - # we could implement OR'ed heads in merge-base, and keep - # a list of commits we have merged so far in MRC to feed - # them to merge-base, but we approximate it by keep using - # the current MRC. We used to update it to $common, which - # was incorrectly doing AND'ed merge-base here, which was - # unneeded. - + MRC="$MRC $SHA1" MRT=$next done diff --git a/git-submodule.sh b/git-submodule.sh index b40f876a2..1c39b593a 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -6,9 +6,10 @@ USAGE="[--quiet] [--cached] \ [add <repo> [-b branch] <path>]|[status|init|update [-i|--init]|summary [-n|--summary-limit <n>] [<commit>]] \ -[--] [<path>...]" +[--] [<path>...]|[foreach <command>]|[sync [--] [<path>...]]" OPTIONS_SPEC= . git-sh-setup +. git-parse-remote require_work_tree command= @@ -30,12 +31,11 @@ say() # Resolve relative url by appending to parent's url resolve_relative_url () { - branch="$(git symbolic-ref HEAD 2>/dev/null)" - remote="$(git config branch.${branch#refs/heads/}.remote)" - remote="${remote:-origin}" + remote=$(get_default_remote) remoteurl=$(git config "remote.$remote.url") || die "remote ($remote) does not have a url defined in .git/config" url="$1" + remoteurl=${remoteurl%/} while test -n "$url" do case "$url" in @@ -50,7 +50,16 @@ resolve_relative_url () break;; esac done - echo "$remoteurl/$url" + echo "$remoteurl/${url%/}" +} + +# +# Get submodule info for registered submodules +# $@ = path to limit submodule list +# +module_list() +{ + git ls-files --stage -- "$@" | grep '^160000 ' } # @@ -199,6 +208,26 @@ cmd_add() } # +# Execute an arbitrary command sequence in each checked out +# submodule +# +# $@ = command to execute +# +cmd_foreach() +{ + module_list | + while read mode sha1 stage path + do + if test -e "$path"/.git + then + say "Entering '$path'" + (cd "$path" && eval "$@") || + die "Stopping at '$path'; script returned non-zero status." + fi + done +} + +# # Register submodules in .git/config # # $@ = requested paths (default to all) @@ -226,7 +255,7 @@ cmd_init() shift done - git ls-files --stage -- "$@" | grep '^160000 ' | + module_list "$@" | while read mode sha1 stage path do # Skip already registered paths @@ -284,7 +313,7 @@ cmd_update() esac done - git ls-files --stage -- "$@" | grep '^160000 ' | + module_list "$@" | while read mode sha1 stage path do name=$(module_name "$path") || exit @@ -549,7 +578,7 @@ cmd_status() shift done - git ls-files --stage -- "$@" | grep '^160000 ' | + module_list "$@" | while read mode sha1 stage path do name=$(module_name "$path") || exit @@ -573,6 +602,50 @@ cmd_status() fi done } +# +# Sync remote urls for submodules +# This makes the value for remote.$remote.url match the value +# specified in .gitmodules. +# +cmd_sync() +{ + while test $# -ne 0 + do + case "$1" in + -q|--quiet) + quiet=1 + shift + ;; + --) + shift + break + ;; + -*) + usage + ;; + *) + break + ;; + esac + done + cd_to_toplevel + module_list "$@" | + while read mode sha1 stage path + do + name=$(module_name "$path") + url=$(git config -f .gitmodules --get submodule."$name".url) + if test -e "$path"/.git + then + ( + unset GIT_DIR + cd "$path" + remote=$(get_default_remote) + say "Synchronizing submodule url for '$name'" + git config remote."$remote".url "$url" + ) + fi + done +} # This loop parses the command line arguments to find the # subcommand name to dispatch. Parsing of the subcommand specific @@ -583,7 +656,7 @@ cmd_status() while test $# != 0 && test -z "$command" do case "$1" in - add | init | update | status | summary) + add | foreach | init | update | status | summary | sync) command=$1 ;; -q|--quiet) diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index aa0eeca24..07f5b5378 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -481,6 +481,19 @@ span.refs span { border-color: #ffccff #ff00ee #ff00ee #ffccff; } +span.refs span a { + text-decoration: none; + color: inherit; +} + +span.refs span a:hover { + text-decoration: underline; +} + +span.refs span.indirect { + font-style: italic; +} + span.refs span.ref { background-color: #aaaaff; border-color: #ccccff #0033cc #0033cc #ccccff; diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 90cd99bf9..29e21564c 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1090,13 +1090,23 @@ sub format_log_line_html { } # format marker of refs pointing to given object + +# the destination action is chosen based on object type and current context: +# - for annotated tags, we choose the tag view unless it's the current view +# already, in which case we go to shortlog view +# - for other refs, we keep the current view if we're in history, shortlog or +# log view, and select shortlog otherwise sub format_ref_marker { my ($refs, $id) = @_; my $markers = ''; if (defined $refs->{$id}) { foreach my $ref (@{$refs->{$id}}) { + # this code exploits the fact that non-lightweight tags are the + # only indirect objects, and that they are the only objects for which + # we want to use tag instead of shortlog as action my ($type, $name) = qw(); + my $indirect = ($ref =~ s/\^\{\}$//); # e.g. tags/v2.6.11 or heads/next if ($ref =~ m!^(.*?)s?/(.*)$!) { $type = $1; @@ -1106,8 +1116,29 @@ sub format_ref_marker { $name = $ref; } - $markers .= " <span class=\"$type\" title=\"$ref\">" . - esc_html($name) . "</span>"; + my $class = $type; + $class .= " indirect" if $indirect; + + my $dest_action = "shortlog"; + + if ($indirect) { + $dest_action = "tag" unless $action eq "tag"; + } elsif ($action =~ /^(history|(short)?log)$/) { + $dest_action = $action; + } + + my $dest = ""; + $dest .= "refs/" unless $ref =~ m!^refs/!; + $dest .= $ref; + + my $link = $cgi->a({ + -href => href( + action=>$dest_action, + hash=>$dest + )}, $name); + + $markers .= " <span class=\"$class\" title=\"$ref\">" . + $link . "</span>"; } } @@ -1918,7 +1949,7 @@ sub git_get_references { while (my $line = <$fd>) { chomp $line; - if ($line =~ m!^([0-9a-fA-F]{40})\srefs/($type/?[^^]+)!) { + if ($line =~ m!^([0-9a-fA-F]{40})\srefs/($type.*)$!) { if (defined $refs{$1}) { push @{$refs{$1}}, $2; } else { diff --git a/hash-object.c b/hash-object.c index 46c06a955..a4d127cf7 100644 --- a/hash-object.c +++ b/hash-object.c @@ -7,16 +7,14 @@ #include "cache.h" #include "blob.h" #include "quote.h" +#include "parse-options.h" -static void hash_object(const char *path, enum object_type type, int write_object) +static void hash_fd(int fd, const char *type, int write_object, const char *path) { - int fd; struct stat st; unsigned char sha1[20]; - fd = open(path, O_RDONLY); - if (fd < 0 || - fstat(fd, &st) < 0 || - index_fd(sha1, fd, &st, write_object, type, path)) + if (fstat(fd, &st) < 0 || + index_fd(sha1, fd, &st, write_object, type_from_string(type), path)) die(write_object ? "Unable to add %s to database" : "Unable to hash %s", path); @@ -24,12 +22,14 @@ static void hash_object(const char *path, enum object_type type, int write_objec maybe_flush_or_die(stdout, "hash to stdout"); } -static void hash_stdin(const char *type, int write_object) +static void hash_object(const char *path, const char *type, int write_object, + const char *vpath) { - unsigned char sha1[20]; - if (index_pipe(sha1, 0, type, write_object)) - die("Unable to add stdin to database"); - printf("%s\n", sha1_to_hex(sha1)); + int fd; + fd = open(path, O_RDONLY); + if (fd < 0) + die("Cannot open %s", path); + hash_fd(fd, type, write_object, vpath); } static void hash_stdin_paths(const char *type, int write_objects) @@ -45,92 +45,91 @@ static void hash_stdin_paths(const char *type, int write_objects) die("line is badly quoted"); strbuf_swap(&buf, &nbuf); } - hash_object(buf.buf, type_from_string(type), write_objects); + hash_object(buf.buf, type, write_objects, buf.buf); } strbuf_release(&buf); strbuf_release(&nbuf); } -static const char hash_object_usage[] = -"git hash-object [ [-t <type>] [-w] [--stdin] <file>... | --stdin-paths < <list-of-paths> ]"; +static const char * const hash_object_usage[] = { + "git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>...", + "git hash-object --stdin-paths < <list-of-paths>", + NULL +}; -int main(int argc, char **argv) +static const char *type; +static int write_object; +static int hashstdin; +static int stdin_paths; +static int no_filters; +static const char *vpath; + +static const struct option hash_object_options[] = { + OPT_STRING('t', NULL, &type, "type", "object type"), + OPT_BOOLEAN('w', NULL, &write_object, "write the object into the object database"), + OPT_BOOLEAN( 0 , "stdin", &hashstdin, "read the object from stdin"), + OPT_BOOLEAN( 0 , "stdin-paths", &stdin_paths, "read file names from stdin"), + OPT_BOOLEAN( 0 , "no-filters", &no_filters, "store file as is without filters"), + OPT_STRING( 0 , "path", &vpath, "file", "process file as it were from this path"), + OPT_END() +}; + +int main(int argc, const char **argv) { int i; - const char *type = blob_type; - int write_object = 0; const char *prefix = NULL; int prefix_length = -1; - int no_more_flags = 0; - int hashstdin = 0; - int stdin_paths = 0; + const char *errstr = NULL; + + type = blob_type; git_config(git_default_config, NULL); - for (i = 1 ; i < argc; i++) { - if (!no_more_flags && argv[i][0] == '-') { - if (!strcmp(argv[i], "-t")) { - if (argc <= ++i) - usage(hash_object_usage); - type = argv[i]; - } - else if (!strcmp(argv[i], "-w")) { - if (prefix_length < 0) { - prefix = setup_git_directory(); - prefix_length = - prefix ? strlen(prefix) : 0; - } - write_object = 1; - } - else if (!strcmp(argv[i], "--")) { - no_more_flags = 1; - } - else if (!strcmp(argv[i], "--help")) - usage(hash_object_usage); - else if (!strcmp(argv[i], "--stdin-paths")) { - if (hashstdin) { - error("Can't use --stdin-paths with --stdin"); - usage(hash_object_usage); - } - stdin_paths = 1; - - } - else if (!strcmp(argv[i], "--stdin")) { - if (stdin_paths) { - error("Can't use %s with --stdin-paths", argv[i]); - usage(hash_object_usage); - } - if (hashstdin) - die("Multiple --stdin arguments are not supported"); - hashstdin = 1; - } - else - usage(hash_object_usage); - } - else { - const char *arg = argv[i]; - - if (stdin_paths) { - error("Can't specify files (such as \"%s\") with --stdin-paths", arg); - usage(hash_object_usage); - } - - if (hashstdin) { - hash_stdin(type, write_object); - hashstdin = 0; - } - if (0 <= prefix_length) - arg = prefix_filename(prefix, prefix_length, - arg); - hash_object(arg, type_from_string(type), write_object); - no_more_flags = 1; - } + argc = parse_options(argc, argv, hash_object_options, hash_object_usage, 0); + + if (write_object) { + prefix = setup_git_directory(); + prefix_length = prefix ? strlen(prefix) : 0; + if (vpath && prefix) + vpath = prefix_filename(prefix, prefix_length, vpath); + } + + if (stdin_paths) { + if (hashstdin) + errstr = "Can't use --stdin-paths with --stdin"; + else if (argc) + errstr = "Can't specify files with --stdin-paths"; + else if (vpath) + errstr = "Can't use --stdin-paths with --path"; + else if (no_filters) + errstr = "Can't use --stdin-paths with --no-filters"; + } + else { + if (hashstdin > 1) + errstr = "Multiple --stdin arguments are not supported"; + if (vpath && no_filters) + errstr = "Can't use --path with --no-filters"; + } + + if (errstr) { + error (errstr); + usage_with_options(hash_object_usage, hash_object_options); + } + + if (hashstdin) + hash_fd(0, type, write_object, vpath); + + for (i = 0 ; i < argc; i++) { + const char *arg = argv[i]; + + if (0 <= prefix_length) + arg = prefix_filename(prefix, prefix_length, arg); + hash_object(arg, type, write_object, + no_filters ? NULL : vpath ? vpath : arg); } if (stdin_paths) hash_stdin_paths(type, write_object); - if (hashstdin) - hash_stdin(type, write_object); return 0; } @@ -1,276 +1,7 @@ -/* - * builtin-help.c - * - * Builtin help-related commands (help, usage, version) - */ #include "cache.h" #include "builtin.h" #include "exec_cmd.h" -#include "common-cmds.h" -#include "parse-options.h" -#include "run-command.h" - -static struct man_viewer_list { - struct man_viewer_list *next; - char name[FLEX_ARRAY]; -} *man_viewer_list; - -static struct man_viewer_info_list { - struct man_viewer_info_list *next; - const char *info; - char name[FLEX_ARRAY]; -} *man_viewer_info_list; - -enum help_format { - HELP_FORMAT_MAN, - HELP_FORMAT_INFO, - HELP_FORMAT_WEB, -}; - -static int show_all = 0; -static enum help_format help_format = HELP_FORMAT_MAN; -static struct option builtin_help_options[] = { - OPT_BOOLEAN('a', "all", &show_all, "print all available commands"), - OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN), - OPT_SET_INT('w', "web", &help_format, "show manual in web browser", - HELP_FORMAT_WEB), - OPT_SET_INT('i', "info", &help_format, "show info page", - HELP_FORMAT_INFO), - OPT_END(), -}; - -static const char * const builtin_help_usage[] = { - "git help [--all] [--man|--web|--info] [command]", - NULL -}; - -static enum help_format parse_help_format(const char *format) -{ - if (!strcmp(format, "man")) - return HELP_FORMAT_MAN; - if (!strcmp(format, "info")) - return HELP_FORMAT_INFO; - if (!strcmp(format, "web") || !strcmp(format, "html")) - return HELP_FORMAT_WEB; - die("unrecognized help format '%s'", format); -} - -static const char *get_man_viewer_info(const char *name) -{ - struct man_viewer_info_list *viewer; - - for (viewer = man_viewer_info_list; viewer; viewer = viewer->next) - { - if (!strcasecmp(name, viewer->name)) - return viewer->info; - } - return NULL; -} - -static int check_emacsclient_version(void) -{ - struct strbuf buffer = STRBUF_INIT; - struct child_process ec_process; - const char *argv_ec[] = { "emacsclient", "--version", NULL }; - int version; - - /* emacsclient prints its version number on stderr */ - memset(&ec_process, 0, sizeof(ec_process)); - ec_process.argv = argv_ec; - ec_process.err = -1; - ec_process.stdout_to_stderr = 1; - if (start_command(&ec_process)) { - fprintf(stderr, "Failed to start emacsclient.\n"); - return -1; - } - strbuf_read(&buffer, ec_process.err, 20); - close(ec_process.err); - - /* - * Don't bother checking return value, because "emacsclient --version" - * seems to always exits with code 1. - */ - finish_command(&ec_process); - - if (prefixcmp(buffer.buf, "emacsclient")) { - fprintf(stderr, "Failed to parse emacsclient version.\n"); - strbuf_release(&buffer); - return -1; - } - - strbuf_remove(&buffer, 0, strlen("emacsclient")); - version = atoi(buffer.buf); - - if (version < 22) { - fprintf(stderr, - "emacsclient version '%d' too old (< 22).\n", - version); - strbuf_release(&buffer); - return -1; - } - - strbuf_release(&buffer); - return 0; -} - -static void exec_woman_emacs(const char* path, const char *page) -{ - if (!check_emacsclient_version()) { - /* This works only with emacsclient version >= 22. */ - struct strbuf man_page = STRBUF_INIT; - - if (!path) - path = "emacsclient"; - strbuf_addf(&man_page, "(woman \"%s\")", page); - execlp(path, "emacsclient", "-e", man_page.buf, NULL); - warning("failed to exec '%s': %s", path, strerror(errno)); - } -} - -static void exec_man_konqueror(const char* path, const char *page) -{ - const char *display = getenv("DISPLAY"); - if (display && *display) { - struct strbuf man_page = STRBUF_INIT; - const char *filename = "kfmclient"; - - /* It's simpler to launch konqueror using kfmclient. */ - if (path) { - const char *file = strrchr(path, '/'); - if (file && !strcmp(file + 1, "konqueror")) { - char *new = xstrdup(path); - char *dest = strrchr(new, '/'); - - /* strlen("konqueror") == strlen("kfmclient") */ - strcpy(dest + 1, "kfmclient"); - path = new; - } - if (file) - filename = file; - } else - path = "kfmclient"; - strbuf_addf(&man_page, "man:%s(1)", page); - execlp(path, filename, "newTab", man_page.buf, NULL); - warning("failed to exec '%s': %s", path, strerror(errno)); - } -} - -static void exec_man_man(const char* path, const char *page) -{ - if (!path) - path = "man"; - execlp(path, "man", page, NULL); - warning("failed to exec '%s': %s", path, strerror(errno)); -} - -static void exec_man_cmd(const char *cmd, const char *page) -{ - struct strbuf shell_cmd = STRBUF_INIT; - strbuf_addf(&shell_cmd, "%s %s", cmd, page); - execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL); - warning("failed to exec '%s': %s", cmd, strerror(errno)); -} - -static void add_man_viewer(const char *name) -{ - struct man_viewer_list **p = &man_viewer_list; - size_t len = strlen(name); - - while (*p) - p = &((*p)->next); - *p = xcalloc(1, (sizeof(**p) + len + 1)); - strncpy((*p)->name, name, len); -} - -static int supported_man_viewer(const char *name, size_t len) -{ - return (!strncasecmp("man", name, len) || - !strncasecmp("woman", name, len) || - !strncasecmp("konqueror", name, len)); -} - -static void do_add_man_viewer_info(const char *name, - size_t len, - const char *value) -{ - struct man_viewer_info_list *new = xcalloc(1, sizeof(*new) + len + 1); - - strncpy(new->name, name, len); - new->info = xstrdup(value); - new->next = man_viewer_info_list; - man_viewer_info_list = new; -} - -static int add_man_viewer_path(const char *name, - size_t len, - const char *value) -{ - if (supported_man_viewer(name, len)) - do_add_man_viewer_info(name, len, value); - else - warning("'%s': path for unsupported man viewer.\n" - "Please consider using 'man.<tool>.cmd' instead.", - name); - - return 0; -} - -static int add_man_viewer_cmd(const char *name, - size_t len, - const char *value) -{ - if (supported_man_viewer(name, len)) - warning("'%s': cmd for supported man viewer.\n" - "Please consider using 'man.<tool>.path' instead.", - name); - else - do_add_man_viewer_info(name, len, value); - - return 0; -} - -static int add_man_viewer_info(const char *var, const char *value) -{ - const char *name = var + 4; - const char *subkey = strrchr(name, '.'); - - if (!subkey) - return error("Config with no key for man viewer: %s", name); - - if (!strcmp(subkey, ".path")) { - if (!value) - return config_error_nonbool(var); - return add_man_viewer_path(name, subkey - name, value); - } - if (!strcmp(subkey, ".cmd")) { - if (!value) - return config_error_nonbool(var); - return add_man_viewer_cmd(name, subkey - name, value); - } - - warning("'%s': unsupported man viewer sub key.", subkey); - return 0; -} - -static int git_help_config(const char *var, const char *value, void *cb) -{ - if (!strcmp(var, "help.format")) { - if (!value) - return config_error_nonbool(var); - help_format = parse_help_format(value); - return 0; - } - if (!strcmp(var, "man.viewer")) { - if (!value) - return config_error_nonbool(var); - add_man_viewer(value); - return 0; - } - if (!prefixcmp(var, "man.")) - return add_man_viewer_info(var, value); - - return git_default_config(var, value, cb); -} +#include "help.h" /* most GUI terminals set COLUMNS (although some don't export it) */ static int term_columns(void) @@ -294,24 +25,9 @@ static int term_columns(void) return 80; } -static inline void mput_char(char c, unsigned int num) +void add_cmdname(struct cmdnames *cmds, const char *name, int len) { - while(num--) - putchar(c); -} - -static struct cmdnames { - int alloc; - int cnt; - struct cmdname { - size_t len; - char name[1]; - } **names; -} main_cmds, other_cmds; - -static void add_cmdname(struct cmdnames *cmds, const char *name, int len) -{ - struct cmdname *ent = xmalloc(sizeof(*ent) + len); + struct cmdname *ent = xmalloc(sizeof(*ent) + len + 1); ent->len = len; memcpy(ent->name, name, len); @@ -342,7 +58,7 @@ static void uniq(struct cmdnames *cmds) cmds->cnt = j; } -static void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) +void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) { int ci, cj, ei; int cmp; @@ -417,19 +133,21 @@ static int is_executable(const char *name) return st.st_mode & S_IXUSR; } -static unsigned int list_commands_in_dir(struct cmdnames *cmds, - const char *path) +static void list_commands_in_dir(struct cmdnames *cmds, + const char *path, + const char *prefix) { - unsigned int longest = 0; - const char *prefix = "git-"; - int prefix_len = strlen(prefix); + int prefix_len; DIR *dir = opendir(path); struct dirent *de; struct strbuf buf = STRBUF_INIT; int len; if (!dir) - return 0; + return; + if (!prefix) + prefix = "git-"; + prefix_len = strlen(prefix); strbuf_addf(&buf, "%s/", path); len = buf.len; @@ -449,100 +167,81 @@ static unsigned int list_commands_in_dir(struct cmdnames *cmds, if (has_extension(de->d_name, ".exe")) entlen -= 4; - if (longest < entlen) - longest = entlen; - add_cmdname(cmds, de->d_name + prefix_len, entlen); } closedir(dir); strbuf_release(&buf); - - return longest; } -static unsigned int load_command_list(void) +void load_command_list(const char *prefix, + struct cmdnames *main_cmds, + struct cmdnames *other_cmds) { - unsigned int longest = 0; - unsigned int len; const char *env_path = getenv("PATH"); - char *paths, *path, *colon; const char *exec_path = git_exec_path(); - if (exec_path) - longest = list_commands_in_dir(&main_cmds, exec_path); - - if (!env_path) { - fprintf(stderr, "PATH not set\n"); - exit(1); + if (exec_path) { + list_commands_in_dir(main_cmds, exec_path, prefix); + qsort(main_cmds->names, main_cmds->cnt, + sizeof(*main_cmds->names), cmdname_compare); + uniq(main_cmds); } - path = paths = xstrdup(env_path); - while (1) { - if ((colon = strchr(path, PATH_SEP))) - *colon = 0; + if (env_path) { + char *paths, *path, *colon; + path = paths = xstrdup(env_path); + while (1) { + if ((colon = strchr(path, PATH_SEP))) + *colon = 0; - len = list_commands_in_dir(&other_cmds, path); - if (len > longest) - longest = len; + list_commands_in_dir(other_cmds, path, prefix); - if (!colon) - break; - path = colon + 1; - } - free(paths); - - qsort(main_cmds.names, main_cmds.cnt, - sizeof(*main_cmds.names), cmdname_compare); - uniq(&main_cmds); - - qsort(other_cmds.names, other_cmds.cnt, - sizeof(*other_cmds.names), cmdname_compare); - uniq(&other_cmds); - exclude_cmds(&other_cmds, &main_cmds); + if (!colon) + break; + path = colon + 1; + } + free(paths); - return longest; + qsort(other_cmds->names, other_cmds->cnt, + sizeof(*other_cmds->names), cmdname_compare); + uniq(other_cmds); + } + exclude_cmds(other_cmds, main_cmds); } -static void list_commands(void) +void list_commands(const char *title, struct cmdnames *main_cmds, + struct cmdnames *other_cmds) { - unsigned int longest = load_command_list(); - const char *exec_path = git_exec_path(); + int i, longest = 0; - if (main_cmds.cnt) { - printf("available git commands in '%s'\n", exec_path); - printf("----------------------------"); - mput_char('-', strlen(exec_path)); + for (i = 0; i < main_cmds->cnt; i++) + if (longest < main_cmds->names[i]->len) + longest = main_cmds->names[i]->len; + for (i = 0; i < other_cmds->cnt; i++) + if (longest < other_cmds->names[i]->len) + longest = other_cmds->names[i]->len; + + if (main_cmds->cnt) { + const char *exec_path = git_exec_path(); + printf("available %s in '%s'\n", title, exec_path); + printf("----------------"); + mput_char('-', strlen(title) + strlen(exec_path)); putchar('\n'); - pretty_print_string_list(&main_cmds, longest); + pretty_print_string_list(main_cmds, longest); putchar('\n'); } - if (other_cmds.cnt) { - printf("git commands available from elsewhere on your $PATH\n"); - printf("---------------------------------------------------\n"); - pretty_print_string_list(&other_cmds, longest); + if (other_cmds->cnt) { + printf("%s available from elsewhere on your $PATH\n", title); + printf("---------------------------------------"); + mput_char('-', strlen(title)); + putchar('\n'); + pretty_print_string_list(other_cmds, longest); putchar('\n'); } } -void list_common_cmds_help(void) -{ - int i, longest = 0; - - for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { - if (longest < strlen(common_cmds[i].name)) - longest = strlen(common_cmds[i].name); - } - - puts("The most commonly used git commands are:"); - for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { - printf(" %s ", common_cmds[i].name); - mput_char(' ', longest - strlen(common_cmds[i].name)); - puts(common_cmds[i].help); - } -} - -static int is_in_cmdlist(struct cmdnames *c, const char *s) +int is_in_cmdlist(struct cmdnames *c, const char *s) { int i; for (i = 0; i < c->cnt; i++) @@ -551,130 +250,6 @@ static int is_in_cmdlist(struct cmdnames *c, const char *s) return 0; } -static int is_git_command(const char *s) -{ - load_command_list(); - return is_in_cmdlist(&main_cmds, s) || - is_in_cmdlist(&other_cmds, s) || - !strcmp(s, "help"); -} - -static const char *prepend(const char *prefix, const char *cmd) -{ - size_t pre_len = strlen(prefix); - size_t cmd_len = strlen(cmd); - char *p = xmalloc(pre_len + cmd_len + 1); - memcpy(p, prefix, pre_len); - strcpy(p + pre_len, cmd); - return p; -} - -static const char *cmd_to_page(const char *git_cmd) -{ - if (!git_cmd) - return "git"; - else if (!prefixcmp(git_cmd, "git")) - return git_cmd; - else if (is_git_command(git_cmd)) - return prepend("git-", git_cmd); - else - return prepend("git", git_cmd); -} - -static void setup_man_path(void) -{ - struct strbuf new_path; - const char *old_path = getenv("MANPATH"); - - strbuf_init(&new_path, 0); - - /* We should always put ':' after our path. If there is no - * old_path, the ':' at the end will let 'man' to try - * system-wide paths after ours to find the manual page. If - * there is old_path, we need ':' as delimiter. */ - strbuf_addstr(&new_path, GIT_MAN_PATH); - strbuf_addch(&new_path, ':'); - if (old_path) - strbuf_addstr(&new_path, old_path); - - setenv("MANPATH", new_path.buf, 1); - - strbuf_release(&new_path); -} - -static void exec_viewer(const char *name, const char *page) -{ - const char *info = get_man_viewer_info(name); - - if (!strcasecmp(name, "man")) - exec_man_man(info, page); - else if (!strcasecmp(name, "woman")) - exec_woman_emacs(info, page); - else if (!strcasecmp(name, "konqueror")) - exec_man_konqueror(info, page); - else if (info) - exec_man_cmd(info, page); - else - warning("'%s': unknown man viewer.", name); -} - -static void show_man_page(const char *git_cmd) -{ - struct man_viewer_list *viewer; - const char *page = cmd_to_page(git_cmd); - - setup_man_path(); - for (viewer = man_viewer_list; viewer; viewer = viewer->next) - { - exec_viewer(viewer->name, page); /* will return when unable */ - } - exec_viewer("man", page); - die("no man viewer handled the request"); -} - -static void show_info_page(const char *git_cmd) -{ - const char *page = cmd_to_page(git_cmd); - setenv("INFOPATH", GIT_INFO_PATH, 1); - execlp("info", "info", "gitman", page, NULL); -} - -static void get_html_page_path(struct strbuf *page_path, const char *page) -{ - struct stat st; - const char *html_path = system_path(GIT_HTML_PATH); - - /* Check that we have a git documentation directory. */ - if (stat(mkpath("%s/git.html", html_path), &st) - || !S_ISREG(st.st_mode)) - die("'%s': not a documentation directory.", html_path); - - strbuf_init(page_path, 0); - strbuf_addf(page_path, "%s/%s.html", html_path, page); -} - -/* - * If open_html is not defined in a platform-specific way (see for - * example compat/mingw.h), we use the script web--browse to display - * HTML. - */ -#ifndef open_html -void open_html(const char *path) -{ - execl_git_cmd("web--browse", "-c", "help.browser", path, NULL); -} -#endif - -static void show_html_page(const char *git_cmd) -{ - const char *page = cmd_to_page(git_cmd); - struct strbuf page_path; /* it leaks but we exec bellow */ - - get_html_page_path(&page_path, page); - - open_html(page_path.buf); -} - void help_unknown_cmd(const char *cmd) { fprintf(stderr, "git: '%s' is not a git-command. See 'git --help'.\n", cmd); @@ -686,49 +261,3 @@ int cmd_version(int argc, const char **argv, const char *prefix) printf("git version %s\n", git_version_string); return 0; } - -int cmd_help(int argc, const char **argv, const char *prefix) -{ - int nongit; - const char *alias; - - setup_git_directory_gently(&nongit); - git_config(git_help_config, NULL); - - argc = parse_options(argc, argv, builtin_help_options, - builtin_help_usage, 0); - - if (show_all) { - printf("usage: %s\n\n", git_usage_string); - list_commands(); - printf("%s\n", git_more_info_string); - return 0; - } - - if (!argv[0]) { - printf("usage: %s\n\n", git_usage_string); - list_common_cmds_help(); - printf("\n%s\n", git_more_info_string); - return 0; - } - - alias = alias_lookup(argv[0]); - if (alias && !is_git_command(argv[0])) { - printf("`git %s' is aliased to `%s'\n", argv[0], alias); - return 0; - } - - switch (help_format) { - case HELP_FORMAT_MAN: - show_man_page(argv[0]); - break; - case HELP_FORMAT_INFO: - show_info_page(argv[0]); - break; - case HELP_FORMAT_WEB: - show_html_page(argv[0]); - break; - } - - return 0; -} @@ -0,0 +1,29 @@ +#ifndef HELP_H +#define HELP_H + +struct cmdnames { + int alloc; + int cnt; + struct cmdname { + size_t len; + char name[FLEX_ARRAY]; + } **names; +}; + +static inline void mput_char(char c, unsigned int num) +{ + while(num--) + putchar(c); +} + +void load_command_list(const char *prefix, + struct cmdnames *main_cmds, + struct cmdnames *other_cmds); +void add_cmdname(struct cmdnames *cmds, const char *name, int len); +/* Here we require that excludes is a sorted list. */ +void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes); +int is_in_cmdlist(struct cmdnames *c, const char *s); +void list_commands(const char *title, struct cmdnames *main_cmds, + struct cmdnames *other_cmds); + +#endif /* HELP_H */ diff --git a/imap-send.c b/imap-send.c index 1ec131092..af7e08c09 100644 --- a/imap-send.c +++ b/imap-send.c @@ -23,71 +23,74 @@ */ #include "cache.h" +#ifdef NO_OPENSSL +typedef void *SSL; +#endif -typedef struct store_conf { +struct store_conf { char *name; const char *path; /* should this be here? its interpretation is driver-specific */ char *map_inbox; char *trash; unsigned max_size; /* off_t is overkill */ unsigned trash_remote_new:1, trash_only_new:1; -} store_conf_t; +}; -typedef struct string_list { +struct string_list { struct string_list *next; char string[1]; -} string_list_t; +}; -typedef struct channel_conf { +struct channel_conf { struct channel_conf *next; char *name; - store_conf_t *master, *slave; + struct store_conf *master, *slave; char *master_name, *slave_name; char *sync_state; - string_list_t *patterns; + struct string_list *patterns; int mops, sops; unsigned max_messages; /* for slave only */ -} channel_conf_t; +}; -typedef struct group_conf { +struct group_conf { struct group_conf *next; char *name; - string_list_t *channels; -} group_conf_t; + struct string_list *channels; +}; /* For message->status */ #define M_RECENT (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */ #define M_DEAD (1<<1) /* expunged */ #define M_FLAGS (1<<2) /* flags fetched */ -typedef struct message { +struct message { struct message *next; - /* string_list_t *keywords; */ + /* struct string_list *keywords; */ size_t size; /* zero implies "not fetched" */ int uid; unsigned char flags, status; -} message_t; +}; -typedef struct store { - store_conf_t *conf; /* foreign */ +struct store { + struct store_conf *conf; /* foreign */ /* currently open mailbox */ const char *name; /* foreign! maybe preset? */ char *path; /* own */ - message_t *msgs; /* own */ + struct message *msgs; /* own */ int uidvalidity; unsigned char opts; /* maybe preset? */ /* note that the following do _not_ reflect stats from msgs, but mailbox totals */ int count; /* # of messages */ int recent; /* # of recent messages - don't trust this beyond the initial read */ -} store_t; +}; -typedef struct { +struct msg_data { char *data; int len; unsigned char flags; unsigned int crlf:1; -} msg_data_t; +}; #define DRV_OK 0 #define DRV_MSG_BAD -1 @@ -96,14 +99,14 @@ typedef struct { static int Verbose, Quiet; -static void imap_info( const char *, ... ); -static void imap_warn( const char *, ... ); +static void imap_info(const char *, ...); +static void imap_warn(const char *, ...); -static char *next_arg( char ** ); +static char *next_arg(char **); -static void free_generic_messages( message_t * ); +static void free_generic_messages(struct message *); -static int nfsnprintf( char *buf, int blen, const char *fmt, ... ); +static int nfsnprintf(char *buf, int blen, const char *fmt, ...); static int nfvasprintf(char **strp, const char *fmt, va_list ap) { @@ -119,67 +122,70 @@ static int nfvasprintf(char **strp, const char *fmt, va_list ap) return len; } -static void arc4_init( void ); -static unsigned char arc4_getbyte( void ); +static void arc4_init(void); +static unsigned char arc4_getbyte(void); -typedef struct imap_server_conf { +struct imap_server_conf { char *name; char *tunnel; char *host; int port; char *user; char *pass; -} imap_server_conf_t; + int use_ssl; + int ssl_verify; +}; -typedef struct imap_store_conf { - store_conf_t gen; - imap_server_conf_t *server; +struct imap_store_conf { + struct store_conf gen; + struct imap_server_conf *server; unsigned use_namespace:1; -} imap_store_conf_t; +}; -#define NIL (void*)0x1 -#define LIST (void*)0x2 +#define NIL (void *)0x1 +#define LIST (void *)0x2 -typedef struct _list { - struct _list *next, *child; +struct imap_list { + struct imap_list *next, *child; char *val; int len; -} list_t; +}; -typedef struct { +struct imap_socket { int fd; -} Socket_t; + SSL *ssl; +}; -typedef struct { - Socket_t sock; +struct imap_buffer { + struct imap_socket sock; int bytes; int offset; char buf[1024]; -} buffer_t; +}; struct imap_cmd; -typedef struct imap { +struct imap { int uidnext; /* from SELECT responses */ - list_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */ + struct imap_list *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */ unsigned caps, rcaps; /* CAPABILITY results */ /* command queue */ int nexttag, num_in_progress, literal_pending; struct imap_cmd *in_progress, **in_progress_append; - buffer_t buf; /* this is BIG, so put it last */ -} imap_t; + struct imap_buffer buf; /* this is BIG, so put it last */ +}; -typedef struct imap_store { - store_t gen; +struct imap_store { + struct store gen; int uidvalidity; - imap_t *imap; + struct imap *imap; const char *prefix; unsigned /*currentnc:1,*/ trashnc:1; -} imap_store_t; +}; struct imap_cmd_cb { - int (*cont)( imap_store_t *ctx, struct imap_cmd *cmd, const char *prompt ); - void (*done)( imap_store_t *ctx, struct imap_cmd *cmd, int response); + int (*cont)(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt); + void (*done)(struct imap_store *ctx, struct imap_cmd *cmd, int response); void *ctx; char *data; int dlen; @@ -201,6 +207,7 @@ enum CAPABILITY { UIDPLUS, LITERALPLUS, NAMESPACE, + STARTTLS, }; static const char *cap_list[] = { @@ -208,13 +215,14 @@ static const char *cap_list[] = { "UIDPLUS", "LITERAL+", "NAMESPACE", + "STARTTLS", }; #define RESP_OK 0 #define RESP_NO 1 #define RESP_BAD 2 -static int get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd ); +static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd); static const char *Flags[] = { @@ -225,42 +233,137 @@ static const char *Flags[] = { "Deleted", }; -static void -socket_perror( const char *func, Socket_t *sock, int ret ) +#ifndef NO_OPENSSL +static void ssl_socket_perror(const char *func) +{ + fprintf(stderr, "%s: %s\n", func, ERR_error_string(ERR_get_error(), 0)); +} +#endif + +static void socket_perror(const char *func, struct imap_socket *sock, int ret) +{ +#ifndef NO_OPENSSL + if (sock->ssl) { + int sslerr = SSL_get_error(sock->ssl, ret); + switch (sslerr) { + case SSL_ERROR_NONE: + break; + case SSL_ERROR_SYSCALL: + perror("SSL_connect"); + break; + default: + ssl_socket_perror("SSL_connect"); + break; + } + } else +#endif + { + if (ret < 0) + perror(func); + else + fprintf(stderr, "%s: unexpected EOF\n", func); + } +} + +static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify) { - if (ret < 0) - perror( func ); +#ifdef NO_OPENSSL + fprintf(stderr, "SSL requested but SSL support not compiled in\n"); + return -1; +#else + SSL_METHOD *meth; + SSL_CTX *ctx; + int ret; + + SSL_library_init(); + SSL_load_error_strings(); + + if (use_tls_only) + meth = TLSv1_method(); else - fprintf( stderr, "%s: unexpected EOF\n", func ); + meth = SSLv23_method(); + + if (!meth) { + ssl_socket_perror("SSLv23_method"); + return -1; + } + + ctx = SSL_CTX_new(meth); + + if (verify) + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); + + if (!SSL_CTX_set_default_verify_paths(ctx)) { + ssl_socket_perror("SSL_CTX_set_default_verify_paths"); + return -1; + } + sock->ssl = SSL_new(ctx); + if (!sock->ssl) { + ssl_socket_perror("SSL_new"); + return -1; + } + if (!SSL_set_fd(sock->ssl, sock->fd)) { + ssl_socket_perror("SSL_set_fd"); + return -1; + } + + ret = SSL_connect(sock->ssl); + if (ret <= 0) { + socket_perror("SSL_connect", sock, ret); + return -1; + } + + return 0; +#endif } -static int -socket_read( Socket_t *sock, char *buf, int len ) +static int socket_read(struct imap_socket *sock, char *buf, int len) { - ssize_t n = xread( sock->fd, buf, len ); + ssize_t n; +#ifndef NO_OPENSSL + if (sock->ssl) + n = SSL_read(sock->ssl, buf, len); + else +#endif + n = xread(sock->fd, buf, len); if (n <= 0) { - socket_perror( "read", sock, n ); - close( sock->fd ); + socket_perror("read", sock, n); + close(sock->fd); sock->fd = -1; } return n; } -static int -socket_write( Socket_t *sock, const char *buf, int len ) +static int socket_write(struct imap_socket *sock, const char *buf, int len) { - int n = write_in_full( sock->fd, buf, len ); + int n; +#ifndef NO_OPENSSL + if (sock->ssl) + n = SSL_write(sock->ssl, buf, len); + else +#endif + n = write_in_full(sock->fd, buf, len); if (n != len) { - socket_perror( "write", sock, n ); - close( sock->fd ); + socket_perror("write", sock, n); + close(sock->fd); sock->fd = -1; } return n; } +static void socket_shutdown(struct imap_socket *sock) +{ +#ifndef NO_OPENSSL + if (sock->ssl) { + SSL_shutdown(sock->ssl); + SSL_free(sock->ssl); + } +#endif + close(sock->fd); +} + /* simple line buffering */ -static int -buffer_gets( buffer_t * b, char **s ) +static int buffer_gets(struct imap_buffer *b, char **s) { int n; int start = b->offset; @@ -274,7 +377,7 @@ buffer_gets( buffer_t * b, char **s ) /* shift down used bytes */ *s = b->buf; - assert( start <= b->bytes ); + assert(start <= b->bytes); n = b->bytes - start; if (n) @@ -284,8 +387,8 @@ buffer_gets( buffer_t * b, char **s ) start = 0; } - n = socket_read( &b->sock, b->buf + b->bytes, - sizeof(b->buf) - b->bytes ); + n = socket_read(&b->sock, b->buf + b->bytes, + sizeof(b->buf) - b->bytes); if (n <= 0) return -1; @@ -294,12 +397,12 @@ buffer_gets( buffer_t * b, char **s ) } if (b->buf[b->offset] == '\r') { - assert( b->offset + 1 < b->bytes ); + assert(b->offset + 1 < b->bytes); if (b->buf[b->offset + 1] == '\n') { b->buf[b->offset] = 0; /* terminate the string */ b->offset += 2; /* next line */ if (Verbose) - puts( *s ); + puts(*s); return 0; } } @@ -309,39 +412,36 @@ buffer_gets( buffer_t * b, char **s ) /* not reached */ } -static void -imap_info( const char *msg, ... ) +static void imap_info(const char *msg, ...) { va_list va; if (!Quiet) { - va_start( va, msg ); - vprintf( msg, va ); - va_end( va ); - fflush( stdout ); + va_start(va, msg); + vprintf(msg, va); + va_end(va); + fflush(stdout); } } -static void -imap_warn( const char *msg, ... ) +static void imap_warn(const char *msg, ...) { va_list va; if (Quiet < 2) { - va_start( va, msg ); - vfprintf( stderr, msg, va ); - va_end( va ); + va_start(va, msg); + vfprintf(stderr, msg, va); + va_end(va); } } -static char * -next_arg( char **s ) +static char *next_arg(char **s) { char *ret; if (!s || !*s) return NULL; - while (isspace( (unsigned char) **s )) + while (isspace((unsigned char) **s)) (*s)++; if (!**s) { *s = NULL; @@ -350,10 +450,10 @@ next_arg( char **s ) if (**s == '"') { ++*s; ret = *s; - *s = strchr( *s, '"' ); + *s = strchr(*s, '"'); } else { ret = *s; - while (**s && !isspace( (unsigned char) **s )) + while (**s && !isspace((unsigned char) **s)) (*s)++; } if (*s) { @@ -365,27 +465,25 @@ next_arg( char **s ) return ret; } -static void -free_generic_messages( message_t *msgs ) +static void free_generic_messages(struct message *msgs) { - message_t *tmsg; + struct message *tmsg; for (; msgs; msgs = tmsg) { tmsg = msgs->next; - free( msgs ); + free(msgs); } } -static int -nfsnprintf( char *buf, int blen, const char *fmt, ... ) +static int nfsnprintf(char *buf, int blen, const char *fmt, ...) { int ret; va_list va; - va_start( va, fmt ); - if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen) - die( "Fatal: buffer too small. Please report a bug.\n"); - va_end( va ); + va_start(va, fmt); + if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen) + die("Fatal: buffer too small. Please report a bug.\n"); + va_end(va); return ret; } @@ -393,21 +491,20 @@ static struct { unsigned char i, j, s[256]; } rs; -static void -arc4_init( void ) +static void arc4_init(void) { int i, fd; unsigned char j, si, dat[128]; - if ((fd = open( "/dev/urandom", O_RDONLY )) < 0 && (fd = open( "/dev/random", O_RDONLY )) < 0) { - fprintf( stderr, "Fatal: no random number source available.\n" ); - exit( 3 ); + if ((fd = open("/dev/urandom", O_RDONLY)) < 0 && (fd = open("/dev/random", O_RDONLY)) < 0) { + fprintf(stderr, "Fatal: no random number source available.\n"); + exit(3); } - if (read_in_full( fd, dat, 128 ) != 128) { - fprintf( stderr, "Fatal: cannot read random number source.\n" ); - exit( 3 ); + if (read_in_full(fd, dat, 128) != 128) { + fprintf(stderr, "Fatal: cannot read random number source.\n"); + exit(3); } - close( fd ); + close(fd); for (i = 0; i < 256; i++) rs.s[i] = i; @@ -423,8 +520,7 @@ arc4_init( void ) arc4_getbyte(); } -static unsigned char -arc4_getbyte( void ) +static unsigned char arc4_getbyte(void) { unsigned char si, sj; @@ -437,54 +533,53 @@ arc4_getbyte( void ) return rs.s[(si + sj) & 0xff]; } -static struct imap_cmd * -v_issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb, - const char *fmt, va_list ap ) +static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx, + struct imap_cmd_cb *cb, + const char *fmt, va_list ap) { - imap_t *imap = ctx->imap; + struct imap *imap = ctx->imap; struct imap_cmd *cmd; int n, bufl; char buf[1024]; - cmd = xmalloc( sizeof(struct imap_cmd) ); - nfvasprintf( &cmd->cmd, fmt, ap ); + cmd = xmalloc(sizeof(struct imap_cmd)); + nfvasprintf(&cmd->cmd, fmt, ap); cmd->tag = ++imap->nexttag; if (cb) cmd->cb = *cb; else - memset( &cmd->cb, 0, sizeof(cmd->cb) ); + memset(&cmd->cb, 0, sizeof(cmd->cb)); while (imap->literal_pending) - get_cmd_result( ctx, NULL ); + get_cmd_result(ctx, NULL); - bufl = nfsnprintf( buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ? + bufl = nfsnprintf(buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ? "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n", - cmd->tag, cmd->cmd, cmd->cb.dlen ); + cmd->tag, cmd->cmd, cmd->cb.dlen); if (Verbose) { if (imap->num_in_progress) - printf( "(%d in progress) ", imap->num_in_progress ); - if (memcmp( cmd->cmd, "LOGIN", 5 )) - printf( ">>> %s", buf ); + printf("(%d in progress) ", imap->num_in_progress); + if (memcmp(cmd->cmd, "LOGIN", 5)) + printf(">>> %s", buf); else - printf( ">>> %d LOGIN <user> <pass>\n", cmd->tag ); + printf(">>> %d LOGIN <user> <pass>\n", cmd->tag); } - if (socket_write( &imap->buf.sock, buf, bufl ) != bufl) { - free( cmd->cmd ); - free( cmd ); + if (socket_write(&imap->buf.sock, buf, bufl) != bufl) { + free(cmd->cmd); + free(cmd); if (cb) - free( cb->data ); + free(cb->data); return NULL; } if (cmd->cb.data) { if (CAP(LITERALPLUS)) { - n = socket_write( &imap->buf.sock, cmd->cb.data, cmd->cb.dlen ); - free( cmd->cb.data ); + n = socket_write(&imap->buf.sock, cmd->cb.data, cmd->cb.dlen); + free(cmd->cb.data); if (n != cmd->cb.dlen || - (n = socket_write( &imap->buf.sock, "\r\n", 2 )) != 2) - { - free( cmd->cmd ); - free( cmd ); + (n = socket_write(&imap->buf.sock, "\r\n", 2)) != 2) { + free(cmd->cmd); + free(cmd); return NULL; } cmd->cb.data = NULL; @@ -499,109 +594,106 @@ v_issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb, return cmd; } -static struct imap_cmd * -issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... ) +static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx, + struct imap_cmd_cb *cb, + const char *fmt, ...) { struct imap_cmd *ret; va_list ap; - va_start( ap, fmt ); - ret = v_issue_imap_cmd( ctx, cb, fmt, ap ); - va_end( ap ); + va_start(ap, fmt); + ret = v_issue_imap_cmd(ctx, cb, fmt, ap); + va_end(ap); return ret; } -static int -imap_exec( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... ) +static int imap_exec(struct imap_store *ctx, struct imap_cmd_cb *cb, + const char *fmt, ...) { va_list ap; struct imap_cmd *cmdp; - va_start( ap, fmt ); - cmdp = v_issue_imap_cmd( ctx, cb, fmt, ap ); - va_end( ap ); + va_start(ap, fmt); + cmdp = v_issue_imap_cmd(ctx, cb, fmt, ap); + va_end(ap); if (!cmdp) return RESP_BAD; - return get_cmd_result( ctx, cmdp ); + return get_cmd_result(ctx, cmdp); } -static int -imap_exec_m( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... ) +static int imap_exec_m(struct imap_store *ctx, struct imap_cmd_cb *cb, + const char *fmt, ...) { va_list ap; struct imap_cmd *cmdp; - va_start( ap, fmt ); - cmdp = v_issue_imap_cmd( ctx, cb, fmt, ap ); - va_end( ap ); + va_start(ap, fmt); + cmdp = v_issue_imap_cmd(ctx, cb, fmt, ap); + va_end(ap); if (!cmdp) return DRV_STORE_BAD; - switch (get_cmd_result( ctx, cmdp )) { + switch (get_cmd_result(ctx, cmdp)) { case RESP_BAD: return DRV_STORE_BAD; case RESP_NO: return DRV_MSG_BAD; default: return DRV_OK; } } -static int -is_atom( list_t *list ) +static int is_atom(struct imap_list *list) { return list && list->val && list->val != NIL && list->val != LIST; } -static int -is_list( list_t *list ) +static int is_list(struct imap_list *list) { return list && list->val == LIST; } -static void -free_list( list_t *list ) +static void free_list(struct imap_list *list) { - list_t *tmp; + struct imap_list *tmp; for (; list; list = tmp) { tmp = list->next; - if (is_list( list )) - free_list( list->child ); - else if (is_atom( list )) - free( list->val ); - free( list ); + if (is_list(list)) + free_list(list->child); + else if (is_atom(list)) + free(list->val); + free(list); } } -static int -parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level ) +static int parse_imap_list_l(struct imap *imap, char **sp, struct imap_list **curp, int level) { - list_t *cur; + struct imap_list *cur; char *s = *sp, *p; int n, bytes; for (;;) { - while (isspace( (unsigned char)*s )) + while (isspace((unsigned char)*s)) s++; if (level && *s == ')') { s++; break; } - *curp = cur = xmalloc( sizeof(*cur) ); + *curp = cur = xmalloc(sizeof(*cur)); curp = &cur->next; cur->val = NULL; /* for clean bail */ if (*s == '(') { /* sublist */ s++; cur->val = LIST; - if (parse_imap_list_l( imap, &s, &cur->child, level + 1 )) + if (parse_imap_list_l(imap, &s, &cur->child, level + 1)) goto bail; } else if (imap && *s == '{') { /* literal */ - bytes = cur->len = strtol( s + 1, &s, 10 ); + bytes = cur->len = strtol(s + 1, &s, 10); if (*s != '}') goto bail; - s = cur->val = xmalloc( cur->len ); + s = cur->val = xmalloc(cur->len); /* dump whats left over in the input buffer */ n = imap->buf.bytes - imap->buf.offset; @@ -610,7 +702,7 @@ parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level ) /* the entire message fit in the buffer */ n = bytes; - memcpy( s, imap->buf.buf + imap->buf.offset, n ); + memcpy(s, imap->buf.buf + imap->buf.offset, n); s += n; bytes -= n; @@ -619,13 +711,13 @@ parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level ) /* now read the rest of the message */ while (bytes > 0) { - if ((n = socket_read (&imap->buf.sock, s, bytes)) <= 0) + if ((n = socket_read(&imap->buf.sock, s, bytes)) <= 0) goto bail; s += n; bytes -= n; } - if (buffer_gets( &imap->buf, &s )) + if (buffer_gets(&imap->buf, &s)) goto bail; } else if (*s == '"') { /* quoted string */ @@ -640,15 +732,14 @@ parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level ) } else { /* atom */ p = s; - for (; *s && !isspace( (unsigned char)*s ); s++) + for (; *s && !isspace((unsigned char)*s); s++) if (level && *s == ')') break; cur->len = s - p; - if (cur->len == 3 && !memcmp ("NIL", p, 3)) { + if (cur->len == 3 && !memcmp("NIL", p, 3)) cur->val = NIL; - } else { + else cur->val = xmemdupz(p, cur->len); - } } if (!level) @@ -660,127 +751,122 @@ parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level ) *curp = NULL; return 0; - bail: +bail: *curp = NULL; return -1; } -static list_t * -parse_imap_list( imap_t *imap, char **sp ) +static struct imap_list *parse_imap_list(struct imap *imap, char **sp) { - list_t *head; + struct imap_list *head; - if (!parse_imap_list_l( imap, sp, &head, 0 )) + if (!parse_imap_list_l(imap, sp, &head, 0)) return head; - free_list( head ); + free_list(head); return NULL; } -static list_t * -parse_list( char **sp ) +static struct imap_list *parse_list(char **sp) { - return parse_imap_list( NULL, sp ); + return parse_imap_list(NULL, sp); } -static void -parse_capability( imap_t *imap, char *cmd ) +static void parse_capability(struct imap *imap, char *cmd) { char *arg; unsigned i; imap->caps = 0x80000000; - while ((arg = next_arg( &cmd ))) + while ((arg = next_arg(&cmd))) for (i = 0; i < ARRAY_SIZE(cap_list); i++) - if (!strcmp( cap_list[i], arg )) + if (!strcmp(cap_list[i], arg)) imap->caps |= 1 << i; imap->rcaps = imap->caps; } -static int -parse_response_code( imap_store_t *ctx, struct imap_cmd_cb *cb, char *s ) +static int parse_response_code(struct imap_store *ctx, struct imap_cmd_cb *cb, + char *s) { - imap_t *imap = ctx->imap; + struct imap *imap = ctx->imap; char *arg, *p; if (*s != '[') return RESP_OK; /* no response code */ s++; - if (!(p = strchr( s, ']' ))) { - fprintf( stderr, "IMAP error: malformed response code\n" ); + if (!(p = strchr(s, ']'))) { + fprintf(stderr, "IMAP error: malformed response code\n"); return RESP_BAD; } *p++ = 0; - arg = next_arg( &s ); - if (!strcmp( "UIDVALIDITY", arg )) { - if (!(arg = next_arg( &s )) || !(ctx->gen.uidvalidity = atoi( arg ))) { - fprintf( stderr, "IMAP error: malformed UIDVALIDITY status\n" ); + arg = next_arg(&s); + if (!strcmp("UIDVALIDITY", arg)) { + if (!(arg = next_arg(&s)) || !(ctx->gen.uidvalidity = atoi(arg))) { + fprintf(stderr, "IMAP error: malformed UIDVALIDITY status\n"); return RESP_BAD; } - } else if (!strcmp( "UIDNEXT", arg )) { - if (!(arg = next_arg( &s )) || !(imap->uidnext = atoi( arg ))) { - fprintf( stderr, "IMAP error: malformed NEXTUID status\n" ); + } else if (!strcmp("UIDNEXT", arg)) { + if (!(arg = next_arg(&s)) || !(imap->uidnext = atoi(arg))) { + fprintf(stderr, "IMAP error: malformed NEXTUID status\n"); return RESP_BAD; } - } else if (!strcmp( "CAPABILITY", arg )) { - parse_capability( imap, s ); - } else if (!strcmp( "ALERT", arg )) { + } else if (!strcmp("CAPABILITY", arg)) { + parse_capability(imap, s); + } else if (!strcmp("ALERT", arg)) { /* RFC2060 says that these messages MUST be displayed * to the user */ - for (; isspace( (unsigned char)*p ); p++); - fprintf( stderr, "*** IMAP ALERT *** %s\n", p ); - } else if (cb && cb->ctx && !strcmp( "APPENDUID", arg )) { - if (!(arg = next_arg( &s )) || !(ctx->gen.uidvalidity = atoi( arg )) || - !(arg = next_arg( &s )) || !(*(int *)cb->ctx = atoi( arg ))) - { - fprintf( stderr, "IMAP error: malformed APPENDUID status\n" ); + for (; isspace((unsigned char)*p); p++); + fprintf(stderr, "*** IMAP ALERT *** %s\n", p); + } else if (cb && cb->ctx && !strcmp("APPENDUID", arg)) { + if (!(arg = next_arg(&s)) || !(ctx->gen.uidvalidity = atoi(arg)) || + !(arg = next_arg(&s)) || !(*(int *)cb->ctx = atoi(arg))) { + fprintf(stderr, "IMAP error: malformed APPENDUID status\n"); return RESP_BAD; } } return RESP_OK; } -static int -get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd ) +static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd) { - imap_t *imap = ctx->imap; + struct imap *imap = ctx->imap; struct imap_cmd *cmdp, **pcmdp, *ncmdp; char *cmd, *arg, *arg1, *p; int n, resp, resp2, tag; for (;;) { - if (buffer_gets( &imap->buf, &cmd )) + if (buffer_gets(&imap->buf, &cmd)) return RESP_BAD; - arg = next_arg( &cmd ); + arg = next_arg(&cmd); if (*arg == '*') { - arg = next_arg( &cmd ); + arg = next_arg(&cmd); if (!arg) { - fprintf( stderr, "IMAP error: unable to parse untagged response\n" ); + fprintf(stderr, "IMAP error: unable to parse untagged response\n"); return RESP_BAD; } - if (!strcmp( "NAMESPACE", arg )) { - imap->ns_personal = parse_list( &cmd ); - imap->ns_other = parse_list( &cmd ); - imap->ns_shared = parse_list( &cmd ); - } else if (!strcmp( "OK", arg ) || !strcmp( "BAD", arg ) || - !strcmp( "NO", arg ) || !strcmp( "BYE", arg )) { - if ((resp = parse_response_code( ctx, NULL, cmd )) != RESP_OK) + if (!strcmp("NAMESPACE", arg)) { + imap->ns_personal = parse_list(&cmd); + imap->ns_other = parse_list(&cmd); + imap->ns_shared = parse_list(&cmd); + } else if (!strcmp("OK", arg) || !strcmp("BAD", arg) || + !strcmp("NO", arg) || !strcmp("BYE", arg)) { + if ((resp = parse_response_code(ctx, NULL, cmd)) != RESP_OK) return resp; - } else if (!strcmp( "CAPABILITY", arg )) - parse_capability( imap, cmd ); - else if ((arg1 = next_arg( &cmd ))) { - if (!strcmp( "EXISTS", arg1 )) - ctx->gen.count = atoi( arg ); - else if (!strcmp( "RECENT", arg1 )) - ctx->gen.recent = atoi( arg ); + } else if (!strcmp("CAPABILITY", arg)) + parse_capability(imap, cmd); + else if ((arg1 = next_arg(&cmd))) { + if (!strcmp("EXISTS", arg1)) + ctx->gen.count = atoi(arg); + else if (!strcmp("RECENT", arg1)) + ctx->gen.recent = atoi(arg); } else { - fprintf( stderr, "IMAP error: unable to parse untagged response\n" ); + fprintf(stderr, "IMAP error: unable to parse untagged response\n"); return RESP_BAD; } } else if (!imap->in_progress) { - fprintf( stderr, "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "" ); + fprintf(stderr, "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : ""); return RESP_BAD; } else if (*arg == '+') { /* This can happen only with the last command underway, as @@ -788,57 +874,57 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd ) cmdp = (struct imap_cmd *)((char *)imap->in_progress_append - offsetof(struct imap_cmd, next)); if (cmdp->cb.data) { - n = socket_write( &imap->buf.sock, cmdp->cb.data, cmdp->cb.dlen ); - free( cmdp->cb.data ); + n = socket_write(&imap->buf.sock, cmdp->cb.data, cmdp->cb.dlen); + free(cmdp->cb.data); cmdp->cb.data = NULL; if (n != (int)cmdp->cb.dlen) return RESP_BAD; } else if (cmdp->cb.cont) { - if (cmdp->cb.cont( ctx, cmdp, cmd )) + if (cmdp->cb.cont(ctx, cmdp, cmd)) return RESP_BAD; } else { - fprintf( stderr, "IMAP error: unexpected command continuation request\n" ); + fprintf(stderr, "IMAP error: unexpected command continuation request\n"); return RESP_BAD; } - if (socket_write( &imap->buf.sock, "\r\n", 2 ) != 2) + if (socket_write(&imap->buf.sock, "\r\n", 2) != 2) return RESP_BAD; if (!cmdp->cb.cont) imap->literal_pending = 0; if (!tcmd) return DRV_OK; } else { - tag = atoi( arg ); + tag = atoi(arg); for (pcmdp = &imap->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next) if (cmdp->tag == tag) goto gottag; - fprintf( stderr, "IMAP error: unexpected tag %s\n", arg ); + fprintf(stderr, "IMAP error: unexpected tag %s\n", arg); return RESP_BAD; - gottag: + gottag: if (!(*pcmdp = cmdp->next)) imap->in_progress_append = pcmdp; imap->num_in_progress--; if (cmdp->cb.cont || cmdp->cb.data) imap->literal_pending = 0; - arg = next_arg( &cmd ); - if (!strcmp( "OK", arg )) + arg = next_arg(&cmd); + if (!strcmp("OK", arg)) resp = DRV_OK; else { - if (!strcmp( "NO", arg )) { - if (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp( cmd, "[TRYCREATE]", 11 ))) { /* SELECT, APPEND or UID COPY */ - p = strchr( cmdp->cmd, '"' ); - if (!issue_imap_cmd( ctx, NULL, "CREATE \"%.*s\"", strchr( p + 1, '"' ) - p + 1, p )) { + if (!strcmp("NO", arg)) { + if (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp(cmd, "[TRYCREATE]", 11))) { /* SELECT, APPEND or UID COPY */ + p = strchr(cmdp->cmd, '"'); + if (!issue_imap_cmd(ctx, NULL, "CREATE \"%.*s\"", strchr(p + 1, '"') - p + 1, p)) { resp = RESP_BAD; goto normal; } /* not waiting here violates the spec, but a server that does not grok this nonetheless violates it too. */ cmdp->cb.create = 0; - if (!(ncmdp = issue_imap_cmd( ctx, &cmdp->cb, "%s", cmdp->cmd ))) { + if (!(ncmdp = issue_imap_cmd(ctx, &cmdp->cb, "%s", cmdp->cmd))) { resp = RESP_BAD; goto normal; } - free( cmdp->cmd ); - free( cmdp ); + free(cmdp->cmd); + free(cmdp); if (!tcmd) return 0; /* ignored */ if (cmdp == tcmd) @@ -846,21 +932,21 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd ) continue; } resp = RESP_NO; - } else /*if (!strcmp( "BAD", arg ))*/ + } else /*if (!strcmp("BAD", arg))*/ resp = RESP_BAD; - fprintf( stderr, "IMAP command '%s' returned response (%s) - %s\n", - memcmp (cmdp->cmd, "LOGIN", 5) ? + fprintf(stderr, "IMAP command '%s' returned response (%s) - %s\n", + memcmp(cmdp->cmd, "LOGIN", 5) ? cmdp->cmd : "LOGIN <user> <pass>", arg, cmd ? cmd : ""); } - if ((resp2 = parse_response_code( ctx, &cmdp->cb, cmd )) > resp) + if ((resp2 = parse_response_code(ctx, &cmdp->cb, cmd)) > resp) resp = resp2; - normal: + normal: if (cmdp->cb.done) - cmdp->cb.done( ctx, cmdp, resp ); - free( cmdp->cb.data ); - free( cmdp->cmd ); - free( cmdp ); + cmdp->cb.done(ctx, cmdp, resp); + free(cmdp->cb.data); + free(cmdp->cmd); + free(cmdp); if (!tcmd || tcmd == cmdp) return resp; } @@ -868,170 +954,184 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd ) /* not reached */ } -static void -imap_close_server( imap_store_t *ictx ) +static void imap_close_server(struct imap_store *ictx) { - imap_t *imap = ictx->imap; + struct imap *imap = ictx->imap; if (imap->buf.sock.fd != -1) { - imap_exec( ictx, NULL, "LOGOUT" ); - close( imap->buf.sock.fd ); + imap_exec(ictx, NULL, "LOGOUT"); + socket_shutdown(&imap->buf.sock); } - free_list( imap->ns_personal ); - free_list( imap->ns_other ); - free_list( imap->ns_shared ); - free( imap ); + free_list(imap->ns_personal); + free_list(imap->ns_other); + free_list(imap->ns_shared); + free(imap); } -static void -imap_close_store( store_t *ctx ) +static void imap_close_store(struct store *ctx) { - imap_close_server( (imap_store_t *)ctx ); - free_generic_messages( ctx->msgs ); - free( ctx ); + imap_close_server((struct imap_store *)ctx); + free_generic_messages(ctx->msgs); + free(ctx); } -static store_t * -imap_open_store( imap_server_conf_t *srvc ) +static struct store *imap_open_store(struct imap_server_conf *srvc) { - imap_store_t *ctx; - imap_t *imap; + struct imap_store *ctx; + struct imap *imap; char *arg, *rsp; struct hostent *he; struct sockaddr_in addr; int s, a[2], preauth; pid_t pid; - ctx = xcalloc( sizeof(*ctx), 1 ); + ctx = xcalloc(sizeof(*ctx), 1); - ctx->imap = imap = xcalloc( sizeof(*imap), 1 ); + ctx->imap = imap = xcalloc(sizeof(*imap), 1); imap->buf.sock.fd = -1; imap->in_progress_append = &imap->in_progress; /* open connection to IMAP server */ if (srvc->tunnel) { - imap_info( "Starting tunnel '%s'... ", srvc->tunnel ); + imap_info("Starting tunnel '%s'... ", srvc->tunnel); - if (socketpair( PF_UNIX, SOCK_STREAM, 0, a )) { - perror( "socketpair" ); - exit( 1 ); + if (socketpair(PF_UNIX, SOCK_STREAM, 0, a)) { + perror("socketpair"); + exit(1); } pid = fork(); if (pid < 0) - _exit( 127 ); + _exit(127); if (!pid) { - if (dup2( a[0], 0 ) == -1 || dup2( a[0], 1 ) == -1) - _exit( 127 ); - close( a[0] ); - close( a[1] ); - execl( "/bin/sh", "sh", "-c", srvc->tunnel, NULL ); - _exit( 127 ); + if (dup2(a[0], 0) == -1 || dup2(a[0], 1) == -1) + _exit(127); + close(a[0]); + close(a[1]); + execl("/bin/sh", "sh", "-c", srvc->tunnel, NULL); + _exit(127); } - close (a[0]); + close(a[0]); imap->buf.sock.fd = a[1]; - imap_info( "ok\n" ); + imap_info("ok\n"); } else { - memset( &addr, 0, sizeof(addr) ); - addr.sin_port = htons( srvc->port ); + memset(&addr, 0, sizeof(addr)); + addr.sin_port = htons(srvc->port); addr.sin_family = AF_INET; - imap_info( "Resolving %s... ", srvc->host ); - he = gethostbyname( srvc->host ); + imap_info("Resolving %s... ", srvc->host); + he = gethostbyname(srvc->host); if (!he) { - perror( "gethostbyname" ); + perror("gethostbyname"); goto bail; } - imap_info( "ok\n" ); + imap_info("ok\n"); addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]); - s = socket( PF_INET, SOCK_STREAM, 0 ); + s = socket(PF_INET, SOCK_STREAM, 0); - imap_info( "Connecting to %s:%hu... ", inet_ntoa( addr.sin_addr ), ntohs( addr.sin_port ) ); - if (connect( s, (struct sockaddr *)&addr, sizeof(addr) )) { - close( s ); - perror( "connect" ); + imap_info("Connecting to %s:%hu... ", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + if (connect(s, (struct sockaddr *)&addr, sizeof(addr))) { + close(s); + perror("connect"); goto bail; } - imap_info( "ok\n" ); imap->buf.sock.fd = s; + if (srvc->use_ssl && + ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) { + close(s); + goto bail; + } + imap_info("ok\n"); } /* read the greeting string */ - if (buffer_gets( &imap->buf, &rsp )) { - fprintf( stderr, "IMAP error: no greeting response\n" ); + if (buffer_gets(&imap->buf, &rsp)) { + fprintf(stderr, "IMAP error: no greeting response\n"); goto bail; } - arg = next_arg( &rsp ); - if (!arg || *arg != '*' || (arg = next_arg( &rsp )) == NULL) { - fprintf( stderr, "IMAP error: invalid greeting response\n" ); + arg = next_arg(&rsp); + if (!arg || *arg != '*' || (arg = next_arg(&rsp)) == NULL) { + fprintf(stderr, "IMAP error: invalid greeting response\n"); goto bail; } preauth = 0; - if (!strcmp( "PREAUTH", arg )) + if (!strcmp("PREAUTH", arg)) preauth = 1; - else if (strcmp( "OK", arg ) != 0) { - fprintf( stderr, "IMAP error: unknown greeting response\n" ); + else if (strcmp("OK", arg) != 0) { + fprintf(stderr, "IMAP error: unknown greeting response\n"); goto bail; } - parse_response_code( ctx, NULL, rsp ); - if (!imap->caps && imap_exec( ctx, NULL, "CAPABILITY" ) != RESP_OK) + parse_response_code(ctx, NULL, rsp); + if (!imap->caps && imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK) goto bail; if (!preauth) { - - imap_info ("Logging in...\n"); +#ifndef NO_OPENSSL + if (!srvc->use_ssl && CAP(STARTTLS)) { + if (imap_exec(ctx, 0, "STARTTLS") != RESP_OK) + goto bail; + if (ssl_socket_connect(&imap->buf.sock, 1, + srvc->ssl_verify)) + goto bail; + /* capabilities may have changed, so get the new capabilities */ + if (imap_exec(ctx, 0, "CAPABILITY") != RESP_OK) + goto bail; + } +#endif + imap_info("Logging in...\n"); if (!srvc->user) { - fprintf( stderr, "Skipping server %s, no user\n", srvc->host ); + fprintf(stderr, "Skipping server %s, no user\n", srvc->host); goto bail; } if (!srvc->pass) { char prompt[80]; - sprintf( prompt, "Password (%s@%s): ", srvc->user, srvc->host ); - arg = getpass( prompt ); + sprintf(prompt, "Password (%s@%s): ", srvc->user, srvc->host); + arg = getpass(prompt); if (!arg) { - perror( "getpass" ); - exit( 1 ); + perror("getpass"); + exit(1); } if (!*arg) { - fprintf( stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host ); + fprintf(stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host); goto bail; } /* * getpass() returns a pointer to a static buffer. make a copy * for long term storage. */ - srvc->pass = xstrdup( arg ); + srvc->pass = xstrdup(arg); } if (CAP(NOLOGIN)) { - fprintf( stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host ); + fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host); goto bail; } - imap_warn( "*** IMAP Warning *** Password is being sent in the clear\n" ); - if (imap_exec( ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass ) != RESP_OK) { - fprintf( stderr, "IMAP error: LOGIN failed\n" ); + if (!imap->buf.sock.ssl) + imap_warn("*** IMAP Warning *** Password is being " + "sent in the clear\n"); + if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) { + fprintf(stderr, "IMAP error: LOGIN failed\n"); goto bail; } } /* !preauth */ ctx->prefix = ""; ctx->trashnc = 1; - return (store_t *)ctx; + return (struct store *)ctx; - bail: - imap_close_store( &ctx->gen ); +bail: + imap_close_store(&ctx->gen); return NULL; } -static int -imap_make_flags( int flags, char *buf ) +static int imap_make_flags(int flags, char *buf) { const char *s; unsigned i, d; @@ -1050,11 +1150,10 @@ imap_make_flags( int flags, char *buf ) #define TUIDL 8 -static int -imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) +static int imap_store_msg(struct store *gctx, struct msg_data *data, int *uid) { - imap_store_t *ctx = (imap_store_t *)gctx; - imap_t *imap = ctx->imap; + struct imap_store *ctx = (struct imap_store *)gctx; + struct imap *imap = ctx->imap; struct imap_cmd_cb cb; char *fmap, *buf; const char *prefix, *box; @@ -1062,14 +1161,14 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) int start, sbreak = 0, ebreak = 0; char flagstr[128], tuid[TUIDL * 2 + 1]; - memset( &cb, 0, sizeof(cb) ); + memset(&cb, 0, sizeof(cb)); fmap = data->data; len = data->len; nocr = !data->crlf; extra = 0, i = 0; if (!CAP(UIDPLUS) && uid) { - nloop: + nloop: start = i; while (i < len) if (fmap[i++] == '\n') { @@ -1078,18 +1177,18 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) sbreak = ebreak = i - 2 + nocr; goto mktid; } - if (!memcmp( fmap + start, "X-TUID: ", 8 )) { + if (!memcmp(fmap + start, "X-TUID: ", 8)) { extra -= (ebreak = i) - (sbreak = start) + nocr; goto mktid; } goto nloop; } /* invalid message */ - free( fmap ); + free(fmap); return DRV_MSG_BAD; - mktid: + mktid: for (j = 0; j < TUIDL; j++) - sprintf( tuid + j * 2, "%02x", arc4_getbyte() ); + sprintf(tuid + j * 2, "%02x", arc4_getbyte()); extra += 8 + TUIDL * 2 + 2; } if (nocr) @@ -1098,7 +1197,7 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) extra++; cb.dlen = len + extra; - buf = cb.data = xmalloc( cb.dlen ); + buf = cb.data = xmalloc(cb.dlen); i = 0; if (!CAP(UIDPLUS) && uid) { if (nocr) { @@ -1109,12 +1208,12 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) } else *buf++ = fmap[i]; } else { - memcpy( buf, fmap, sbreak ); + memcpy(buf, fmap, sbreak); buf += sbreak; } - memcpy( buf, "X-TUID: ", 8 ); + memcpy(buf, "X-TUID: ", 8); buf += 8; - memcpy( buf, tuid, TUIDL * 2 ); + memcpy(buf, tuid, TUIDL * 2); buf += TUIDL * 2; *buf++ = '\r'; *buf++ = '\n'; @@ -1128,13 +1227,13 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) } else *buf++ = fmap[i]; } else - memcpy( buf, fmap + i, len - i ); + memcpy(buf, fmap + i, len - i); - free( fmap ); + free(fmap); d = 0; if (data->flags) { - d = imap_make_flags( data->flags, flagstr ); + d = imap_make_flags(data->flags, flagstr); flagstr[d++] = ' '; } flagstr[d] = 0; @@ -1147,11 +1246,11 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) imap->caps = imap->rcaps & ~(1 << LITERALPLUS); } else { box = gctx->name; - prefix = !strcmp( box, "INBOX" ) ? "" : ctx->prefix; + prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix; cb.create = 0; } cb.ctx = uid; - ret = imap_exec_m( ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr ); + ret = imap_exec_m(ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr); imap->caps = imap->rcaps; if (ret != DRV_OK) return ret; @@ -1165,8 +1264,7 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) #define CHUNKSIZE 0x1000 -static int -read_message( FILE *f, msg_data_t *msg ) +static int read_message(FILE *f, struct msg_data *msg) { struct strbuf buf; @@ -1183,8 +1281,7 @@ read_message( FILE *f, msg_data_t *msg ) return msg->len; } -static int -count_messages( msg_data_t *msg ) +static int count_messages(struct msg_data *msg) { int count = 0; char *p = msg->data; @@ -1194,7 +1291,7 @@ count_messages( msg_data_t *msg ) count++; p += 5; } - p = strstr( p+5, "\nFrom "); + p = strstr(p+5, "\nFrom "); if (!p) break; p++; @@ -1202,22 +1299,21 @@ count_messages( msg_data_t *msg ) return count; } -static int -split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs ) +static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs) { char *p, *data; - memset( msg, 0, sizeof *msg ); + memset(msg, 0, sizeof *msg); if (*ofs >= all_msgs->len) return 0; - data = &all_msgs->data[ *ofs ]; + data = &all_msgs->data[*ofs]; msg->len = all_msgs->len - *ofs; if (msg->len < 5 || prefixcmp(data, "From ")) return 0; - p = strchr( data, '\n' ); + p = strchr(data, '\n'); if (p) { p = &p[1]; msg->len -= p-data; @@ -1225,7 +1321,7 @@ split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs ) data = p; } - p = strstr( data, "\nFrom " ); + p = strstr(data, "\nFrom "); if (p) msg->len = &p[1] - data; @@ -1234,24 +1330,24 @@ split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs ) return 1; } -static imap_server_conf_t server = -{ +static struct imap_server_conf server = { NULL, /* name */ NULL, /* tunnel */ NULL, /* host */ 0, /* port */ NULL, /* user */ NULL, /* pass */ + 0, /* use_ssl */ + 1, /* ssl_verify */ }; static char *imap_folder; -static int -git_imap_config(const char *key, const char *val, void *cb) +static int git_imap_config(const char *key, const char *val, void *cb) { char imap_key[] = "imap."; - if (strncmp( key, imap_key, sizeof imap_key - 1 )) + if (strncmp(key, imap_key, sizeof imap_key - 1)) return 0; if (!val) @@ -1259,90 +1355,96 @@ git_imap_config(const char *key, const char *val, void *cb) key += sizeof imap_key - 1; - if (!strcmp( "folder", key )) { - imap_folder = xstrdup( val ); - } else if (!strcmp( "host", key )) { - { - if (!prefixcmp(val, "imap:")) - val += 5; - if (!server.port) - server.port = 143; + if (!strcmp("folder", key)) { + imap_folder = xstrdup(val); + } else if (!strcmp("host", key)) { + if (!prefixcmp(val, "imap:")) + val += 5; + else if (!prefixcmp(val, "imaps:")) { + val += 6; + server.use_ssl = 1; } if (!prefixcmp(val, "//")) val += 2; - server.host = xstrdup( val ); - } - else if (!strcmp( "user", key )) - server.user = xstrdup( val ); - else if (!strcmp( "pass", key )) - server.pass = xstrdup( val ); - else if (!strcmp( "port", key )) - server.port = git_config_int( key, val ); - else if (!strcmp( "tunnel", key )) - server.tunnel = xstrdup( val ); + server.host = xstrdup(val); + } else if (!strcmp("user", key)) + server.user = xstrdup(val); + else if (!strcmp("pass", key)) + server.pass = xstrdup(val); + else if (!strcmp("port", key)) + server.port = git_config_int(key, val); + else if (!strcmp("tunnel", key)) + server.tunnel = xstrdup(val); + else if (!strcmp("sslverify", key)) + server.ssl_verify = git_config_bool(key, val); return 0; } -int -main(int argc, char **argv) +int main(int argc, char **argv) { - msg_data_t all_msgs, msg; - store_t *ctx = NULL; + struct msg_data all_msgs, msg; + struct store *ctx = NULL; int uid = 0; int ofs = 0; int r; int total, n = 0; + int nongit_ok; /* init the random number generator */ arc4_init(); + setup_git_directory_gently(&nongit_ok); git_config(git_imap_config, NULL); + if (!server.port) + server.port = server.use_ssl ? 993 : 143; + if (!imap_folder) { - fprintf( stderr, "no imap store specified\n" ); + fprintf(stderr, "no imap store specified\n"); return 1; } if (!server.host) { if (!server.tunnel) { - fprintf( stderr, "no imap host specified\n" ); + fprintf(stderr, "no imap host specified\n"); return 1; } server.host = "tunnel"; } /* read the messages */ - if (!read_message( stdin, &all_msgs )) { - fprintf(stderr,"nothing to send\n"); + if (!read_message(stdin, &all_msgs)) { + fprintf(stderr, "nothing to send\n"); return 1; } - total = count_messages( &all_msgs ); + total = count_messages(&all_msgs); if (!total) { - fprintf(stderr,"no messages to send\n"); + fprintf(stderr, "no messages to send\n"); return 1; } /* write it to the imap server */ - ctx = imap_open_store( &server ); + ctx = imap_open_store(&server); if (!ctx) { - fprintf( stderr,"failed to open store\n"); + fprintf(stderr, "failed to open store\n"); return 1; } - fprintf( stderr, "sending %d message%s\n", total, (total!=1)?"s":"" ); + fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : ""); ctx->name = imap_folder; while (1) { unsigned percent = n * 100 / total; - fprintf( stderr, "%4u%% (%d/%d) done\r", percent, n, total ); - if (!split_msg( &all_msgs, &msg, &ofs )) + fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total); + if (!split_msg(&all_msgs, &msg, &ofs)) + break; + r = imap_store_msg(ctx, &msg, &uid); + if (r != DRV_OK) break; - r = imap_store_msg( ctx, &msg, &uid ); - if (r != DRV_OK) break; n++; } - fprintf( stderr,"\n" ); + fprintf(stderr, "\n"); - imap_close_store( ctx ); + imap_close_store(ctx); return 0; } diff --git a/log-tree.c b/log-tree.c index bd8b9e45a..30cd5bb22 100644 --- a/log-tree.c +++ b/log-tree.c @@ -432,7 +432,7 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log struct commit_list *parents; unsigned const char *sha1 = commit->object.sha1; - if (!opt->diff) + if (!opt->diff && !DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS)) return 0; /* Root commit? */ diff --git a/pack-revindex.c b/pack-revindex.c index cd300bdff..6096b6224 100644 --- a/pack-revindex.c +++ b/pack-revindex.c @@ -142,3 +142,15 @@ struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs) } while (lo < hi); die("internal error: pack revindex corrupt"); } + +void discard_revindex(void) +{ + if (pack_revindex_hashsz) { + int i; + for (i = 0; i < pack_revindex_hashsz; i++) + if (pack_revindex[i].revindex) + free(pack_revindex[i].revindex); + free(pack_revindex); + pack_revindex_hashsz = 0; + } +} diff --git a/pack-revindex.h b/pack-revindex.h index 36a514a6c..8d5027ad9 100644 --- a/pack-revindex.h +++ b/pack-revindex.h @@ -7,5 +7,6 @@ struct revindex_entry { }; struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs); +void discard_revindex(void); #endif @@ -1,4 +1,5 @@ #include "cache.h" +#include "run-command.h" /* * This is split up from the rest of git so that we can do @@ -8,7 +9,7 @@ static int spawned_pager; #ifndef __MINGW32__ -static void run_pager(const char *pager) +static void pager_preexec(void) { /* * Work around bug in "less" by not starting it until we @@ -20,17 +21,13 @@ static void run_pager(const char *pager) FD_SET(0, &in); select(1, &in, NULL, &in, NULL); - execlp(pager, pager, NULL); - execl("/bin/sh", "sh", "-c", pager, NULL); + setenv("LESS", "FRSX", 0); } -#else -#include "run-command.h" +#endif static const char *pager_argv[] = { "sh", "-c", NULL, NULL }; -static struct child_process pager_process = { - .argv = pager_argv, - .in = -1 -}; +static struct child_process pager_process; + static void wait_for_pager(void) { fflush(stdout); @@ -40,14 +37,9 @@ static void wait_for_pager(void) close(2); finish_command(&pager_process); } -#endif void setup_pager(void) { -#ifndef __MINGW32__ - pid_t pid; - int fd[2]; -#endif const char *pager = getenv("GIT_PAGER"); if (!isatty(1)) @@ -66,37 +58,13 @@ void setup_pager(void) spawned_pager = 1; /* means we are emitting to terminal */ -#ifndef __MINGW32__ - if (pipe(fd) < 0) - return; - pid = fork(); - if (pid < 0) { - close(fd[0]); - close(fd[1]); - return; - } - - /* return in the child */ - if (!pid) { - dup2(fd[1], 1); - dup2(fd[1], 2); - close(fd[0]); - close(fd[1]); - return; - } - - /* The original process turns into the PAGER */ - dup2(fd[0], 0); - close(fd[0]); - close(fd[1]); - - setenv("LESS", "FRSX", 0); - run_pager(pager); - die("unable to execute pager '%s'", pager); - exit(255); -#else /* spawn the pager */ pager_argv[2] = pager; + pager_process.argv = pager_argv; + pager_process.in = -1; +#ifndef __MINGW32__ + pager_process.preexec_cb = pager_preexec; +#endif if (start_command(&pager_process)) return; @@ -107,7 +75,6 @@ void setup_pager(void) /* this makes sure that the parent terminates after the pager */ atexit(wait_for_pager); -#endif } int pager_in_use(void) diff --git a/read-cache.c b/read-cache.c index 35fec468b..5150c1e14 100644 --- a/read-cache.c +++ b/read-cache.c @@ -8,6 +8,11 @@ #include "cache-tree.h" #include "refs.h" #include "dir.h" +#include "tree.h" +#include "commit.h" +#include "diff.h" +#include "diffcore.h" +#include "revision.h" /* Index extensions. * @@ -1118,6 +1123,10 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en ce->ce_size = ntohl(ondisk->size); /* On-disk flags are just 16 bits */ ce->ce_flags = ntohs(ondisk->flags); + + /* For future extension: we do not understand this entry yet */ + if (ce->ce_flags & CE_EXTENDED) + die("Unknown index entry format"); hashcpy(ce->sha1, ondisk->sha1); len = ce->ce_flags & CE_NAMEMASK; @@ -1479,3 +1488,59 @@ int read_index_unmerged(struct index_state *istate) istate->cache_nr = dst - istate->cache; return !!last; } + +struct update_callback_data +{ + int flags; + int add_errors; +}; + +static void update_callback(struct diff_queue_struct *q, + struct diff_options *opt, void *cbdata) +{ + int i; + struct update_callback_data *data = cbdata; + + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + const char *path = p->one->path; + switch (p->status) { + default: + die("unexpected diff status %c", p->status); + case DIFF_STATUS_UNMERGED: + case DIFF_STATUS_MODIFIED: + case DIFF_STATUS_TYPE_CHANGED: + if (add_file_to_index(&the_index, path, data->flags)) { + if (!(data->flags & ADD_CACHE_IGNORE_ERRORS)) + die("updating files failed"); + data->add_errors++; + } + break; + case DIFF_STATUS_DELETED: + if (data->flags & ADD_CACHE_IGNORE_REMOVAL) + break; + if (!(data->flags & ADD_CACHE_PRETEND)) + remove_file_from_index(&the_index, path); + if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE)) + printf("remove '%s'\n", path); + break; + } + } +} + +int add_files_to_cache(const char *prefix, const char **pathspec, int flags) +{ + struct update_callback_data data; + struct rev_info rev; + init_revisions(&rev, prefix); + setup_revisions(0, NULL, &rev, NULL); + rev.prune_data = pathspec; + rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; + rev.diffopt.format_callback = update_callback; + data.flags = flags; + data.add_errors = 0; + rev.diffopt.format_callback_data = &data; + run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); + return !!data.add_errors; +} + diff --git a/receive-pack.c b/receive-pack.c index d44c19e6b..b81678a97 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -407,7 +407,7 @@ static const char *unpack(void) char keep_arg[256]; struct child_process ip; - s = sprintf(keep_arg, "--keep=receive-pack %i on ", getpid()); + s = sprintf(keep_arg, "--keep=receive-pack %"PRIuMAX" on ", (uintmax_t) getpid()); if (gethostname(keep_arg + s, sizeof(keep_arg) - s)) strcpy(keep_arg + s, "localhost"); @@ -449,6 +449,26 @@ static int verify_refname(char *name, int is_glob) return result; } +/* + * This function frees a refspec array. + * Warning: code paths should be checked to ensure that the src + * and dst pointers are always freeable pointers as well + * as the refspec pointer itself. + */ +void free_refspecs(struct refspec *refspec, int nr_refspec) +{ + int i; + + if (!refspec) + return; + + for (i = 0; i < nr_refspec; i++) { + free(refspec[i].src); + free(refspec[i].dst); + } + free(refspec); +} + static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify) { int i; @@ -567,7 +587,12 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp invalid: if (verify) { - free(rs); + /* + * nr_refspec must be greater than zero and i must be valid + * since it is only possible to reach this point from within + * the for loop above. + */ + free_refspecs(rs, i+1); return NULL; } die("Invalid refspec '%s'", refspec[i]); @@ -579,7 +604,7 @@ int valid_fetch_refspec(const char *fetch_refspec_str) struct refspec *refspec; refspec = parse_refspec_internal(1, fetch_refspec, 1, 1); - free(refspec); + free_refspecs(refspec, 1); return !!refspec; } @@ -78,6 +78,7 @@ void ref_remove_duplicates(struct ref *ref_map); int valid_fetch_refspec(const char *refspec); struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec); struct refspec *parse_push_refspec(int nr_refspec, const char **refspec); +void free_refspecs(struct refspec *refspec, int nr_refspec); int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, int nr_refspec, const char **refspec, int all); diff --git a/run-command.c b/run-command.c index bbb9c777e..caab37457 100644 --- a/run-command.c +++ b/run-command.c @@ -111,6 +111,8 @@ int start_command(struct child_process *cmd) unsetenv(*cmd->env); } } + if (cmd->preexec_cb) + cmd->preexec_cb(); if (cmd->git_cmd) { execv_git_cmd(cmd->argv); } else { diff --git a/run-command.h b/run-command.h index 5203a9ebb..4f2b7d7d4 100644 --- a/run-command.h +++ b/run-command.h @@ -42,6 +42,7 @@ struct child_process { unsigned no_stderr:1; unsigned git_cmd:1; /* if this is to be git sub-command */ unsigned stdout_to_stderr:1; + void (*preexec_cb)(void); }; int start_command(struct child_process *); diff --git a/sha1_file.c b/sha1_file.c index 32e4664b1..9ee1ed16a 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -990,6 +990,7 @@ void prepare_packed_git(void) void reprepare_packed_git(void) { + discard_revindex(); prepare_packed_git_run_once = 0; prepare_packed_git(); } @@ -2360,51 +2361,22 @@ int has_sha1_file(const unsigned char *sha1) return has_loose_object(sha1); } -int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) +static int index_mem(unsigned char *sha1, void *buf, size_t size, + int write_object, enum object_type type, const char *path) { - struct strbuf buf; - int ret; - - strbuf_init(&buf, 0); - if (strbuf_read(&buf, fd, 4096) < 0) { - strbuf_release(&buf); - return -1; - } - - if (!type) - type = blob_type; - if (write_object) - ret = write_sha1_file(buf.buf, buf.len, type, sha1); - else - ret = hash_sha1_file(buf.buf, buf.len, type, sha1); - strbuf_release(&buf); - - return ret; -} - -int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, - enum object_type type, const char *path) -{ - size_t size = xsize_t(st->st_size); - void *buf = NULL; int ret, re_allocated = 0; - if (size) - buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); - close(fd); - if (!type) type = OBJ_BLOB; /* * Convert blobs to git internal format */ - if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) { + if ((type == OBJ_BLOB) && path) { struct strbuf nbuf; strbuf_init(&nbuf, 0); if (convert_to_git(path, buf, size, &nbuf, write_object ? safe_crlf : 0)) { - munmap(buf, size); buf = strbuf_detach(&nbuf, &size); re_allocated = 1; } @@ -2414,12 +2386,33 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, ret = write_sha1_file(buf, size, typename(type), sha1); else ret = hash_sha1_file(buf, size, typename(type), sha1); - if (re_allocated) { + if (re_allocated) free(buf); - return ret; - } - if (size) + return ret; +} + +int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, + enum object_type type, const char *path) +{ + int ret; + size_t size = xsize_t(st->st_size); + + if (!S_ISREG(st->st_mode)) { + struct strbuf sbuf; + strbuf_init(&sbuf, 0); + if (strbuf_read(&sbuf, fd, 4096) >= 0) + ret = index_mem(sha1, sbuf.buf, sbuf.len, write_object, + type, path); + else + ret = -1; + strbuf_release(&sbuf); + } else if (size) { + void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + ret = index_mem(sha1, buf, size, write_object, type, path); munmap(buf, size); + } else + ret = index_mem(sha1, NULL, size, write_object, type, path); + close(fd); return ret; } diff --git a/sha1_name.c b/sha1_name.c index 4fb77f886..41b680915 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -349,7 +349,10 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) else nth = -1; } - if (0 <= nth) + if (100000000 <= nth) { + at_time = nth; + nth = -1; + } else if (0 <= nth) at_time = 0; else { char *tmp = xstrndup(str + at + 2, reflog_len); @@ -3,14 +3,6 @@ #include "exec_cmd.h" #include "strbuf.h" -/* Stubs for functions that make no sense for git-shell. These stubs - * are provided here to avoid linking in external redundant modules. - */ -void release_pack_memory(size_t need, int fd){} -void trace_argv_printf(const char **argv, const char *fmt, ...){} -void trace_printf(const char *fmt, ...){} - - static int do_generic_cmd(const char *me, char *arg) { const char *my_argv[4]; diff --git a/t/.gitignore b/t/.gitignore index b27e28008..7dcbb232c 100644 --- a/t/.gitignore +++ b/t/.gitignore @@ -1,2 +1,2 @@ -/trash directory +/trash directory* /test-results diff --git a/t/Makefile b/t/Makefile index 0d65cedaa..ed49c20b1 100644 --- a/t/Makefile +++ b/t/Makefile @@ -14,7 +14,8 @@ SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh) TSVN = $(wildcard t91[0-9][0-9]-*.sh) -all: pre-clean $(T) aggregate-results clean +all: pre-clean + $(MAKE) aggregate-results-and-cleanup $(T): @echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS) @@ -25,6 +26,10 @@ pre-clean: clean: $(RM) -r 'trash directory' test-results +aggregate-results-and-cleanup: $(T) + $(MAKE) aggregate-results + $(MAKE) clean + aggregate-results: '$(SHELL_PATH_SQ)' ./aggregate-results.sh test-results/t*-* @@ -34,4 +39,3 @@ full-svn-test: $(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=0 LC_ALL=en_US.UTF-8 .PHONY: pre-clean $(T) aggregate-results clean -.NOTPARALLEL: diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index dc473dfb5..6ac312b90 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -14,7 +14,7 @@ fi LIB_HTTPD_PATH=${LIB_HTTPD_PATH-'/usr/sbin/apache2'} LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'8111'} -TEST_PATH="$PWD"/../lib-httpd +TEST_PATH="$TEST_DIRECTORY"/lib-httpd HTTPD_ROOT_PATH="$PWD"/httpd HTTPD_DOCUMENT_ROOT_PATH=$HTTPD_ROOT_PATH/www diff --git a/t/t0022-crlf-rename.sh b/t/t0022-crlf-rename.sh index 7d1ce2d05..f1e1d4886 100755 --- a/t/t0022-crlf-rename.sh +++ b/t/t0022-crlf-rename.sh @@ -6,13 +6,13 @@ test_description='ignore CR in CRLF sequence while computing similiarity' test_expect_success setup ' - cat ../t0022-crlf-rename.sh >sample && + cat "$TEST_DIRECTORY"/t0022-crlf-rename.sh >sample && git add sample && test_tick && git commit -m Initial && - sed -e "s/\$/
/" ../t0022-crlf-rename.sh >elpmas && + sed -e "s/\$/
/" "$TEST_DIRECTORY"/t0022-crlf-rename.sh >elpmas && git add elpmas && rm -f sample && diff --git a/t/t0055-beyond-symlinks.sh b/t/t0055-beyond-symlinks.sh new file mode 100755 index 000000000..b29c37a5a --- /dev/null +++ b/t/t0055-beyond-symlinks.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +test_description='update-index and add refuse to add beyond symlinks' + +. ./test-lib.sh + +test_expect_success setup ' + >a && + mkdir b && + ln -s b c && + >c/d && + git update-index --add a b/d +' + +test_expect_success 'update-index --add beyond symlinks' ' + test_must_fail git update-index --add c/d && + ! ( git ls-files | grep c/d ) +' + +test_expect_success 'add beyond symlinks' ' + test_must_fail git add c/d && + ! ( git ls-files | grep c/d ) +' + +test_done diff --git a/t/t1000-read-tree-m-3way.sh b/t/t1000-read-tree-m-3way.sh index 807fb83af..22ba7a544 100755 --- a/t/t1000-read-tree-m-3way.sh +++ b/t/t1000-read-tree-m-3way.sh @@ -72,7 +72,7 @@ In addition: ' . ./test-lib.sh -. ../lib-read-tree-m-3way.sh +. "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh ################################################################ # Trivial "majority when 3 stages exist" merge plus #2ALT, #3ALT diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh index 1ec053513..fcdd15a35 100755 --- a/t/t1007-hash-object.sh +++ b/t/t1007-hash-object.sh @@ -49,16 +49,28 @@ setup_repo # Argument checking test_expect_success "multiple '--stdin's are rejected" ' - test_must_fail git hash-object --stdin --stdin < example + echo example | test_must_fail git hash-object --stdin --stdin ' test_expect_success "Can't use --stdin and --stdin-paths together" ' - test_must_fail git hash-object --stdin --stdin-paths && - test_must_fail git hash-object --stdin-paths --stdin + echo example | test_must_fail git hash-object --stdin --stdin-paths && + echo example | test_must_fail git hash-object --stdin-paths --stdin ' test_expect_success "Can't pass filenames as arguments with --stdin-paths" ' - test_must_fail git hash-object --stdin-paths hello < example + echo example | test_must_fail git hash-object --stdin-paths hello +' + +test_expect_success "Can't use --path with --stdin-paths" ' + echo example | test_must_fail git hash-object --stdin-paths --path=foo +' + +test_expect_success "Can't use --stdin-paths with --no-filters" ' + echo example | test_must_fail git hash-object --stdin-paths --no-filters +' + +test_expect_success "Can't use --path with --no-filters" ' + test_must_fail git hash-object --no-filters --path=foo ' # Behavior @@ -93,6 +105,42 @@ test_expect_success 'git hash-object --stdin file1 <file0 first operates on file test "$obname1" = "$obname1new" ' +test_expect_success 'check that appropriate filter is invoke when --path is used' ' + echo fooQ | tr Q "\\015" >file0 && + cp file0 file1 && + echo "file0 -crlf" >.gitattributes && + echo "file1 crlf" >>.gitattributes && + git config core.autocrlf true && + file0_sha=$(git hash-object file0) && + file1_sha=$(git hash-object file1) && + test "$file0_sha" != "$file1_sha" && + path1_sha=$(git hash-object --path=file1 file0) && + path0_sha=$(git hash-object --path=file0 file1) && + test "$file0_sha" = "$path0_sha" && + test "$file1_sha" = "$path1_sha" && + path1_sha=$(cat file0 | git hash-object --path=file1 --stdin) && + path0_sha=$(cat file1 | git hash-object --path=file0 --stdin) && + test "$file0_sha" = "$path0_sha" && + test "$file1_sha" = "$path1_sha" && + git config --unset core.autocrlf +' + +test_expect_success 'check that --no-filters option works' ' + echo fooQ | tr Q "\\015" >file0 && + cp file0 file1 && + echo "file0 -crlf" >.gitattributes && + echo "file1 crlf" >>.gitattributes && + git config core.autocrlf true && + file0_sha=$(git hash-object file0) && + file1_sha=$(git hash-object file1) && + test "$file0_sha" != "$file1_sha" && + nofilters_file1=$(git hash-object --no-filters file1) && + test "$file0_sha" = "$nofilters_file1" && + nofilters_file1=$(cat file1 | git hash-object --stdin) && + test "$file0_sha" = "$nofilters_file1" && + git config --unset core.autocrlf +' + pop_repo for args in "-w --stdin" "--stdin -w"; do diff --git a/t/t3504-cherry-pick-rerere.sh b/t/t3504-cherry-pick-rerere.sh new file mode 100755 index 000000000..f7b3518a3 --- /dev/null +++ b/t/t3504-cherry-pick-rerere.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +test_description='cherry-pick should rerere for conflicts' + +. ./test-lib.sh + +test_expect_success setup ' + echo foo >foo && + git add foo && test_tick && git commit -q -m 1 && + echo foo-master >foo && + git add foo && test_tick && git commit -q -m 2 && + + git checkout -b dev HEAD^ && + echo foo-dev >foo && + git add foo && test_tick && git commit -q -m 3 && + git config rerere.enabled true +' + +test_expect_success 'conflicting merge' ' + test_must_fail git merge master +' + +test_expect_success 'fixup' ' + echo foo-dev >foo && + git add foo && test_tick && git commit -q -m 4 && + git reset --hard HEAD^ + echo foo-dev >expect +' + +test_expect_success 'cherry-pick conflict' ' + test_must_fail git cherry-pick master && + test_cmp expect foo +' + +test_expect_success 'reconfigure' ' + git config rerere.enabled false + git reset --hard +' + +test_expect_success 'cherry-pick conflict without rerere' ' + test_must_fail git cherry-pick master && + test_must_fail test_cmp expect foo +' + +test_done diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh index 883281dbd..f4f41847f 100755 --- a/t/t3900-i18n-commit.sh +++ b/t/t3900-i18n-commit.sh @@ -16,7 +16,7 @@ test_expect_success setup ' : >F && git add F && T=$(git write-tree) && - C=$(git commit-tree $T <../t3900/1-UTF-8.txt) && + C=$(git commit-tree $T <"$TEST_DIRECTORY"/t3900/1-UTF-8.txt) && git update-ref HEAD $C && git-tag C0 ' @@ -32,7 +32,7 @@ do git config i18n.commitencoding $H && git-checkout -b $H C0 && echo $H >F && - git-commit -a -F ../t3900/$H.txt + git-commit -a -F "$TEST_DIRECTORY"/t3900/$H.txt ' done @@ -57,13 +57,13 @@ test_expect_success 'config to remove customization' ' ' test_expect_success 'ISO-8859-1 should be shown in UTF-8 now' ' - compare_with ISO-8859-1 ../t3900/1-UTF-8.txt + compare_with ISO-8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt ' for H in EUCJP ISO-2022-JP do test_expect_success "$H should be shown in UTF-8 now" ' - compare_with '$H' ../t3900/2-UTF-8.txt + compare_with '$H' "$TEST_DIRECTORY"/t3900/2-UTF-8.txt ' done @@ -82,7 +82,7 @@ for H in ISO-8859-1 EUCJP ISO-2022-JP do test_expect_success "$H should be shown in itself now" ' git config i18n.commitencoding '$H' && - compare_with '$H' ../t3900/'$H'.txt + compare_with '$H' "$TEST_DIRECTORY"/t3900/'$H'.txt ' done @@ -91,13 +91,13 @@ test_expect_success 'config to tweak customization' ' ' test_expect_success 'ISO-8859-1 should be shown in UTF-8 now' ' - compare_with ISO-8859-1 ../t3900/1-UTF-8.txt + compare_with ISO-8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt ' for H in EUCJP ISO-2022-JP do test_expect_success "$H should be shown in UTF-8 now" ' - compare_with '$H' ../t3900/2-UTF-8.txt + compare_with '$H' "$TEST_DIRECTORY"/t3900/2-UTF-8.txt ' done @@ -107,7 +107,7 @@ do for H in EUCJP ISO-2022-JP do test_expect_success "$H should be shown in $J now" ' - compare_with '$H' ../t3900/'$J'.txt + compare_with '$H' "$TEST_DIRECTORY"/t3900/'$J'.txt ' done done @@ -115,7 +115,7 @@ done for H in ISO-8859-1 EUCJP ISO-2022-JP do test_expect_success "No conversion with $H" ' - compare_with "--encoding=none '$H'" ../t3900/'$H'.txt + compare_with "--encoding=none '$H'" "$TEST_DIRECTORY"/t3900/'$H'.txt ' done diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh index 235f37283..f68ff5305 100755 --- a/t/t3901-i18n-patch.sh +++ b/t/t3901-i18n-patch.sh @@ -35,7 +35,7 @@ test_expect_success setup ' # use UTF-8 in author and committer name to match the # i18n.commitencoding settings - . ../t3901-utf8.txt && + . "$TEST_DIRECTORY"/t3901-utf8.txt && test_tick && echo "$GIT_AUTHOR_NAME" >mine && @@ -57,7 +57,7 @@ test_expect_success setup ' # the second one on the side branch is ISO-8859-1 git config i18n.commitencoding ISO-8859-1 && # use author and committer name in ISO-8859-1 to match it. - . ../t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901-8859-1.txt && test_tick && echo Yet another >theirs && git add theirs && @@ -101,7 +101,7 @@ test_expect_success 'rebase (U/U)' ' # The result will be committed by GIT_COMMITTER_NAME -- # we want UTF-8 encoded name. - . ../t3901-utf8.txt && + . "$TEST_DIRECTORY"/t3901-utf8.txt && git checkout -b test && git-rebase master && @@ -111,7 +111,7 @@ test_expect_success 'rebase (U/U)' ' test_expect_success 'rebase (U/L)' ' git config i18n.commitencoding UTF-8 && git config i18n.logoutputencoding ISO-8859-1 && - . ../t3901-utf8.txt && + . "$TEST_DIRECTORY"/t3901-utf8.txt && git reset --hard side && git-rebase master && @@ -123,7 +123,7 @@ test_expect_success 'rebase (L/L)' ' # In this test we want ISO-8859-1 encoded commits as the result git config i18n.commitencoding ISO-8859-1 && git config i18n.logoutputencoding ISO-8859-1 && - . ../t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901-8859-1.txt && git reset --hard side && git-rebase master && @@ -136,7 +136,7 @@ test_expect_success 'rebase (L/U)' ' # to get ISO-8859-1 results. git config i18n.commitencoding ISO-8859-1 && git config i18n.logoutputencoding UTF-8 && - . ../t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901-8859-1.txt && git reset --hard side && git-rebase master && @@ -149,7 +149,7 @@ test_expect_success 'cherry-pick(U/U)' ' git config i18n.commitencoding UTF-8 && git config i18n.logoutputencoding UTF-8 && - . ../t3901-utf8.txt && + . "$TEST_DIRECTORY"/t3901-utf8.txt && git reset --hard master && git cherry-pick side^ && @@ -164,7 +164,7 @@ test_expect_success 'cherry-pick(L/L)' ' git config i18n.commitencoding ISO-8859-1 && git config i18n.logoutputencoding ISO-8859-1 && - . ../t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901-8859-1.txt && git reset --hard master && git cherry-pick side^ && @@ -179,7 +179,7 @@ test_expect_success 'cherry-pick(U/L)' ' git config i18n.commitencoding UTF-8 && git config i18n.logoutputencoding ISO-8859-1 && - . ../t3901-utf8.txt && + . "$TEST_DIRECTORY"/t3901-utf8.txt && git reset --hard master && git cherry-pick side^ && @@ -195,7 +195,7 @@ test_expect_success 'cherry-pick(L/U)' ' git config i18n.commitencoding ISO-8859-1 && git config i18n.logoutputencoding UTF-8 && - . ../t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901-8859-1.txt && git reset --hard master && git cherry-pick side^ && @@ -208,7 +208,7 @@ test_expect_success 'cherry-pick(L/U)' ' test_expect_success 'rebase --merge (U/U)' ' git config i18n.commitencoding UTF-8 && git config i18n.logoutputencoding UTF-8 && - . ../t3901-utf8.txt && + . "$TEST_DIRECTORY"/t3901-utf8.txt && git reset --hard side && git-rebase --merge master && @@ -219,7 +219,7 @@ test_expect_success 'rebase --merge (U/U)' ' test_expect_success 'rebase --merge (U/L)' ' git config i18n.commitencoding UTF-8 && git config i18n.logoutputencoding ISO-8859-1 && - . ../t3901-utf8.txt && + . "$TEST_DIRECTORY"/t3901-utf8.txt && git reset --hard side && git-rebase --merge master && @@ -231,7 +231,7 @@ test_expect_success 'rebase --merge (L/L)' ' # In this test we want ISO-8859-1 encoded commits as the result git config i18n.commitencoding ISO-8859-1 && git config i18n.logoutputencoding ISO-8859-1 && - . ../t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901-8859-1.txt && git reset --hard side && git-rebase --merge master && @@ -244,7 +244,7 @@ test_expect_success 'rebase --merge (L/U)' ' # to get ISO-8859-1 results. git config i18n.commitencoding ISO-8859-1 && git config i18n.logoutputencoding UTF-8 && - . ../t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901-8859-1.txt && git reset --hard side && git-rebase --merge master && diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh index c44b27aeb..6ddd46915 100755 --- a/t/t4000-diff-format.sh +++ b/t/t4000-diff-format.sh @@ -7,7 +7,7 @@ test_description='Test built-in diff output engine. ' . ./test-lib.sh -. ../diff-lib.sh +. "$TEST_DIRECTORY"/diff-lib.sh echo >path0 'Line 1 Line 2 diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh index a32692417..71bac83dd 100755 --- a/t/t4001-diff-rename.sh +++ b/t/t4001-diff-rename.sh @@ -7,7 +7,7 @@ test_description='Test rename detection in diff engine. ' . ./test-lib.sh -. ../diff-lib.sh +. "$TEST_DIRECTORY"/diff-lib.sh echo >path0 'Line 1 Line 2 diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh index a4cfde6b2..cc3681f16 100755 --- a/t/t4002-diff-basic.sh +++ b/t/t4002-diff-basic.sh @@ -7,7 +7,7 @@ test_description='Test diff raw-output. ' . ./test-lib.sh -. ../lib-read-tree-m-3way.sh +. "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh cat >.test-plain-OA <<\EOF :000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d A AA @@ -169,6 +169,20 @@ test_expect_success \ cmp -s .test-a .test-recursive-AB' test_expect_success \ + 'diff-tree --stdin of known trees.' \ + 'echo $tree_A $tree_B | git diff-tree --stdin > .test-a && + echo $tree_A $tree_B > .test-plain-ABx && + cat .test-plain-AB >> .test-plain-ABx && + cmp -s .test-a .test-plain-ABx' + +test_expect_success \ + 'diff-tree --stdin of known trees.' \ + 'echo $tree_A $tree_B | git diff-tree -r --stdin > .test-a && + echo $tree_A $tree_B > .test-recursive-ABx && + cat .test-recursive-AB >> .test-recursive-ABx && + cmp -s .test-a .test-recursive-ABx' + +test_expect_success \ 'diff-cache O with A in cache' \ 'git read-tree $tree_A && git diff-index --cached $tree_O >.test-a && diff --git a/t/t4003-diff-rename-1.sh b/t/t4003-diff-rename-1.sh index 8b1f87528..c6130c401 100755 --- a/t/t4003-diff-rename-1.sh +++ b/t/t4003-diff-rename-1.sh @@ -7,11 +7,11 @@ test_description='More rename detection ' . ./test-lib.sh -. ../diff-lib.sh ;# test-lib chdir's into trash +. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash test_expect_success \ 'prepare reference tree' \ - 'cat ../../COPYING >COPYING && + 'cat "$TEST_DIRECTORY"/../COPYING >COPYING && echo frotz >rezrov && git update-index --add COPYING rezrov && tree=$(git write-tree) && @@ -99,7 +99,7 @@ test_expect_success \ test_expect_success \ 'prepare work tree once again' \ - 'cat ../../COPYING >COPYING && + 'cat "$TEST_DIRECTORY"/../COPYING >COPYING && git update-index --add --remove COPYING COPYING.1' # tree has COPYING and rezrov. work tree has COPYING and COPYING.1, diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh index 3d25be7a6..b35af9b42 100755 --- a/t/t4004-diff-rename-symlink.sh +++ b/t/t4004-diff-rename-symlink.sh @@ -10,7 +10,7 @@ copy of symbolic links, but should not produce rename/copy followed by an edit for them. ' . ./test-lib.sh -. ../diff-lib.sh +. "$TEST_DIRECTORY"/diff-lib.sh test_expect_success \ 'prepare reference tree' \ diff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh index 663001731..1ba359d47 100755 --- a/t/t4005-diff-rename-2.sh +++ b/t/t4005-diff-rename-2.sh @@ -7,11 +7,11 @@ test_description='Same rename detection as t4003 but testing diff-raw. ' . ./test-lib.sh -. ../diff-lib.sh ;# test-lib chdir's into trash +. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash test_expect_success \ 'prepare reference tree' \ - 'cat ../../COPYING >COPYING && + 'cat "$TEST_DIRECTORY"/../COPYING >COPYING && echo frotz >rezrov && git update-index --add COPYING rezrov && tree=$(git write-tree) && @@ -71,7 +71,7 @@ test_expect_success \ test_expect_success \ 'prepare work tree once again' \ - 'cat ../../COPYING >COPYING && + 'cat "$TEST_DIRECTORY"/../COPYING >COPYING && git update-index --add --remove COPYING COPYING.1' git diff-index -C --find-copies-harder $tree >current diff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh index 104a4e149..42072d724 100755 --- a/t/t4007-rename-3.sh +++ b/t/t4007-rename-3.sh @@ -7,12 +7,12 @@ test_description='Rename interaction with pathspec. ' . ./test-lib.sh -. ../diff-lib.sh ;# test-lib chdir's into trash +. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash test_expect_success \ 'prepare reference tree' \ 'mkdir path0 path1 && - cp ../../COPYING path0/COPYING && + cp "$TEST_DIRECTORY"/../COPYING path0/COPYING && git update-index --add path0/COPYING && tree=$(git write-tree) && echo $tree' diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh index 26c2e4aa6..7e343a9cd 100755 --- a/t/t4008-diff-break-rewrite.sh +++ b/t/t4008-diff-break-rewrite.sh @@ -22,12 +22,12 @@ four changes in total. Further, with -B and -M together, these should turn into two renames. ' . ./test-lib.sh -. ../diff-lib.sh ;# test-lib chdir's into trash +. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash test_expect_success \ setup \ - 'cat ../../README >file0 && - cat ../../COPYING >file1 && + 'cat "$TEST_DIRECTORY"/../README >file0 && + cat "$TEST_DIRECTORY"/../COPYING >file1 && git update-index --add file0 file1 && tree=$(git write-tree) && echo "$tree"' diff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh index d2b45e7b8..de3f17478 100755 --- a/t/t4009-diff-rename-4.sh +++ b/t/t4009-diff-rename-4.sh @@ -7,11 +7,11 @@ test_description='Same rename detection as t4003 but testing diff-raw -z. ' . ./test-lib.sh -. ../diff-lib.sh ;# test-lib chdir's into trash +. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash test_expect_success \ 'prepare reference tree' \ - 'cat ../../COPYING >COPYING && + 'cat "$TEST_DIRECTORY"/../COPYING >COPYING && echo frotz >rezrov && git update-index --add COPYING rezrov && tree=$(git write-tree) && @@ -78,7 +78,7 @@ test_expect_success \ test_expect_success \ 'prepare work tree once again' \ - 'cat ../../COPYING >COPYING && + 'cat "$TEST_DIRECTORY"/../COPYING >COPYING && git update-index --add --remove COPYING COPYING.1' git diff-index -z -C --find-copies-harder $tree >current diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh index ad3d9e484..9322298ec 100755 --- a/t/t4010-diff-pathspec.sh +++ b/t/t4010-diff-pathspec.sh @@ -10,7 +10,7 @@ Prepare: path1/file1 ' . ./test-lib.sh -. ../diff-lib.sh ;# test-lib chdir's into trash +. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash test_expect_success \ setup \ diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh index c6d13693b..02efecae3 100755 --- a/t/t4011-diff-symlink.sh +++ b/t/t4011-diff-symlink.sh @@ -7,7 +7,7 @@ test_description='Test diff of symlinks. ' . ./test-lib.sh -. ../diff-lib.sh +. "$TEST_DIRECTORY"/diff-lib.sh cat > expected << EOF diff --git a/frotz b/frotz diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh index eced1f30f..69934991c 100755 --- a/t/t4012-diff-binary.sh +++ b/t/t4012-diff-binary.sh @@ -12,7 +12,7 @@ test_expect_success 'prepare repository' \ 'echo AIT >a && echo BIT >b && echo CIT >c && echo DIT >d && git update-index --add a b c d && echo git >a && - cat ../test4012.png >b && + cat "$TEST_DIRECTORY"/test4012.png >b && echo git >c && cat b b >d' diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 9337b8106..1a6b52234 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -99,7 +99,7 @@ do test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'` cnt=`expr $test_count + 1` pfx=`printf "%04d" $cnt` - expect="../t4013/diff.$test" + expect="$TEST_DIRECTORY/t4013/diff.$test" actual="$pfx-diff.$test" test_expect_success "git $cmd" ' diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 7fe853c20..9d99dc288 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -230,4 +230,29 @@ test_expect_success 'shortlog of cover-letter wraps overly-long onelines' ' ' +cat > expect << EOF +--- + file | 16 ++++++++++++++++ + 1 files changed, 16 insertions(+), 0 deletions(-) + +diff --git a/file b/file +index 40f36c6..2dc5c23 100644 +--- a/file ++++ b/file +@@ -13,4 +13,20 @@ C + 10 + D + E + F ++5 +EOF + +test_expect_success 'format-patch respects -U' ' + + git format-patch -U4 -2 && + sed -e "1,/^$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output && + test_cmp expect output + +' + test_done diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh index b1cbd36d1..fc2307eaa 100755 --- a/t/t4015-diff-whitespace.sh +++ b/t/t4015-diff-whitespace.sh @@ -7,7 +7,7 @@ test_description='Test special whitespace in diff engine. ' . ./test-lib.sh -. ../diff-lib.sh +. "$TEST_DIRECTORY"/diff-lib.sh # Ray Lehtiniemi's example diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh index 637b4e19d..dfe3fbc74 100755 --- a/t/t4020-diff-external.sh +++ b/t/t4020-diff-external.sh @@ -104,7 +104,7 @@ echo NULZbetweenZwords | perl -pe 'y/Z/\000/' > file test_expect_success 'force diff with "diff"' ' echo >.gitattributes "file diff" && git diff >actual && - test_cmp ../t4020/diff.NUL actual + test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual ' test_done diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh index bf996fc41..2a537a21e 100755 --- a/t/t4022-diff-rewrite.sh +++ b/t/t4022-diff-rewrite.sh @@ -6,12 +6,12 @@ test_description='rewrite diff' test_expect_success setup ' - cat ../../COPYING >test && + cat "$TEST_DIRECTORY"/../COPYING >test && git add test && tr \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \ "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM" \ - <../../COPYING >test + <"$TEST_DIRECTORY"/../COPYING >test ' diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh index 4dbfc6e8b..297ddb5a2 100755 --- a/t/t4023-diff-rename-typechange.sh +++ b/t/t4023-diff-rename-typechange.sh @@ -7,21 +7,21 @@ test_description='typechange rename detection' test_expect_success setup ' rm -f foo bar && - cat ../../COPYING >foo && + cat "$TEST_DIRECTORY"/../COPYING >foo && ln -s linklink bar && git add foo bar && git commit -a -m Initial && git tag one && rm -f foo bar && - cat ../../COPYING >bar && + cat "$TEST_DIRECTORY"/../COPYING >bar && ln -s linklink foo && git add foo bar && git commit -a -m Second && git tag two && rm -f foo bar && - cat ../../COPYING >foo && + cat "$TEST_DIRECTORY"/../COPYING >foo && git add foo && git commit -a -m Third && git tag three && @@ -35,15 +35,15 @@ test_expect_success setup ' # This is purely for sanity check rm -f foo bar && - cat ../../COPYING >foo && - cat ../../Makefile >bar && + cat "$TEST_DIRECTORY"/../COPYING >foo && + cat "$TEST_DIRECTORY"/../Makefile >bar && git add foo bar && git commit -a -m Fifth && git tag five && rm -f foo bar && - cat ../../Makefile >foo && - cat ../../COPYING >bar && + cat "$TEST_DIRECTORY"/../Makefile >foo && + cat "$TEST_DIRECTORY"/../COPYING >bar && git add foo bar && git commit -a -m Sixth && git tag six diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh index ba6679c6e..1c2edebb0 100755 --- a/t/t4027-diff-submodule.sh +++ b/t/t4027-diff-submodule.sh @@ -3,7 +3,7 @@ test_description='difference in submodules' . ./test-lib.sh -. ../diff-lib.sh +. "$TEST_DIRECTORY"/diff-lib.sh _z40=0000000000000000000000000000000000000000 test_expect_success setup ' diff --git a/t/t4029-diff-trailing-space.sh b/t/t4029-diff-trailing-space.sh new file mode 100755 index 000000000..4ca65e033 --- /dev/null +++ b/t/t4029-diff-trailing-space.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# +# Copyright (c) Jim Meyering +# +test_description='diff honors config option, diff.suppress-blank-empty' + +. ./test-lib.sh + +cat <<\EOF > exp || +diff --git a/f b/f +index 5f6a263..8cb8bae 100644 +--- a/f ++++ b/f +@@ -1,2 +1,2 @@ + +-x ++y +EOF +exit 1 + +test_expect_success \ + "$test_description" \ + 'printf "\nx\n" > f && + git add f && + git commit -q -m. f && + printf "\ny\n" > f && + git config --bool diff.suppress-blank-empty true && + git diff f > actual && + test_cmp exp actual && + perl -i.bak -p -e "s/^\$/ /" exp && + git config --bool diff.suppress-blank-empty false && + git diff f > actual && + test_cmp exp actual && + git config --bool --unset diff.suppress-blank-empty && + git diff f > actual && + test_cmp exp actual + ' + +test_done diff --git a/t/t4100-apply-stat.sh b/t/t4100-apply-stat.sh index e0c67740a..9b433de83 100755 --- a/t/t4100-apply-stat.sh +++ b/t/t4100-apply-stat.sh @@ -17,13 +17,13 @@ do test_expect_success "$title" ' git apply --stat --summary \ <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" >current && - test_cmp ../t4100/t-apply-$num.expect current + test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current ' test_expect_success "$title with recount" ' sed -e "$UNC" <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" | git apply --recount --stat --summary >current && - test_cmp ../t4100/t-apply-$num.expect current + test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current ' done <<\EOF rename diff --git a/t/t4101-apply-nonl.sh b/t/t4101-apply-nonl.sh index da8abcf36..e3443d004 100755 --- a/t/t4101-apply-nonl.sh +++ b/t/t4101-apply-nonl.sh @@ -21,9 +21,10 @@ do do test $i -eq $j && continue cat frotz.$i >frotz - test_expect_success \ - "apply diff between $i and $j" \ - "git apply <../t4101/diff.$i-$j && diff frotz.$j frotz" + test_expect_success "apply diff between $i and $j" ' + git apply <"$TEST_DIRECTORY"/t4101/diff.$i-$j && + test_cmp frotz.$j frotz + ' done done diff --git a/t/t4104-apply-boundary.sh b/t/t4104-apply-boundary.sh index e7e2913de..0e3ce3611 100755 --- a/t/t4104-apply-boundary.sh +++ b/t/t4104-apply-boundary.sh @@ -27,6 +27,15 @@ test_expect_success setup ' git diff victim >add-a-patch.with && git diff --unified=0 >add-a-patch.without && + : insert at line two + for i in b a '"$L"' y + do + echo $i + done >victim && + cat victim >insert-a-expect && + git diff victim >insert-a-patch.with && + git diff --unified=0 >insert-a-patch.without && + : modify at the head for i in a '"$L"' y do @@ -55,7 +64,7 @@ test_expect_success setup ' git diff --unified=0 >add-z-patch.without && : modify at the tail - for i in a '"$L"' y + for i in b '"$L"' z do echo $i done >victim && @@ -81,7 +90,7 @@ do with) u= ;; without) u='--unidiff-zero ' ;; esac - for kind in add-a add-z mod-a mod-z del-a del-z + for kind in add-a add-z insert-a mod-a mod-z del-a del-z do test_expect_success "apply $kind-patch $with context" ' cat original >victim && @@ -95,7 +104,7 @@ do done done -for kind in add-a add-z mod-a mod-z del-a del-z +for kind in add-a add-z insert-a mod-a mod-z del-a del-z do rm -f $kind-ng.without sed -e "s/^diff --git /diff /" \ diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh index 198e3503d..fe1458942 100755 --- a/t/t5100-mailinfo.sh +++ b/t/t5100-mailinfo.sh @@ -8,27 +8,28 @@ test_description='git mailinfo and git mailsplit test' . ./test-lib.sh test_expect_success 'split sample box' \ - 'git mailsplit -o. ../t5100/sample.mbox >last && + 'git mailsplit -o. "$TEST_DIRECTORY"/t5100/sample.mbox >last && last=`cat last` && echo total is $last && test `cat last` = 11' for mail in `echo 00*` do - test_expect_success "mailinfo $mail" \ - "git mailinfo -u msg$mail patch$mail <$mail >info$mail && + test_expect_success "mailinfo $mail" ' + git mailinfo -u msg$mail patch$mail <$mail >info$mail && echo msg && - diff ../t5100/msg$mail msg$mail && + test_cmp "$TEST_DIRECTORY"/t5100/msg$mail msg$mail && echo patch && - diff ../t5100/patch$mail patch$mail && + test_cmp "$TEST_DIRECTORY"/t5100/patch$mail patch$mail && echo info && - diff ../t5100/info$mail info$mail" + test_cmp "$TEST_DIRECTORY"/t5100/info$mail info$mail + ' done test_expect_success 'respect NULs' ' - git mailsplit -d3 -o. ../t5100/nul-plain && - cmp ../t5100/nul-plain 001 && + git mailsplit -d3 -o. "$TEST_DIRECTORY"/t5100/nul-plain && + test_cmp "$TEST_DIRECTORY"/t5100/nul-plain 001 && (cat 001 | git mailinfo msg patch) && test 4 = $(wc -l < patch) @@ -36,10 +37,10 @@ test_expect_success 'respect NULs' ' test_expect_success 'Preserve NULs out of MIME encoded message' ' - git mailsplit -d5 -o. ../t5100/nul-b64.in && - cmp ../t5100/nul-b64.in 00001 && + git mailsplit -d5 -o. "$TEST_DIRECTORY"/t5100/nul-b64.in && + test_cmp "$TEST_DIRECTORY"/t5100/nul-b64.in 00001 && git mailinfo msg patch <00001 && - cmp ../t5100/nul-b64.expect patch + test_cmp "$TEST_DIRECTORY"/t5100/nul-b64.expect patch ' diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index 645583f9d..83abe5f25 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -187,6 +187,12 @@ test_expect_success \ test-3-${packname_3}.idx' test_expect_success \ + 'verify pack -v' \ + 'git verify-pack -v test-1-${packname_1}.idx \ + test-2-${packname_2}.idx \ + test-3-${packname_3}.idx' + +test_expect_success \ 'verify-pack catches mismatched .idx and .pack files' \ 'cat test-1-${packname_1}.idx >test-3.idx && cat test-2-${packname_2}.pack >test-3.pack && diff --git a/t/t5306-pack-nobase.sh b/t/t5306-pack-nobase.sh new file mode 100755 index 000000000..f4931c0c2 --- /dev/null +++ b/t/t5306-pack-nobase.sh @@ -0,0 +1,80 @@ +#!/bin/sh +# +# Copyright (c) 2008 Google Inc. +# + +test_description='git-pack-object with missing base + +' +. ./test-lib.sh + +# Create A-B chain +# +test_expect_success \ + 'setup base' \ + 'for a in a b c d e f g h i; do echo $a >>text; done && + echo side >side && + git update-index --add text side && + A=$(echo A | git commit-tree $(git write-tree)) && + + echo m >>text && + git update-index text && + B=$(echo B | git commit-tree $(git write-tree) -p $A) && + git update-ref HEAD $B + ' + +# Create repository with C whose parent is B. +# Repository contains C, C^{tree}, C:text, B, B^{tree}. +# Repository is missing B:text (best delta base for C:text). +# Repository is missing A (parent of B). +# Repository is missing A:side. +# +test_expect_success \ + 'setup patch_clone' \ + 'base_objects=$(pwd)/.git/objects && + (mkdir patch_clone && + cd patch_clone && + git init && + echo "$base_objects" >.git/objects/info/alternates && + echo q >>text && + git read-tree $B && + git update-index text && + git update-ref HEAD $(echo C | git commit-tree $(git write-tree) -p $B) && + rm .git/objects/info/alternates && + + git --git-dir=../.git cat-file commit $B | + git hash-object -t commit -w --stdin && + + git --git-dir=../.git cat-file tree "$B^{tree}" | + git hash-object -t tree -w --stdin + ) && + C=$(git --git-dir=patch_clone/.git rev-parse HEAD) + ' + +# Clone patch_clone indirectly by cloning base and fetching. +# +test_expect_success \ + 'indirectly clone patch_clone' \ + '(mkdir user_clone && + cd user_clone && + git init && + git pull ../.git && + test $(git rev-parse HEAD) = $B && + + git pull ../patch_clone/.git && + test $(git rev-parse HEAD) = $C + ) + ' + +# Cloning the patch_clone directly should fail. +# +test_expect_success \ + 'clone of patch_clone is incomplete' \ + '(mkdir user_direct && + cd user_direct && + git init && + test_must_fail git fetch ../patch_clone/.git + ) + ' + +test_done diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 362cf7e92..7125baebb 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -137,7 +137,7 @@ test_expect_success "clone shallow object count" \ "test \"in-pack: 18\" = \"$(grep in-pack count.shallow)\"" count_output () { - sed -e '/^in-pack:/d' -e '/^packs:/d' -e '/: 0$/d' "$1" + sed -e '/^in-pack:/d' -e '/^packs:/d' -e '/^size-pack:/d' -e '/: 0$/d' "$1" } test_expect_success "clone shallow object count (part 2)" ' diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index be9ee9326..c4496635d 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -109,7 +109,7 @@ test_expect_success 'remove remote' ' cat > test/expect << EOF * remote origin - URL: $(pwd)/one/.git + URL: $(pwd)/one Remote branch merged with 'git pull' while on branch master master New remote branch (next fetch will store in remotes/origin) @@ -140,7 +140,7 @@ test_expect_success 'show' ' cat > test/expect << EOF * remote origin - URL: $(pwd)/one/.git + URL: $(pwd)/one Remote branch merged with 'git pull' while on branch master master Tracked remote branches @@ -169,7 +169,7 @@ test_expect_success 'prune' ' cat > test/expect << EOF Pruning origin -URL: $(pwd)/one/.git +URL: $(pwd)/one * [would prune] origin/side2 EOF diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh index 8becbc3f3..1f4608d8b 100755 --- a/t/t5515-fetch-merge-logic.sh +++ b/t/t5515-fetch-merge-logic.sh @@ -131,9 +131,9 @@ do test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'` cnt=`expr $test_count + 1` pfx=`printf "%04d" $cnt` - expect_f="../../t5515/fetch.$test" + expect_f="$TEST_DIRECTORY/t5515/fetch.$test" actual_f="$pfx-fetch.$test" - expect_r="../../t5515/refs.$test" + expect_r="$TEST_DIRECTORY/t5515/refs.$test" actual_r="$pfx-refs.$test" test_expect_success "$cmd" ' diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh index b0d242e3e..da9588645 100755 --- a/t/t5540-http-push.sh +++ b/t/t5540-http-push.sh @@ -19,7 +19,7 @@ then exit fi -. ../lib-httpd.sh +. "$TEST_DIRECTORY"/lib-httpd.sh if ! start_httpd >&3 2>&4 then diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh index 8f5de097e..b4e8fbaa5 100755 --- a/t/t6002-rev-list-bisect.sh +++ b/t/t6002-rev-list-bisect.sh @@ -5,7 +5,7 @@ test_description='Tests git rev-list --bisect functionality' . ./test-lib.sh -. ../t6000lib.sh # t6xxx specific functions +. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions # usage: test_bisection max-diff bisect-option head ^prune... # diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh index 5daa0be8c..2c73f2da7 100755 --- a/t/t6003-rev-list-topo-order.sh +++ b/t/t6003-rev-list-topo-order.sh @@ -6,7 +6,7 @@ test_description='Tests git rev-list --topo-order functionality' . ./test-lib.sh -. ../t6000lib.sh # t6xxx specific functions +. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions list_duplicates() { diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh index b6e57b242..04e4b7c5c 100755 --- a/t/t6010-merge-base.sh +++ b/t/t6010-merge-base.sh @@ -108,4 +108,52 @@ test_expect_success 'compute merge-base (all)' \ 'MB=$(git merge-base --all PL PR) && expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"' +# Another set to demonstrate base between one commit and a merge +# in the documentation. + +test_expect_success 'merge-base for octopus-step (setup)' ' + test_tick && git commit --allow-empty -m root && git tag MMR && + test_tick && git commit --allow-empty -m 1 && git tag MM1 && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m A && git tag MMA && + git checkout MM1 && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m B && git tag MMB && + git checkout MMR && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m C && git tag MMC +' + +test_expect_success 'merge-base A B C' ' + MB=$(git merge-base --all MMA MMB MMC) && + MM1=$(git rev-parse --verify MM1) && + test "$MM1" = "$MB" +' + +test_expect_success 'criss-cross merge-base for octopus-step (setup)' ' + git reset --hard MMR && + test_tick && git commit --allow-empty -m 1 && git tag CC1 && + git reset --hard E && + test_tick && git commit --allow-empty -m 2 && git tag CC2 && + test_tick && git merge -s ours CC1 && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m B && git tag CCB && + git reset --hard CC1 && + test_tick && git merge -s ours CC2 && + test_tick && git commit --allow-empty -m A && git tag CCA +' + +test_expect_success 'merge-base B A^^ A^^2' ' + MB0=$(git merge-base --all CCB CCA^^ CCA^^2 | sort) && + MB1=$(git rev-parse CC1 CC2 | sort) && + test "$MB0" = "$MB1" +' + test_done diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh index f674c48ca..42620e073 100755 --- a/t/t6023-merge-file.sh +++ b/t/t6023-merge-file.sh @@ -136,7 +136,7 @@ test_expect_success "expected conflict markers" "test_cmp expect out" test_expect_success 'binary files cannot be merged' ' test_must_fail git merge-file -p \ - orig.txt ../test4012.png new1.txt 2> merge.err && + orig.txt "$TEST_DIRECTORY"/test4012.png new1.txt 2> merge.err && grep "Cannot merge binary files" merge.err ' diff --git a/t/t6027-merge-binary.sh b/t/t6027-merge-binary.sh index 92ca1f0f8..b519626ca 100755 --- a/t/t6027-merge-binary.sh +++ b/t/t6027-merge-binary.sh @@ -6,7 +6,7 @@ test_description='ask merge-recursive to merge binary files' test_expect_success setup ' - cat ../test4012.png >m && + cat "$TEST_DIRECTORY"/test4012.png >m && git add m && git ls-files -s | sed -e "s/ 0 / 1 /" >E1 && test_tick && diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh index 919552a2f..f105fab98 100755 --- a/t/t6101-rev-parse-parents.sh +++ b/t/t6101-rev-parse-parents.sh @@ -6,7 +6,7 @@ test_description='Test git rev-parse with different parent options' . ./test-lib.sh -. ../t6000lib.sh # t6xxx specific functions +. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions date >path0 git update-index --add path0 diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh index bc7434941..8f5a06f7d 100755 --- a/t/t6200-fmt-merge-msg.sh +++ b/t/t6200-fmt-merge-msg.sh @@ -83,13 +83,13 @@ test_expect_success 'merge-msg test #1' ' ' cat >expected <<EOF -Merge branch 'left' of ../$test +Merge branch 'left' of $TEST_DIRECTORY/$test EOF test_expect_success 'merge-msg test #2' ' git checkout master && - git fetch ../"$test" left && + git fetch "$TEST_DIRECTORY/$test" left && git fmt-merge-msg <.git/FETCH_HEAD >actual && test_cmp expected actual diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index 910a28c7e..78167980b 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -6,7 +6,7 @@ test_description='git mv in subdirs' test_expect_success \ 'prepare reference tree' \ 'mkdir path0 path1 && - cp ../../COPYING path0/COPYING && + cp "$TEST_DIRECTORY"/../COPYING path0/COPYING && git add path0/COPYING && git-commit -m add -a' @@ -40,7 +40,7 @@ test_expect_success \ test_expect_success \ 'adding another file' \ - 'cp ../../README path0/README && + 'cp "$TEST_DIRECTORY"/../README path0/README && git add path0/README && git-commit -m add2 -a' diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index a0ab096c8..f92d414e6 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -250,4 +250,12 @@ test_expect_success 'Tag name filtering strips gpg signature' ' test_cmp expect actual ' +test_expect_success 'Tag name filtering allows slashes in tag names' ' + git tag -m tag-with-slash X/1 && + git cat-file tag X/1 | sed -e s,X/1,X/2, > expect && + git filter-branch -f --tag-name-filter "echo X/2" && + git cat-file tag X/2 > actual && + test_cmp expect actual +' + test_done diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 8d44c2ed1..198244c57 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -625,7 +625,7 @@ esac # Name and email: C O Mitter <committer@example.com> # No password given, to enable non-interactive operation. -cp -R ../t7004 ./gpghome +cp -R "$TEST_DIRECTORY"/t7004 ./gpghome chmod 0700 gpghome GNUPGHOME="$(pwd)/gpghome" export GNUPGHOME diff --git a/t/t7101-reset.sh b/t/t7101-reset.sh index 0d9874bfd..ffaeb3983 100755 --- a/t/t7101-reset.sh +++ b/t/t7101-reset.sh @@ -9,7 +9,7 @@ test_description='git-reset should cull empty subdirs' test_expect_success \ 'creating initial files' \ 'mkdir path0 && - cp ../../COPYING path0/COPYING && + cp "$TEST_DIRECTORY"/../COPYING path0/COPYING && git add path0/COPYING && git-commit -m add -a' @@ -17,10 +17,10 @@ test_expect_success \ 'creating second files' \ 'mkdir path1 && mkdir path1/path2 && - cp ../../COPYING path1/path2/COPYING && - cp ../../COPYING path1/COPYING && - cp ../../COPYING COPYING && - cp ../../COPYING path0/COPYING-TOO && + cp "$TEST_DIRECTORY"/../COPYING path1/path2/COPYING && + cp "$TEST_DIRECTORY"/../COPYING path1/COPYING && + cp "$TEST_DIRECTORY"/../COPYING COPYING && + cp "$TEST_DIRECTORY"/../COPYING path0/COPYING-TOO && git add path1/path2/COPYING && git add path1/COPYING && git add COPYING && diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 9ad5d635a..1dff84d2f 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -337,4 +337,36 @@ test_expect_success \ test refs/heads/delete-me = "$(git symbolic-ref HEAD)" && test_must_fail git checkout --track -b track' +test_expect_success \ + 'checkout with --track fakes a sensible -b <name>' ' + git update-ref refs/remotes/origin/koala/bear renamer && + git update-ref refs/new/koala/bear renamer && + + git checkout --track origin/koala/bear && + test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" && + test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" && + + git checkout master && git branch -D koala/bear && + + git checkout --track refs/remotes/origin/koala/bear && + test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" && + test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" && + + git checkout master && git branch -D koala/bear && + + git checkout --track remotes/origin/koala/bear && + test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" && + test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" && + + git checkout master && git branch -D koala/bear && + + git checkout --track refs/new/koala/bear && + test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" && + test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" +' + +test_expect_success \ + 'checkout with --track, but without -b, fails with too short tracked name' ' + test_must_fail git checkout --track renamer' + test_done diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh index 809bdba63..7ae0bd0e3 100755 --- a/t/t7500-commit.sh +++ b/t/t7500-commit.sh @@ -46,15 +46,24 @@ test_expect_success 'unedited template with comments should not commit' ' ' test_expect_success 'a Signed-off-by line by itself should not commit' ' - ! GIT_EDITOR=../t7500/add-signed-off git commit --template "$TEMPLATE" + ( + test_set_editor "$TEST_DIRECTORY"/t7500/add-signed-off && + test_must_fail git commit --template "$TEMPLATE" + ) ' test_expect_success 'adding comments to a template should not commit' ' - ! GIT_EDITOR=../t7500/add-comments git commit --template "$TEMPLATE" + ( + test_set_editor "$TEST_DIRECTORY"/t7500/add-comments && + test_must_fail git commit --template "$TEMPLATE" + ) ' test_expect_success 'adding real content to a template should commit' ' - GIT_EDITOR=../t7500/add-content git commit --template "$TEMPLATE" && + ( + test_set_editor "$TEST_DIRECTORY"/t7500/add-content && + git commit --template "$TEMPLATE" + ) && commit_msg_is "template linecommit message" ' @@ -62,7 +71,10 @@ test_expect_success '-t option should be short for --template' ' echo "short template" > "$TEMPLATE" && echo "new content" >> foo && git add foo && - GIT_EDITOR=../t7500/add-content git commit -t "$TEMPLATE" && + ( + test_set_editor "$TEST_DIRECTORY"/t7500/add-content && + git commit -t "$TEMPLATE" + ) && commit_msg_is "short templatecommit message" ' @@ -71,7 +83,10 @@ test_expect_success 'config-specified template should commit' ' git config commit.template "$TEMPLATE" && echo "more content" >> foo && git add foo && - GIT_EDITOR=../t7500/add-content git commit && + ( + test_set_editor "$TEST_DIRECTORY"/t7500/add-content && + git commit + ) && git config --unset commit.template && commit_msg_is "new templatecommit message" ' @@ -79,7 +94,7 @@ test_expect_success 'config-specified template should commit' ' test_expect_success 'explicit commit message should override template' ' echo "still more content" >> foo && git add foo && - GIT_EDITOR=../t7500/add-content git commit --template "$TEMPLATE" \ + GIT_EDITOR="$TEST_DIRECTORY"/t7500/add-content git commit --template "$TEMPLATE" \ -m "command line msg" && commit_msg_is "command line msg" ' @@ -88,8 +103,10 @@ test_expect_success 'commit message from file should override template' ' echo "content galore" >> foo && git add foo && echo "standard input msg" | - GIT_EDITOR=../t7500/add-content git commit \ - --template "$TEMPLATE" --file - && + ( + test_set_editor "$TEST_DIRECTORY"/t7500/add-content && + git commit --template "$TEMPLATE" --file - + ) && commit_msg_is "standard input msg" ' @@ -132,10 +149,12 @@ EOF test_expect_success '--signoff' ' echo "yet another content *narf*" >> foo && - echo "zort" | - GIT_EDITOR=../t7500/add-content git commit -s -F - foo && + echo "zort" | ( + test_set_editor "$TEST_DIRECTORY"/t7500/add-content && + git commit -s -F - foo + ) && git cat-file commit HEAD | sed "1,/^$/d" > output && - diff expect output + test_cmp expect output ' test_expect_success 'commit message from file (1)' ' diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index dbc90bc41..6a2b12558 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -230,6 +230,10 @@ test_expect_success 'test option parsing' ' test_must_fail git merge ' +test_expect_success 'reject non-strategy with a git-merge-foo name' ' + test_must_fail git merge -s index c1 +' + test_expect_success 'merge c0 with c1' ' git reset --hard c0 && git merge c1 && diff --git a/t/t7606-merge-custom.sh b/t/t7606-merge-custom.sh new file mode 100755 index 000000000..de9b6ed5b --- /dev/null +++ b/t/t7606-merge-custom.sh @@ -0,0 +1,49 @@ +#!/bin/sh + +test_description='git-merge + +Testing a custom strategy.' + +. ./test-lib.sh + +cat >git-merge-theirs <<EOF +#!$SHELL_PATH +eval git read-tree --reset -u \\\$\$# +EOF +chmod +x git-merge-theirs +PATH=.:$PATH +export PATH + +test_expect_success 'setup' ' + echo c0 >c0.c && + git add c0.c && + git commit -m c0 && + git tag c0 && + echo c1 >c1.c && + git add c1.c && + git commit -m c1 && + git tag c1 && + git reset --hard c0 && + echo c1c1 >c1.c && + echo c2 >c2.c && + git add c1.c c2.c && + git commit -m c2 && + git tag c2 +' + +test_expect_success 'merge c2 with a custom strategy' ' + git reset --hard c1 && + git merge -s theirs c2 && + test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" && + test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" && + test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" && + test "$(git rev-parse c2^{tree})" = "$(git rev-parse HEAD^{tree})" && + git diff --exit-code && + git diff --exit-code c2 HEAD && + git diff --exit-code c2 && + test -f c0.c && + grep c1c1 c1.c && + test -f c2.c +' + +test_done diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh index eabec2e06..45cb60ea4 100755 --- a/t/t8001-annotate.sh +++ b/t/t8001-annotate.sh @@ -4,7 +4,7 @@ test_description='git annotate' . ./test-lib.sh PROG='git annotate' -. ../annotate-tests.sh +. "$TEST_DIRECTORY"/annotate-tests.sh test_expect_success \ 'Annotating an old revision works' \ diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh index 92ece30fa..597cf0486 100755 --- a/t/t8002-blame.sh +++ b/t/t8002-blame.sh @@ -4,6 +4,6 @@ test_description='git blame' . ./test-lib.sh PROG='git blame -c' -. ../annotate-tests.sh +. "$TEST_DIRECTORY"/annotate-tests.sh test_done diff --git a/t/t9110-git-svn-use-svm-props.sh b/t/t9110-git-svn-use-svm-props.sh index 04d2a65c0..83bd1cf17 100755 --- a/t/t9110-git-svn-use-svm-props.sh +++ b/t/t9110-git-svn-use-svm-props.sh @@ -8,7 +8,7 @@ test_description='git-svn useSvmProps test' . ./lib-git-svn.sh test_expect_success 'load svm repo' ' - svnadmin load -q "$rawsvnrepo" < ../t9110/svm.dump && + svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9110/svm.dump && git-svn init --minimize-url -R arr -i bar "$svnrepo"/mirror/arr && git-svn init --minimize-url -R argh -i dir "$svnrepo"/mirror/argh && git-svn init --minimize-url -R argh -i e \ diff --git a/t/t9111-git-svn-use-svnsync-props.sh b/t/t9111-git-svn-use-svnsync-props.sh index a8d74dcd3..c5dfd61e4 100755 --- a/t/t9111-git-svn-use-svnsync-props.sh +++ b/t/t9111-git-svn-use-svnsync-props.sh @@ -8,7 +8,7 @@ test_description='git-svn useSvnsyncProps test' . ./lib-git-svn.sh test_expect_success 'load svnsync repo' ' - svnadmin load -q "$rawsvnrepo" < ../t9111/svnsync.dump && + svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9111/svnsync.dump && git-svn init --minimize-url -R arr -i bar "$svnrepo"/bar && git-svn init --minimize-url -R argh -i dir "$svnrepo"/dir && git-svn init --minimize-url -R argh -i e "$svnrepo"/dir/a/b/c/d/e && diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh index f0fbd3aff..b0ba1f020 100755 --- a/t/t9115-git-svn-dcommit-funky-renames.sh +++ b/t/t9115-git-svn-dcommit-funky-renames.sh @@ -8,7 +8,7 @@ test_description='git-svn dcommit can commit renames of files with ugly names' . ./lib-git-svn.sh test_expect_success 'load repository with strange names' ' - svnadmin load -q "$rawsvnrepo" < ../t9115/funky-names.dump && + svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9115/funky-names.dump && start_httpd gtk+ ' diff --git a/t/t9121-git-svn-fetch-renamed-dir.sh b/t/t9121-git-svn-fetch-renamed-dir.sh index 99230b081..92e69a2a7 100755 --- a/t/t9121-git-svn-fetch-renamed-dir.sh +++ b/t/t9121-git-svn-fetch-renamed-dir.sh @@ -8,7 +8,7 @@ test_description='git-svn can fetch renamed directories' . ./lib-git-svn.sh test_expect_success 'load repository with renamed directory' ' - svnadmin load -q "$rawsvnrepo" < ../t9121/renamed-dir.dump + svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9121/renamed-dir.dump ' test_expect_success 'init and fetch repository' ' diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh index 3e32e84e6..988c3ac75 100755 --- a/t/t9200-git-cvsexportcommit.sh +++ b/t/t9200-git-cvsexportcommit.sh @@ -45,8 +45,8 @@ test_expect_success \ 'mkdir A B C D E F && echo hello1 >A/newfile1.txt && echo hello2 >B/newfile2.txt && - cp ../test9200a.png C/newfile3.png && - cp ../test9200a.png D/newfile4.png && + cp "$TEST_DIRECTORY"/test9200a.png C/newfile3.png && + cp "$TEST_DIRECTORY"/test9200a.png D/newfile4.png && git add A/newfile1.txt && git add B/newfile2.txt && git add C/newfile3.png && @@ -71,8 +71,8 @@ test_expect_success \ rm -f B/newfile2.txt && rm -f C/newfile3.png && echo Hello5 >E/newfile5.txt && - cp ../test9200b.png D/newfile4.png && - cp ../test9200a.png F/newfile6.png && + cp "$TEST_DIRECTORY"/test9200b.png D/newfile4.png && + cp "$TEST_DIRECTORY"/test9200a.png F/newfile6.png && git add E/newfile5.txt && git add F/newfile6.png && git commit -a -m "Test: Remove, add and update" && @@ -160,7 +160,7 @@ test_expect_success \ 'mkdir "G g" && echo ok then >"G g/with spaces.txt" && git add "G g/with spaces.txt" && \ - cp ../test9200a.png "G g/with spaces.png" && \ + cp "$TEST_DIRECTORY"/test9200a.png "G g/with spaces.png" && \ git add "G g/with spaces.png" && git commit -a -m "With spaces" && id=$(git rev-list --max-count=1 HEAD) && @@ -172,7 +172,7 @@ test_expect_success \ test_expect_success \ 'Update file with spaces in file name' \ 'echo Ok then >>"G g/with spaces.txt" && - cat ../test9200a.png >>"G g/with spaces.png" && \ + cat "$TEST_DIRECTORY"/test9200a.png >>"G g/with spaces.png" && \ git add "G g/with spaces.png" && git commit -a -m "Update with spaces" && id=$(git rev-list --max-count=1 HEAD) && @@ -197,7 +197,7 @@ test_expect_success \ 'mkdir -p Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö && echo Foo >Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt && git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt && - cp ../test9200a.png Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png && + cp "$TEST_DIRECTORY"/test9200a.png Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png && git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png && git commit -a -m "Går det så går det" && \ id=$(git rev-list --max-count=1 HEAD) && diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index c6bc0a607..bd5d5af66 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -5,7 +5,7 @@ test_description='test git-fast-import utility' . ./test-lib.sh -. ../diff-lib.sh ;# test-lib chdir's into trash +. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash file2_data='file2 second line of EOF' diff --git a/t/t9301-fast-export.sh b/t/t9301-fast-export.sh index c19b4a2ba..3cb9f8070 100755 --- a/t/t9301-fast-export.sh +++ b/t/t9301-fast-export.sh @@ -67,7 +67,7 @@ test_expect_success 'iso-8859-1' ' git config i18n.commitencoding ISO-8859-1 && # use author and committer name in ISO-8859-1 to match it. - . ../t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901-8859-1.txt && test_tick && echo rosten >file && git commit -s -m den file && diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index ae7082be1..46ba19be7 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -25,9 +25,9 @@ our \$site_name = "[localhost]"; our \$site_header = ""; our \$site_footer = ""; our \$home_text = "indextext.html"; -our @stylesheets = ("file:///$safe_pwd/../../gitweb/gitweb.css"); -our \$logo = "file:///$safe_pwd/../../gitweb/git-logo.png"; -our \$favicon = "file:///$safe_pwd/../../gitweb/git-favicon.png"; +our @stylesheets = ("file:///$TEST_DIRECTORY/../gitweb/gitweb.css"); +our \$logo = "file:///$TEST_DIRECTORY/../gitweb/git-logo.png"; +our \$favicon = "file:///$TEST_DIRECTORY/../gitweb/git-favicon.png"; our \$projects_list = ""; our \$export_ok = ""; our \$strict_export = ""; @@ -54,7 +54,7 @@ gitweb_run () { # written to web server logs, so we are not interested in that: # we are interested only in properly formatted errors/warnings rm -f gitweb.log && - perl -- "$(pwd)/../../gitweb/gitweb.perl" \ + perl -- "$TEST_DIRECTORY/../gitweb/gitweb.perl" \ >/dev/null 2>gitweb.log && if grep -q -s "^[[]" gitweb.log >/dev/null; then false; else true; fi @@ -525,20 +525,20 @@ test_debug 'cat gitweb.log' test_expect_success \ 'encode(commit): utf8' \ - '. ../t3901-utf8.txt && + '. "$TEST_DIRECTORY"/t3901-utf8.txt && echo "UTF-8" >> file && git add file && - git commit -F ../t3900/1-UTF-8.txt && + git commit -F "$TEST_DIRECTORY"/t3900/1-UTF-8.txt && gitweb_run "p=.git;a=commit"' test_debug 'cat gitweb.log' test_expect_success \ 'encode(commit): iso-8859-1' \ - '. ../t3901-8859-1.txt && + '. "$TEST_DIRECTORY"/t3901-8859-1.txt && echo "ISO-8859-1" >> file && git add file && git config i18n.commitencoding ISO-8859-1 && - git commit -F ../t3900/ISO-8859-1.txt && + git commit -F "$TEST_DIRECTORY"/t3900/ISO-8859-1.txt && git config --unset i18n.commitencoding && gitweb_run "p=.git;a=commit"' test_debug 'cat gitweb.log' diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh index 9706ee577..0f04ba094 100755 --- a/t/t9700-perl-git.sh +++ b/t/t9700-perl-git.sh @@ -39,6 +39,6 @@ test_expect_success \ test_external_without_stderr \ 'Perl API' \ - perl ../t9700/test.pl + perl "$TEST_DIRECTORY"/t9700/test.pl test_done diff --git a/t/t9700/test.pl b/t/t9700/test.pl index 4d2312548..851cea4a4 100755 --- a/t/t9700/test.pl +++ b/t/t9700/test.pl @@ -14,10 +14,7 @@ use File::Temp; BEGIN { use_ok('Git') } # set up -our $repo_dir = "trash directory"; our $abs_repo_dir = Cwd->cwd; -die "this must be run by calling the t/t97* shell script(s)\n" - if basename(Cwd->cwd) ne $repo_dir; ok(our $r = Git->repository(Directory => "."), "open repository"); # config diff --git a/t/test-lib.sh b/t/test-lib.sh index 11c027571..e2b106cb6 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -406,7 +406,7 @@ test_create_repo () { error "bug in the test script: not 1 parameter to test-create-repo" owd=`pwd` repo="$1" - mkdir "$repo" + mkdir -p "$repo" cd "$repo" || error "Cannot setup test environment" "$GIT_EXEC_PATH/git" init "--template=$GIT_EXEC_PATH/templates/blt/" >&3 2>&4 || error "cannot run git init -- have you built things yet?" @@ -449,6 +449,11 @@ test_done () { # we will leave things as they are. say_color pass "passed all $msg" + + test -d "$remove_trash" && + cd "$(dirname "$remove_trash")" && + rm -rf "$(basename "$remove_trash")" + exit 0 ;; *) @@ -485,7 +490,8 @@ fi . ../GIT-BUILD-OPTIONS # Test repository -test="trash directory" +test="trash directory.$(basename "$0" .sh)" +test ! -z "$debug" || remove_trash="$TEST_DIRECTORY/$test" rm -fr "$test" || { trap - exit echo >&5 "FATAL: Cannot prepare test area" diff --git a/templates/Makefile b/templates/Makefile index cc3fc3094..0722a926f 100644 --- a/templates/Makefile +++ b/templates/Makefile @@ -23,13 +23,13 @@ all: boilerplates.made custom bpsrc = $(filter-out %~,$(wildcard *--*)) boilerplates.made : $(bpsrc) - $(QUIET)ls *--* 2>/dev/null | \ + $(QUIET)umask 022 && ls *--* 2>/dev/null | \ while read boilerplate; \ do \ case "$$boilerplate" in *~) continue ;; esac && \ dst=`echo "$$boilerplate" | sed -e 's|^this|.|;s|--|/|g'` && \ dir=`expr "$$dst" : '\(.*\)/'` && \ - $(INSTALL) -d -m 755 blt/$$dir && \ + mkdir -p blt/$$dir && \ case "$$boilerplate" in \ *--) ;; \ *) cp -p $$boilerplate blt/$$dst ;; \ diff --git a/xdiff-interface.c b/xdiff-interface.c index 61dc5c547..944ad9887 100644 --- a/xdiff-interface.c +++ b/xdiff-interface.c @@ -1,5 +1,12 @@ #include "cache.h" #include "xdiff-interface.h" +#include "strbuf.h" + +struct xdiff_emit_state { + xdiff_emit_consume_fn consume; + void *consume_callback_data; + struct strbuf remainder; +}; static int parse_num(char **cp_p, int *num_p) { @@ -55,13 +62,13 @@ static void consume_one(void *priv_, char *s, unsigned long size) unsigned long this_size; ep = memchr(s, '\n', size); this_size = (ep == NULL) ? size : (ep - s + 1); - priv->consume(priv, s, this_size); + priv->consume(priv->consume_callback_data, s, this_size); size -= this_size; s += this_size; } } -int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf) +static int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf) { struct xdiff_emit_state *priv = priv_; int i; @@ -69,36 +76,22 @@ int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf) for (i = 0; i < nbuf; i++) { if (mb[i].ptr[mb[i].size-1] != '\n') { /* Incomplete line */ - priv->remainder = xrealloc(priv->remainder, - priv->remainder_size + - mb[i].size); - memcpy(priv->remainder + priv->remainder_size, - mb[i].ptr, mb[i].size); - priv->remainder_size += mb[i].size; + strbuf_add(&priv->remainder, mb[i].ptr, mb[i].size); continue; } /* we have a complete line */ - if (!priv->remainder) { + if (!priv->remainder.len) { consume_one(priv, mb[i].ptr, mb[i].size); continue; } - priv->remainder = xrealloc(priv->remainder, - priv->remainder_size + - mb[i].size); - memcpy(priv->remainder + priv->remainder_size, - mb[i].ptr, mb[i].size); - consume_one(priv, priv->remainder, - priv->remainder_size + mb[i].size); - free(priv->remainder); - priv->remainder = NULL; - priv->remainder_size = 0; + strbuf_add(&priv->remainder, mb[i].ptr, mb[i].size); + consume_one(priv, priv->remainder.buf, priv->remainder.len); + strbuf_reset(&priv->remainder); } - if (priv->remainder) { - consume_one(priv, priv->remainder, priv->remainder_size); - free(priv->remainder); - priv->remainder = NULL; - priv->remainder_size = 0; + if (priv->remainder.len) { + consume_one(priv, priv->remainder.buf, priv->remainder.len); + strbuf_reset(&priv->remainder); } return 0; } @@ -141,6 +134,25 @@ int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t co return xdl_diff(&a, &b, xpp, xecfg, xecb); } +int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2, + xdiff_emit_consume_fn fn, void *consume_callback_data, + xpparam_t const *xpp, + xdemitconf_t const *xecfg, xdemitcb_t *xecb) +{ + int ret; + struct xdiff_emit_state state; + + memset(&state, 0, sizeof(state)); + state.consume = fn; + state.consume_callback_data = consume_callback_data; + xecb->outf = xdiff_outf; + xecb->priv = &state; + strbuf_init(&state.remainder, 0); + ret = xdi_diff(mf1, mf2, xpp, xecfg, xecb); + strbuf_release(&state.remainder); + return ret; +} + int read_mmfile(mmfile_t *ptr, const char *filename) { struct stat st; diff --git a/xdiff-interface.h b/xdiff-interface.h index f7f791d96..558492ba3 100644 --- a/xdiff-interface.h +++ b/xdiff-interface.h @@ -3,18 +3,13 @@ #include "xdiff/xdiff.h" -struct xdiff_emit_state; - typedef void (*xdiff_emit_consume_fn)(void *, char *, unsigned long); -struct xdiff_emit_state { - xdiff_emit_consume_fn consume; - char *remainder; - unsigned long remainder_size; -}; - int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb); -int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf); +int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2, + xdiff_emit_consume_fn fn, void *consume_callback_data, + xpparam_t const *xpp, + xdemitconf_t const *xecfg, xdemitcb_t *xecb); int parse_hunk_header(char *line, int len, int *ob, int *on, int *nb, int *nn); |