From: Junio C Hamano Date: Sun, 30 Jul 2006 20:24:29 +0000 (-0700) Subject: Merge branch 'jn/make' X-Git-Tag: v1.4.2-rc3~36 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/2dcb927f37976ef5185cef5452516b170b14cd6c?hp=2d023581c9a0ae5efdebfd0084d54d09669a25d5 Merge branch 'jn/make' * jn/make: Set datarootdir in config.mak.in Quote all calls to GIT_CONF_APPEND_LINE Typofix in configure.ac comment. configure.ac vertical whitespace usage cleanup autoconf: Checks for some programs autoconf: Checks for libraries autoconf: Checks for some library functions. autoconf: Checks for typedefs, structures, and compiler characteristics. autoconf: Preparing the way for autodetection Copy description of build configuration variables to configure.ac Teach make clean about configure and autoconf autoconf: Use autoconf to write installation directories to config.mak.autogen --- diff --git a/.gitignore b/.gitignore index e103777026..fb0fa3f16a 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ git-http-push git-imap-send git-index-pack git-init-db +git-instaweb git-local-fetch git-log git-lost-found @@ -106,6 +107,7 @@ git-ssh-push git-ssh-upload git-status git-stripspace +git-svn git-svnimport git-symbolic-ref git-tag diff --git a/Documentation/Makefile b/Documentation/Makefile index cc83610588..0d9ffb4ad9 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -46,6 +46,7 @@ all: html man html: $(DOC_HTML) +$(DOC_HTML) $(DOC_MAN1) $(DOC_MAN7): asciidoc.conf man: man1 man7 man1: $(DOC_MAN1) diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 8601949e80..90722c21fa 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -49,7 +49,7 @@ People on the git mailing list need to be able to read and comment on the changes you are submitting. It is important for a developer to be able to "quote" your changes, using standard e-mail tools, so that they may comment on specific portions of -your code. For this reason, all patches should be submited +your code. For this reason, all patches should be submitted "inline". WARNING: Be wary of your MUAs word-wrap corrupting your patch. Do not cut-n-paste your patch; you can lose tabs that way if you are not careful. diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf index 7ce71510de..8196d787ab 100644 --- a/Documentation/asciidoc.conf +++ b/Documentation/asciidoc.conf @@ -9,6 +9,8 @@ [attributes] caret=^ +startsb=[ +endsb=] ifdef::backend-docbook[] [gitlink-inlinemacro] diff --git a/Documentation/config.txt b/Documentation/config.txt index a04c5adf8e..465eb13e76 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -91,6 +91,18 @@ core.warnAmbiguousRefs:: If true, git will warn you if the ref name you passed it is ambiguous and might match multiple refs in the .git/refs/ tree. True by default. +core.compression:: + An integer -1..9, indicating the compression level for objects that + are not in a pack file. -1 is the zlib and git default. 0 means no + compression, and 1..9 are various speed/size tradeoffs, 9 being + slowest. + +core.legacyheaders:: + A boolean which enables the legacy object header format in case + you want to interoperate with old clients accessing the object + database directly (where the "http://" and "rsync://" protocols + count as direct access). + alias.*:: Command aliases for the gitlink:git[1] command wrapper - e.g. after defining "alias.last = cat-file commit HEAD", the invocation @@ -104,10 +116,31 @@ apply.whitespace:: Tells `git-apply` how to handle whitespaces, in the same way as the '--whitespace' option. See gitlink:git-apply[1]. +diff.color:: + When true (or `always`), always use colors in patch. + When false (or `never`), never. When set to `auto`, use + colors only when the output is to the terminal. + +diff.color.:: + Use customized color for diff colorization. `` + specifies which part of the patch to use the specified + color, and is one of `plain` (context text), `meta` + (metainformation), `frag` (hunk header), `old` (removed + lines), or `new` (added lines). The value for these + configuration variables can be one of: `normal`, `bold`, + `dim`, `ul`, `blink`, `reverse`, `reset`, `black`, + `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, or + `white`. + diff.renameLimit:: The number of files to consider when performing the copy/rename detection; equivalent to the git diff option '-l'. +diff.renames:: + Tells git to detect renames. If set to any boolean value, it + will enable basic rename detection. If set to "copies" or + "copy", it will detect copies, as well. + format.headers:: Additional email headers to include in a patch to be submitted by mail. See gitlink:git-format-patch[1]. @@ -166,6 +199,10 @@ merge.summary:: Whether to include summaries of merged commits in newly created merge commit messages. False by default. +pack.window:: + The size of the window used by gitlink:git-pack-objects[1] when no + window size is given on the command line. Defaults to 10. + pull.octopus:: The default merge strategy to use when pulling multiple branches at once. @@ -181,6 +218,17 @@ showbranch.default:: The default set of branches for gitlink:git-show-branch[1]. See gitlink:git-show-branch[1]. +tar.umask:: + By default, git-link:git-tar-tree[1] sets file and directories modes + to 0666 or 0777. While this is both useful and acceptable for projects + such as the Linux Kernel, it might be excessive for other projects. + With this variable, it becomes possible to tell + git-link:git-tar-tree[1] to apply a specific umask to the modes above. + The special value "user" indicates that the user's current umask will + be used. This should be enough for most projects, as it will lead to + the same permissions as git-link:git-checkout[1] would use. The default + value remains 0, which means world read-write. + user.email:: Your email address to be recorded in any newly created commits. Can be overridden by the 'GIT_AUTHOR_EMAIL' and 'GIT_COMMITTER_EMAIL' diff --git a/Documentation/cvs-migration.txt b/Documentation/cvs-migration.txt index 1fbca83141..d2b0bd38de 100644 --- a/Documentation/cvs-migration.txt +++ b/Documentation/cvs-migration.txt @@ -93,7 +93,7 @@ machine where the repository is hosted. If you don't want to give them a full shell on the machine, there is a restricted shell which only allows users to do git pushes and pulls; see gitlink:git-shell[1]. -Put all the committers should in the same group, and make the repository +Put all the committers in the same group, and make the repository writable by that group: ------------------------------------------------ diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index f523ec2fbe..47ba9a403a 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -4,18 +4,21 @@ -u:: Synonym for "-p". +--raw:: + Generate the raw format. + --patch-with-raw:: - Generate patch but keep also the default raw diff output. + Synonym for "-p --raw". --stat:: - Generate a diffstat instead of a patch. + Generate a diffstat. --summary:: Output a condensed summary of extended header information such as creations, renames and mode changes. --patch-with-stat:: - Generate patch and prepend its diffstat. + Synonym for "-p --stat". -z:: \0 line termination on output @@ -26,10 +29,25 @@ --name-status:: Show only names and status of changed files. +--color:: + Show colored diff. + +--no-color:: + Turn off colored diff, even when the configuration file + gives the default to color output. + +--no-renames:: + Turn off rename detection, even when the configuration + file gives the default to do so. + --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. + line when generating a patch format output. + +--binary:: + In addition to --full-index, output "binary diff" that + can be applied with "git apply". --abbrev[=]:: Instead of showing the full 40-byte hexadecimal object @@ -94,5 +112,11 @@ Swap two inputs; that is, show differences from index or on-disk file to tree contents. +--text:: + Treat all files as text. + +-a:: + Shorthand for "--text". + For more detailed explanation on these common options, see also link:diffcore.html[diffcore documentation]. diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 0fe66f2d0c..517a86b238 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -15,9 +15,9 @@ SYNOPSIS DESCRIPTION ----------- Updates the index file for given paths, or all modified files if -'-a' is specified, and makes a commit object. The command -VISUAL and EDITOR environment variables to edit the commit log -message. +'-a' is specified, and makes a commit object. The command specified +by either the VISUAL or EDITOR environment variables are used to edit +the commit log message. Several environment variable are used during commits. They are documented in gitlink:git-commit-tree[1]. diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt index 56bd3e517d..092d0d6730 100644 --- a/Documentation/git-cvsexportcommit.txt +++ b/Documentation/git-cvsexportcommit.txt @@ -8,7 +8,7 @@ git-cvsexportcommit - Export a commit to a CVS checkout SYNOPSIS -------- -'git-cvsexportcommmit' [-h] [-v] [-c] [-p] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID +'git-cvsexportcommit' [-h] [-v] [-c] [-p] [-a] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID DESCRIPTION @@ -36,9 +36,13 @@ OPTIONS commit if any hunks fail to apply or there were other problems. -p:: - Be pedantic (paranoid) when applying patches. Invokes patch with + Be pedantic (paranoid) when applying patches. Invokes patch with --fuzz=0 +-a:: + Add authorship information. Adds Author line, and Committer (if + different from Author) to the message. + -f:: Force the merge even if the files are not up to date. diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt index 4c357daf6a..0f7d274eab 100644 --- a/Documentation/git-daemon.txt +++ b/Documentation/git-daemon.txt @@ -11,7 +11,7 @@ SYNOPSIS 'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all] [--timeout=n] [--init-timeout=n] [--strict-paths] [--base-path=path] [--user-path | --user-path=path] - [directory...] + [--reuseaddr] [--detach] [--pid-file=file] [directory...] DESCRIPTION ----------- @@ -82,6 +82,17 @@ OPTIONS --verbose:: Log details about the incoming connections and requested files. +--reuseaddr:: + Use SO_REUSEADDR when binding the listening socket. + This allows the server to restart without waiting for + old connections to time out. + +--detach:: + Detach from the shell. Implies --syslog. + +--pid-file=file:: + Save the process id in 'file'. + :: A directory to add to the whitelist of allowed directories. Unless --strict-paths is specified this will also include subdirectories diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt index 481b8b3aa0..7248b35d95 100644 --- a/Documentation/git-diff-files.txt +++ b/Documentation/git-diff-files.txt @@ -37,7 +37,7 @@ omit diff output for unmerged entries and just show "Unmerged". commit with these flags. -q:: - Remain silent even on nonexisting files + Remain silent even on nonexistent files Output format ------------- diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt index 7ab2080376..228c4d95bd 100644 --- a/Documentation/git-diff.txt +++ b/Documentation/git-diff.txt @@ -8,24 +8,24 @@ git-diff - Show changes between commits, commit and working tree, etc SYNOPSIS -------- -'git-diff' [ --diff-options ] {0,2} [...] +'git-diff' [ --diff-options ] {0,2} [...] DESCRIPTION ----------- -Show changes between two ents, an ent and the working tree, an -ent and the index file, or the index file and the working tree. +Show changes between two trees, a tree and the working tree, a +tree and the index file, or the index file and the working tree. The combination of what is compared with what is determined by -the number of ents given to the command. +the number of trees given to the command. -* When no is given, the working tree and the index - file is compared, using `git-diff-files`. +* When no is given, the working tree and the index + file are compared, using `git-diff-files`. -* When one is given, the working tree and the named - tree is compared, using `git-diff-index`. The option +* When one is given, the working tree and the named + tree are compared, using `git-diff-index`. The option `--cached` can be given to compare the index file and the named tree. -* When two s are given, these two trees are compared +* When two s are given, these two trees are compared using `git-diff-tree`. OPTIONS diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index 4ca0014dac..67425dc035 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -9,8 +9,9 @@ git-format-patch - Prepare patches for e-mail submission SYNOPSIS -------- [verse] -'git-format-patch' [-n | -k] [-o | --stdout] [--attach] +'git-format-patch' [-n | -k] [-o | --stdout] [--attach] [--thread] [-s | --signoff] [--diff-options] [--start-number ] + [--in-reply-to=Message-Id] [..] DESCRIPTION @@ -35,6 +36,10 @@ they are created in the current working directory. If -n is specified, instead of "[PATCH] Subject", the first line is formatted as "[PATCH n/m] Subject". +If given --thread, git-format-patch will generate In-Reply-To and +References headers to make the second and subsequent patch mails appear +as replies to the first mail; this also generates a Message-Id header to +reference. OPTIONS ------- @@ -63,6 +68,15 @@ OPTIONS --attach:: Create attachments instead of inlining patches. +--thread:: + Add In-Reply-To and References headers to make the second and + subsequent mails appear as replies to the first. Also generates + the Message-Id header to reference. + +--in-reply-to=Message-Id:: + Make the first mail (or all the mails with --no-thread) appear as a + reply to the given Message-Id, which avoids breaking threads to + provide a new patch series. CONFIGURATION ------------- diff --git a/Documentation/git-http-fetch.txt b/Documentation/git-http-fetch.txt index bc1a132891..3d508094af 100644 --- a/Documentation/git-http-fetch.txt +++ b/Documentation/git-http-fetch.txt @@ -8,7 +8,7 @@ git-http-fetch - downloads a remote git repository via HTTP SYNOPSIS -------- -'git-http-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] +'git-http-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [--stdin] DESCRIPTION ----------- @@ -33,6 +33,12 @@ commit-id:: Writes the commit-id into the filename under $GIT_DIR/refs/ on the local end after the transfer is complete. +--stdin:: + Instead of a commit id on the commandline (which is not expected in this + case), 'git-http-fetch' expects lines on stdin in the format + + ['\t'] + Author ------ Written by Linus Torvalds diff --git a/Documentation/git-init-db.txt b/Documentation/git-init-db.txt index 8a150d861f..0a4fc14b97 100644 --- a/Documentation/git-init-db.txt +++ b/Documentation/git-init-db.txt @@ -25,7 +25,7 @@ DESCRIPTION ----------- This command creates an empty git repository - basically a `.git` directory with subdirectories for `objects`, `refs/heads`, `refs/tags`, and -templated files. +template files. An initial `HEAD` file that references the HEAD of the master branch is also created. diff --git a/Documentation/git-instaweb.txt b/Documentation/git-instaweb.txt new file mode 100644 index 0000000000..7dd393b97f --- /dev/null +++ b/Documentation/git-instaweb.txt @@ -0,0 +1,84 @@ +git-instaweb(1) +=============== + +NAME +---- +git-instaweb - instantly browse your working repository in gitweb + +SYNOPSIS +-------- +'git-instaweb' [--local] [--httpd=] [--port=] [--browser=] + +'git-instaweb' [--start] [--stop] [--restart] + +DESCRIPTION +----------- +A simple script to setup gitweb and a web server for browsing the local +repository. + +OPTIONS +------- + +-l|--local:: + Only bind the web server to the local IP (127.0.0.1). + +-d|--httpd:: + The HTTP daemon command-line that will be executed. + Command-line options may be specified here, and the + configuration file will be added at the end of the command-line. + Currently, lighttpd and apache2 are the only supported servers. + (Default: lighttpd) + +-m|--module-path:: + The module path (only needed if httpd is Apache). + (Default: /usr/lib/apache2/modules) + +-p|--port:: + The port number to bind the httpd to. (Default: 1234) + +-b|--browser:: + + The web browser command-line to execute to view the gitweb page. + If blank, the URL of the gitweb instance will be printed to + stdout. (Default: 'firefox') + +--start:: + Start the httpd instance and exit. This does not generate + any of the configuration files for spawning a new instance. + +--stop:: + Stop the httpd instance and exit. This does not generate + any of the configuration files for spawning a new instance, + nor does it close the browser. + +--restart:: + Restart the httpd instance and exit. This does not generate + any of the configuration files for spawning a new instance. + +CONFIGURATION +------------- + +You may specify configuration in your .git/config + +----------------------------------------------------------------------- +[instaweb] + local = true + httpd = apache2 -f + port = 4321 + browser = konqueror + modulepath = /usr/lib/apache2/modules + +----------------------------------------------------------------------- + +Author +------ +Written by Eric Wong + +Documentation +-------------- +Documentation by Eric Wong . + +GIT +--- +Part of the gitlink:git[7] suite + diff --git a/Documentation/git-local-fetch.txt b/Documentation/git-local-fetch.txt index 87abec1c4e..2fbdfe086a 100644 --- a/Documentation/git-local-fetch.txt +++ b/Documentation/git-local-fetch.txt @@ -29,6 +29,12 @@ OPTIONS Writes the commit-id into the filename under $GIT_DIR/refs/ on the local end after the transfer is complete. +--stdin:: + Instead of a commit id on the commandline (which is not expected in this + case), 'git-local-fetch' expects lines on stdin in the format + + ['\t'] + Author ------ Written by Junio C Hamano diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index 4d8a2ad2d7..8520b97111 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -207,7 +207,7 @@ An exclude pattern is of the following format: An example: -------------------------------------------------------------- - $ cat .git/ignore + $ cat .git/info/exclude # ignore objects and archives, anywhere in the tree. *.[oa] $ cat Documentation/.gitignore @@ -217,7 +217,7 @@ An example: !foo.html $ git-ls-files --ignored \ --exclude='Documentation/*.[0-9]' \ - --exclude-from=.git/ignore \ + --exclude-from=.git/info/exclude \ --exclude-per-directory=.gitignore -------------------------------------------------------------- diff --git a/Documentation/git-mailsplit.txt b/Documentation/git-mailsplit.txt index 209e36bacb..5a17801f6a 100644 --- a/Documentation/git-mailsplit.txt +++ b/Documentation/git-mailsplit.txt @@ -25,7 +25,7 @@ OPTIONS -b:: If any file doesn't begin with a From line, assume it is a - single mail message instead of signalling error. + single mail message instead of signaling error. -d:: Instead of the default 4 digits with leading zeros, diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index 4ce799b520..bebf30ad3d 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -83,7 +83,7 @@ your local modifications interfere with the merge, again, it stops before touching anything. So in the above two "failed merge" case, you do not have to -worry about lossage of data --- you simply were not ready to do +worry about loss of data --- you simply were not ready to do a merge, so no merge happened at all. You may want to finish whatever you were in the middle of doing, and retry the same pull after you are done and ready. diff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt index 39a1434a0e..37fbf66efb 100644 --- a/Documentation/git-name-rev.txt +++ b/Documentation/git-name-rev.txt @@ -26,14 +26,14 @@ OPTIONS List all commits reachable from all refs --stdin:: - Read from stdin, append "()" to all sha1's of name'able + Read from stdin, append "()" to all sha1's of nameable commits, and pass to stdout EXAMPLE ------- Given a commit, find out where it is relative to the local refs. Say somebody -wrote you about that phantastic commit 33db5f4d9027a10e477ccf054b2c1ab94f74c85a. +wrote you about that fantastic commit 33db5f4d9027a10e477ccf054b2c1ab94f74c85a. Of course, you look into the commit, but that only tells you what happened, but not the context. diff --git a/Documentation/git-p4import.txt b/Documentation/git-p4import.txt index 0858e5efbe..ee9e8fa909 100644 --- a/Documentation/git-p4import.txt +++ b/Documentation/git-p4import.txt @@ -128,7 +128,7 @@ Tags A git tag of the form p4/xx is created for every change imported from the Perforce repository where xx is the Perforce changeset number. Therefore after the import you can use git to access any commit by its -Perforce number, eg. git show p4/327. +Perforce number, e.g. git show p4/327. The tag associated with the HEAD commit is also how `git-p4import` determines if there are new changes to incrementally import from the @@ -143,7 +143,7 @@ may delete the tags. Notes ----- -You can interrupt the import (eg. ctrl-c) at any time and restart it +You can interrupt the import (e.g. ctrl-c) at any time and restart it without worry. Author information is automatically determined by querying the diff --git a/Documentation/git-pack-redundant.txt b/Documentation/git-pack-redundant.txt index 8fb0659438..7d54b17e37 100644 --- a/Documentation/git-pack-redundant.txt +++ b/Documentation/git-pack-redundant.txt @@ -29,7 +29,7 @@ OPTIONS --all:: - Processes all packs. Any filenames on the commandline are ignored. + Processes all packs. Any filenames on the command line are ignored. --alt-odb:: Don't require objects present in packs from alternate object diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index d5b5ca167c..56afd64f42 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -67,7 +67,7 @@ Some short-cut notations are also supported. -f, \--force:: Usually, the command refuses to update a remote ref that is - not a descendent of the local ref used to overwrite it. + not a descendant of the local ref used to overwrite it. This flag disables the check. This can cause the remote repository to lose commits; use it with care. diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt index 803c0d5cae..b03d66f61c 100644 --- a/Documentation/git-repo-config.txt +++ b/Documentation/git-repo-config.txt @@ -119,8 +119,8 @@ you can set the filemode to true with % git repo-config core.filemode true ------------ -The hypothetic proxy command entries actually have a postfix to discern -to what URL they apply. Here is how to change the entry for kernel.org +The hypothetical proxy command entries actually have a postfix to discern +what URL they apply to. Here is how to change the entry for kernel.org to "ssh". ------------ diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index ad6d14c55a..f60eacd93e 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -15,6 +15,7 @@ SYNOPSIS [ \--sparse ] [ \--no-merges ] [ \--remove-empty ] + [ \--not ] [ \--all ] [ \--topo-order ] [ \--parents ] @@ -37,6 +38,14 @@ not in 'baz'". A special notation .. can be used as a short-hand for {caret} . +Another special notation is ... which is useful for +merges. The resulting set of commits is the symmetric difference +between the two operands. The following two commands are equivalent: + +------------ +$ git-rev-list A B --not $(git-merge-base --all A B) +$ git-rev-list A...B +------------ OPTIONS ------- @@ -47,6 +56,9 @@ OPTIONS Print the contents of the commit in raw-format; each record is separated with a NUL character. +--parents:: + Print the parents of the commit. + --objects:: Print the object IDs of any object referenced by the listed commits. 'git-rev-list --objects foo ^bar' thus means "send me all object IDs @@ -55,7 +67,7 @@ OPTIONS --objects-edge:: Similar to `--objects`, but also print the IDs of - excluded commits refixed with a `-` character. This is + excluded commits prefixed with a `-` character. This is used by `git-pack-objects` to build 'thin' pack, which records objects in deltified form based on objects contained in these excluded commits to reduce network @@ -93,6 +105,14 @@ OPTIONS --remove-empty:: Stop when a given path disappears from the tree. +--no-merges:: + Do not print commits with more than one parent. + +--not:: + Reverses the meaning of the '{caret}' prefix (or lack + thereof) for all following revision specifiers, up to + the next `--not`. + --all:: Pretend as if all the refs in `$GIT_DIR/refs/` are listed on the command line as . diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 627cde8520..b761b4b965 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -156,11 +156,6 @@ syntax. and dereference the tag recursively until a non-tag object is found. -'git-rev-parse' also accepts a prefix '{caret}' to revision parameter, -which is passed to 'git-rev-list'. Two revision parameters -concatenated with '..' is a short-hand for writing a range -between them. I.e. 'r1..r2' is equivalent to saying '{caret}r1 r2' - Here is an illustration, by Jon Loeliger. Both node B and C are a commit parents of commit node A. Parent commits are ordered left-to-right. @@ -168,9 +163,9 @@ left-to-right. G H I J \ / \ / D E F - \ | / - \ | / - \|/ + \ | / \ + \ | / | + \|/ | B C \ / \ / @@ -188,6 +183,40 @@ left-to-right. J = F^2 = B^3^2 = A^^3^2 +SPECIFYING RANGES +----------------- + +History traversing commands such as `git-log` operate on a set +of commits, not just a single commit. To these commands, +specifying a single revision with the notation described in the +previous section means the set of commits reachable from that +commit, following the commit ancestry chain. + +To exclude commits reachable from a commit, a prefix `{caret}` +notation is used. E.g. "`{caret}r1 r2`" means commits reachable +from `r2` but exclude the ones reachable from `r1`. + +This set operation appears so often that there is a shorthand +for it. "`r1..r2`" is equivalent to "`{caret}r1 r2`". It is +the difference of two sets (subtract the set of commits +reachable from `r1` from the set of commits reachable from +`r2`). + +A similar notation "`r1\...r2`" is called symmetric difference +of `r1` and `r2` and is defined as +"`r1 r2 --not $(git-merge-base --all r1 r2)`". +It it the set of commits that are reachable from either one of +`r1` or `r2` but not from both. + +Here are a few examples: + + D A B D + D F A B C D F + ^A G B D + ^A F B C F + G...I C D F G I + ^B G I C D F G I + Author ------ Written by Linus Torvalds and diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt index f115b45ef6..a2445a48fc 100644 --- a/Documentation/git-show-branch.txt +++ b/Documentation/git-show-branch.txt @@ -52,6 +52,11 @@ OPTIONS appear in topological order (i.e., descendant commits are shown before their parents). +--sparse:: + By default, the output omits merges that are reachable + from only one tip being shown. This option makes them + visible. + --more=:: Usually the command stops output upon showing the commit that is the common ancestor of all the branches. This diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt new file mode 100644 index 0000000000..7d86809844 --- /dev/null +++ b/Documentation/git-svn.txt @@ -0,0 +1,319 @@ +git-svn(1) +========== + +NAME +---- +git-svn - bidirectional operation between a single Subversion branch and git + +SYNOPSIS +-------- +'git-svn' [options] [arguments] + +DESCRIPTION +----------- +git-svn is a simple conduit for changesets between a single Subversion +branch and git. + +git-svn is not to be confused with git-svnimport. The were designed +with very different goals in mind. + +git-svn is designed for an individual developer who wants a +bidirectional flow of changesets between a single branch in Subversion +and an arbitrary number of branches in git. git-svnimport is designed +for read-only operation on repositories that match a particular layout +(albeit the recommended one by SVN developers). + +For importing svn, git-svnimport is potentially more powerful when +operating on repositories organized under the recommended +trunk/branch/tags structure, and should be faster, too. + +git-svn mostly ignores the very limited view of branching that +Subversion has. This allows git-svn to be much easier to use, +especially on repositories that are not organized in a manner that +git-svnimport is designed for. + +COMMANDS +-------- +init:: + Creates an empty git repository with additional metadata + directories for git-svn. The Subversion URL must be specified + as a command-line argument. + +fetch:: + Fetch unfetched revisions from the Subversion URL we are + tracking. refs/remotes/git-svn will be updated to the + latest revision. + + Note: You should never attempt to modify the remotes/git-svn + branch outside of git-svn. Instead, create a branch from + remotes/git-svn and work on that branch. Use the 'commit' + command (see below) to write git commits back to + remotes/git-svn. + + See 'Additional Fetch Arguments' if you are interested in + manually joining branches on commit. + +commit:: + Commit specified commit or tree objects to SVN. This relies on + your imported fetch data being up-to-date. This makes + absolutely no attempts to do patching when committing to SVN, it + simply overwrites files with those specified in the tree or + commit. All merging is assumed to have taken place + independently of git-svn functions. + +rebuild:: + Not a part of daily usage, but this is a useful command if + you've just cloned a repository (using git-clone) that was + tracked with git-svn. Unfortunately, git-clone does not clone + git-svn metadata and the svn working tree that git-svn uses for + its operations. This rebuilds the metadata so git-svn can + resume fetch operations. A Subversion URL may be optionally + specified at the command-line if the directory/repository you're + tracking has moved or changed protocols. + +show-ignore:: + Recursively finds and lists the svn:ignore property on + directories. The output is suitable for appending to + the $GIT_DIR/info/exclude file. + +OPTIONS +------- +-r :: +--revision :: + Only used with the 'fetch' command. + + Takes any valid -r svn would accept and passes it + directly to svn. -r: ranges and "{" DATE "}" syntax + is also supported. This is passed directly to svn, see svn + documentation for more details. + + This can allow you to make partial mirrors when running fetch. + +-:: +--stdin:: + Only used with the 'commit' command. + + Read a list of commits from stdin and commit them in reverse + order. Only the leading sha1 is read from each line, so + git-rev-list --pretty=oneline output can be used. + +--rmdir:: + Only used with the 'commit' command. + + Remove directories from the SVN tree if there are no files left + behind. SVN can version empty directories, and they are not + removed by default if there are no files left in them. git + cannot version empty directories. Enabling this flag will make + the commit to SVN act like git. + + repo-config key: svn.rmdir + +-e:: +--edit:: + Only used with the 'commit' command. + + Edit the commit message before committing to SVN. This is off by + default for objects that are commits, and forced on when committing + tree objects. + + repo-config key: svn.edit + +-l:: +--find-copies-harder:: + Both of these are only used with the 'commit' command. + + They are both passed directly to git-diff-tree see + git-diff-tree(1) for more information. + + repo-config key: svn.l + repo-config key: svn.findcopiesharder + +-A:: +--authors-file=:: + + Syntax is compatible with the files used by git-svnimport and + git-cvsimport: + +------------------------------------------------------------------------ +loginname = Joe User +------------------------------------------------------------------------ + + If this option is specified and git-svn encounters an SVN + committer name that does not exist in the authors-file, git-svn + will abort operation. The user will then have to add the + appropriate entry. Re-running the previous git-svn command + after the authors-file is modified should continue operation. + + repo-config key: svn.authors-file + +ADVANCED OPTIONS +---------------- +-b:: +--branch :: + Used with 'fetch' or 'commit'. + + This can be used to join arbitrary git branches to remotes/git-svn + on new commits where the tree object is equivalent. + + When used with different GIT_SVN_ID values, tags and branches in + SVN can be tracked this way, as can some merges where the heads + end up having completely equivalent content. This can even be + used to track branches across multiple SVN _repositories_. + + This option may be specified multiple times, once for each + branch. + + repo-config key: svn.branch + +-i:: +--id :: + This sets GIT_SVN_ID (instead of using the environment). See + the section on "Tracking Multiple Repositories or Branches" for + more information on using GIT_SVN_ID. + +COMPATIBILITY OPTIONS +--------------------- +--upgrade:: + Only used with the 'rebuild' command. + + Run this if you used an old version of git-svn that used + "git-svn-HEAD" instead of "remotes/git-svn" as the branch + for tracking the remote. + +--no-ignore-externals:: + Only used with the 'fetch' and 'rebuild' command. + + By default, git-svn passes --ignore-externals to svn to avoid + fetching svn:external trees into git. Pass this flag to enable + externals tracking directly via git. + + Versions of svn that do not support --ignore-externals are + automatically detected and this flag will be automatically + enabled for them. + + Otherwise, do not enable this flag unless you know what you're + doing. + + repo-config key: svn.noignoreexternals + +Basic Examples +~~~~~~~~~~~~~~ + +Tracking and contributing to an Subversion managed-project: + +------------------------------------------------------------------------ +# Initialize a tree (like git init-db): + git-svn init http://svn.foo.org/project/trunk +# Fetch remote revisions: + git-svn fetch +# Create your own branch to hack on: + git checkout -b my-branch remotes/git-svn +# Commit only the git commits you want to SVN: + git-svn commit [ ...] +# Commit all the git commits from my-branch that don't exist in SVN: + git-svn commit remotes/git-svn..my-branch +# Something is committed to SVN, pull the latest into your branch: + git-svn fetch && git pull . remotes/git-svn +# Append svn:ignore settings to the default git exclude file: + git-svn show-ignore >> .git/info/exclude +------------------------------------------------------------------------ + +DESIGN PHILOSOPHY +----------------- +Merge tracking in Subversion is lacking and doing branched development +with Subversion is cumbersome as a result. git-svn completely forgoes +any automated merge/branch tracking on the Subversion side and leaves it +entirely up to the user on the git side. It's simply not worth it to do +a useful translation when the original signal is weak. + +TRACKING MULTIPLE REPOSITORIES OR BRANCHES +------------------------------------------ +This is for advanced users, most users should ignore this section. + +Because git-svn does not care about relationships between different +branches or directories in a Subversion repository, git-svn has a simple +hack to allow it to track an arbitrary number of related _or_ unrelated +SVN repositories via one git repository. Simply set the GIT_SVN_ID +environment variable to a name other other than "git-svn" (the default) +and git-svn will ignore the contents of the $GIT_DIR/git-svn directory +and instead do all of its work in $GIT_DIR/$GIT_SVN_ID for that +invocation. The interface branch will be remotes/$GIT_SVN_ID, instead of +remotes/git-svn. Any remotes/$GIT_SVN_ID branch should never be modified +by the user outside of git-svn commands. + +ADDITIONAL FETCH ARGUMENTS +-------------------------- +This is for advanced users, most users should ignore this section. + +Unfetched SVN revisions may be imported as children of existing commits +by specifying additional arguments to 'fetch'. Additional parents may +optionally be specified in the form of sha1 hex sums at the +command-line. Unfetched SVN revisions may also be tied to particular +git commits with the following syntax: + + svn_revision_number=git_commit_sha1 + +This allows you to tie unfetched SVN revision 375 to your current HEAD:: + + `git-svn fetch 375=$(git-rev-parse HEAD)` + +Advanced Example: Tracking a Reorganized Repository +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If you're tracking a directory that has moved, or otherwise been +branched or tagged off of another directory in the repository and you +care about the full history of the project, then you can read this +section. + +This is how Yann Dirson tracked the trunk of the ufoai directory when +the /trunk directory of his repository was moved to /ufoai/trunk and +he needed to continue tracking /ufoai/trunk where /trunk left off. + +------------------------------------------------------------------------ + # This log message shows when the repository was reorganized: + r166 | ydirson | 2006-03-02 01:36:55 +0100 (Thu, 02 Mar 2006) | 1 line + Changed paths: + D /trunk + A /ufoai/trunk (from /trunk:165) + + # First we start tracking the old revisions: + GIT_SVN_ID=git-oldsvn git-svn init \ + https://svn.sourceforge.net/svnroot/ufoai/trunk + GIT_SVN_ID=git-oldsvn git-svn fetch -r1:165 + + # And now, we continue tracking the new revisions: + GIT_SVN_ID=git-newsvn git-svn init \ + https://svn.sourceforge.net/svnroot/ufoai/ufoai/trunk + GIT_SVN_ID=git-newsvn git-svn fetch \ + 166=`git-rev-parse refs/remotes/git-oldsvn` +------------------------------------------------------------------------ + +BUGS +---- +If somebody commits a conflicting changeset to SVN at a bad moment +(right before you commit) causing a conflict and your commit to fail, +your svn working tree ($GIT_DIR/git-svn/tree) may be dirtied. The +easiest thing to do is probably just to rm -rf $GIT_DIR/git-svn/tree and +run 'rebuild'. + +We ignore all SVN properties except svn:executable. Too difficult to +map them since we rely heavily on git write-tree being _exactly_ the +same on both the SVN and git working trees and I prefer not to clutter +working trees with metadata files. + +svn:keywords can't be ignored in Subversion (at least I don't know of +a way to ignore them). + +Renamed and copied directories are not detected by git and hence not +tracked when committing to SVN. I do not plan on adding support for +this as it's quite difficult and time-consuming to get working for all +the possible corner cases (git doesn't do it, either). Renamed and +copied files are fully supported if they're similar enough for git to +detect them. + +Author +------ +Written by Eric Wong . + +Documentation +------------- +Written by Eric Wong . diff --git a/Documentation/git-tar-tree.txt b/Documentation/git-tar-tree.txt index f2675c4193..7a99acf2ec 100644 --- a/Documentation/git-tar-tree.txt +++ b/Documentation/git-tar-tree.txt @@ -37,7 +37,20 @@ OPTIONS Instead of making a tar archive from local repository, retrieve a tar archive from a remote repository. -Examples +CONFIGURATION +------------- +By default, file and directories modes are set to 0666 or 0777. It is +possible to change this by setting the "umask" variable in the +repository configuration as follows : + +[tar] + umask = 002 ;# group friendly + +The special umask value "user" indicates that the user's current umask +will be used instead. The default value remains 0, which means world +readable/writable files and directories. + +EXAMPLES -------- git tar-tree HEAD junk | (cd /var/tmp/ && tar xf -):: diff --git a/Documentation/git-tools.txt b/Documentation/git-tools.txt index d79523f56d..0914cbb0ba 100644 --- a/Documentation/git-tools.txt +++ b/Documentation/git-tools.txt @@ -42,7 +42,7 @@ History Viewers - *gitk* (shipped with git-core) - gitk is a simple TK GUI for browsing history of GIT repositories easily. + gitk is a simple Tk GUI for browsing history of GIT repositories easily. - *gitview* (contrib/) diff --git a/Documentation/git-upload-tar.txt b/Documentation/git-upload-tar.txt index a1019a0231..394af62015 100644 --- a/Documentation/git-upload-tar.txt +++ b/Documentation/git-upload-tar.txt @@ -17,7 +17,7 @@ to the other end over the git protocol. This command is usually not invoked directly by the end user. The UI for the protocol is on the 'git-tar-tree' side, and the -program pair is meant to be used to get a tar achive from a +program pair is meant to be used to get a tar archive from a remote repository. diff --git a/Documentation/git.txt b/Documentation/git.txt index 51f20c6e67..7310a2b8b8 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -8,7 +8,8 @@ git - the stupid content tracker SYNOPSIS -------- -'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ARGS] +'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate] + [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS] DESCRIPTION ----------- @@ -41,6 +42,15 @@ OPTIONS environment variable. If no path is given 'git' will print the current setting and then exit. +-p|--paginate:: + Pipe all output into 'less' (or if set, $PAGER). + +--git-dir=:: + Set the path to the repository. This can also be controlled by + setting the GIT_DIR environment variable. + +--bare:: + Same as --git-dir=`pwd`. FURTHER DOCUMENTATION --------------------- @@ -478,7 +488,7 @@ Configuration Mechanism Starting from 0.99.9 (actually mid 0.99.8.GIT), `.git/config` file is used to hold per-repository configuration options. It is a -simple text file modelled after `.ini` format familiar to some +simple text file modeled after `.ini` format familiar to some people. Here is an example: ------------ @@ -615,6 +625,13 @@ git Diffs gitlink:git-diff-files[1]; gitlink:git-diff-tree[1] +other +~~~~~ +'GIT_TRACE':: + If this variable is set git will print `trace:` messages on + stderr telling about alias expansion, built-in command + execution and external command execution. + Discussion[[Discussion]] ------------------------ include::README[] diff --git a/Documentation/glossary.txt b/Documentation/glossary.txt index 116ddb7fbf..14449ca8ba 100644 --- a/Documentation/glossary.txt +++ b/Documentation/glossary.txt @@ -86,7 +86,7 @@ directory:: ent:: Favorite synonym to "tree-ish" by some total geeks. See `http://en.wikipedia.org/wiki/Ent_(Middle-earth)` for an in-depth - explanation. + explanation. Avoid this term, not to confuse people. fast forward:: A fast-forward is a special type of merge where you have diff --git a/Documentation/howto/isolate-bugs-with-bisect.txt b/Documentation/howto/isolate-bugs-with-bisect.txt index edbcd4c661..926bbdc3cb 100644 --- a/Documentation/howto/isolate-bugs-with-bisect.txt +++ b/Documentation/howto/isolate-bugs-with-bisect.txt @@ -28,7 +28,7 @@ Then do and at this point "git bisect" will churn for a while, and tell you what the mid-point between those two commits are, and check that state out as -the head of the bew "bisect" branch. +the head of the new "bisect" branch. Compile and reboot. diff --git a/Documentation/howto/rebase-from-internal-branch.txt b/Documentation/howto/rebase-from-internal-branch.txt index c2d4a91c7c..fcd64e9b9b 100644 --- a/Documentation/howto/rebase-from-internal-branch.txt +++ b/Documentation/howto/rebase-from-internal-branch.txt @@ -124,7 +124,7 @@ up your changes, along with other changes. The two commits #2' and #3' in the above picture record the same changes your e-mail submission for #2 and #3 contained, but -probably with the new sign-off line added by the upsteam +probably with the new sign-off line added by the upstream maintainer and definitely with different committer and ancestry information, they are different objects from #2 and #3 commits. diff --git a/Documentation/repository-layout.txt b/Documentation/repository-layout.txt index b52dfdc308..275d18bb54 100644 --- a/Documentation/repository-layout.txt +++ b/Documentation/repository-layout.txt @@ -120,9 +120,11 @@ info/grafts:: info/exclude:: This file, by convention among Porcelains, stores the - exclude pattern list. `git status` looks at it, but - otherwise it is not looked at by any of the core git - commands. + exclude pattern list. `.gitignore` is the per-directory + ignore file. `git status`, `git add`, `git rm` and `git + clean` look at it but the core git commands do not look + at it. See also: gitlink:git-ls-files[1] `--exclude-from` + and `--exclude-per-directory`. remotes:: Stores shorthands to be used to give URL and default diff --git a/Documentation/technical/pack-heuristics.txt b/Documentation/technical/pack-heuristics.txt index 9aadd5cee5..103eb5d989 100644 --- a/Documentation/technical/pack-heuristics.txt +++ b/Documentation/technical/pack-heuristics.txt @@ -73,7 +73,7 @@ The traditional insight: yes -And Bable-like confusion flowed. +And Babel-like confusion flowed. oh, hmm, and I'm not sure what this sliding window means either @@ -257,7 +257,7 @@ proclaim it a non-issue. Good style too! (type, basename, size)). Then we walk through this list, and calculate a delta of - each object against the last n (tunable paramater) objects, + each object against the last n (tunable parameter) objects, and pick the smallest of these deltas. Vastly simplified, but the essence is there! @@ -395,7 +395,7 @@ used as setup for a later optimization, which is a real word: do "object name->location in packfile" translation. I'm assuming the real win for delta-ing large->small is - more homogenous statistics for gzip to run over? + more homogeneous statistics for gzip to run over? (You have to put the bytes in one place or another, but putting them in a larger blob wins on compression) @@ -448,7 +448,7 @@ design options, etc. Bugs happen, but they are "simple" bugs. And bugs that actually get some object store detail wrong are almost always - so obious that they never go anywhere. + so obvious that they never go anywhere. Yeah. diff --git a/Documentation/urls.txt b/Documentation/urls.txt index 74774134e3..26ecba53fb 100644 --- a/Documentation/urls.txt +++ b/Documentation/urls.txt @@ -10,20 +10,21 @@ to name the remote repository: - https://host.xz/path/to/repo.git/ - git://host.xz/path/to/repo.git/ - git://host.xz/~user/path/to/repo.git/ -- ssh://host.xz/path/to/repo.git/ -- ssh://host.xz/~user/path/to/repo.git/ -- ssh://host.xz/~/path/to/repo.git +- ssh://{startsb}user@{endsb}host.xz/path/to/repo.git/ +- ssh://{startsb}user@{endsb}host.xz/~user/path/to/repo.git/ +- ssh://{startsb}user@{endsb}host.xz/~/path/to/repo.git =============================================================== -SSH Is the default transport protocol and also supports an -scp-like syntax. Both syntaxes support username expansion, +SSH is the default transport protocol. You can optionally specify +which user to log-in as, and an alternate, scp-like syntax is also +supported. Both syntaxes support username expansion, as does the native git protocol. The following three are identical to the last three above, respectively: =============================================================== -- host.xz:/path/to/repo.git/ -- host.xz:~user/path/to/repo.git/ -- host.xz:path/to/repo.git +- {startsb}user@{endsb}host.xz:/path/to/repo.git/ +- {startsb}user@{endsb}host.xz:~user/path/to/repo.git/ +- {startsb}user@{endsb}host.xz:path/to/repo.git =============================================================== To sync with a local directory, use: @@ -47,7 +48,7 @@ Then such a short-hand is specified in place of without parameters on the command line, specified on `Push:` lines or `Pull:` lines are used for `git-push` and `git-fetch`/`git-pull`, -respectively. Multiple `Push:` and and `Pull:` lines may +respectively. Multiple `Push:` and `Pull:` lines may be specified for additional branch mappings. The name of a file in `$GIT_DIR/branches` directory can be diff --git a/INSTALL b/INSTALL index 28245b3e68..ba9778cd4d 100644 --- a/INSTALL +++ b/INSTALL @@ -53,7 +53,7 @@ Issues of note: - "libcurl" and "curl" executable. git-http-fetch and git-fetch use them. If you do not use http - transfer, you are probabaly OK if you do not have + transfer, you are probably OK if you do not have them. - expat library; git-http-push uses it for remote lock @@ -78,7 +78,7 @@ Issues of note: git, and if you only use git to track other peoples work you'll never notice the lack of it. - - "wish", the TCL/Tk windowing shell is used in gitk to show the + - "wish", the Tcl/Tk windowing shell is used in gitk to show the history graphically - "ssh" is used to push and pull over the net diff --git a/Makefile b/Makefile index cef65861c4..814c5642e7 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,22 @@ all: # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link. # Enable it on Windows. By default, symrefs are still used. # +# Define NO_SVN_TESTS if you want to skip time-consuming SVN interoperability +# tests. These tests take up a significant amount of the total test time +# but are not needed unless you plan to talk to SVN repos. +# +# Define NO_FINK if you are building on Darwin/Mac OS X, have Fink +# installed in /sw, but don't want GIT to link against any libraries +# installed there. If defined you may specify your own (or Fink's) +# include directories and library directories by defining CFLAGS +# and LDFLAGS appropriately. +# +# Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X, +# have DarwinPorts installed in /opt/local, but don't want GIT to +# link against any libraries installed there. If defined you may +# specify your own (or DarwinPort's) include directories and +# library directories by defining CFLAGS and LDFLAGS appropriately. +# # Define PPC_SHA1 environment variable when running make to make use of # a bundled SHA1 routine optimized for PowerPC. # @@ -60,7 +76,7 @@ all: # Define NO_ACCURATE_DIFF if your diff program at least sometimes misses # a missing newline at the end of the file. # -# Define NO_PYTHON if you want to loose all benefits of the recursive merge. +# Define NO_PYTHON if you want to lose all benefits of the recursive merge. # # Define COLLISION_CHECK below if you believe that SHA1's # 1461501637330902918203684832716283019655932542976 hashes do not give you @@ -122,7 +138,7 @@ SCRIPT_SH = \ git-fetch.sh \ git-ls-remote.sh \ git-merge-one-file.sh git-parse-remote.sh \ - git-prune.sh git-pull.sh git-rebase.sh \ + git-pull.sh git-rebase.sh \ git-repack.sh git-request-pull.sh git-reset.sh \ git-resolve.sh git-revert.sh git-sh-setup.sh \ git-tag.sh git-verify-tag.sh \ @@ -133,10 +149,10 @@ SCRIPT_SH = \ SCRIPT_PERL = \ git-archimport.perl git-cvsimport.perl git-relink.perl \ - git-shortlog.perl git-fmt-merge-msg.perl git-rerere.perl \ + git-shortlog.perl git-rerere.perl \ git-annotate.perl git-cvsserver.perl \ git-svnimport.perl git-mv.perl git-cvsexportcommit.perl \ - git-send-email.perl + git-send-email.perl git-svn.perl SCRIPT_PYTHON = \ git-merge-recursive.py @@ -144,7 +160,7 @@ SCRIPT_PYTHON = \ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ $(patsubst %.perl,%,$(SCRIPT_PERL)) \ $(patsubst %.py,%,$(SCRIPT_PYTHON)) \ - git-cherry-pick git-status + git-cherry-pick git-status git-instaweb # The ones that do not have to link with lcrypto, lz nor xdiff. SIMPLE_PROGRAMS = \ @@ -175,7 +191,8 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X git-update-ref$X \ git-ls-files$X git-ls-tree$X git-get-tar-commit-id$X \ git-read-tree$X git-commit-tree$X git-write-tree$X \ git-apply$X git-show-branch$X git-diff-files$X git-update-index$X \ - git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X + git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X \ + git-fmt-merge-msg$X git-prune$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -219,7 +236,7 @@ LIB_OBJS = \ server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \ tag.o tree.o usage.o config.o environment.o ctype.o copy.o \ fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \ - alloc.o $(DIFF_OBJS) + alloc.o merge-file.o $(DIFF_OBJS) BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ @@ -231,7 +248,7 @@ BUILTIN_OBJS = \ builtin-apply.o builtin-show-branch.o builtin-diff-files.o \ builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \ builtin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \ - builtin-update-ref.o + builtin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz @@ -251,15 +268,17 @@ ifeq ($(uname_S),Darwin) NEEDS_SSL_WITH_CRYPTO = YesPlease NEEDS_LIBICONV = YesPlease NO_STRLCPY = YesPlease - ## fink - ifeq ($(shell test -d /sw/lib && echo y),y) - ALL_CFLAGS += -I/sw/include - ALL_LDFLAGS += -L/sw/lib + ifndef NO_FINK + ifeq ($(shell test -d /sw/lib && echo y),y) + ALL_CFLAGS += -I/sw/include + ALL_LDFLAGS += -L/sw/lib + endif endif - ## darwinports - ifeq ($(shell test -d /opt/local/lib && echo y),y) - ALL_CFLAGS += -I/opt/local/include - ALL_LDFLAGS += -L/opt/local/lib + ifndef NO_DARWIN_PORTS + ifeq ($(shell test -d /opt/local/lib && echo y),y) + ALL_CFLAGS += -I/opt/local/include + ALL_LDFLAGS += -L/opt/local/lib + endif endif endif ifeq ($(uname_S),SunOS) @@ -471,7 +490,7 @@ ifdef NO_ACCURATE_DIFF ALL_CFLAGS += -DNO_ACCURATE_DIFF endif -# Shell quote (do not use $(call) to accomodate ancient setups); +# Shell quote (do not use $(call) to accommodate ancient setups); SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER)) @@ -516,6 +535,7 @@ common-cmds.h: Documentation/git-*.txt $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh rm -f $@ $@+ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ + -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ -e 's/@@NO_CURL@@/$(NO_CURL)/g' \ -e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \ @@ -548,6 +568,20 @@ git-status: git-commit cp $< $@+ mv $@+ $@ +git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css + rm -f $@ $@+ + sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ + -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ + -e 's/@@NO_CURL@@/$(NO_CURL)/g' \ + -e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \ + -e '/@@GITWEB_CGI@@/r gitweb/gitweb.cgi' \ + -e '/@@GITWEB_CGI@@/d' \ + -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \ + -e '/@@GITWEB_CSS@@/d' \ + $@.sh | sed "s|/usr/bin/git|$(bindir)/git|" > $@+ + chmod +x $@+ + mv $@+ $@ + # These can record GIT_VERSION git$X git.spec \ $(patsubst %.sh,%,$(SCRIPT_SH)) \ @@ -581,6 +615,8 @@ $(SIMPLE_PROGRAMS) : git-%$X : %.o $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIB_FILE) $(SIMPLE_LIB) +ssh-pull.o: ssh-fetch.c +ssh-push.o: ssh-upload.c git-local-fetch$X: fetch.o git-ssh-fetch$X: rsh.o fetch.o git-ssh-upload$X: rsh.o @@ -590,11 +626,11 @@ git-ssh-push$X: rsh.o git-imap-send$X: imap-send.o $(LIB_FILE) http.o http-fetch.o http-push.o: http.h -git-http-fetch$X: fetch.o http.o http-fetch.o $(LIB_FILE) +git-http-fetch$X: fetch.o http.o http-fetch.o $(GITLIBS) $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) -git-http-push$X: revision.o http.o http-push.o $(LIB_FILE) +git-http-push$X: revision.o http.o http-push.o $(GITLIBS) $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) @@ -640,6 +676,7 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS # with that. export NO_PYTHON +export NO_SVN_TESTS test: all $(MAKE) -C t/ all @@ -653,6 +690,12 @@ test-delta$X: test-delta.c diff-delta.o patch-delta.o test-dump-cache-tree$X: dump-cache-tree.o $(GITLIBS) $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) +test-sha1$X: test-sha1.o $(GITLIBS) + $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) + +check-sha1:: test-sha1$X + ./test-sha1.sh + check: for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done diff --git a/blame.c b/blame.c index c86e2fd4b3..b04b8f58aa 100644 --- a/blame.c +++ b/blame.c @@ -21,11 +21,11 @@ #define DEBUG 0 static const char blame_usage[] = "[-c] [-l] [-t] [-S ] [--] file [commit]\n" - " -c, --compability Use the same output mode as git-annotate (Default: off)\n" - " -l, --long Show long commit SHA1 (Default: off)\n" - " -t, --time Show raw timestamp (Default: off)\n" - " -S, --revs-file Use revisions from revs-file instead of calling git-rev-list\n" - " -h, --help This message"; + " -c, --compatibility Use the same output mode as git-annotate (Default: off)\n" + " -l, --long Show long commit SHA1 (Default: off)\n" + " -t, --time Show raw timestamp (Default: off)\n" + " -S, --revs-file Use revisions from revs-file instead of calling git-rev-list\n" + " -h, --help This message"; static struct commit **blame_lines; static int num_blame_lines; @@ -44,8 +44,8 @@ struct util_info { }; struct chunk { - int off1, len1; // --- - int off2, len2; // +++ + int off1, len1; /* --- */ + int off2, len2; /* +++ */ }; struct patch { @@ -255,7 +255,7 @@ static void print_map(struct commit *cmit, struct commit *other) } #endif -// p is a patch from commit to other. +/* p is a patch from commit to other. */ static void fill_line_map(struct commit *commit, struct commit *other, struct patch *p) { @@ -747,7 +747,7 @@ int main(int argc, const char **argv) const char *filename = NULL, *commit = NULL; char filename_buf[256]; int sha1_len = 8; - int compability = 0; + int compatibility = 0; int show_raw_time = 0; int options = 1; struct commit* start_commit; @@ -774,8 +774,8 @@ int main(int argc, const char **argv) sha1_len = 40; continue; } else if(!strcmp(argv[i], "-c") || - !strcmp(argv[i], "--compability")) { - compability = 1; + !strcmp(argv[i], "--compatibility")) { + compatibility = 1; continue; } else if(!strcmp(argv[i], "-t") || !strcmp(argv[i], "--time")) { @@ -784,7 +784,7 @@ int main(int argc, const char **argv) } else if(!strcmp(argv[i], "-S")) { if (i + 1 < argc && !read_ancestry(argv[i + 1], &sha1_p)) { - compability = 1; + compatibility = 1; i++; continue; } @@ -884,7 +884,7 @@ int main(int argc, const char **argv) u = c->util; get_commit_info(c, &ci); fwrite(sha1_to_hex(c->object.sha1), sha1_len, 1, stdout); - if(compability) { + if(compatibility) { printf("\t(%10s\t%10s\t%d)", ci.author, format_time(ci.author_time, ci.author_tz, show_raw_time), diff --git a/blob.c b/blob.c index 496f270043..d1af2e62f1 100644 --- a/blob.c +++ b/blob.c @@ -10,12 +10,12 @@ struct blob *lookup_blob(const unsigned char *sha1) if (!obj) { struct blob *ret = alloc_blob_node(); created_object(sha1, &ret->object); - ret->object.type = TYPE_BLOB; + ret->object.type = OBJ_BLOB; return ret; } if (!obj->type) - obj->type = TYPE_BLOB; - if (obj->type != TYPE_BLOB) { + obj->type = OBJ_BLOB; + if (obj->type != OBJ_BLOB) { error("Object %s is a %s, not a blob", sha1_to_hex(sha1), typename(obj->type)); return NULL; diff --git a/builtin-add.c b/builtin-add.c index bfbbb1bf52..3a73a173f7 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -21,8 +21,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p for (specs = 0; pathspec[specs]; specs++) /* nothing */; - seen = xmalloc(specs); - memset(seen, 0, specs); + seen = xcalloc(specs, 1); src = dst = dir->entries; i = dir->nr; @@ -181,7 +180,7 @@ int cmd_add(int argc, const char **argv, char **envp) if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || - commit_lock_file(&lock_file)) + close(newfd) || commit_lock_file(&lock_file)) die("Unable to write new index file"); } diff --git a/builtin-apply.c b/builtin-apply.c index e9ead002d3..d4381d9a8f 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -14,14 +14,15 @@ #include "delta.h" #include "builtin.h" -// --check turns on checking that the working tree matches the -// files that are being modified, but doesn't apply the patch -// --stat does just a diffstat, and doesn't actually apply -// --numstat does numeric diffstat, and doesn't actually apply -// --index-info shows the old and new index info for paths if available. -// --index updates the cache as well. -// --cached updates only the cache without ever touching the working tree. -// +/* + * --check turns on checking that the working tree matches the + * files that are being modified, but doesn't apply the patch + * --stat does just a diffstat, and doesn't actually apply + * --numstat does numeric diffstat, and doesn't actually apply + * --index-info shows the old and new index info for paths if available. + * --index updates the cache as well. + * --cached updates only the cache without ever touching the working tree. + */ static const char *prefix; static int prefix_length = -1; static int newfd = -1; @@ -119,7 +120,7 @@ struct fragment { struct patch { char *new_name, *old_name, *def_name; unsigned int old_mode, new_mode; - int is_rename, is_copy, is_new, is_delete, is_binary; + int is_rename, is_copy, is_new, is_delete, is_binary, is_reverse; #define BINARY_DELTA_DEFLATED 1 #define BINARY_LITERAL_DEFLATED 2 unsigned long deflate_origlen; @@ -284,8 +285,8 @@ static void parse_traditional_patch(const char *first, const char *second, struc { char *name; - first += 4; // skip "--- " - second += 4; // skip "+++ " + first += 4; /* skip "--- " */ + second += 4; /* skip "+++ " */ if (is_dev_null(first)) { patch->is_new = 1; patch->is_delete = 0; @@ -765,7 +766,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc continue; /* - * Make sure we don't find any unconnected patch fragmants. + * Make sure we don't find any unconnected patch fragments. * That's a sign that we didn't find a header, and that a * patch has become corrupted/broken up. */ @@ -990,7 +991,7 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch) * so one line can fit up to 13 groups that would decode * to 52 bytes max. The length byte 'A'-'Z' corresponds * to 1-26 bytes, and 'a'-'z' corresponds to 27-52 bytes. - * The end of binary is signalled with an empty line. + * The end of binary is signaled with an empty line. */ int llen, used; struct fragment *fragment; @@ -1118,6 +1119,34 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) return offset + hdrsize + patchsize; } +#define swap(a,b) myswap((a),(b),sizeof(a)) + +#define myswap(a, b, size) do { \ + unsigned char mytmp[size]; \ + memcpy(mytmp, &a, size); \ + memcpy(&a, &b, size); \ + memcpy(&b, mytmp, size); \ +} while (0) + +static void reverse_patches(struct patch *p) +{ + for (; p; p = p->next) { + struct fragment *frag = p->fragments; + + swap(p->new_name, p->old_name); + swap(p->new_mode, p->old_mode); + swap(p->is_new, p->is_delete); + swap(p->lines_added, p->lines_deleted); + swap(p->old_sha1_prefix, p->new_sha1_prefix); + + for (; frag; frag = frag->next) { + swap(frag->newpos, frag->oldpos); + swap(frag->newlines, frag->oldlines); + } + p->is_reverse = !p->is_reverse; + } +} + static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"; static const char minuses[]= "----------------------------------------------------------------------"; @@ -1335,7 +1364,7 @@ static int apply_line(char *output, const char *patch, int plen) } static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, - int inaccurate_eof) + int reverse, int inaccurate_eof) { int match_beginning, match_end; char *buf = desc->buffer; @@ -1349,6 +1378,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, int pos, lines; while (size > 0) { + char first; int len = linelen(patch, size); int plen; @@ -1365,16 +1395,23 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, plen = len-1; if (len < size && patch[len] == '\\') plen--; - switch (*patch) { + first = *patch; + if (reverse) { + if (first == '-') + first = '+'; + else if (first == '+') + first = '-'; + } + switch (first) { case ' ': case '-': memcpy(old + oldsize, patch + 1, plen); oldsize += plen; - if (*patch == '-') + if (first == '-') break; /* Fall-through for ' ' */ case '+': - if (*patch != '+' || !no_add) + if (first != '+' || !no_add) newsize += apply_line(new + newsize, patch, plen); break; @@ -1498,6 +1535,12 @@ static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch) void *data; void *result; + /* Binary patch is irreversible */ + if (patch->is_reverse) + return error("cannot reverse-apply a binary patch to '%s'", + patch->new_name + ? patch->new_name : patch->old_name); + data = inflate_it(fragment->patch, fragment->size, patch->deflate_origlen); if (!data) @@ -1614,7 +1657,8 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch) return apply_binary(desc, patch); while (frag) { - if (apply_one_fragment(desc, frag, patch->inaccurate_eof) < 0) + if (apply_one_fragment(desc, frag, patch->is_reverse, + patch->inaccurate_eof) < 0) return error("patch failed: %s:%ld", name, frag->oldpos); frag = frag->next; @@ -1663,13 +1707,14 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry * return 0; } -static int check_patch(struct patch *patch) +static int check_patch(struct patch *patch, struct patch *prev_patch) { struct stat st; const char *old_name = patch->old_name; const char *new_name = patch->new_name; const char *name = old_name ? old_name : new_name; struct cache_entry *ce = NULL; + int ok_if_exists; if (old_name) { int changed = 0; @@ -1727,13 +1772,33 @@ static int check_patch(struct patch *patch) old_name, st_mode, patch->old_mode); } + if (new_name && prev_patch && prev_patch->is_delete && + !strcmp(prev_patch->old_name, new_name)) + /* A type-change diff is always split into a patch to + * delete old, immediately followed by a patch to + * create new (see diff.c::run_diff()); in such a case + * it is Ok that the entry to be deleted by the + * previous patch is still in the working tree and in + * the index. + */ + ok_if_exists = 1; + else + ok_if_exists = 0; + if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) { - if (check_index && cache_name_pos(new_name, strlen(new_name)) >= 0) + if (check_index && + cache_name_pos(new_name, strlen(new_name)) >= 0 && + !ok_if_exists) return error("%s: already exists in index", new_name); if (!cached) { - if (!lstat(new_name, &st)) - return error("%s: already exists in working directory", new_name); - if (errno != ENOENT) + struct stat nst; + if (!lstat(new_name, &nst)) { + if (S_ISDIR(nst.st_mode) || ok_if_exists) + ; /* ok */ + else + return error("%s: already exists in working directory", new_name); + } + else if ((errno != ENOENT) && (errno != ENOTDIR)) return error("%s: %s", new_name, strerror(errno)); } if (!patch->new_mode) { @@ -1761,10 +1826,13 @@ static int check_patch(struct patch *patch) static int check_patch_list(struct patch *patch) { + struct patch *prev_patch = NULL; int error = 0; - for (;patch ; patch = patch->next) - error |= check_patch(patch); + for (prev_patch = NULL; patch ; patch = patch->next) { + error |= check_patch(patch, prev_patch); + prev_patch = patch; + } return error; } @@ -2009,6 +2077,16 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned return; } + if (errno == EEXIST || errno == EACCES) { + /* We may be trying to create a file where a directory + * used to be. + */ + struct stat st; + errno = 0; + if (!lstat(path, &st) && S_ISDIR(st.st_mode) && !rmdir(path)) + errno = EEXIST; + } + if (errno == EEXIST) { unsigned int nr = getpid(); @@ -2043,32 +2121,42 @@ static void create_file(struct patch *patch) cache_tree_invalidate_path(active_cache_tree, path); } -static void write_out_one_result(struct patch *patch) +/* phase zero is to remove, phase one is to create */ +static void write_out_one_result(struct patch *patch, int phase) { if (patch->is_delete > 0) { - remove_file(patch); + if (phase == 0) + remove_file(patch); return; } if (patch->is_new > 0 || patch->is_copy) { - create_file(patch); + if (phase == 1) + create_file(patch); return; } /* * Rename or modification boils down to the same * thing: remove the old, write the new */ - remove_file(patch); + if (phase == 0) + remove_file(patch); + if (phase == 1) create_file(patch); } static void write_out_results(struct patch *list, int skipped_patch) { + int phase; + if (!list && !skipped_patch) die("No changes"); - while (list) { - write_out_one_result(list); - list = list->next; + for (phase = 0; phase < 2; phase++) { + struct patch *l = list; + while (l) { + write_out_one_result(l, phase); + l = l->next; + } } } @@ -2097,7 +2185,8 @@ static int use_patch(struct patch *p) return 1; } -static int apply_patch(int fd, const char *filename, int inaccurate_eof) +static int apply_patch(int fd, const char *filename, + int reverse, int inaccurate_eof) { unsigned long offset, size; char *buffer = read_patch_file(fd, &size); @@ -2117,6 +2206,8 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) nr = parse_chunk(buffer + offset, size, patch); if (nr < 0) break; + if (reverse) + reverse_patches(patch); if (use_patch(patch)) { patch_stats(patch); *listp = patch; @@ -2181,6 +2272,7 @@ int cmd_apply(int argc, const char **argv, char **envp) { int i; int read_stdin = 1; + int reverse = 0; int inaccurate_eof = 0; const char *whitespace_option = NULL; @@ -2191,7 +2283,7 @@ int cmd_apply(int argc, const char **argv, char **envp) int fd; if (!strcmp(arg, "-")) { - apply_patch(0, "", inaccurate_eof); + apply_patch(0, "", reverse, inaccurate_eof); read_stdin = 0; continue; } @@ -2268,6 +2360,10 @@ int cmd_apply(int argc, const char **argv, char **envp) parse_whitespace_option(arg + 13); continue; } + if (!strcmp(arg, "-R") || !strcmp(arg, "--reverse")) { + reverse = 1; + continue; + } if (!strcmp(arg, "--inaccurate-eof")) { inaccurate_eof = 1; continue; @@ -2288,12 +2384,12 @@ int cmd_apply(int argc, const char **argv, char **envp) usage(apply_usage); read_stdin = 0; set_default_whitespace_mode(whitespace_option); - apply_patch(fd, arg, inaccurate_eof); + apply_patch(fd, arg, reverse, inaccurate_eof); close(fd); } set_default_whitespace_mode(whitespace_option); if (read_stdin) - apply_patch(0, "", inaccurate_eof); + apply_patch(0, "", reverse, inaccurate_eof); if (whitespace_error) { if (squelch_whitespace_errors && squelch_whitespace_errors < whitespace_error) { @@ -2323,7 +2419,7 @@ int cmd_apply(int argc, const char **argv, char **envp) if (write_index) { if (write_cache(newfd, active_cache, active_nr) || - commit_lock_file(&lock_file)) + close(newfd) || commit_lock_file(&lock_file)) die("Unable to write new index file"); } diff --git a/builtin-diff-files.c b/builtin-diff-files.c index 5afc1d7208..2e10118623 100644 --- a/builtin-diff-files.c +++ b/builtin-diff-files.c @@ -18,8 +18,8 @@ int cmd_diff_files(int argc, const char **argv, char **envp) struct rev_info rev; int silent = 0; - git_config(git_diff_config); init_revisions(&rev); + git_config(git_default_config); /* no "diff" UI options */ rev.abbrev = 0; argc = setup_revisions(argc, argv, &rev, NULL); @@ -36,6 +36,9 @@ int cmd_diff_files(int argc, const char **argv, char **envp) usage(diff_files_usage); argv++; argc--; } + if (!rev.diffopt.output_format) + rev.diffopt.output_format = DIFF_FORMAT_RAW; + /* * Make sure there are NO revision (i.e. pending object) parameter, * rev.max_count is reasonable (0 <= n <= 3), diff --git a/builtin-diff-index.c b/builtin-diff-index.c index c42ef9a7a7..dc52c054ee 100644 --- a/builtin-diff-index.c +++ b/builtin-diff-index.c @@ -15,8 +15,8 @@ int cmd_diff_index(int argc, const char **argv, char **envp) int cached = 0; int i; - git_config(git_diff_config); init_revisions(&rev); + git_config(git_default_config); /* no "diff" UI options */ rev.abbrev = 0; argc = setup_revisions(argc, argv, &rev, NULL); @@ -28,6 +28,9 @@ int cmd_diff_index(int argc, const char **argv, char **envp) else usage(diff_cache_usage); } + if (!rev.diffopt.output_format) + rev.diffopt.output_format = DIFF_FORMAT_RAW; + /* * Make sure there is one revision (i.e. pending object), * and there is no revision filtering parameters. diff --git a/builtin-diff-stages.c b/builtin-diff-stages.c index 7c157ca889..9c62702941 100644 --- a/builtin-diff-stages.c +++ b/builtin-diff-stages.c @@ -61,7 +61,7 @@ int cmd_diff_stages(int ac, const char **av, char **envp) const char *prefix = setup_git_directory(); const char **pathspec = NULL; - git_config(git_diff_config); + git_config(git_default_config); /* no "diff" UI options */ read_cache(); diff_setup(&diff_options); while (1 < ac && av[1][0] == '-') { @@ -85,6 +85,9 @@ int cmd_diff_stages(int ac, const char **av, char **envp) ac--; av++; } + if (!diff_options.output_format) + diff_options.output_format = DIFF_FORMAT_RAW; + if (ac < 3 || sscanf(av[1], "%d", &stage1) != 1 || ! (0 <= stage1 && stage1 <= 3) || diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c index 3409a39a9f..8957b459de 100644 --- a/builtin-diff-tree.c +++ b/builtin-diff-tree.c @@ -67,9 +67,9 @@ int cmd_diff_tree(int argc, const char **argv, char **envp) static struct rev_info *opt = &log_tree_opt; int read_stdin = 0; - git_config(git_diff_config); - nr_sha1 = 0; init_revisions(opt); + git_config(git_default_config); /* no "diff" UI options */ + nr_sha1 = 0; opt->abbrev = 0; opt->diff = 1; argc = setup_revisions(argc, argv, opt, NULL); @@ -84,6 +84,9 @@ int cmd_diff_tree(int argc, const char **argv, char **envp) usage(diff_tree_usage); } + if (!opt->diffopt.output_format) + opt->diffopt.output_format = DIFF_FORMAT_RAW; + /* * NOTE! We expect "a ^b" to be equal to "a..b", so we * reverse the order of the objects if the second one diff --git a/builtin-diff.c b/builtin-diff.c index 99a2f76605..dca223235d 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -39,8 +39,6 @@ static int builtin_diff_files(struct rev_info *revs, revs->max_count = 3; else if (!strcmp(arg, "-q")) silent = 1; - else if (!strcmp(arg, "--raw")) - revs->diffopt.output_format = DIFF_FORMAT_RAW; else usage(builtin_diff_usage); argv++; argc--; @@ -56,7 +54,7 @@ static int builtin_diff_files(struct rev_info *revs, 3 < revs->max_count) usage(builtin_diff_usage); if (revs->max_count < 0 && - (revs->diffopt.output_format == DIFF_FORMAT_PATCH)) + (revs->diffopt.output_format & DIFF_FORMAT_PATCH)) revs->combine_merges = revs->dense_combined_merges = 1; /* * Backward compatibility wart - "diff-files -s" used to @@ -107,14 +105,9 @@ static int builtin_diff_b_f(struct rev_info *revs, /* Blob vs file in the working tree*/ struct stat st; - while (1 < argc) { - const char *arg = argv[1]; - if (!strcmp(arg, "--raw")) - revs->diffopt.output_format = DIFF_FORMAT_RAW; - else - usage(builtin_diff_usage); - argv++; argc--; - } + if (argc > 1) + usage(builtin_diff_usage); + if (lstat(path, &st)) die("'%s': %s", path, strerror(errno)); if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) @@ -137,14 +130,9 @@ static int builtin_diff_blobs(struct rev_info *revs, */ unsigned mode = canon_mode(S_IFREG | 0644); - while (1 < argc) { - const char *arg = argv[1]; - if (!strcmp(arg, "--raw")) - revs->diffopt.output_format = DIFF_FORMAT_RAW; - else - usage(builtin_diff_usage); - argv++; argc--; - } + if (argc > 1) + usage(builtin_diff_usage); + stuff_change(&revs->diffopt, mode, mode, blob[1].sha1, blob[0].sha1, @@ -162,8 +150,6 @@ static int builtin_diff_index(struct rev_info *revs, const char *arg = argv[1]; if (!strcmp(arg, "--cached")) cached = 1; - else if (!strcmp(arg, "--raw")) - revs->diffopt.output_format = DIFF_FORMAT_RAW; else usage(builtin_diff_usage); argv++; argc--; @@ -185,17 +171,12 @@ static int builtin_diff_tree(struct rev_info *revs, { const unsigned char *(sha1[2]); int swap = 0; - while (1 < argc) { - const char *arg = argv[1]; - if (!strcmp(arg, "--raw")) - revs->diffopt.output_format = DIFF_FORMAT_RAW; - else - usage(builtin_diff_usage); - argv++; argc--; - } + + if (argc > 1) + usage(builtin_diff_usage); /* We saw two trees, ent[0] and ent[1]. - * if ent[1] is unintesting, they are swapped + * if ent[1] is uninteresting, they are swapped */ if (ent[1].item->flags & UNINTERESTING) swap = 1; @@ -214,14 +195,9 @@ static int builtin_diff_combined(struct rev_info *revs, const unsigned char (*parent)[20]; int i; - while (1 < argc) { - const char *arg = argv[1]; - if (!strcmp(arg, "--raw")) - revs->diffopt.output_format = DIFF_FORMAT_RAW; - else - usage(builtin_diff_usage); - argv++; argc--; - } + if (argc > 1) + usage(builtin_diff_usage); + if (!revs->dense_combined_merges && !revs->combine_merges) revs->dense_combined_merges = revs->combine_merges = 1; parent = xmalloc(ents * sizeof(*parent)); @@ -274,11 +250,16 @@ int cmd_diff(int argc, const char **argv, char **envp) * Other cases are errors. */ - git_config(git_diff_config); init_revisions(&rev); - rev.diffopt.output_format = DIFF_FORMAT_PATCH; + git_config(git_diff_ui_config); + diff_setup(&rev.diffopt); argc = setup_revisions(argc, argv, &rev, NULL); + if (!rev.diffopt.output_format) { + rev.diffopt.output_format = DIFF_FORMAT_PATCH; + diff_setup_done(&rev.diffopt); + } + /* Do we have --cached and not have a pending object, then * default to HEAD by hand. Eek. */ @@ -305,9 +286,9 @@ int cmd_diff(int argc, const char **argv, char **envp) obj = deref_tag(obj, NULL, 0); if (!obj) die("invalid object '%s' given.", name); - if (obj->type == TYPE_COMMIT) + if (obj->type == OBJ_COMMIT) obj = &((struct commit *)obj)->tree->object; - if (obj->type == TYPE_TREE) { + if (obj->type == OBJ_TREE) { if (ARRAY_SIZE(ent) <= ents) die("more than %d trees given: '%s'", (int) ARRAY_SIZE(ent), name); @@ -317,7 +298,7 @@ int cmd_diff(int argc, const char **argv, char **envp) ents++; continue; } - if (obj->type == TYPE_BLOB) { + if (obj->type == OBJ_BLOB) { if (2 <= blobs) die("more than two blobs given: '%s'", name); memcpy(blob[blobs].sha1, obj->sha1, 20); @@ -366,7 +347,15 @@ int cmd_diff(int argc, const char **argv, char **envp) return builtin_diff_index(&rev, argc, argv); else if (ents == 2) return builtin_diff_tree(&rev, argc, argv, ent); + else if ((ents == 3) && (ent[0].item->flags & UNINTERESTING)) { + /* diff A...B where there is one sane merge base between + * A and B. We have ent[0] == merge-base, ent[1] == A, + * and ent[2] == B. Show diff between the base and B. + */ + return builtin_diff_tree(&rev, argc, argv, ent); + } else - return builtin_diff_combined(&rev, argc, argv, ent, ents); + return builtin_diff_combined(&rev, argc, argv, + ent, ents); usage(builtin_diff_usage); } diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c new file mode 100644 index 0000000000..f20b27b8a9 --- /dev/null +++ b/builtin-fmt-merge-msg.c @@ -0,0 +1,359 @@ +#include "cache.h" +#include "commit.h" +#include "diff.h" +#include "revision.h" +#include "tag.h" + +static const char *fmt_merge_msg_usage = + "git-fmt-merge-msg [--summary] [--no-summary] [--file ]"; + +static int merge_summary = 0; + +static int fmt_merge_msg_config(const char *key, const char *value) +{ + if (!strcmp("merge.summary", key)) + merge_summary = git_config_bool(key, value); + return 0; +} + +struct list { + char **list; + void **payload; + unsigned nr, alloc; +}; + +static void append_to_list(struct list *list, char *value, void *payload) +{ + if (list->nr == list->alloc) { + list->alloc += 32; + list->list = realloc(list->list, sizeof(char *) * list->alloc); + list->payload = realloc(list->payload, + sizeof(char *) * list->alloc); + } + list->payload[list->nr] = payload; + list->list[list->nr++] = value; +} + +static int find_in_list(struct list *list, char *value) +{ + int i; + + for (i = 0; i < list->nr; i++) + if (!strcmp(list->list[i], value)) + return i; + + return -1; +} + +static void free_list(struct list *list) +{ + int i; + + if (list->alloc == 0) + return; + + for (i = 0; i < list->nr; i++) { + free(list->list[i]); + if (list->payload[i]) + free(list->payload[i]); + } + free(list->list); + free(list->payload); + list->nr = list->alloc = 0; +} + +struct src_data { + struct list branch, tag, r_branch, generic; + int head_status; +}; + +static struct list srcs = { NULL, NULL, 0, 0}; +static struct list origins = { NULL, NULL, 0, 0}; + +static int handle_line(char *line) +{ + int i, len = strlen(line); + unsigned char *sha1; + char *src, *origin; + struct src_data *src_data; + int pulling_head = 0; + + if (len < 43 || line[40] != '\t') + return 1; + + if (!strncmp(line + 41, "not-for-merge", 13)) + return 0; + + if (line[41] != '\t') + return 2; + + line[40] = 0; + sha1 = xmalloc(20); + i = get_sha1(line, sha1); + line[40] = '\t'; + if (i) + return 3; + + if (line[len - 1] == '\n') + line[len - 1] = 0; + line += 42; + + src = strstr(line, " of "); + if (src) { + *src = 0; + src += 4; + pulling_head = 0; + } else { + src = line; + pulling_head = 1; + } + + i = find_in_list(&srcs, src); + if (i < 0) { + i = srcs.nr; + append_to_list(&srcs, strdup(src), + xcalloc(1, sizeof(struct src_data))); + } + src_data = srcs.payload[i]; + + if (pulling_head) { + origin = strdup(src); + src_data->head_status |= 1; + } else if (!strncmp(line, "branch ", 7)) { + origin = strdup(line + 7); + append_to_list(&src_data->branch, origin, NULL); + src_data->head_status |= 2; + } else if (!strncmp(line, "tag ", 4)) { + origin = line; + append_to_list(&src_data->tag, strdup(origin + 4), NULL); + src_data->head_status |= 2; + } else if (!strncmp(line, "remote branch ", 14)) { + origin = strdup(line + 14); + append_to_list(&src_data->r_branch, origin, NULL); + src_data->head_status |= 2; + } else { + origin = strdup(src); + append_to_list(&src_data->generic, strdup(line), NULL); + src_data->head_status |= 2; + } + + if (!strcmp(".", src) || !strcmp(src, origin)) { + int len = strlen(origin); + if (origin[0] == '\'' && origin[len - 1] == '\'') { + char *new_origin = malloc(len - 1); + memcpy(new_origin, origin + 1, len - 2); + new_origin[len - 1] = 0; + origin = new_origin; + } else + origin = strdup(origin); + } else { + char *new_origin = malloc(strlen(origin) + strlen(src) + 5); + sprintf(new_origin, "%s of %s", origin, src); + origin = new_origin; + } + append_to_list(&origins, origin, sha1); + return 0; +} + +static void print_joined(const char *singular, const char *plural, + struct list *list) +{ + if (list->nr == 0) + return; + if (list->nr == 1) { + printf("%s%s", singular, list->list[0]); + } else { + int i; + printf("%s", plural); + for (i = 0; i < list->nr - 1; i++) + printf("%s%s", i > 0 ? ", " : "", list->list[i]); + printf(" and %s", list->list[list->nr - 1]); + } +} + +static void shortlog(const char *name, unsigned char *sha1, + struct commit *head, struct rev_info *rev, int limit) +{ + int i, count = 0; + struct commit *commit; + struct object *branch; + struct list subjects = { NULL, NULL, 0, 0 }; + int flags = UNINTERESTING | TREECHANGE | SEEN | SHOWN | ADDED; + + branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40); + if (!branch || branch->type != OBJ_COMMIT) + return; + + setup_revisions(0, NULL, rev, NULL); + rev->ignore_merges = 1; + add_pending_object(rev, branch, name); + add_pending_object(rev, &head->object, "^HEAD"); + head->object.flags |= UNINTERESTING; + prepare_revision_walk(rev); + while ((commit = get_revision(rev)) != NULL) { + char *oneline, *bol, *eol; + + /* ignore merges */ + if (commit->parents && commit->parents->next) + continue; + + count++; + if (subjects.nr > limit) + continue; + + bol = strstr(commit->buffer, "\n\n"); + if (!bol) { + append_to_list(&subjects, strdup(sha1_to_hex( + commit->object.sha1)), + NULL); + continue; + } + + bol += 2; + eol = strchr(bol, '\n'); + + if (eol) { + int len = eol - bol; + oneline = malloc(len + 1); + memcpy(oneline, bol, len); + oneline[len] = 0; + } else + oneline = strdup(bol); + append_to_list(&subjects, oneline, NULL); + } + + if (count > limit) + printf("\n* %s: (%d commits)\n", name, count); + else + printf("\n* %s:\n", name); + + for (i = 0; i < subjects.nr; i++) + if (i >= limit) + printf(" ...\n"); + else + printf(" %s\n", subjects.list[i]); + + clear_commit_marks((struct commit *)branch, flags); + clear_commit_marks(head, flags); + free_commit_list(rev->commits); + rev->commits = NULL; + rev->pending.nr = 0; + + free_list(&subjects); +} + +int cmd_fmt_merge_msg(int argc, char **argv, char **envp) +{ + int limit = 20, i = 0; + char line[1024]; + FILE *in = stdin; + const char *sep = ""; + unsigned char head_sha1[20]; + const char *head, *current_branch; + + git_config(fmt_merge_msg_config); + + while (argc > 1) { + if (!strcmp(argv[1], "--summary")) + merge_summary = 1; + else if (!strcmp(argv[1], "--no-summary")) + merge_summary = 0; + else if (!strcmp(argv[1], "-F") || !strcmp(argv[1], "--file")) { + if (argc < 2) + die ("Which file?"); + if (!strcmp(argv[2], "-")) + in = stdin; + else { + fclose(in); + in = fopen(argv[2], "r"); + } + argc--; argv++; + } else + break; + argc--; argv++; + } + + if (argc > 1) + usage(fmt_merge_msg_usage); + + /* get current branch */ + head = strdup(git_path("HEAD")); + current_branch = resolve_ref(head, head_sha1, 1); + current_branch += strlen(head) - 4; + free((char *)head); + if (!strncmp(current_branch, "refs/heads/", 11)) + current_branch += 11; + + while (fgets(line, sizeof(line), in)) { + i++; + if (line[0] == 0) + continue; + if (handle_line(line)) + die ("Error in line %d: %s", i, line); + } + + printf("Merge "); + for (i = 0; i < srcs.nr; i++) { + struct src_data *src_data = srcs.payload[i]; + const char *subsep = ""; + + printf(sep); + sep = "; "; + + if (src_data->head_status == 1) { + printf(srcs.list[i]); + continue; + } + if (src_data->head_status == 3) { + subsep = ", "; + printf("HEAD"); + } + if (src_data->branch.nr) { + printf(subsep); + subsep = ", "; + print_joined("branch ", "branches ", &src_data->branch); + } + if (src_data->r_branch.nr) { + printf(subsep); + subsep = ", "; + print_joined("remote branch ", "remote branches ", + &src_data->r_branch); + } + if (src_data->tag.nr) { + printf(subsep); + subsep = ", "; + print_joined("tag ", "tags ", &src_data->tag); + } + if (src_data->generic.nr) { + printf(subsep); + print_joined("commit ", "commits ", &src_data->generic); + } + if (strcmp(".", srcs.list[i])) + printf(" of %s", srcs.list[i]); + } + + if (!strcmp("master", current_branch)) + putchar('\n'); + else + printf(" into %s\n", current_branch); + + if (merge_summary) { + struct commit *head; + struct rev_info rev; + + head = lookup_commit(head_sha1); + init_revisions(&rev); + rev.commit_format = CMIT_FMT_ONELINE; + rev.ignore_merges = 1; + rev.limited = 1; + + for (i = 0; i < origins.nr; i++) + shortlog(origins.list[i], origins.payload[i], + head, &rev, limit); + } + + /* No cleanup yet; is standalone anyway */ + + return 0; +} + diff --git a/builtin-grep.c b/builtin-grep.c index 2e7986cece..a79bac305a 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -82,17 +82,47 @@ static int pathspec_matches(const char **paths, const char *name) return 0; } +enum grep_pat_token { + GREP_PATTERN, + GREP_AND, + GREP_OPEN_PAREN, + GREP_CLOSE_PAREN, + GREP_NOT, + GREP_OR, +}; + struct grep_pat { struct grep_pat *next; const char *origin; int no; + enum grep_pat_token token; const char *pattern; regex_t regexp; }; +enum grep_expr_node { + GREP_NODE_ATOM, + GREP_NODE_NOT, + GREP_NODE_AND, + GREP_NODE_OR, +}; + +struct grep_expr { + enum grep_expr_node node; + union { + struct grep_pat *atom; + struct grep_expr *unary; + struct { + struct grep_expr *left; + struct grep_expr *right; + } binary; + } u; +}; + struct grep_opt { struct grep_pat *pattern_list; struct grep_pat **pattern_tail; + struct grep_expr *pattern_expression; regex_t regexp; unsigned linenum:1; unsigned invert:1; @@ -105,43 +135,224 @@ struct grep_opt { #define GREP_BINARY_NOMATCH 1 #define GREP_BINARY_TEXT 2 unsigned binary:2; + unsigned extended:1; int regflags; unsigned pre_context; unsigned post_context; }; static void add_pattern(struct grep_opt *opt, const char *pat, - const char *origin, int no) + const char *origin, int no, enum grep_pat_token t) { struct grep_pat *p = xcalloc(1, sizeof(*p)); p->pattern = pat; p->origin = origin; p->no = no; + p->token = t; *opt->pattern_tail = p; opt->pattern_tail = &p->next; p->next = NULL; } +static void compile_regexp(struct grep_pat *p, struct grep_opt *opt) +{ + int err = regcomp(&p->regexp, p->pattern, opt->regflags); + if (err) { + char errbuf[1024]; + char where[1024]; + if (p->no) + sprintf(where, "In '%s' at %d, ", + p->origin, p->no); + else if (p->origin) + sprintf(where, "%s, ", p->origin); + else + where[0] = 0; + regerror(err, &p->regexp, errbuf, 1024); + regfree(&p->regexp); + die("%s'%s': %s", where, p->pattern, errbuf); + } +} + +#if DEBUG +static inline void indent(int in) +{ + int i; + for (i = 0; i < in; i++) putchar(' '); +} + +static void dump_pattern_exp(struct grep_expr *x, int in) +{ + switch (x->node) { + case GREP_NODE_ATOM: + indent(in); + puts(x->u.atom->pattern); + break; + case GREP_NODE_NOT: + indent(in); + puts("--not"); + dump_pattern_exp(x->u.unary, in+1); + break; + case GREP_NODE_AND: + dump_pattern_exp(x->u.binary.left, in+1); + indent(in); + puts("--and"); + dump_pattern_exp(x->u.binary.right, in+1); + break; + case GREP_NODE_OR: + dump_pattern_exp(x->u.binary.left, in+1); + indent(in); + puts("--or"); + dump_pattern_exp(x->u.binary.right, in+1); + break; + } +} + +static void looking_at(const char *msg, struct grep_pat **list) +{ + struct grep_pat *p = *list; + fprintf(stderr, "%s: looking at ", msg); + if (!p) + fprintf(stderr, "empty\n"); + else + fprintf(stderr, "<%s>\n", p->pattern); +} +#else +#define looking_at(a,b) do {} while(0) +#endif + +static struct grep_expr *compile_pattern_expr(struct grep_pat **); +static struct grep_expr *compile_pattern_atom(struct grep_pat **list) +{ + struct grep_pat *p; + struct grep_expr *x; + + looking_at("atom", list); + + p = *list; + switch (p->token) { + case GREP_PATTERN: /* atom */ + x = xcalloc(1, sizeof (struct grep_expr)); + x->node = GREP_NODE_ATOM; + x->u.atom = p; + *list = p->next; + return x; + case GREP_OPEN_PAREN: + *list = p->next; + x = compile_pattern_expr(list); + if (!x) + return NULL; + if (!*list || (*list)->token != GREP_CLOSE_PAREN) + die("unmatched parenthesis"); + *list = (*list)->next; + return x; + default: + return NULL; + } +} + +static struct grep_expr *compile_pattern_not(struct grep_pat **list) +{ + struct grep_pat *p; + struct grep_expr *x; + + looking_at("not", list); + + p = *list; + switch (p->token) { + case GREP_NOT: + if (!p->next) + die("--not not followed by pattern expression"); + *list = p->next; + x = xcalloc(1, sizeof (struct grep_expr)); + x->node = GREP_NODE_NOT; + x->u.unary = compile_pattern_not(list); + if (!x->u.unary) + die("--not followed by non pattern expression"); + return x; + default: + return compile_pattern_atom(list); + } +} + +static struct grep_expr *compile_pattern_and(struct grep_pat **list) +{ + struct grep_pat *p; + struct grep_expr *x, *y, *z; + + looking_at("and", list); + + x = compile_pattern_not(list); + p = *list; + if (p && p->token == GREP_AND) { + if (!p->next) + die("--and not followed by pattern expression"); + *list = p->next; + y = compile_pattern_and(list); + if (!y) + die("--and not followed by pattern expression"); + z = xcalloc(1, sizeof (struct grep_expr)); + z->node = GREP_NODE_AND; + z->u.binary.left = x; + z->u.binary.right = y; + return z; + } + return x; +} + +static struct grep_expr *compile_pattern_or(struct grep_pat **list) +{ + struct grep_pat *p; + struct grep_expr *x, *y, *z; + + looking_at("or", list); + + x = compile_pattern_and(list); + p = *list; + if (x && p && p->token != GREP_CLOSE_PAREN) { + y = compile_pattern_or(list); + if (!y) + die("not a pattern expression %s", p->pattern); + z = xcalloc(1, sizeof (struct grep_expr)); + z->node = GREP_NODE_OR; + z->u.binary.left = x; + z->u.binary.right = y; + return z; + } + return x; +} + +static struct grep_expr *compile_pattern_expr(struct grep_pat **list) +{ + looking_at("expr", list); + + return compile_pattern_or(list); +} + static void compile_patterns(struct grep_opt *opt) { struct grep_pat *p; + + /* First compile regexps */ for (p = opt->pattern_list; p; p = p->next) { - int err = regcomp(&p->regexp, p->pattern, opt->regflags); - if (err) { - char errbuf[1024]; - char where[1024]; - if (p->no) - sprintf(where, "In '%s' at %d, ", - p->origin, p->no); - else if (p->origin) - sprintf(where, "%s, ", p->origin); - else - where[0] = 0; - regerror(err, &p->regexp, errbuf, 1024); - regfree(&p->regexp); - die("%s'%s': %s", where, p->pattern, errbuf); - } + if (p->token == GREP_PATTERN) + compile_regexp(p, opt); + else + opt->extended = 1; } + + if (!opt->extended) + return; + + /* Then bundle them up in an expression. + * A classic recursive descent parser would do. + */ + p = opt->pattern_list; + opt->pattern_expression = compile_pattern_expr(&p); +#if DEBUG + dump_pattern_exp(opt->pattern_expression, 0); +#endif + if (p) + die("incomplete pattern expression: %s", p->pattern); } static char *end_of_line(char *cp, unsigned long *left) @@ -196,6 +407,79 @@ static int fixmatch(const char *pattern, char *line, regmatch_t *match) } } +static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol) +{ + int hit = 0; + regmatch_t pmatch[10]; + + if (!opt->fixed) { + regex_t *exp = &p->regexp; + hit = !regexec(exp, bol, ARRAY_SIZE(pmatch), + pmatch, 0); + } + else { + hit = !fixmatch(p->pattern, bol, pmatch); + } + + if (hit && opt->word_regexp) { + /* Match beginning must be either + * beginning of the line, or at word + * boundary (i.e. the last char must + * not be alnum or underscore). + */ + if ((pmatch[0].rm_so < 0) || + (eol - bol) <= pmatch[0].rm_so || + (pmatch[0].rm_eo < 0) || + (eol - bol) < pmatch[0].rm_eo) + die("regexp returned nonsense"); + if (pmatch[0].rm_so != 0 && + word_char(bol[pmatch[0].rm_so-1])) + hit = 0; + if (pmatch[0].rm_eo != (eol-bol) && + word_char(bol[pmatch[0].rm_eo])) + hit = 0; + } + return hit; +} + +static int match_expr_eval(struct grep_opt *opt, + struct grep_expr *x, + char *bol, char *eol) +{ + switch (x->node) { + case GREP_NODE_ATOM: + return match_one_pattern(opt, x->u.atom, bol, eol); + break; + case GREP_NODE_NOT: + return !match_expr_eval(opt, x->u.unary, bol, eol); + case GREP_NODE_AND: + return (match_expr_eval(opt, x->u.binary.left, bol, eol) && + match_expr_eval(opt, x->u.binary.right, bol, eol)); + case GREP_NODE_OR: + return (match_expr_eval(opt, x->u.binary.left, bol, eol) || + match_expr_eval(opt, x->u.binary.right, bol, eol)); + } + die("Unexpected node type (internal error) %d\n", x->node); +} + +static int match_expr(struct grep_opt *opt, char *bol, char *eol) +{ + struct grep_expr *x = opt->pattern_expression; + return match_expr_eval(opt, x, bol, eol); +} + +static int match_line(struct grep_opt *opt, char *bol, char *eol) +{ + struct grep_pat *p; + if (opt->extended) + return match_expr(opt, bol, eol); + for (p = opt->pattern_list; p; p = p->next) { + if (match_one_pattern(opt, p, bol, eol)) + return 1; + } + return 0; +} + static int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size) { @@ -231,46 +515,15 @@ static int grep_buffer(struct grep_opt *opt, const char *name, hunk_mark = "--\n"; while (left) { - regmatch_t pmatch[10]; char *eol, ch; int hit = 0; - struct grep_pat *p; eol = end_of_line(bol, &left); ch = *eol; *eol = 0; - for (p = opt->pattern_list; p; p = p->next) { - if (!opt->fixed) { - regex_t *exp = &p->regexp; - hit = !regexec(exp, bol, ARRAY_SIZE(pmatch), - pmatch, 0); - } - else { - hit = !fixmatch(p->pattern, bol, pmatch); - } + hit = match_line(opt, bol, eol); - if (hit && opt->word_regexp) { - /* Match beginning must be either - * beginning of the line, or at word - * boundary (i.e. the last char must - * not be alnum or underscore). - */ - if ((pmatch[0].rm_so < 0) || - (eol - bol) <= pmatch[0].rm_so || - (pmatch[0].rm_eo < 0) || - (eol - bol) < pmatch[0].rm_eo) - die("regexp returned nonsense"); - if (pmatch[0].rm_so != 0 && - word_char(bol[pmatch[0].rm_so-1])) - hit = 0; - if (pmatch[0].rm_eo != (eol-bol) && - word_char(bol[pmatch[0].rm_eo])) - hit = 0; - } - if (hit) - break; - } /* "grep -v -e foo -e bla" should list lines * that do not have either, so inversion should * be done outside. @@ -446,12 +699,14 @@ static int exec_grep(int argc, const char **argv) static int external_grep(struct grep_opt *opt, const char **paths, int cached) { - int i, nr, argc, hit, len; + int i, nr, argc, hit, len, status; const char *argv[MAXARGS+1]; char randarg[ARGBUF]; char *argptr = randarg; struct grep_pat *p; + if (opt->extended) + return -1; len = nr = 0; push_arg("grep"); if (opt->fixed) @@ -536,12 +791,17 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) argv[argc++] = name; if (argc < MAXARGS) continue; - hit += exec_grep(argc, argv); + status = exec_grep(argc, argv); + if (0 < status) + hit = 1; argc = nr; } - if (argc > nr) - hit += exec_grep(argc, argv); - return 0; + if (argc > nr) { + status = exec_grep(argc, argv); + if (0 < status) + hit = 1; + } + return hit; } static int grep_cache(struct grep_opt *opt, const char **paths, int cached) @@ -631,9 +891,9 @@ static int grep_tree(struct grep_opt *opt, const char **paths, static int grep_object(struct grep_opt *opt, const char **paths, struct object *obj, const char *name) { - if (obj->type == TYPE_BLOB) + if (obj->type == OBJ_BLOB) return grep_sha1(opt, obj->sha1, name); - if (obj->type == TYPE_COMMIT || obj->type == TYPE_TREE) { + if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) { struct tree_desc tree; void *data; int hit; @@ -652,6 +912,13 @@ static int grep_object(struct grep_opt *opt, const char **paths, static const char builtin_grep_usage[] = "git-grep