diff --git a/.gitignore b/.gitignore index 5eaba41995..dab5bc2a3c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,6 @@ git-archive git-bisect git-blame git-branch -git-browse-help git-bundle git-cat-file git-check-attr @@ -52,6 +51,7 @@ git-gc git-get-tar-commit-id git-grep git-hash-object +git-help--browse git-http-fetch git-http-push git-imap-send diff --git a/.mailmap b/.mailmap index 1f729c223c..695739ea70 100644 --- a/.mailmap +++ b/.mailmap @@ -25,6 +25,7 @@ Karl Hasselström Kent Engstrom Lars Doelle Lars Doelle +Li Hong Lukas Sandström Martin Langhoff Michael Coleman diff --git a/Documentation/.gitignore b/Documentation/.gitignore index a37b2152bd..2f938f471a 100644 --- a/Documentation/.gitignore +++ b/Documentation/.gitignore @@ -2,6 +2,7 @@ *.html *.[1-8] *.made +git.info howto-index.txt doc.dep cmds-*.txt diff --git a/Documentation/Makefile b/Documentation/Makefile index 1fd48ab367..76df06cd61 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -24,6 +24,9 @@ ARTICLES += git-tools ARTICLES += glossary # with their own formatting rules. SP_ARTICLES = howto/revert-branch-rebase user-manual +API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technical/api-index.txt, $(wildcard technical/api-*.txt))) +SP_ARTICLES += $(API_DOCS) +SP_ARTICLES += technical/api-index DOC_HTML += $(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES)) @@ -142,10 +145,12 @@ cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT) git.7 git.html: git.txt clean: - $(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7 *.texi *.texi+ howto-index.txt howto/*.html doc.dep + $(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7 *.texi *.texi+ git.info + $(RM) howto-index.txt howto/*.html doc.dep + $(RM) technical/api-*.html technical/api-index.txt $(RM) $(cmds_txt) *.made -%.html : %.txt +$(MAN_HTML): %.html : %.txt $(RM) $@+ $@ $(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf \ $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $< @@ -164,6 +169,14 @@ clean: user-manual.xml: user-manual.txt user-manual.conf $(ASCIIDOC) -b docbook -d book $< +technical/api-index.txt: technical/api-index-skel.txt \ + technical/api-index.sh $(patsubst %,%.txt,$(API_DOCS)) + cd technical && sh ./api-index.sh + +$(patsubst %,%.html,$(API_DOCS) technical/api-index): %.html : %.txt + $(ASCIIDOC) -b xhtml11 -f asciidoc.conf \ + $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) $*.txt + XSLT = docbook.xsl XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes-1.5.4.txt index 6645565c52..cd79124958 100644 --- a/Documentation/RelNotes-1.5.4.txt +++ b/Documentation/RelNotes-1.5.4.txt @@ -7,15 +7,17 @@ Removal * "git svnimport" was removed in favor of "git svn". It is still there in the source tree (contrib/examples) but unsupported. + * As git-commit and git-status have been rewritten, "git runstatus" + helper script lost all its users and has been removed. + Deprecation notices ------------------- - * Next feature release of git (this change is scheduled for v1.5.5 but - it could slip) will by default install dashed form of commands - (e.g. "git-commit") outside of users' normal $PATH, and will install - only selected commands ("git" itself, and "gitk") in $PATH. This - implies: + * Next feature release of git (this change is scheduled for v1.6.0) + will by default install dashed form of commands (e.g. "git-commit") + outside of users' normal $PATH, and will install only selected + commands ("git" itself, and "gitk") in $PATH. This implies: - Using dashed form of git commands (e.g. "git-commit") from the command line has been informally deprecated since early 2006, but @@ -35,7 +37,7 @@ Deprecation notices * The post-receive hook was introduced in March 2007 to supersede post-update hook, primarily to overcome the command line length limitation of the latter. Use of post-update hook will be deprecated - in future versions of git, perhaps in v1.5.5. + in future versions of git, starting from v1.6.0. * "git lost-found" was deprecated in favor of "git fsck"'s --lost-found option, and will be removed in the future. @@ -43,6 +45,17 @@ Deprecation notices * "git peek-remote" is deprecated, as "git ls-remote" was written in C and works for all transports, and will be removed in the future. + * From v1.6.0, the repack.usedeltabaseoffset config option will default + to true, which will give denser packfile (i.e. more efficient storage). + The downside is that git older than version 1.4.4 will not be able + to directly use a repository packed using this setting. + + * From v1.6.0, the pack.indexversion config option will default to 2, + which is slightly more efficient, and makes repacking more immune to + data corruptions. Git older than version 1.5.2 may revert to version 1 + of the pack index with a manual "git index-pack" to be able to directly + access corresponding pack files. + Updates since v1.5.3 -------------------- @@ -74,6 +87,9 @@ Updates since v1.5.3 mean "always" (even when the output is not going to a terminal). This has been corrected to mean the same thing as "auto". + * "git diff" Porcelain now respects diff.external configuration, which + is another way to specify GIT_EXTERNAL_DIFF. + * HTTP proxy can be specified per remote repository using remote.*.httpproxy configuration, or global http.proxy configuration variable. @@ -98,6 +114,8 @@ Updates since v1.5.3 * "git rebase --interactive" mode can now work on detached HEAD. + * Other minor to serious bugs in "git rebase -i" has been fixed. + * "git rebase" now detaches head during its operation, so after a successful "git rebase" operation, the reflog entry branch@{1} for the current branch points at the commit before the rebase was @@ -224,6 +242,12 @@ Updates since v1.5.3 "git commit"; the parameters to -m options are formatted as separate paragraphs. + * The format "git show" outputs an annotated tag has been updated to + include "Tagger: " and "Date: " lines from the tag itself. Strictly + speaking this is a backward incompatible change, but this is a + reasonable usability fix and people's script shouldn't have been + relying on the exact output from "git show" Porcelain anyway. + * "git cvsexportcommit" learned -w option to specify and switch to the CVS working directory. @@ -250,6 +274,9 @@ Updates since v1.5.3 between svn and git; a new representation that is much more compact for this information has been introduced to correct this. + * "git svn" left temporary index files it used without cleaning them + up; this was corrected. + * "git status" from a subdirectory now shows relative paths, which makes copy-and-pasting for git-checkout/git-add/git-rm easier. The traditional behaviour to show the full path relative to the top of @@ -286,6 +313,9 @@ this release, unless otherwise noted. These fixes are only in v1.5.4 and not backported to v1.5.3 maintenance series. + * The way "git diff --check" behaves is much more consistent with the way + "git apply --whitespace=warn" works. + * "git svn" talking with the SVN over http will correctly quote branch and project names. @@ -294,6 +324,6 @@ series. -- exec >/var/tmp/1 -O=v1.5.4-rc0 +O=v1.5.4-rc0-85-gdbedf97 echo O=`git describe refs/heads/master` git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint diff --git a/Documentation/config.txt b/Documentation/config.txt index fabe7f859f..ee0884545b 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -307,7 +307,7 @@ core.whitespace:: before a tab character in the initial indent part of the line as an error (enabled by default). * `indent-with-non-tab` treats a line that is indented with 8 or more - space characters that can be replaced with tab characters. + space characters as an error (not enabled by default). alias.*:: Command aliases for the gitlink:git[1] command wrapper - e.g. @@ -448,6 +448,13 @@ diff.autorefreshindex:: affects only `git diff` Porcelain, and not lower level `diff` commands, such as `git diff-files`. +diff.external:: + If this config variable is set, diff generation is not + performed using the internal diff machinery, but using the + given command. Note: if you want to use an external diff + program only on a subset of your files, you might want to + use gitlink:gitattributes[5] instead. + diff.renameLimit:: The number of files to consider when performing the copy/rename detection; equivalent to the git diff option '-l'. @@ -469,7 +476,7 @@ fetch.unpackLimit:: format.numbered:: A boolean which can enable sequence numbers in patch subjects. - Seting this option to "auto" will enable it only if there is + Setting this option to "auto" will enable it only if there is more than one patch. See --numbered option in gitlink:git-format-patch[1]. @@ -671,6 +678,10 @@ merge..recursive:: performing an internal merge between common ancestors. See gitlink:gitattributes[5] for details. +mergetool..path:: + Override the path for the given tool. This is useful in case + your tool is not in the PATH. + 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. diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 5d22b7b58c..9ecc1d7bc4 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -93,7 +93,9 @@ endif::git-format-patch[] --check:: Warn if changes introduce trailing whitespace - or an indent that uses a space before a tab. + or an indent that uses a space before a tab. Exits with + non-zero status if problems are found. Not compatible with + --exit-code. --full-index:: Instead of the first handful characters, show full diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt index 99e47c9c25..f1e48dd021 100644 --- a/Documentation/git-daemon.txt +++ b/Documentation/git-daemon.txt @@ -31,8 +31,8 @@ pass some directory paths as 'git-daemon' arguments, you can further restrict the offers to a whitelist comprising of those. By default, only `upload-pack` service is enabled, which serves -`git-fetch-pack` and `git-peek-remote` clients that are invoked -from `git-fetch`, `git-ls-remote`, and `git-clone`. +`git-fetch-pack` and `git-ls-remote` clients, which are invoked +from `git-fetch`, `git-pull`, and `git-clone`. This is ideally suited for read-only updates, i.e., pulling from git repositories. @@ -166,7 +166,7 @@ the per-repository configuration file can be used to enable or disable them. upload-pack:: - This serves `git-fetch-pack` and `git-peek-remote` + This serves `git-fetch-pack` and `git-ls-remote` clients. It is enabled by default, but a repository can disable it by setting `daemon.uploadpack` configuration item to `false`. diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt index ac9e15d774..a8ffcbe78b 100644 --- a/Documentation/git-help.txt +++ b/Documentation/git-help.txt @@ -7,7 +7,7 @@ git-help - display help information about git SYNOPSIS -------- -'git help' [-a|--all|-i|--info|-w|--web] [COMMAND] +'git help' [-a|--all|-i|--info|-m|--man|-w|--web] [COMMAND] DESCRIPTION ----------- @@ -21,7 +21,7 @@ printed on the standard output. If a git command is named, a manual page for that command is brought up. The 'man' program is used by default for this purpose, but this -can be overriden by other options. +can be overridden by other options or configuration variables. Note that 'git --help ...' is identical as 'git help ...' because the former is internally converted into the latter. @@ -30,30 +30,63 @@ OPTIONS ------- -a|--all:: Prints all the available commands on the standard output. This - option superseeds any other option. + option supersedes any other option. -i|--info:: Use the 'info' program to display the manual page, instead of the 'man' program that is used by default. +-m|--man:: + Use the 'man' program to display the manual page. This may be + used to override a value set in the 'help.format' + configuration variable. + -w|--web:: Use a web browser to display the HTML manual page, instead of the 'man' program that is used by default. + The web browser can be specified using the configuration variable 'help.browser', or 'web.browser' if the former is not set. If none of -these config variables is set, the 'git-browse-help' script (called by -'git-help') will pick a suitable default. +these config variables is set, the 'git-help--browse' helper script +(called by 'git-help') will pick a suitable default. + -You can explicitly provide a full path to your prefered browser by +You can explicitly provide a full path to your preferred browser by setting the configuration variable 'browser..path'. For example, you can configure the absolute path to firefox by setting -'browser.firefox.path'. Otherwise, 'git-browse-help' assumes the tool +'browser.firefox.path'. Otherwise, 'git-help--browse' assumes the tool is available in PATH. + Note that the script tries, as much as possible, to display the HTML page in a new tab on an already opened browser. +CONFIGURATION VARIABLES +----------------------- + +If no command line option is passed, the 'help.format' configuration +variable will be checked. The following values are supported for this +variable; they make 'git-help' behave as their corresponding command +line option: + +* "man" corresponds to '-m|--man', +* "info" corresponds to '-i|--info', +* "web" or "html" correspond to '-w|--web', + +The 'help.browser', 'web.browser' and 'browser..path' will also +be checked if the 'web' format is chosen (either by command line +option or configuration variable). See '-w|--web' in the OPTIONS +section above. + +Note that these configuration variables should probably be set using +the '--global' flag, for example like this: + +------------------------------------------------ +$ git config --global help.format web +$ git config --global web.browser firefox +------------------------------------------------ + +as they are probably more user specific than repository specific. +See gitlink:git-config[1] for more information about this. + Author ------ Written by Junio C Hamano and the git-list diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt index 07484a4fd0..e51351dd53 100644 --- a/Documentation/git-init.txt +++ b/Documentation/git-init.txt @@ -52,7 +52,7 @@ is given: - 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository readable by all users. -By default, the configuration flag receive.denyNonFastforward is enabled +By default, the configuration flag receive.denyNonFastForwards is enabled in shared repositories, so that you cannot force a non fast-forwarding push into it. diff --git a/Documentation/git-ls-remote.txt b/Documentation/git-ls-remote.txt index 93e9a60330..445bedaa95 100644 --- a/Documentation/git-ls-remote.txt +++ b/Documentation/git-ls-remote.txt @@ -30,7 +30,7 @@ OPTIONS Specify the full path of gitlink:git-upload-pack[1] on the remote host. This allows listing references from repositories accessed via SSH and where the SSH daemon does not use the PATH configured by the - user. Also see the '--exec' option for gitlink:git-peek-remote[1]. + user. :: Location of the repository. The shorthand defined in diff --git a/Documentation/git-peek-remote.txt b/Documentation/git-peek-remote.txt index 38a5325af7..09b005b10f 100644 --- a/Documentation/git-peek-remote.txt +++ b/Documentation/git-peek-remote.txt @@ -28,9 +28,6 @@ OPTIONS shells, but prefer having a lean .bashrc file (they set most of the things up in .bash_profile). -\--exec=:: - Same \--upload-pack=. - :: A remote host that houses the repository. When this part is specified, 'git-upload-pack' is invoked via diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index 989fbf3562..a03f9fe5fa 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -274,7 +274,7 @@ limiting may be applied. --quiet:: Don't print anything to standard output. This form of - git-rev-list is primarly meant to allow the caller to + git-rev-list is primarily meant to allow the caller to test the exit status to see if a range of objects is fully connected (or not). It is faster than redirecting stdout to /dev/null as the output does not have to be formatted. diff --git a/Documentation/git-runstatus.txt b/Documentation/git-runstatus.txt deleted file mode 100644 index dee5d0da9d..0000000000 --- a/Documentation/git-runstatus.txt +++ /dev/null @@ -1,68 +0,0 @@ -git-runstatus(1) -================ - -NAME ----- -git-runstatus - A helper for git-status and git-commit - - -SYNOPSIS --------- -'git-runstatus' [--color|--nocolor] [--amend] [--verbose] [--untracked] - - -DESCRIPTION ------------ -Examines paths in the working tree that has changes unrecorded -to the index file, and changes between the index file and the -current HEAD commit. The former paths are what you _could_ -commit by running 'git add' (or 'git rm' if you are deleting) before running 'git -commit', and the latter paths are what you _would_ commit by -running 'git commit'. - -If there is no path that is different between the index file and -the current HEAD commit, the command exits with non-zero status. - -Note that this is _not_ the user level command you would want to -run from the command line. Use 'git-status' instead. - - -OPTIONS -------- ---color:: - Show colored status, highlighting modified file names. - ---nocolor:: - Turn off coloring. - ---amend:: - Show status based on HEAD^1, not HEAD, i.e. show what - 'git-commit --amend' would do. - ---verbose:: - Show unified diff of all file changes. - ---untracked:: - Show files in untracked directories, too. Without this - option only its name and a trailing slash are displayed - for each untracked directory. - - -OUTPUT ------- -The output from this command is designed to be used as a commit -template comments, and all the output lines are prefixed with '#'. - - -Author ------- -Originally written by Linus Torvalds as part -of git-commit, and later rewritten in C by Jeff King. - -Documentation --------------- -Documentation by David Greaves, Junio C Hamano and the git-list . - -GIT ---- -Part of the gitlink:git[7] suite diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 659215ac72..f0bd2851d8 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -148,7 +148,7 @@ sendemail.identity:: 'sendemail..' will have higher precedence than 'sendemail.'. This is useful to declare multiple SMTP identities and to hoist sensitive authentication information - out of the repository and into the global configuation file. + out of the repository and into the global configuration file. sendemail.aliasesfile:: To avoid typing long email addresses, point this to one or more diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt index 2355aa5e86..7893a50886 100644 --- a/Documentation/git-show-ref.txt +++ b/Documentation/git-show-ref.txt @@ -160,7 +160,7 @@ to get a listing of all tags together with what they dereference. SEE ALSO -------- -gitlink:git-ls-remote[1], gitlink:git-peek-remote[1] +gitlink:git-ls-remote[1] AUTHORS ------- diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 335e973a6a..3f59705686 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -68,7 +68,8 @@ FILES When initializing submodules, a .gitmodules file in the top-level directory of the containing repository is used to find the url of each submodule. This file should be formatted in the same way as $GIR_DIR/config. The key -to each submodule url is "submodule.$name.url". +to each submodule url is "submodule.$name.url". See gitlink:gitmodules[5] +for details. AUTHOR diff --git a/Documentation/git.txt b/Documentation/git.txt index a29b634e7a..37235b993d 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -153,6 +153,8 @@ introductions to the underlying git architecture. See also the link:howto-index.html[howto] documents for some useful examples. +The internals are documented link:technical/api-index.html[here]. + GIT COMMANDS ------------ @@ -500,7 +502,7 @@ as tags and branch heads. The object database contains objects of three main types: blobs, which hold file data; trees, which point to blobs and other trees to build up -directory heirarchies; and commits, which each reference a single tree +directory hierarchies; and commits, which each reference a single tree and some number of parent commits. The commit, equivalent to what other systems call a "changeset" or @@ -520,7 +522,7 @@ efficiency may later be compressed together into "pack files". Named pointers called refs mark interesting points in history. A ref may contain the SHA1 name of an object or the name of another ref. Refs with names beginning `ref/head/` contain the SHA1 name of the most -recent commit (or "head") of a branch under developement. SHA1 names of +recent commit (or "head") of a branch under development. SHA1 names of tags of interest are stored under `ref/tags/`. A special ref named `HEAD` contains the name of the currently checked-out branch. diff --git a/Documentation/install-webdoc.sh b/Documentation/install-webdoc.sh index cd3a18eb7f..2135a8ee1f 100755 --- a/Documentation/install-webdoc.sh +++ b/Documentation/install-webdoc.sh @@ -2,9 +2,16 @@ T="$1" -for h in *.html *.txt howto/*.txt howto/*.html RelNotes-*.txt *.css +for h in \ + *.txt *.html \ + howto/*.txt howto/*.html \ + technical/*.txt technical/*.html \ + RelNotes-*.txt *.css do - if test -f "$T/$h" && + if test ! -f "$h" + then + : did not match + elif test -f "$T/$h" && diff -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h" then :; # up to date @@ -16,7 +23,10 @@ do fi done strip_leading=`echo "$T/" | sed -e 's|.|.|g'` -for th in "$T"/*.html "$T"/*.txt "$T"/howto/*.txt "$T"/howto/*.html +for th in \ + "$T"/*.html "$T"/*.txt \ + "$T"/howto/*.txt "$T"/howto/*.html \ + "$T"/technical/*.txt "$T"/technical/*.html do h=`expr "$th" : "$strip_leading"'\(.*\)'` case "$h" in diff --git a/Documentation/technical/.gitignore b/Documentation/technical/.gitignore new file mode 100644 index 0000000000..8aa891daee --- /dev/null +++ b/Documentation/technical/.gitignore @@ -0,0 +1 @@ +api-index.txt diff --git a/Documentation/technical/api-allocation-growing.txt b/Documentation/technical/api-allocation-growing.txt new file mode 100644 index 0000000000..43dbe09f73 --- /dev/null +++ b/Documentation/technical/api-allocation-growing.txt @@ -0,0 +1,34 @@ +allocation growing API +====================== + +Dynamically growing an array using realloc() is error prone and boring. + +Define your array with: + +* a pointer (`ary`) that points at the array, initialized to `NULL`; + +* an integer variable (`alloc`) that keeps track of how big the current + allocation is, initialized to `0`; + +* another integer variable (`nr`) to keep track of how many elements the + array currently has, initialized to `0`. + +Then before adding `n`th element to the array, call `ALLOC_GROW(ary, n, +alloc)`. This ensures that the array can hold at least `n` elements by +calling `realloc(3)` and adjusting `alloc` variable. + +------------ +sometype *ary; +size_t nr; +size_t alloc + +for (i = 0; i < nr; i++) + if (we like ary[i] already) + return; + +/* we did not like any existing one, so add one */ +ALLOC_GROW(ary, nr + 1, alloc); +ary[nr++] = value you like; +------------ + +You are responsible for updating the `nr` variable. diff --git a/Documentation/technical/api-builtin.txt b/Documentation/technical/api-builtin.txt new file mode 100644 index 0000000000..52cdb4c520 --- /dev/null +++ b/Documentation/technical/api-builtin.txt @@ -0,0 +1,63 @@ +builtin API +=========== + +Adding a new built-in +--------------------- + +There are 4 things to do to add a bulit-in command implementation to +git: + +. Define the implementation of the built-in command `foo` with + signature: + + int cmd_foo(int argc, const char **argv, const char *prefix); + +. Add the external declaration for the function to `builtin.h`. + +. Add the command to `commands[]` table in `handle_internal_command()`, + defined in `git.c`. The entry should look like: + + { "foo", cmd_foo, }, + + where options is the bitwise-or of: + +`RUN_SETUP`:: + + Make sure there is a git directory to work on, and if there is a + work tree, chdir to the top of it if the command was invoked + in a subdirectory. If there is no work tree, no chdir() is + done. + +`USE_PAGER`:: + + If the standard output is connected to a tty, spawn a pager and + feed our output to it. + +. Add `builtin-foo.o` to `BUILTIN_OBJS` in `Makefile`. + +Additionally, if `foo` is a new command, there are 3 more things to do: + +. Add tests to `t/` directory. + +. Write documentation in `Documentation/git-foo.txt`. + +. Add an entry for `git-foo` to the list at the end of + `Documentation/cmd-list.perl`. + + +How a built-in is called +------------------------ + +The implementation `cmd_foo()` takes three parameters, `argc`, `argv, +and `prefix`. The first two are similar to what `main()` of a +standalone command would be called with. + +When `RUN_SETUP` is specified in the `commands[]` table, and when you +were started from a subdirectory of the work tree, `cmd_foo()` is called +after chdir(2) to the top of the work tree, and `prefix` gets the path +to the subdirectory the command started from. This allows you to +convert a user-supplied pathname (typically relative to that directory) +to a pathname relative to the top of the work tree. + +The return value from `cmd_foo()` becomes the exit status of the +command. diff --git a/Documentation/technical/api-decorate.txt b/Documentation/technical/api-decorate.txt new file mode 100644 index 0000000000..1d52a6ce14 --- /dev/null +++ b/Documentation/technical/api-decorate.txt @@ -0,0 +1,6 @@ +decorate API +============ + +Talk about + +(Linus) diff --git a/Documentation/technical/api-diff.txt b/Documentation/technical/api-diff.txt new file mode 100644 index 0000000000..822609bcd1 --- /dev/null +++ b/Documentation/technical/api-diff.txt @@ -0,0 +1,166 @@ +diff API +======== + +The diff API is for programs that compare two sets of files (e.g. two +trees, one tree and the index) and present the found difference in +various ways. The calling program is responsible for feeding the API +pairs of files, one from the "old" set and the corresponding one from +"new" set, that are different. The library called through this API is +called diffcore, and is responsible for two things. + +* finding total rewrites (`-B`), renames (`-M`) and copies (`-C`), and + changes that touch a string (`-S`), as specified by the caller. + +* outputting the differences in various formats, as specified by the + caller. + +Calling sequence +---------------- + +* Prepare `struct diff_options` to record the set of diff options, and + then call `diff_setup()` to initialize this structure. This sets up + the vanilla default. + +* Fill in the options structure to specify desired output format, rename + detection, etc. `diff_opt_parse()` can be used to parse options given + from the command line in a way consistent with existing git-diff + family of programs. + +* Call `diff_setup_done()`; this inspects the options set up so far for + internal consistency and make necessary tweaking to it (e.g. if + textual patch output was asked, recursive behaviour is turned on). + +* As you find different pairs of files, call `diff_change()` to feed + modified files, `diff_addremove()` to feed created or deleted files, + or `diff_unmerged()` to feed a file whose state is 'unmerged' to the + API. These are thin wrappers to a lower-level `diff_queue()` function + that is flexible enough to record any of these kinds of changes. + +* Once you finish feeding the pairs of files, call `diffcore_std()`. + This will tell the diffcore library to go ahead and do its work. + +* Calling `diffcore_flush()` will produce the output. + + +Data structures +--------------- + +* `struct diff_filespec` + +This is the internal representation for a single file (blob). It +records the blob object name (if known -- for a work tree file it +typically is a NUL SHA-1), filemode and pathname. This is what the +`diff_addremove()`, `diff_change()` and `diff_unmerged()` synthesize and +feed `diff_queue()` function with. + +* `struct diff_filepair` + +This records a pair of `struct diff_filespec`; the filespec for a file +in the "old" set (i.e. preimage) is called `one`, and the filespec for a +file in the "new" set (i.e. postimage) is called `two`. A change that +represents file creation has NULL in `one`, and file deletion has NULL +in `two`. + +A `filepair` starts pointing at `one` and `two` that are from the same +filename, but `diffcore_std()` can break pairs and match component +filespecs with other filespecs from a different filepair to form new +filepair. This is called 'rename detection'. + +* `struct diff_queue` + +This is a collection of filepairs. Notable members are: + +`queue`:: + + An array of pointers to `struct diff_filepair`. This + dynamically grows as you add filepairs; + +`alloc`:: + + The allocated size of the `queue` array; + +`nr`:: + + The number of elements in the `queue` array. + + +* `struct diff_options` + +This describes the set of options the calling program wants to affect +the operation of diffcore library with. + +Notable members are: + +`output_format`:: + The output format used when `diff_flush()` is run. + +`context`:: + Number of context lines to generate in patch output. + +`break_opt`, `detect_rename`, `rename-score`, `rename_limit`:: + Affects the way detection logic for complete rewrites, renames + and copies. + +`abbrev`:: + Number of hexdigits to abbrevate raw format output to. + +`pickaxe`:: + A constant string (can and typically does contain newlines to + look for a block of text, not just a single line) to filter out + the filepairs that do not change the number of strings contained + in its preimage and postmage of the diff_queue. + +`flags`:: + This is mostly a collection of boolean options that affects the + operation, but some do not have anything to do with the diffcore + library. + +BINARY, TEXT;; + Affects the way how a file that is seemingly binary is treated. + +FULL_INDEX;; + Tells the patch output format not to use abbreviated object + names on the "index" lines. + +FIND_COPIES_HARDER;; + Tells the diffcore library that the caller is feeding unchanged + filepairs to allow copies from unmodified files be detected. + +COLOR_DIFF;; + Output should be colored. + +COLOR_DIFF_WORDS;; + Output is a colored word-diff. + +NO_INDEX;; + Tells diff-files that the input is not tracked files but files + in random locations on the filesystem. + +ALLOW_EXTERNAL;; + Tells output routine that it is Ok to call user specified patch + output routine. Plumbing disables this to ensure stable output. + +QUIET;; + Do not show any output. + +REVERSE_DIFF;; + Tells the library that the calling program is feeding the + filepairs reversed; `one` is two, and `two` is one. + +EXIT_WITH_STATUS;; + For communication between the calling program and the options + parser; tell the calling program to signal the presense of + difference using program exit code. + +HAS_CHANGES;; + Internal; used for optimization to see if there is any change. + +SILENT_ON_REMOVE;; + Affects if diff-files shows removed files. + +RECURSIVE, TREE_IN_RECURSIVE;; + Tells if tree traversal done by tree-diff should recursively + descend into a tree object pair that are different in preimage + and postimage set. + +(JC) diff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt new file mode 100644 index 0000000000..5bbd18f020 --- /dev/null +++ b/Documentation/technical/api-directory-listing.txt @@ -0,0 +1,76 @@ +directory listing API +===================== + +The directory listing API is used to enumerate paths in the work tree, +optionally taking `.git/info/exclude` and `.gitignore` files per +directory into account. + +Data structure +-------------- + +`struct dir_struct` structure is used to pass directory traversal +options to the library and to record the paths discovered. The notable +options are: + +`exclude_per_dir`:: + + The name of the file to be read in each directory for excluded + files (typically `.gitignore`). + +`collect_ignored`:: + + Include paths that are to be excluded in the result. + +`show_ignored`:: + + The traversal is for finding just ignored files, not unignored + files. + +`show_other_directories`:: + + Include a directory that is not tracked. + +`hide_empty_directories`:: + + Do not include a directory that is not tracked and is empty. + +`no_gitlinks`:: + + If set, recurse into a directory that looks like a git + directory. Otherwise it is shown as a directory. + +The result of the enumeration is left in these fields:: + +`entries[]`:: + + An array of `struct dir_entry`, each element of which describes + a path. + +`nr`:: + + The number of members in `entries[]` array. + +`alloc`:: + + Internal use; keeps track of allocation of `entries[]` array. + + +Calling sequence +---------------- + +* Prepare `struct dir_struct dir` and clear it with `memset(&dir, 0, + sizeof(dir))`. + +* Call `add_exclude()` to add single exclude pattern, + `add_excludes_from_file()` to add patterns from a file + (e.g. `.git/info/exclude`), and/or set `dir.exclude_per_dir`. A + short-hand function `setup_standard_excludes()` can be used to set up + the standard set of exclude settings. + +* Set options described in the Data Structure section above. + +* Call `read_directory()`. + +* Use `dir.entries[]`. + +(JC) diff --git a/Documentation/technical/api-gitattributes.txt b/Documentation/technical/api-gitattributes.txt new file mode 100644 index 0000000000..9d97eaa9de --- /dev/null +++ b/Documentation/technical/api-gitattributes.txt @@ -0,0 +1,111 @@ +gitattributes API +================= + +gitattributes mechanism gives a uniform way to associate various +attributes to set of paths. + + +Data Structure +-------------- + +`struct git_attr`:: + + An attribute is an opaque object that is identified by its name. + Pass the name and its length to `git_attr()` function to obtain + the object of this type. The internal representation of this + structure is of no interest to the calling programs. + +`struct git_attr_check`:: + + This structure represents a set of attributes to check in a call + to `git_checkattr()` function, and receives the results. + + +Calling Sequence +---------------- + +* Prepare an array of `struct git_attr_check` to define the list of + attributes you would want to check. To populate this array, you would + need to define necessary attributes by calling `git_attr()` function. + +* Call git_checkattr() to check the attributes for the path. + +* Inspect `git_attr_check` structure to see how each of the attribute in + the array is defined for the path. + + +Attribute Values +---------------- + +An attribute for a path can be in one of four states: Set, Unset, +Unspecified or set to a string, and `.value` member of `struct +git_attr_check` records it. There are three macros to check these: + +`ATTR_TRUE()`:: + + Returns true if the attribute is Set for the path. + +`ATTR_FALSE()`:: + + Returns true if the attribute is Unset for the path. + +`ATTR_UNSET()`:: + + Returns true if the attribute is Unspecified for the path. + +If none of the above returns true, `.value` member points at a string +value of the attribute for the path. + + +Example +------- + +To see how attributes "crlf" and "indent" are set for different paths. + +. Prepare an array of `struct git_attr_check` with two elements (because + we are checking two attributes). Initialize their `attr` member with + pointers to `struct git_attr` obtained by calling `git_attr()`: + +------------ +static struct git_attr_check check[2]; +static void setup_check(void) +{ + if (check[0].attr) + return; /* already done */ + check[0].attr = git_attr("crlf", 4); + check[1].attr = git_attr("ident", 5); +} +------------ + +. Call `git_checkattr()` with the prepared array of `struct git_attr_check`: + +------------ + const char *path; + + setup_check(); + git_checkattr(path, ARRAY_SIZE(check), check); +------------ + +. Act on `.value` member of the result, left in `check[]`: + +------------ + const char *value = check[0].value; + + if (ATTR_TRUE(value)) { + The attribute is Set, by listing only the name of the + attribute in the gitattributes file for the path. + } else if (ATTR_FALSE(value)) { + The attribute is Unset, by listing the name of the + attribute prefixed with a dash - for the path. + } else if (ATTR_UNSET(value)) { + The attribute is not set nor unset for the path. + } else if (!strcmp(value, "input")) { + If none of ATTR_TRUE(), ATTR_FALSE(), or ATTR_UNSET() is + true, the value is a string set in the gitattributes + file for the path by saying "attr=value". + } else if (... other check using value as string ...) { + ... + } +------------ + +(JC) diff --git a/Documentation/technical/api-grep.txt b/Documentation/technical/api-grep.txt new file mode 100644 index 0000000000..a69cc8964d --- /dev/null +++ b/Documentation/technical/api-grep.txt @@ -0,0 +1,8 @@ +grep API +======== + +Talk about , things like: + +* grep_buffer() + +(JC) diff --git a/Documentation/technical/api-hash.txt b/Documentation/technical/api-hash.txt new file mode 100644 index 0000000000..c784d3edcb --- /dev/null +++ b/Documentation/technical/api-hash.txt @@ -0,0 +1,6 @@ +hash API +======== + +Talk about + +(Linus) diff --git a/Documentation/technical/api-in-core-index.txt b/Documentation/technical/api-in-core-index.txt new file mode 100644 index 0000000000..adbdbf5d75 --- /dev/null +++ b/Documentation/technical/api-in-core-index.txt @@ -0,0 +1,21 @@ +in-core index API +================= + +Talk about and , things like: + +* cache -> the_index macros +* read_index() +* write_index() +* ie_match_stat() and ie_modified(); how they are different and when to + use which. +* index_name_pos() +* remove_index_entry_at() +* remove_file_from_index() +* add_file_to_index() +* add_index_entry() +* refresh_index() +* discard_index() +* cache_tree_invalidate_path() +* cache_tree_update() + +(JC, Linus) diff --git a/Documentation/technical/api-index-skel.txt b/Documentation/technical/api-index-skel.txt new file mode 100644 index 0000000000..af7cc2e395 --- /dev/null +++ b/Documentation/technical/api-index-skel.txt @@ -0,0 +1,15 @@ +GIT API Documents +================= + +GIT has grown a set of internal API over time. This collection +documents them. + +//////////////////////////////////////////////////////////////// +// table of contents begin +//////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////// +// table of contents end +//////////////////////////////////////////////////////////////// + +2007-11-24 diff --git a/Documentation/technical/api-index.sh b/Documentation/technical/api-index.sh new file mode 100755 index 0000000000..9c3f4131b8 --- /dev/null +++ b/Documentation/technical/api-index.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +( + c=//////////////////////////////////////////////////////////////// + skel=api-index-skel.txt + sed -e '/^\/\/ table of contents begin/q' "$skel" + echo "$c" + + ls api-*.txt | + while read filename + do + case "$filename" in + api-index-skel.txt | api-index.txt) continue ;; + esac + title=$(sed -e 1q "$filename") + html=${filename%.txt}.html + echo "* link:$html[$title]" + done + echo "$c" + sed -n -e '/^\/\/ table of contents end/,$p' "$skel" +) >api-index.txt+ + +if test -f api-index.txt && cmp api-index.txt api-index.txt+ >/dev/null +then + rm -f api-index.txt+ +else + mv api-index.txt+ api-index.txt +fi diff --git a/Documentation/technical/api-lockfile.txt b/Documentation/technical/api-lockfile.txt new file mode 100644 index 0000000000..73ac1025fd --- /dev/null +++ b/Documentation/technical/api-lockfile.txt @@ -0,0 +1,12 @@ +lockfile API +============ + +Talk about , things like: + +* lockfile lifetime -- atexit(3) looks at them, do not put them on the + stack; +* hold_lock_file_for_update() +* commit_lock_file() +* rollback_rock_file() + +(JC, Dscho, Shawn) diff --git a/Documentation/technical/api-object-access.txt b/Documentation/technical/api-object-access.txt new file mode 100644 index 0000000000..03bb0e950d --- /dev/null +++ b/Documentation/technical/api-object-access.txt @@ -0,0 +1,15 @@ +object access API +================= + +Talk about and family, things like + +* read_sha1_file() +* read_object_with_reference() +* has_sha1_file() +* write_sha1_file() +* pretend_sha1_file() +* lookup_{object,commit,tag,blob,tree} +* parse_{object,commit,tag,blob,tree} +* Use of object flags + +(JC, Shawn, Daniel, Dscho, Linus) diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt new file mode 100644 index 0000000000..b7cda94f54 --- /dev/null +++ b/Documentation/technical/api-parse-options.txt @@ -0,0 +1,6 @@ +parse-options API +================= + +Talk about + +(Pierre) diff --git a/Documentation/technical/api-path-list.txt b/Documentation/technical/api-path-list.txt new file mode 100644 index 0000000000..d077683171 --- /dev/null +++ b/Documentation/technical/api-path-list.txt @@ -0,0 +1,9 @@ +path-list API +============= + +Talk about , things like + +* it is not just paths but strings in general; +* the calling sequence. + +(Dscho) diff --git a/Documentation/technical/api-quote.txt b/Documentation/technical/api-quote.txt new file mode 100644 index 0000000000..e8a1bce94e --- /dev/null +++ b/Documentation/technical/api-quote.txt @@ -0,0 +1,10 @@ +quote API +========= + +Talk about , things like + +* sq_quote and unquote +* c_style quote and unquote +* quoting for foreign languages + +(JC) diff --git a/Documentation/technical/api-revision-walking.txt b/Documentation/technical/api-revision-walking.txt new file mode 100644 index 0000000000..01a24551af --- /dev/null +++ b/Documentation/technical/api-revision-walking.txt @@ -0,0 +1,9 @@ +revision walking API +==================== + +Talk about , things like: + +* two diff_options, one for path limiting, another for output; +* calling sequence: init_revisions(), setup_revsions(), get_revision(); + +(Linus, JC, Dscho) diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt new file mode 100644 index 0000000000..19d2f64f73 --- /dev/null +++ b/Documentation/technical/api-run-command.txt @@ -0,0 +1,10 @@ +run-command API +=============== + +Talk about , and things like: + +* Environment the command runs with (e.g. GIT_DIR); +* File descriptors and pipes; +* Exit status; + +(Hannes, Dscho, Shawn) diff --git a/Documentation/technical/api-setup.txt b/Documentation/technical/api-setup.txt new file mode 100644 index 0000000000..4f63a04d7d --- /dev/null +++ b/Documentation/technical/api-setup.txt @@ -0,0 +1,13 @@ +setup API +========= + +Talk about + +* setup_git_directory() +* setup_git_directory_gently() +* is_inside_git_dir() +* is_inside_work_tree() +* setup_work_tree() +* get_pathspec() + +(Dscho) diff --git a/Documentation/technical/api-strbuf.txt b/Documentation/technical/api-strbuf.txt new file mode 100644 index 0000000000..a52e4f36d5 --- /dev/null +++ b/Documentation/technical/api-strbuf.txt @@ -0,0 +1,6 @@ +strbuf API +========== + +Talk about + +(Pierre, JC) diff --git a/Documentation/technical/api-tree-walking.txt b/Documentation/technical/api-tree-walking.txt new file mode 100644 index 0000000000..e3ddf91284 --- /dev/null +++ b/Documentation/technical/api-tree-walking.txt @@ -0,0 +1,12 @@ +tree walking API +================ + +Talk about , things like + +* struct tree_desc +* init_tree_desc +* tree_entry_extract +* update_tree_entry +* get_tree_entry + +(JC, Linus) diff --git a/Documentation/technical/api-xdiff-interface.txt b/Documentation/technical/api-xdiff-interface.txt new file mode 100644 index 0000000000..6296ecad1d --- /dev/null +++ b/Documentation/technical/api-xdiff-interface.txt @@ -0,0 +1,7 @@ +xdiff interface API +=================== + +Talk about our calling convention to xdiff library, including +xdiff_emit_consume_fn. + +(Dscho, JC) diff --git a/Documentation/technical/pack-format.txt b/Documentation/technical/pack-format.txt index e5b31c81fa..a80baa4382 100644 --- a/Documentation/technical/pack-format.txt +++ b/Documentation/technical/pack-format.txt @@ -1,9 +1,9 @@ GIT pack format =============== -= pack-*.pack file has the following format: += pack-*.pack files have the following format: - - The header appears at the beginning and consists of the following: + - A header appears at the beginning and consists of the following: 4-byte signature: The signature is: {'P', 'A', 'C', 'K'} @@ -34,18 +34,14 @@ GIT pack format - The trailer records 20-byte SHA1 checksum of all of the above. -= pack-*.idx file has the following format: += Original (version 1) pack-*.idx files have the following format: - The header consists of 256 4-byte network byte order integers. N-th entry of this table records the number of objects in the corresponding pack, the first byte of whose - object name are smaller than N. This is called the + object name is less than or equal to N. This is called the 'first-level fan-out' table. - Observation: we would need to extend this to an array of - 8-byte integers to go beyond 4G objects per pack, but it is - not strictly necessary. - - The header is followed by sorted 24-byte entries, one entry per object in the pack. Each entry is: @@ -55,10 +51,6 @@ GIT pack format 20-byte object name. - Observation: we would definitely need to extend this to - 8-byte integer plus 20-byte object name to handle a packfile - that is larger than 4GB. - - The file is concluded with a trailer: A copy of the 20-byte SHA1 checksum at the end of @@ -68,31 +60,30 @@ GIT pack format Pack Idx file: - idx - +--------------------------------+ - | fanout[0] = 2 |-. - +--------------------------------+ | + -- +--------------------------------+ +fanout | fanout[0] = 2 (for example) |-. +table +--------------------------------+ | | fanout[1] | | +--------------------------------+ | | fanout[2] | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | - | fanout[255] | | - +--------------------------------+ | -main | offset | | -index | object name 00XXXXXXXXXXXXXXXX | | -table +--------------------------------+ | - | offset | | - | object name 00XXXXXXXXXXXXXXXX | | - +--------------------------------+ | - .-| offset |<+ - | | object name 01XXXXXXXXXXXXXXXX | - | +--------------------------------+ - | | offset | - | | object name 01XXXXXXXXXXXXXXXX | - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - | | offset | - | | object name FFXXXXXXXXXXXXXXXX | - | +--------------------------------+ + | fanout[255] = total objects |---. + -- +--------------------------------+ | | +main | offset | | | +index | object name 00XXXXXXXXXXXXXXXX | | | +table +--------------------------------+ | | + | offset | | | + | object name 00XXXXXXXXXXXXXXXX | | | + +--------------------------------+<+ | + .-| offset | | + | | object name 01XXXXXXXXXXXXXXXX | | + | +--------------------------------+ | + | | offset | | + | | object name 01XXXXXXXXXXXXXXXX | | + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | + | | offset | | + | | object name FFXXXXXXXXXXXXXXXX | | + --| +--------------------------------+<--+ trailer | | packfile checksum | | +--------------------------------+ | | idxfile checksum | @@ -116,3 +107,40 @@ Pack file entry: <+ 20-byte base object name SHA1 (the size above is the size of the delta data that follows). delta data, deflated. + + += Version 2 pack-*.idx files support packs larger than 4 GiB, and + have some other reorganizations. They have the format: + + - A 4-byte magic number '\377tOc' which is an unreasonable + fanout[0] value. + + - A 4-byte version number (= 2) + + - A 256-entry fan-out table just like v1. + + - A table of sorted 20-byte SHA1 object names. These are + packed together without offset values to reduce the cache + footprint of the binary search for a specific object name. + + - A table of 4-byte CRC32 values of the packed object data. + This is new in v2 so compressed data can be copied directly + from pack to pack during repacking withough undetected + data corruption. + + - A table of 4-byte offset values (in network byte order). + These are usually 31-bit pack file offsets, but large + offsets are encoded as an index into the next table with + the msbit set. + + - A table of 8-byte offset entries (empty for pack files less + than 2 GiB). Pack files are organized with heavily used + objects toward the front, so most object references should + not need to refer to this table. + + - The same trailer as a v1 pack file: + + A copy of the 20-byte SHA1 checksum at the end of + corresponding packfile. + + 20-byte SHA1-checksum of all of the above. diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 93a47b439b..f2b42068f3 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1994,7 +1994,7 @@ $ git push ssh://yourserver.com/~you/proj.git +master ------------------------------------------------- Normally whenever a branch head in a public repository is modified, it -is modified to point to a descendent of the commit that it pointed to +is modified to point to a descendant of the commit that it pointed to before. By forcing a push in this situation, you break that convention. (See <>.) @@ -2921,7 +2921,7 @@ As you can see, a commit is defined by: - a tree: The SHA1 name of a tree object (as defined below), representing the contents of a directory at a certain point in time. - parent(s): The SHA1 name of some number of commits which represent the - immediately prevoius step(s) in the history of the project. The + immediately previous step(s) in the history of the project. The example above has one parent; merge commits may have more than one. A commit with no parents is called a "root" commit, and represents the initial revision of a project. Each project must have @@ -3242,7 +3242,7 @@ to replace them by hand. Back up your repository before attempting this in case you corrupt things even more in the process. We'll assume that the problem is a single missing or corrupted blob, -which is sometimes a solveable problem. (Recovering missing trees and +which is sometimes a solvable problem. (Recovering missing trees and especially commits is *much* harder). Before starting, verify that there is corruption, and figure out where diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index eaf5abf1a7..ad7e056620 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.4-rc0.GIT +DEF_VER=v1.5.4-rc1.GIT LF=' ' diff --git a/Makefile b/Makefile index 47a1fb3504..12accac519 100644 --- a/Makefile +++ b/Makefile @@ -230,7 +230,7 @@ SCRIPT_SH = \ git-lost-found.sh git-quiltimport.sh git-submodule.sh \ git-filter-branch.sh \ git-stash.sh \ - git-browse-help.sh + git-help--browse.sh SCRIPT_PERL = \ git-add--interactive.perl \ @@ -923,6 +923,7 @@ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css -e '/@@GITWEB_CGI@@/d' \ -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \ -e '/@@GITWEB_CSS@@/d' \ + -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \ $@.sh > $@+ && \ chmod +x $@+ && \ mv $@+ $@ @@ -976,7 +977,7 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS) $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) -builtin-revert.o builtin-runstatus.o wt-status.o: wt-status.h +builtin-revert.o wt-status.o: wt-status.h $(LIB_FILE): $(LIB_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) @@ -1190,9 +1191,8 @@ check-docs:: case "$$v" in \ git-merge-octopus | git-merge-ours | git-merge-recursive | \ git-merge-resolve | git-merge-stupid | git-merge-subtree | \ - git-add--interactive | git-fsck-objects | git-init-db | \ - git-rebase--interactive | \ - git-repo-config | git-fetch--tool ) continue ;; \ + git-fsck-objects | git-init-db | git-repo-config | \ + git-?*--?* ) continue ;; \ esac ; \ test -f "Documentation/$$v.txt" || \ echo "no doc: $$v"; \ diff --git a/builtin-apply.c b/builtin-apply.c index 64bc3cb995..f2a0bf8930 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -900,56 +900,22 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc static void check_whitespace(const char *line, int len, unsigned ws_rule) { - const char *err = "Adds trailing whitespace"; - int seen_space = 0; - int i; + char *err; + unsigned result = check_and_emit_line(line + 1, len - 1, ws_rule, + NULL, NULL, NULL, NULL); + if (!result) + return; - /* - * We know len is at least two, since we have a '+' and we - * checked that the last character was a '\n' before calling - * this function. That is, an addition of an empty line would - * check the '+' here. Sneaky... - */ - if ((ws_rule & WS_TRAILING_SPACE) && isspace(line[len-2])) - goto error; - - /* - * Make sure that there is no space followed by a tab in - * indentation. - */ - if (ws_rule & WS_SPACE_BEFORE_TAB) { - err = "Space in indent is followed by a tab"; - for (i = 1; i < len; i++) { - if (line[i] == '\t') { - if (seen_space) - goto error; - } - else if (line[i] == ' ') - seen_space = 1; - else - break; - } - } - - /* - * Make sure that the indentation does not contain more than - * 8 spaces. - */ - if ((ws_rule & WS_INDENT_WITH_NON_TAB) && - (8 < len) && !strncmp("+ ", line, 9)) { - err = "Indent more than 8 places with spaces"; - goto error; - } - return; - - error: whitespace_error++; if (squelch_whitespace_errors && squelch_whitespace_errors < whitespace_error) ; - else - fprintf(stderr, "%s.\n%s:%d:%.*s\n", - err, patch_input_file, linenr, len-2, line+1); + else { + err = whitespace_error_string(result); + fprintf(stderr, "%s:%d: %s.\n%.*s\n", + patch_input_file, linenr, err, len - 2, line + 1); + free(err); + } } /* @@ -1584,8 +1550,8 @@ static int apply_line(char *output, const char *patch, int plen, int i; int add_nl_to_tail = 0; int fixed = 0; - int last_tab_in_indent = -1; - int last_space_in_indent = -1; + int last_tab_in_indent = 0; + int last_space_in_indent = 0; int need_fix_leading_space = 0; char *buf; @@ -1616,13 +1582,12 @@ static int apply_line(char *output, const char *patch, int plen, if (ch == '\t') { last_tab_in_indent = i; if ((ws_rule & WS_SPACE_BEFORE_TAB) && - 0 <= last_space_in_indent) + 0 < last_space_in_indent) need_fix_leading_space = 1; } else if (ch == ' ') { last_space_in_indent = i; if ((ws_rule & WS_INDENT_WITH_NON_TAB) && - last_tab_in_indent < 0 && - 8 <= i) + 8 <= i - last_tab_in_indent) need_fix_leading_space = 1; } else diff --git a/builtin-blame.c b/builtin-blame.c index 5466d01f9a..d98caaf217 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -125,7 +125,6 @@ static void origin_decref(struct origin *o) if (o && --o->refcnt <= 0) { if (o->file.ptr) free(o->file.ptr); - memset(o, 0, sizeof(*o)); free(o); } } @@ -542,7 +541,7 @@ static struct patch *compare_buffer(mmfile_t *file_p, mmfile_t *file_o, state.ret->chunks = NULL; state.ret->num = 0; - xdl_diff(file_p, file_o, &xpp, &xecfg, &ecb); + xdi_diff(file_p, file_o, &xpp, &xecfg, &ecb); if (state.ret->num) { struct chunk *chunk; diff --git a/builtin-commit.c b/builtin-commit.c index 9cb7589ac6..0a9101324f 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -280,7 +280,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix) return false_lock.filename; } -static int run_status(FILE *fp, const char *index_file, const char *prefix) +static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn) { struct wt_status s; @@ -296,6 +296,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix) s.untracked = untracked_files; s.index_file = index_file; s.fp = fp; + s.nowarn = nowarn; wt_status_print(&s); @@ -412,7 +413,7 @@ static int prepare_log_message(const char *index_file, const char *prefix) saved_color_setting = wt_status_use_color; wt_status_use_color = 0; - commitable = run_status(fp, index_file, prefix); + commitable = run_status(fp, index_file, prefix, 1); wt_status_use_color = saved_color_setting; fclose(fp); @@ -536,7 +537,7 @@ static int parse_and_validate_options(int argc, const char *argv[], die("Option -m cannot be combined with -c/-C/-F."); if (edit_message) use_message = edit_message; - if (amend) + if (amend && !use_message) use_message = "HEAD"; if (use_message) { unsigned char sha1[20]; @@ -606,7 +607,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) index_file = prepare_index(argc, argv, prefix); - commitable = run_status(stdout, index_file, prefix); + commitable = run_status(stdout, index_file, prefix, 0); rollback_index_files(); @@ -661,6 +662,10 @@ static void print_summary(const char *prefix, const unsigned char *sha1) rev.show_root_diff = 1; rev.commit_format = get_commit_format("format:%h: %s"); rev.always_show_header = 0; + rev.diffopt.detect_rename = 1; + rev.diffopt.rename_limit = 100; + rev.diffopt.break_opt = 0; + diff_setup_done(&rev.diffopt); printf("Created %scommit ", initial_commit ? "initial " : ""); @@ -717,7 +722,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) if (!prepare_log_message(index_file, prefix) && !in_merge && !allow_empty && !(amend && is_a_merge(head_sha1))) { - run_status(stdout, index_file, prefix); + run_status(stdout, index_file, prefix, 0); rollback_index_files(); unlink(commit_editmsg); return 1; diff --git a/builtin-describe.c b/builtin-describe.c index 6eeb9b5045..7a148a2c26 100644 --- a/builtin-describe.c +++ b/builtin-describe.c @@ -267,12 +267,14 @@ int cmd_describe(int argc, const char **argv, const char *prefix) if (contains) { const char **args = xmalloc((4 + argc) * sizeof(char*)); - args[0] = "name-rev"; - args[1] = "--name-only"; - args[2] = "--tags"; - memcpy(args + 3, argv, argc * sizeof(char*)); - args[3 + argc] = NULL; - return cmd_name_rev(3 + argc, args, prefix); + int i = 0; + args[i++] = "name-rev"; + args[i++] = "--name-only"; + if (!all) + args[i++] = "--tags"; + memcpy(args + i, argv, argc * sizeof(char*)); + args[i + argc] = NULL; + return cmd_name_rev(i + argc, args, prefix); } if (argc == 0) { diff --git a/builtin-diff-files.c b/builtin-diff-files.c index 046b7e34b5..9c04111656 100644 --- a/builtin-diff-files.c +++ b/builtin-diff-files.c @@ -31,7 +31,5 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_RAW; result = run_diff_files_cmd(&rev, argc, argv); - if (DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS)) - return DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0; - return result; + return diff_result_code(&rev.diffopt, result); } diff --git a/builtin-diff-index.c b/builtin-diff-index.c index 556c506bfa..0f2390a20a 100644 --- a/builtin-diff-index.c +++ b/builtin-diff-index.c @@ -44,7 +44,5 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix) return -1; } result = run_diff_index(&rev, cached); - if (DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS)) - return DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0; - return result; + return diff_result_code(&rev.diffopt, result); } diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c index 2e13716eec..ebc50efbd2 100644 --- a/builtin-diff-tree.c +++ b/builtin-diff-tree.c @@ -117,23 +117,21 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) break; } - if (!read_stdin) - return DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS) - && DIFF_OPT_TST(&opt->diffopt, HAS_CHANGES); + if (read_stdin) { + if (opt->diffopt.detect_rename) + opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE | + DIFF_SETUP_USE_CACHE); + while (fgets(line, sizeof(line), stdin)) { + unsigned char sha1[20]; - if (opt->diffopt.detect_rename) - opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE | - DIFF_SETUP_USE_CACHE); - while (fgets(line, sizeof(line), stdin)) { - unsigned char sha1[20]; - - if (get_sha1_hex(line, sha1)) { - fputs(line, stdout); - fflush(stdout); + if (get_sha1_hex(line, sha1)) { + fputs(line, stdout); + fflush(stdout); + } + else + diff_tree_stdin(line); } - else - diff_tree_stdin(line); } - return DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS) - && DIFF_OPT_TST(&opt->diffopt, HAS_CHANGES); + + return diff_result_code(&opt->diffopt, 0); } diff --git a/builtin-diff.c b/builtin-diff.c index 55fb84c730..29365a0b17 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -244,7 +244,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix) DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL); DIFF_OPT_SET(&rev.diffopt, RECURSIVE); - /* If the user asked for our exit code then don't start a + /* + * If the user asked for our exit code then don't start a * pager or we would end up reporting its exit code instead. */ if (!DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS)) @@ -351,9 +352,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) else result = builtin_diff_combined(&rev, argc, argv, ent, ents); - if (DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS)) - result = DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0; - + result = diff_result_code(&rev.diffopt, result); if (1 < rev.diffopt.skip_stat_unmatch) refresh_index_quietly(); return result; diff --git a/builtin-log.c b/builtin-log.c index cc3cc9069a..dcc9f81793 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -244,7 +244,28 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix) return cmd_log_walk(&rev); } -static int show_object(const unsigned char *sha1, int suppress_header) +static void show_tagger(char *buf, int len, struct rev_info *rev) +{ + char *email_end, *p; + unsigned long date; + int tz; + + email_end = memchr(buf, '>', len); + if (!email_end) + return; + p = ++email_end; + while (isspace(*p)) + p++; + date = strtoul(p, &p, 10); + while (isspace(*p)) + p++; + tz = (int)strtol(p, NULL, 10); + printf("Tagger: %.*s\nDate: %s\n", (int)(email_end - buf), buf, + show_date(date, tz, rev->date_mode)); +} + +static int show_object(const unsigned char *sha1, int show_tag_object, + struct rev_info *rev) { unsigned long size; enum object_type type; @@ -254,11 +275,14 @@ static int show_object(const unsigned char *sha1, int suppress_header) if (!buf) return error("Could not read object %s", sha1_to_hex(sha1)); - if (suppress_header) - while (offset < size && buf[offset++] != '\n') { - int new_offset = offset; + if (show_tag_object) + while (offset < size && buf[offset] != '\n') { + int new_offset = offset + 1; while (new_offset < size && buf[new_offset++] != '\n') ; /* do nothing */ + if (!prefixcmp(buf + offset, "tagger ")) + show_tagger(buf + offset + 7, + new_offset - offset - 7, rev); offset = new_offset; } @@ -299,16 +323,16 @@ int cmd_show(int argc, const char **argv, const char *prefix) const char *name = objects[i].name; switch (o->type) { case OBJ_BLOB: - ret = show_object(o->sha1, 0); + ret = show_object(o->sha1, 0, NULL); break; case OBJ_TAG: { struct tag *t = (struct tag *)o; - printf("%stag %s%s\n\n", + printf("%stag %s%s\n", diff_get_color_opt(&rev.diffopt, DIFF_COMMIT), t->tag, diff_get_color_opt(&rev.diffopt, DIFF_RESET)); - ret = show_object(o->sha1, 1); + ret = show_object(o->sha1, 1, &rev); objects[i].item = (struct object *)t->tagged; i--; break; diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 7dd0d7f826..e0ce114be7 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1594,6 +1594,15 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, #ifdef THREADED_DELTA_SEARCH +/* + * The main thread waits on the condition that (at least) one of the workers + * has stopped working (which is indicated in the .working member of + * struct thread_params). + * When a work thread has completed its work, it sets .working to 0 and + * signals the main thread and waits on the condition that .data_ready + * becomes 1. + */ + struct thread_params { pthread_t thread; struct object_entry **list; @@ -1601,37 +1610,50 @@ struct thread_params { unsigned remaining; int window; int depth; + int working; + int data_ready; + pthread_mutex_t mutex; + pthread_cond_t cond; unsigned *processed; }; -static pthread_mutex_t data_request = PTHREAD_MUTEX_INITIALIZER; -static pthread_mutex_t data_ready = PTHREAD_MUTEX_INITIALIZER; -static pthread_mutex_t data_provider = PTHREAD_MUTEX_INITIALIZER; -static struct thread_params *data_requester; +static pthread_cond_t progress_cond = PTHREAD_COND_INITIALIZER; static void *threaded_find_deltas(void *arg) { struct thread_params *me = arg; - for (;;) { - pthread_mutex_lock(&data_request); - data_requester = me; - pthread_mutex_unlock(&data_provider); - pthread_mutex_lock(&data_ready); - pthread_mutex_unlock(&data_request); - - if (!me->remaining) - return NULL; - + while (me->remaining) { find_deltas(me->list, &me->remaining, me->window, me->depth, me->processed); + + progress_lock(); + me->working = 0; + pthread_cond_signal(&progress_cond); + progress_unlock(); + + /* + * We must not set ->data_ready before we wait on the + * condition because the main thread may have set it to 1 + * before we get here. In order to be sure that new + * work is available if we see 1 in ->data_ready, it + * was initialized to 0 before this thread was spawned + * and we reset it to 0 right away. + */ + pthread_mutex_lock(&me->mutex); + while (!me->data_ready) + pthread_cond_wait(&me->cond, &me->mutex); + me->data_ready = 0; + pthread_mutex_unlock(&me->mutex); } + /* leave ->working 1 so that this doesn't get more work assigned */ + return NULL; } static void ll_find_deltas(struct object_entry **list, unsigned list_size, int window, int depth, unsigned *processed) { - struct thread_params *target, p[delta_search_threads]; + struct thread_params p[delta_search_threads]; int i, ret, active_threads = 0; if (delta_search_threads <= 1) { @@ -1639,49 +1661,42 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size, return; } - pthread_mutex_lock(&data_provider); - pthread_mutex_lock(&data_ready); - - /* Start work threads. */ - for (i = 0; i < delta_search_threads; i++) { - p[i].window = window; - p[i].depth = depth; - p[i].processed = processed; - p[i].remaining = 0; - ret = pthread_create(&p[i].thread, NULL, - threaded_find_deltas, &p[i]); - if (ret) - die("unable to create thread: %s", strerror(ret)); - active_threads++; - } - - /* Then partition the work amongst them. */ + /* Partition the work amongst work threads. */ for (i = 0; i < delta_search_threads; i++) { unsigned sub_size = list_size / (delta_search_threads - i); - pthread_mutex_lock(&data_provider); - target = data_requester; - if (!sub_size) { - pthread_mutex_unlock(&data_ready); - pthread_join(target->thread, NULL); - active_threads--; - continue; - } + p[i].window = window; + p[i].depth = depth; + p[i].processed = processed; + p[i].working = 1; + p[i].data_ready = 0; /* try to split chunks on "path" boundaries */ while (sub_size < list_size && list[sub_size]->hash && list[sub_size]->hash == list[sub_size-1]->hash) sub_size++; - target->list = list; - target->list_size = sub_size; - target->remaining = sub_size; - pthread_mutex_unlock(&data_ready); + p[i].list = list; + p[i].list_size = sub_size; + p[i].remaining = sub_size; list += sub_size; list_size -= sub_size; } + /* Start work threads. */ + for (i = 0; i < delta_search_threads; i++) { + if (!p[i].list_size) + continue; + pthread_mutex_init(&p[i].mutex, NULL); + pthread_cond_init(&p[i].cond, NULL); + ret = pthread_create(&p[i].thread, NULL, + threaded_find_deltas, &p[i]); + if (ret) + die("unable to create thread: %s", strerror(ret)); + active_threads++; + } + /* * Now let's wait for work completion. Each time a thread is done * with its work, we steal half of the remaining work from the @@ -1690,13 +1705,21 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size, * until the remaining object list segments are simply too short * to be worth splitting anymore. */ - do { + while (active_threads) { + struct thread_params *target = NULL; struct thread_params *victim = NULL; unsigned sub_size = 0; - pthread_mutex_lock(&data_provider); - target = data_requester; progress_lock(); + for (;;) { + for (i = 0; !target && i < delta_search_threads; i++) + if (!p[i].working) + target = &p[i]; + if (target) + break; + pthread_cond_wait(&progress_cond, &progress_mutex); + } + for (i = 0; i < delta_search_threads; i++) if (p[i].remaining > 2*window && (!victim || victim->remaining < p[i].remaining)) @@ -1723,17 +1746,23 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size, victim->list_size -= sub_size; victim->remaining -= sub_size; } - progress_unlock(); - target->list_size = sub_size; target->remaining = sub_size; - pthread_mutex_unlock(&data_ready); + target->working = 1; + progress_unlock(); + + pthread_mutex_lock(&target->mutex); + target->data_ready = 1; + pthread_cond_signal(&target->cond); + pthread_mutex_unlock(&target->mutex); if (!sub_size) { pthread_join(target->thread, NULL); + pthread_cond_destroy(&target->cond); + pthread_mutex_destroy(&target->mutex); active_threads--; } - } while (active_threads); + } } #else diff --git a/builtin-rerere.c b/builtin-rerere.c index 74493237c9..37e6248138 100644 --- a/builtin-rerere.c +++ b/builtin-rerere.c @@ -260,7 +260,7 @@ static int diff_two(const char *file1, const char *label1, memset(&xecfg, 0, sizeof(xecfg)); xecfg.ctxlen = 3; ecb.outf = outf; - xdl_diff(&minus, &plus, &xpp, &xecfg, &ecb); + xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb); free(minus.ptr); free(plus.ptr); diff --git a/builtin-runstatus.c b/builtin-runstatus.c deleted file mode 100644 index 8d167a9674..0000000000 --- a/builtin-runstatus.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "wt-status.h" - -extern int wt_status_use_color; - -static const char runstatus_usage[] = -"git-runstatus [--color|--nocolor] [--amend] [--verbose] [--untracked]"; - -int cmd_runstatus(int argc, const char **argv, const char *prefix) -{ - struct wt_status s; - int i; - - git_config(git_status_config); - wt_status_prepare(&s); - s.prefix = prefix; - - for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "--color")) - wt_status_use_color = 1; - else if (!strcmp(argv[i], "--nocolor")) - wt_status_use_color = 0; - else if (!strcmp(argv[i], "--amend")) { - s.amend = 1; - s.reference = "HEAD^1"; - } - else if (!strcmp(argv[i], "--verbose")) - s.verbose = 1; - else if (!strcmp(argv[i], "--untracked")) - s.untracked = 1; - else - usage(runstatus_usage); - } - - wt_status_print(&s); - return s.commitable ? 0 : 1; -} diff --git a/cache.h b/cache.h index f8b87e0736..ac44ac20a2 100644 --- a/cache.h +++ b/cache.h @@ -670,6 +670,10 @@ void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, i extern unsigned whitespace_rule_cfg; extern unsigned whitespace_rule(const char *); extern unsigned parse_whitespace_rule(const char *); +extern unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule, + FILE *stream, const char *set, + const char *reset, const char *ws); +extern char *whitespace_error_string(unsigned ws); /* ls-files */ int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen); diff --git a/combine-diff.c b/combine-diff.c index 5a658dc0d5..e22db89932 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -226,7 +226,7 @@ static void combine_diff(const unsigned char *parent, mmfile_t *result_file, state.num_parent = num_parent; state.n = n; - xdl_diff(&parent_file, result_file, &xpp, &xecfg, &ecb); + xdi_diff(&parent_file, result_file, &xpp, &xecfg, &ecb); free(parent_file.ptr); /* Assign line numbers for this parent. diff --git a/command-list.txt b/command-list.txt index 28342da959..6c2b1d830d 100644 --- a/command-list.txt +++ b/command-list.txt @@ -98,7 +98,6 @@ git-revert mainporcelain git-rev-list plumbinginterrogators git-rev-parse ancillaryinterrogators git-rm mainporcelain common -git-runstatus ancillaryinterrogators git-send-email foreignscminterface git-send-pack synchingrepositories git-shell synchelpers diff --git a/config.c b/config.c index 49d2b427e5..9a5c5470cc 100644 --- a/config.c +++ b/config.c @@ -610,46 +610,36 @@ static int write_error(void) static int store_write_section(int fd, const char* key) { - const char *dot = strchr(key, '.'); - int len1 = store.baselen, len2 = -1; + const char *dot; + int i, success; + struct strbuf sb; - dot = strchr(key, '.'); + strbuf_init(&sb, 0); + dot = memchr(key, '.', store.baselen); if (dot) { - int dotlen = dot - key; - if (dotlen < len1) { - len2 = len1 - dotlen - 1; - len1 = dotlen; + strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key); + for (i = dot - key + 1; i < store.baselen; i++) { + if (key[i] == '"') + strbuf_addch(&sb, '\\'); + strbuf_addch(&sb, key[i]); } + strbuf_addstr(&sb, "\"]\n"); + } else { + strbuf_addf(&sb, "[%.*s]\n", store.baselen, key); } - if (write_in_full(fd, "[", 1) != 1 || - write_in_full(fd, key, len1) != len1) - return 0; - if (len2 >= 0) { - if (write_in_full(fd, " \"", 2) != 2) - return 0; - while (--len2 >= 0) { - unsigned char c = *++dot; - if (c == '"') - if (write_in_full(fd, "\\", 1) != 1) - return 0; - if (write_in_full(fd, &c, 1) != 1) - return 0; - } - if (write_in_full(fd, "\"", 1) != 1) - return 0; - } - if (write_in_full(fd, "]\n", 2) != 2) - return 0; + success = write_in_full(fd, sb.buf, sb.len) == sb.len; + strbuf_release(&sb); - return 1; + return success; } static int store_write_pair(int fd, const char* key, const char* value) { - int i; - int length = strlen(key+store.baselen+1); - int quote = 0; + int i, success; + int length = strlen(key + store.baselen + 1); + const char *quote = ""; + struct strbuf sb; /* * Check to see if the value needs to be surrounded with a dq pair. @@ -659,43 +649,38 @@ static int store_write_pair(int fd, const char* key, const char* value) * configuration parser. */ if (value[0] == ' ') - quote = 1; + quote = "\""; for (i = 0; value[i]; i++) if (value[i] == ';' || value[i] == '#') - quote = 1; - if (i && value[i-1] == ' ') - quote = 1; + quote = "\""; + if (i && value[i - 1] == ' ') + quote = "\""; + + strbuf_init(&sb, 0); + strbuf_addf(&sb, "\t%.*s = %s", + length, key + store.baselen + 1, quote); - if (write_in_full(fd, "\t", 1) != 1 || - write_in_full(fd, key+store.baselen+1, length) != length || - write_in_full(fd, " = ", 3) != 3) - return 0; - if (quote && write_in_full(fd, "\"", 1) != 1) - return 0; for (i = 0; value[i]; i++) switch (value[i]) { case '\n': - if (write_in_full(fd, "\\n", 2) != 2) - return 0; + strbuf_addstr(&sb, "\\n"); break; case '\t': - if (write_in_full(fd, "\\t", 2) != 2) - return 0; + strbuf_addstr(&sb, "\\t"); break; case '"': case '\\': - if (write_in_full(fd, "\\", 1) != 1) - return 0; + strbuf_addch(&sb, '\\'); default: - if (write_in_full(fd, value+i, 1) != 1) - return 0; + strbuf_addch(&sb, value[i]); break; } - if (quote && write_in_full(fd, "\"", 1) != 1) - return 0; - if (write_in_full(fd, "\n", 1) != 1) - return 0; - return 1; + strbuf_addf(&sb, "%s\n", quote); + + success = write_in_full(fd, sb.buf, sb.len) == sb.len; + strbuf_release(&sb); + + return success; } static ssize_t find_beginning_of_line(const char* contents, size_t size, diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 58e0e53cd6..343364de04 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -291,7 +291,7 @@ __git_commands () for i in $(git help -a|egrep '^ ') do case $i in - add--interactive) : plumbing;; + *--*) : helper pattern;; applymbox) : ask gittus;; applypatch) : ask gittus;; archimport) : import;; @@ -308,7 +308,6 @@ __git_commands () diff-tree) : plumbing;; fast-import) : import;; fsck-objects) : plumbing;; - fetch--tool) : plumbing;; fetch-pack) : plumbing;; fmt-merge-msg) : plumbing;; for-each-ref) : plumbing;; diff --git a/diff-delta.c b/diff-delta.c index 9e440a9299..a4e28df714 100644 --- a/diff-delta.c +++ b/diff-delta.c @@ -212,11 +212,24 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize) if (hash_count[i] <= HASH_LIMIT) continue; - entries -= hash_count[i] - HASH_LIMIT; /* We leave exactly HASH_LIMIT entries in the bucket */ + entries -= hash_count[i] - HASH_LIMIT; entry = hash[i]; acc = 0; + + /* + * Assume that this loop is gone through exactly + * HASH_LIMIT times and is entered and left with + * acc==0. So the first statement in the loop + * contributes (hash_count[i]-HASH_LIMIT)*HASH_LIMIT + * to the accumulator, and the inner loop consequently + * is run (hash_count[i]-HASH_LIMIT) times, removing + * one element from the list each time. Since acc + * balances out to 0 at the final run, the inner loop + * body can't be left with entry==NULL. So we indeed + * encounter entry==NULL in the outer loop only. + */ do { acc += hash_count[i] - HASH_LIMIT; if (acc > 0) { @@ -229,30 +242,17 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize) } entry = entry->next; } while (entry); - - /* Assume that this loop is gone through exactly - * HASH_LIMIT times and is entered and left with - * acc==0. So the first statement in the loop - * contributes (hash_count[i]-HASH_LIMIT)*HASH_LIMIT - * to the accumulator, and the inner loop consequently - * is run (hash_count[i]-HASH_LIMIT) times, removing - * one element from the list each time. Since acc - * balances out to 0 at the final run, the inner loop - * body can't be left with entry==NULL. So we indeed - * encounter entry==NULL in the outer loop only. - */ } free(hash_count); - /* Now create the packed index in array form rather than - * linked lists */ - + /* + * Now create the packed index in array form + * rather than linked lists. + */ memsize = sizeof(*index) + sizeof(*packed_hash) * (hsize+1) + sizeof(*packed_entry) * entries; - mem = malloc(memsize); - if (!mem) { free(hash); return NULL; @@ -264,24 +264,24 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize) index->src_size = bufsize; index->hash_mask = hmask; - mem = index + 1; + mem = index->hash; packed_hash = mem; mem = packed_hash + (hsize+1); packed_entry = mem; - /* Coalesce all entries belonging to one linked list into - * consecutive array entries */ - for (i = 0; i < hsize; i++) { + /* + * Coalesce all entries belonging to one linked list + * into consecutive array entries. + */ packed_hash[i] = packed_entry; for (entry = hash[i]; entry; entry = entry->next) *packed_entry++ = entry->entry; } - /* Sentinel value to indicate the length of the last hash - * bucket */ - + /* Sentinel value to indicate the length of the last hash bucket */ packed_hash[hsize] = packed_entry; + assert(packed_entry - (struct index_entry *)mem == entries); free(hash); diff --git a/diff.c b/diff.c index 9c79ee2891..e26584cdfc 100644 --- a/diff.c +++ b/diff.c @@ -20,6 +20,7 @@ static int diff_detect_rename_default; static int diff_rename_limit_default = 100; static int diff_use_color_default; +static const char *external_diff_cmd_cfg; int diff_auto_refresh_index = 1; static char diff_colors[][COLOR_MAXLEN] = { @@ -163,6 +164,10 @@ int git_diff_ui_config(const char *var, const char *value) diff_auto_refresh_index = git_config_bool(var, value); return 0; } + if (!strcmp(var, "diff.external")) { + external_diff_cmd_cfg = xstrdup(value); + return 0; + } if (!prefixcmp(var, "diff.")) { const char *ep = strrchr(var, '.'); @@ -209,6 +214,8 @@ static const char *external_diff(void) if (done_preparing) return external_diff_cmd; external_diff_cmd = getenv("GIT_EXTERNAL_DIFF"); + if (!external_diff_cmd) + external_diff_cmd = external_diff_cmd_cfg; done_preparing = 1; return external_diff_cmd; } @@ -439,7 +446,7 @@ static void diff_words_show(struct diff_words_data *diff_words) ecb.outf = xdiff_outf; ecb.priv = diff_words; diff_words->xm.consume = fn_out_diff_words_aux; - xdl_diff(&minus, &plus, &xpp, &xecfg, &ecb); + xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb); free(minus.ptr); free(plus.ptr); @@ -486,88 +493,9 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix) static void emit_line(const char *set, const char *reset, const char *line, int len) { - if (len > 0 && line[len-1] == '\n') - len--; fputs(set, stdout); fwrite(line, len, 1, stdout); - puts(reset); -} - -static void emit_line_with_ws(int nparents, - const char *set, const char *reset, const char *ws, - const char *line, int len, unsigned ws_rule) -{ - int col0 = nparents; - int last_tab_in_indent = -1; - int last_space_in_indent = -1; - int i; - int tail = len; - int need_highlight_leading_space = 0; - /* - * The line is a newly added line. Does it have funny leading - * whitespaces? In indent, SP should never precede a TAB. In - * addition, under "indent with non tab" rule, there should not - * be more than 8 consecutive spaces. - */ - for (i = col0; i < len; i++) { - if (line[i] == '\t') { - last_tab_in_indent = i; - if ((ws_rule & WS_SPACE_BEFORE_TAB) && - 0 <= last_space_in_indent) - need_highlight_leading_space = 1; - } - else if (line[i] == ' ') - last_space_in_indent = i; - else - break; - } - if ((ws_rule & WS_INDENT_WITH_NON_TAB) && - 0 <= last_space_in_indent && - last_tab_in_indent < 0 && - 8 <= (i - col0)) { - last_tab_in_indent = i; - need_highlight_leading_space = 1; - } - fputs(set, stdout); - fwrite(line, col0, 1, stdout); fputs(reset, stdout); - if (((i == len) || line[i] == '\n') && i != col0) { - /* The whole line was indent */ - emit_line(ws, reset, line + col0, len - col0); - return; - } - i = col0; - if (need_highlight_leading_space) { - while (i < last_tab_in_indent) { - if (line[i] == ' ') { - fputs(ws, stdout); - putchar(' '); - fputs(reset, stdout); - } - else - putchar(line[i]); - i++; - } - } - tail = len - 1; - if (line[tail] == '\n' && i < tail) - tail--; - if (ws_rule & WS_TRAILING_SPACE) { - while (i < tail) { - if (!isspace(line[tail])) - break; - tail--; - } - } - if ((i < tail && line[tail + 1] != '\n')) { - /* This has whitespace between tail+1..len */ - fputs(set, stdout); - fwrite(line + i, tail - i + 1, 1, stdout); - fputs(reset, stdout); - emit_line(ws, reset, line + tail + 1, len - tail - 1); - } - else - emit_line(set, reset, line + i, len - i); } static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len) @@ -577,9 +505,13 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons if (!*ws) emit_line(set, reset, line, len); - else - emit_line_with_ws(ecbdata->nparents, set, reset, ws, - line, len, ecbdata->ws_rule); + else { + /* Emit just the prefix, then the rest. */ + emit_line(set, reset, line, ecbdata->nparents); + (void)check_and_emit_line(line + ecbdata->nparents, + len - ecbdata->nparents, ecbdata->ws_rule, + stdout, set, reset, ws); + } } static void fn_out_consume(void *priv, char *line, unsigned long len) @@ -1031,6 +963,7 @@ struct checkdiff_t { const char *filename; int lineno, color_diff; unsigned ws_rule; + unsigned status; }; static void checkdiff_consume(void *priv, char *line, unsigned long len) @@ -1039,44 +972,19 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) const char *ws = diff_get_color(data->color_diff, DIFF_WHITESPACE); const char *reset = diff_get_color(data->color_diff, DIFF_RESET); const char *set = diff_get_color(data->color_diff, DIFF_FILE_NEW); + char *err; if (line[0] == '+') { - int i, spaces = 0, space_before_tab = 0, white_space_at_end = 0; - - /* check space before tab */ - for (i = 1; i < len; i++) { - if (line[i] == ' ') - spaces++; - else if (line[i] == '\t') { - if (spaces) { - space_before_tab = 1; - break; - } - } - else - break; - } - - /* check whitespace at line end */ - if (line[len - 1] == '\n') - len--; - if (isspace(line[len - 1])) - white_space_at_end = 1; - - if (space_before_tab || white_space_at_end) { - printf("%s:%d: %s", data->filename, data->lineno, ws); - if (space_before_tab) { - printf("space before tab"); - if (white_space_at_end) - putchar(','); - } - if (white_space_at_end) - printf("whitespace at end"); - printf(":%s ", reset); - emit_line_with_ws(1, set, reset, ws, line, len, - data->ws_rule); - } - + data->status = check_and_emit_line(line + 1, len - 1, + data->ws_rule, NULL, NULL, NULL, NULL); + if (!data->status) + return; + err = whitespace_error_string(data->status); + printf("%s:%d: %s.\n", data->filename, data->lineno, err); + free(err); + emit_line(set, reset, line, 1); + (void)check_and_emit_line(line + 1, len - 1, data->ws_rule, + stdout, set, reset, ws); data->lineno++; } else if (line[0] == ' ') data->lineno++; @@ -1393,7 +1301,7 @@ static void builtin_diff(const char *name_a, if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) ecbdata.diff_words = xcalloc(1, sizeof(struct diff_words_data)); - xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) free_diff_words_data(&ecbdata); } @@ -1446,7 +1354,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b, xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; ecb.outf = xdiff_outf; ecb.priv = diffstat; - xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); } free_and_return: @@ -1486,11 +1394,13 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, xpp.flags = XDF_NEED_MINIMAL; ecb.outf = xdiff_outf; ecb.priv = &data; - xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); } free_and_return: diff_free_filespec_data(one); diff_free_filespec_data(two); + if (data.status) + DIFF_OPT_SET(o, CHECK_FAILED); } struct diff_filespec *alloc_filespec(const char *path) @@ -2898,7 +2808,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) xecfg.flags = XDL_EMIT_FUNCNAMES; ecb.outf = xdiff_outf; ecb.priv = &data; - xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); } SHA1_Final(sha1, &ctx); @@ -3171,6 +3081,20 @@ void diffcore_std(struct diff_options *options) DIFF_OPT_CLR(options, HAS_CHANGES); } +int diff_result_code(struct diff_options *opt, int status) +{ + int result = 0; + if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) && + !(opt->output_format & DIFF_FORMAT_CHECKDIFF)) + return status; + if (DIFF_OPT_TST(opt, EXIT_WITH_STATUS) && + DIFF_OPT_TST(opt, HAS_CHANGES)) + result |= 01; + if ((opt->output_format & DIFF_FORMAT_CHECKDIFF) && + DIFF_OPT_TST(opt, CHECK_FAILED)) + result |= 02; + return result; +} void diff_addremove(struct diff_options *options, int addremove, unsigned mode, diff --git a/diff.h b/diff.h index a52496a108..7e8000a5d7 100644 --- a/diff.h +++ b/diff.h @@ -59,6 +59,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q, #define DIFF_OPT_ALLOW_EXTERNAL (1 << 13) #define DIFF_OPT_EXIT_WITH_STATUS (1 << 14) #define DIFF_OPT_REVERSE_DIFF (1 << 15) +#define DIFF_OPT_CHECK_FAILED (1 << 16) #define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag) #define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag) #define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag) @@ -246,4 +247,6 @@ extern int run_diff_index(struct rev_info *revs, int cached); extern int do_diff_cache(const unsigned char *, struct diff_options *); extern int diff_flush_patch_id(struct diff_options *, unsigned char *); +extern int diff_result_code(struct diff_options *, int); + #endif /* DIFF_H */ diff --git a/dir.c b/dir.c index 6b3273d1d1..3e345c2fc5 100644 --- a/dir.c +++ b/dir.c @@ -169,7 +169,10 @@ static int add_excludes_from_file_1(const char *fname, } buf = xmalloc(size+1); if (read_in_full(fd, buf, size) != size) + { + free(buf); goto err; + } close(fd); if (buf_p) diff --git a/fast-import.c b/fast-import.c index fb809d392e..e9935ff362 100644 --- a/fast-import.c +++ b/fast-import.c @@ -196,7 +196,7 @@ struct mem_pool struct mem_pool *next_pool; char *next_free; char *end; - char space[FLEX_ARRAY]; /* more */ + uintmax_t space[FLEX_ARRAY]; /* more */ }; struct atom_str @@ -536,15 +536,15 @@ static void *pool_alloc(size_t len) total_allocd += sizeof(struct mem_pool) + mem_pool_alloc; p = xmalloc(sizeof(struct mem_pool) + mem_pool_alloc); p->next_pool = mem_pool; - p->next_free = p->space; + p->next_free = (char *) p->space; p->end = p->next_free + mem_pool_alloc; mem_pool = p; } r = p->next_free; - /* round out to a pointer alignment */ - if (len & (sizeof(void*) - 1)) - len += sizeof(void*) - (len & (sizeof(void*) - 1)); + /* round out to a 'uintmax_t' alignment */ + if (len & (sizeof(uintmax_t) - 1)) + len += sizeof(uintmax_t) - (len & (sizeof(uintmax_t) - 1)); p->next_free += len; return r; } diff --git a/git-clone.sh b/git-clone.sh index aa73a9e775..83d00c6ef9 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -93,11 +93,12 @@ fi http_fetch () { # $1 = Remote, $2 = Local - curl -nsfL $curl_extra_args "$1" >"$2" || - case $? in - 126|127) exit ;; - *) return $? ;; - esac + curl -nsfL $curl_extra_args "$1" >"$2" + curl_exit_status=$? + case $curl_exit_status in + 126|127) exit ;; + *) return $curl_exit_status ;; + esac } clone_dumb_http () { @@ -190,7 +191,7 @@ do die "clones are always made with separate-remote layout" ;; --reference) shift; reference="$1" ;; - -o,--origin) + -o|--origin) shift; case "$1" in '') diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index 92e41620fd..d2e50c3429 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -195,11 +195,11 @@ foreach my $f (@files) { my %cvsstat; if (@canstatusfiles) { if ($opt_u) { - my @updated = safe_pipe_capture(@cvs, 'update', @canstatusfiles); + my @updated = xargs_safe_pipe_capture([@cvs, 'update'], @canstatusfiles); print @updated; } my @cvsoutput; - @cvsoutput= safe_pipe_capture(@cvs, 'status', @canstatusfiles); + @cvsoutput = xargs_safe_pipe_capture([@cvs, 'status'], @canstatusfiles); my $matchcount = 0; foreach my $l (@cvsoutput) { chomp $l; @@ -295,7 +295,7 @@ if ($dirtypatch) { if ($opt_c) { print "Autocommit\n $cmd\n"; - print safe_pipe_capture(@cvs, 'commit', '-F', '.msg', @files); + print xargs_safe_pipe_capture([@cvs, 'commit', '-F', '.msg'], @files); if ($?) { die "Exiting: The commit did not succeed"; } @@ -335,15 +335,24 @@ sub safe_pipe_capture { return wantarray ? @output : join('',@output); } -sub safe_pipe_capture_blob { - my $output; - if (my $pid = open my $child, '-|') { - local $/; - undef $/; - $output = (<$child>); - close $child or die join(' ',@_).": $! $?"; - } else { - exec(@_) or die "$! $?"; # exec() can fail the executable can't be found - } - return $output; +sub xargs_safe_pipe_capture { + my $MAX_ARG_LENGTH = 65536; + my $cmd = shift; + my @output; + my $output; + while(@_) { + my @args; + my $length = 0; + while(@_ && $length < $MAX_ARG_LENGTH) { + push @args, shift; + $length += length($args[$#args]); + } + if (wantarray) { + push @output, safe_pipe_capture(@$cmd, @args); + } + else { + $output .= safe_pipe_capture(@$cmd, @args); + } + } + return wantarray ? @output : $output; } diff --git a/git-filter-branch.sh b/git-filter-branch.sh index 29d35fd27c..ae29f47e41 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -290,7 +290,7 @@ while read commit parents; do eval "$filter_tree" < /dev/null || die "tree filter failed: $filter_tree" - git diff-index -r $commit | cut -f 2- | tr '\n' '\0' | \ + git diff-index -r $commit | cut -f 2- | tr '\012' '\000' | \ xargs -0 git update-index --add --replace --remove git ls-files -z --others | \ xargs -0 git update-index --add --replace --remove @@ -342,7 +342,6 @@ done < "$tempdir"/heads _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" -count=0 echo while read ref do @@ -380,7 +379,6 @@ do ;; esac git update-ref -m "filter-branch: backup" "$orig_namespace$ref" $sha1 - count=$(($count+1)) done < "$tempdir"/heads # TODO: This should possibly go, with the semantics that all positive given @@ -423,9 +421,6 @@ fi cd ../.. rm -rf "$tempdir" -echo -test $count -gt 0 && echo "These refs were rewritten:" -git show-ref | grep ^"$orig_namespace" unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE test -z "$ORIG_GIT_DIR" || GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR diff --git a/git-browse-help.sh b/git-help--browse.sh similarity index 96% rename from git-browse-help.sh rename to git-help--browse.sh index b465911c9a..10b0a36a3d 100755 --- a/git-browse-help.sh +++ b/git-help--browse.sh @@ -39,7 +39,7 @@ valid_tool() { } init_browser_path() { - test -z "$GIT_DIR" || browser_path=`git config browser.$1.path` + browser_path=`git config browser.$1.path` test -z "$browser_path" && browser_path=$1 } @@ -71,7 +71,7 @@ do shift done -if test -z "$browser" && test -n "$GIT_DIR" +if test -z "$browser" then for opt in "help.browser" "web.browser" do diff --git a/git-instaweb.sh b/git-instaweb.sh index 42d8d7fc6e..ad0723ccc6 100755 --- a/git-instaweb.sh +++ b/git-instaweb.sh @@ -3,6 +3,7 @@ # Copyright (c) 2006 Eric Wong # +PERL='@@PERL@@' OPTIONS_KEEPDASHDASH= OPTIONS_SPEC="\ git-instaweb [options] (--start | --stop | --restart) @@ -232,16 +233,18 @@ EOF } script=' -s#^\(my\|our\) $projectroot =.*#\1 $projectroot = "'$(dirname "$fqgitdir")'";# -s#\(my\|our\) $gitbin =.*#\1 $gitbin = "'$GIT_EXEC_PATH'";# -s#\(my\|our\) $projects_list =.*#\1 $projects_list = $projectroot;# -s#\(my\|our\) $git_temp =.*#\1 $git_temp = "'$fqgitdir/gitweb/tmp'";#' +s#^(my|our) \$projectroot =.*#$1 \$projectroot = "'$(dirname "$fqgitdir")'";#; +s#(my|our) \$gitbin =.*#$1 \$gitbin = "'$GIT_EXEC_PATH'";#; +s#(my|our) \$projects_list =.*#$1 \$projects_list = \$projectroot;#; +s#(my|our) \$git_temp =.*#$1 \$git_temp = "'$fqgitdir/gitweb/tmp'";#;' gitweb_cgi () { cat > "$1.tmp" <<\EOFGITWEB @@GITWEB_CGI@@ EOFGITWEB - sed "$script" "$1.tmp" > "$1" + # Use the configured full path to perl to match the generated + # scripts' 'hashpling' line + "$PERL" -p -e "$script" "$1.tmp" > "$1" chmod +x "$1" rm -f "$1.tmp" } @@ -273,4 +276,4 @@ esac start_httpd url=http://127.0.0.1:$port -"$browser" $url || echo $url +test -n "$browser" && "$browser" $url || echo $url diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index f83e00fe8f..47581ced5a 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -289,22 +289,22 @@ do_next () { output git reset --soft HEAD^ pick_one -n $sha1 || failed=t echo "$author_script" > "$DOTEST"/author-script - case $failed in - f) + if test $failed = f + then # This is like --amend, but with a different message eval "$author_script" GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \ GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \ GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \ - $USE_OUTPUT git commit -F "$MSG" $EDIT_COMMIT - ;; - t) + $USE_OUTPUT git commit --no-verify -F "$MSG" $EDIT_COMMIT || failed=t + fi + if test $failed = t + then cp "$MSG" "$GIT_DIR"/MERGE_MSG warn warn "Could not apply $sha1... $rest" die_with_patch $sha1 "" - ;; - esac + fi ;; *) warn "Unknown command: $command $sha1 $rest" @@ -322,7 +322,12 @@ do_next () { test -f "$DOTEST"/current-commit && current_commit=$(cat "$DOTEST"/current-commit) && git rev-parse HEAD > "$REWRITTEN"/$current_commit - NEWHEAD=$(cat "$REWRITTEN"/$OLDHEAD) + if test -f "$REWRITTEN"/$OLDHEAD + then + NEWHEAD=$(cat "$REWRITTEN"/$OLDHEAD) + else + NEWHEAD=$OLDHEAD + fi else NEWHEAD=$(git rev-parse HEAD) fi && @@ -366,8 +371,9 @@ do . "$DOTEST"/author-script && { test ! -f "$DOTEST"/amend || git reset --soft HEAD^ } && - export GIT_AUTHOR_NAME GIT_AUTHOR_NAME GIT_AUTHOR_DATE && - git commit -F "$DOTEST"/message -e + export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE && + git commit --no-verify -F "$DOTEST"/message -e || + die "Could not commit staged changes." require_clean_work_tree do_rest diff --git a/git-send-email.perl b/git-send-email.perl index 1d6f466453..248d035088 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -580,7 +580,7 @@ sub send_message $ccline = "\nCc: $cc"; } my $sanitized_sender = sanitize_address($sender); - make_message_id(); + make_message_id() unless defined($message_id); my $header = "From: $sanitized_sender To: $to${ccline} @@ -718,6 +718,9 @@ foreach my $t (@files) { } push @xh, $_; } + elsif (/^Message-Id: (.*)/i) { + $message_id = $1; + } elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) { push @xh, $_; } @@ -805,6 +808,7 @@ foreach my $t (@files) { $references = "$message_id"; } } + $message_id = undef; } if ($compose) { diff --git a/git-sh-setup.sh b/git-sh-setup.sh index b2b4462a57..55abe8ebee 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -124,13 +124,8 @@ get_author_ident_from_commit () { # Make sure we are in a valid repository of a vintage we understand, # if we require to be in a git repository. -if test -n "$NONGIT_OK" +if test -z "$NONGIT_OK" then - if git rev-parse --git-dir >/dev/null 2>&1 - then - : ${GIT_DIR=.git} - fi -else if [ -z "$SUBDIRECTORY_OK" ] then : ${GIT_DIR=.git} diff --git a/git-svn.perl b/git-svn.perl index 54d784469a..c51f1e7391 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -197,8 +197,8 @@ for (my $i = 0; $i < @ARGV; $i++) { } }; -# make sure we're always running -unless ($cmd =~ /(?:clone|init|multi-init)$/) { +# make sure we're always running at the top-level working directory +unless ($cmd && $cmd =~ /(?:clone|init|multi-init)$/) { unless (-d $ENV{GIT_DIR}) { if ($git_dir_user_set) { die "GIT_DIR=$ENV{GIT_DIR} explicitly set, ", @@ -396,6 +396,7 @@ sub cmd_set_tree { } $gs->set_tree($_) foreach @revs; print "Done committing ",scalar @revs," revisions to SVN\n"; + unlink $gs->{index}; } sub cmd_dcommit { @@ -514,6 +515,7 @@ sub cmd_dcommit { $last_rev = $cmt_rev; } } + unlink $gs->{index}; } sub cmd_find_rev { @@ -1374,6 +1376,7 @@ sub fetch_all { ($base, $head) = parse_revision_argument($base, $head); $ra->gs_fetch_loop_common($base, $head, \@gs, \@globs); + unlink $_->{index} foreach @gs; } sub read_all_remotes { @@ -2049,6 +2052,43 @@ sub full_url { $self->{url} . (length $self->{path} ? '/' . $self->{path} : ''); } + +sub set_commit_header_env { + my ($log_entry) = @_; + my %env; + foreach my $ned (qw/NAME EMAIL DATE/) { + foreach my $ac (qw/AUTHOR COMMITTER/) { + $env{"GIT_${ac}_${ned}"} = $ENV{"GIT_${ac}_${ned}"}; + } + } + + $ENV{GIT_AUTHOR_NAME} = $log_entry->{name}; + $ENV{GIT_AUTHOR_EMAIL} = $log_entry->{email}; + $ENV{GIT_AUTHOR_DATE} = $ENV{GIT_COMMITTER_DATE} = $log_entry->{date}; + + $ENV{GIT_COMMITTER_NAME} = (defined $log_entry->{commit_name}) + ? $log_entry->{commit_name} + : $log_entry->{name}; + $ENV{GIT_COMMITTER_EMAIL} = (defined $log_entry->{commit_email}) + ? $log_entry->{commit_email} + : $log_entry->{email}; + \%env; +} + +sub restore_commit_header_env { + my ($env) = @_; + foreach my $ned (qw/NAME EMAIL DATE/) { + foreach my $ac (qw/AUTHOR COMMITTER/) { + my $k = "GIT_${ac}_${ned}"; + if (defined $env->{$k}) { + $ENV{$k} = $env->{$k}; + } else { + delete $ENV{$k}; + } + } + } +} + sub do_git_commit { my ($self, $log_entry) = @_; my $lr = $self->last_rev; @@ -2061,17 +2101,7 @@ sub do_git_commit { croak "$log_entry->{revision} = $c already exists! ", "Why are we refetching it?\n"; } - $ENV{GIT_AUTHOR_NAME} = $log_entry->{name}; - $ENV{GIT_AUTHOR_EMAIL} = $log_entry->{email}; - $ENV{GIT_AUTHOR_DATE} = $ENV{GIT_COMMITTER_DATE} = $log_entry->{date}; - - $ENV{GIT_COMMITTER_NAME} = (defined $log_entry->{commit_name}) - ? $log_entry->{commit_name} - : $log_entry->{name}; - $ENV{GIT_COMMITTER_EMAIL} = (defined $log_entry->{commit_email}) - ? $log_entry->{commit_email} - : $log_entry->{email}; - + my $old_env = set_commit_header_env($log_entry); my $tree = $log_entry->{tree}; if (!defined $tree) { $tree = $self->tmp_index_do(sub { @@ -2086,6 +2116,7 @@ sub do_git_commit { defined(my $pid = open3(my $msg_fh, my $out_fh, '>&STDERR', @exec)) or croak $!; print $msg_fh $log_entry->{log} or croak $!; + restore_commit_header_env($old_env); unless ($self->no_metadata) { print $msg_fh "\ngit-svn-id: $log_entry->{metadata}\n" or croak $!; @@ -2363,11 +2394,20 @@ sub make_log_entry { my ($commit_name, $commit_email) = ($name, $email); if ($_use_log_author) { - if ($log_entry{log} =~ /From:\s+(.*?)\s+<(.*)>\s*\n/) { - ($name, $email) = ($1, $2); - } elsif ($log_entry{log} =~ - /Signed-off-by:\s+(.*?)\s+<(.*)>\s*\n/) { + my $name_field; + if ($log_entry{log} =~ /From:\s+(.*\S)\s*\n/i) { + $name_field = $1; + } elsif ($log_entry{log} =~ /Signed-off-by:\s+(.*\S)\s*\n/i) { + $name_field = $1; + } + if (!defined $name_field) { + # + } elsif ($name_field =~ /(.*?)\s+<(.*)>/) { ($name, $email) = ($1, $2); + } elsif ($name_field =~ /(.*)@/) { + ($name, $email) = ($1, $name_field); + } else { + ($name, $email) = ($name_field, 'unknown'); } } if (defined $headrev && $self->use_svm_props) { @@ -3033,6 +3073,20 @@ sub add_file { sub add_directory { my ($self, $path, $cp_path, $cp_rev) = @_; + my $gpath = $self->git_path($path); + if ($gpath eq '') { + my ($ls, $ctx) = command_output_pipe(qw/ls-tree + -r --name-only -z/, + $self->{c}); + local $/ = "\0"; + while (<$ls>) { + chomp; + $self->{gii}->remove($_); + print "\tD\t$_\n" unless $::_q; + } + command_close_pipe($ls, $ctx); + $self->{empty}->{$path} = 0; + } my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#); delete $self->{empty}->{$dir}; $self->{empty}->{$path} = 1; @@ -3123,9 +3177,15 @@ sub close_file { } sysseek($fh, 0, 0) or croak $!; if ($fb->{mode_b} == 120000) { - sysread($fh, my $buf, 5) == 5 or croak $!; - $buf eq 'link ' or die "$path has mode 120000", - "but is not a link\n"; + eval { + sysread($fh, my $buf, 5) == 5 or croak $!; + $buf eq 'link ' or die "$path has mode 120000", + " but is not a link"; + }; + if ($@) { + warn "$@\n"; + sysseek($fh, 0, 0) or croak $!; + } } defined(my $pid = open my $out,'-|') or die "Can't fork: $!\n"; if (!$pid) { diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 24b31582af..28bb8c3933 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1511,7 +1511,7 @@ sub config_to_int { sub config_to_multi { my $val = shift; - return ref($val) ? $val : [ $val ]; + return ref($val) ? $val : (defined($val) ? [ $val ] : []); } sub git_get_project_config { @@ -2233,6 +2233,7 @@ sub git_get_heads_list { my ($hash, $name, $title) = split(' ', $refinfo, 3); my ($committer, $epoch, $tz) = ($committerinfo =~ /^(.*) ([0-9]+) (.*)$/); + $ref_item{'fullname'} = $name; $name =~ s!^refs/heads/!!; $ref_item{'name'} = $name; @@ -2270,6 +2271,7 @@ sub git_get_tags_list { my ($id, $type, $name, $refid, $reftype, $title) = split(' ', $refinfo, 6); my ($creator, $epoch, $tz) = ($creatorinfo =~ /^(.*) ([0-9]+) (.*)$/); + $ref_item{'fullname'} = $name; $name =~ s!^refs/tags/!!; $ref_item{'type'} = $type; @@ -3690,8 +3692,8 @@ sub git_tags_body { "" . " | " . $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'})}, $tag{'reftype'}); if ($tag{'reftype'} eq "commit") { - print " | " . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'})}, "shortlog") . - " | " . $cgi->a({-href => href(action=>"log", hash=>$tag{'name'})}, "log"); + print " | " . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'fullname'})}, "shortlog") . + " | " . $cgi->a({-href => href(action=>"log", hash=>$tag{'fullname'})}, "log"); } elsif ($tag{'reftype'} eq "blob") { print " | " . $cgi->a({-href => href(action=>"blob_plain", hash=>$tag{'refid'})}, "raw"); } @@ -3726,13 +3728,13 @@ sub git_heads_body { $alternate ^= 1; print "$ref{'age'}\n" . ($curr ? "" : "") . - $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'name'}), + $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'fullname'}), -class => "list name"},esc_html($ref{'name'})) . "\n" . "" . - $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'name'})}, "shortlog") . " | " . - $cgi->a({-href => href(action=>"log", hash=>$ref{'name'})}, "log") . " | " . - $cgi->a({-href => href(action=>"tree", hash=>$ref{'name'}, hash_base=>$ref{'name'})}, "tree") . + $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'fullname'})}, "shortlog") . " | " . + $cgi->a({-href => href(action=>"log", hash=>$ref{'fullname'})}, "log") . " | " . + $cgi->a({-href => href(action=>"tree", hash=>$ref{'fullname'}, hash_base=>$ref{'name'})}, "tree") . "\n" . ""; } @@ -4288,7 +4290,7 @@ sub git_blob { open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash or die_error(undef, "Couldn't cat $file_name, $hash"); my $mimetype = blob_mimetype($fd, $file_name); - if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)!) { + if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)! && -B $fd) { close $fd; return git_blob_plain($mimetype); } @@ -4329,16 +4331,7 @@ sub git_blob { } git_print_page_path($file_name, "blob", $hash_base); print "
\n"; - if ($mimetype =~ m!^text/!) { - my $nr; - while (my $line = <$fd>) { - chomp $line; - $nr++; - $line = untabify($line); - printf "
%4i %s
\n", - $nr, $nr, $nr, esc_html($line, -nbsp=>1); - } - } elsif ($mimetype =~ m!^image/!) { + if ($mimetype =~ m!^image/!) { print qq!$file_name"blob_plain", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name) . qq!" />\n!; + } else { + my $nr; + while (my $line = <$fd>) { + chomp $line; + $nr++; + $line = untabify($line); + printf "
%4i %s
\n", + $nr, $nr, $nr, esc_html($line, -nbsp=>1); + } } close $fd or print "Reading blob failed.\n"; diff --git a/help.c b/help.c index c7c64dc03a..dcb6a3f6c5 100644 --- a/help.c +++ b/help.c @@ -9,6 +9,48 @@ #include "common-cmds.h" #include "dir.h" +static const char *help_default_format; + +static enum help_format { + man_format, + info_format, + web_format, +#ifdef __MINGW32__ +} help_format = web_format; +#else +} help_format = man_format; +#endif + +static void parse_help_format(const char *format) +{ + if (!format) { + help_format = man_format; + return; + } + if (!strcmp(format, "man")) { + help_format = man_format; + return; + } + if (!strcmp(format, "info")) { + help_format = info_format; + return; + } + if (!strcmp(format, "web") || !strcmp(format, "html")) { + help_format = web_format; + return; + } + die("unrecognized help format '%s'", format); +} + +static int git_help_config(const char *var, const char *value) +{ + if (!strcmp(var, "help.format")) { + help_default_format = xstrdup(value); + return 0; + } + return git_default_config(var, value); +} + /* most GUI terminals set COLUMNS (although some don't export it) */ static int term_columns(void) { @@ -318,7 +360,7 @@ static void show_html_page(const char *git_cmd) } #else const char *page = cmd_to_page(git_cmd); - execl_git_cmd("browse-help", page, NULL); + execl_git_cmd("help--browse", page, NULL); #endif } @@ -357,12 +399,30 @@ int cmd_help(int argc, const char **argv, const char *prefix) show_info_page(argc > 2 ? argv[2] : NULL); } - else -#ifdef __MINGW32__ - show_html_page(help_cmd); -#else - show_man_page(help_cmd); -#endif + else if (!strcmp(help_cmd, "--man") || !strcmp(help_cmd, "-m")) { + show_man_page(argc > 2 ? argv[2] : NULL); + } + + else { + int nongit; + + setup_git_directory_gently(&nongit); + git_config(git_help_config); + if (help_default_format) + parse_help_format(help_default_format); + + switch (help_format) { + case man_format: + show_man_page(help_cmd); + break; + case info_format: + show_info_page(help_cmd); + break; + case web_format: + show_html_page(help_cmd); + break; + } + } return 0; } diff --git a/http-push.c b/http-push.c index fffbe9ccb4..64be904921 100644 --- a/http-push.c +++ b/http-push.c @@ -75,7 +75,6 @@ static int aborted; static signed char remote_dir_exists[256]; static struct curl_slist *no_pragma_header; -static struct curl_slist *default_headers; static int push_verbosely; static int push_all = MATCH_REFS_NONE; @@ -495,10 +494,11 @@ static void start_put(struct transfer_request *request) memset(&stream, 0, sizeof(stream)); deflateInit(&stream, zlib_compression_level); size = deflateBound(&stream, len + hdrlen); - request->buffer.buffer = xmalloc(size); + strbuf_init(&request->buffer.buf, size); + request->buffer.posn = 0; /* Compress it */ - stream.next_out = request->buffer.buffer; + stream.next_out = (unsigned char *)request->buffer.buf.buf; stream.avail_out = size; /* First header.. */ @@ -515,8 +515,7 @@ static void start_put(struct transfer_request *request) deflateEnd(&stream); free(unpacked); - request->buffer.size = stream.total_out; - request->buffer.posn = 0; + request->buffer.buf.len = stream.total_out; request->url = xmalloc(strlen(remote->url) + strlen(request->lock->token) + 51); @@ -538,7 +537,7 @@ static void start_put(struct transfer_request *request) slot->callback_func = process_response; slot->callback_data = request; curl_easy_setopt(slot->curl, CURLOPT_INFILE, &request->buffer); - curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, request->buffer.size); + curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, request->buffer.buf.len); curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null); curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT); @@ -925,11 +924,14 @@ static int fetch_index(unsigned char *sha1) hex); } } else { + free(url); return error("Unable to start request"); } - if (has_pack_index(sha1)) + if (has_pack_index(sha1)) { + free(url); return 0; + } if (push_verbosely) fprintf(stderr, "Getting index for pack %s\n", hex); @@ -939,9 +941,11 @@ static int fetch_index(unsigned char *sha1) filename = sha1_pack_index_name(sha1); snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename); indexfile = fopen(tmpfile, "a"); - if (!indexfile) + if (!indexfile) { + free(url); return error("Unable to open local file %s for pack index", tmpfile); + } slot = get_active_slot(); slot->results = &results; @@ -1003,18 +1007,13 @@ static int fetch_indices(void) { unsigned char sha1[20]; char *url; - struct buffer buffer; + struct strbuf buffer = STRBUF_INIT; char *data; int i = 0; struct active_request_slot *slot; struct slot_results results; - data = xcalloc(1, 4096); - buffer.size = 4096; - buffer.posn = 0; - buffer.buffer = data; - if (push_verbosely) fprintf(stderr, "Getting pack list\n"); @@ -1030,7 +1029,7 @@ static int fetch_indices(void) if (start_active_slot(slot)) { run_active_slot(slot); if (results.curl_result != CURLE_OK) { - free(buffer.buffer); + strbuf_release(&buffer); free(url); if (results.http_code == 404) return 0; @@ -1038,18 +1037,18 @@ static int fetch_indices(void) return error("%s", curl_errorstr); } } else { - free(buffer.buffer); + strbuf_release(&buffer); free(url); return error("Unable to start request"); } free(url); - data = buffer.buffer; - while (i < buffer.posn) { + data = buffer.buf; + while (i < buffer.len) { switch (data[i]) { case 'P': i++; - if (i + 52 < buffer.posn && + if (i + 52 < buffer.len && !prefixcmp(data + i, " pack-") && !prefixcmp(data + i + 46, ".pack\n")) { get_sha1_hex(data + i + 6, sha1); @@ -1064,89 +1063,10 @@ static int fetch_indices(void) i++; } - free(buffer.buffer); + strbuf_release(&buffer); return 0; } -static inline int needs_quote(int ch) -{ - if (((ch >= 'A') && (ch <= 'Z')) - || ((ch >= 'a') && (ch <= 'z')) - || ((ch >= '0') && (ch <= '9')) - || (ch == '/') - || (ch == '-') - || (ch == '.')) - return 0; - return 1; -} - -static inline int hex(int v) -{ - if (v < 10) return '0' + v; - else return 'A' + v - 10; -} - -static char *quote_ref_url(const char *base, const char *ref) -{ - const char *cp; - char *dp, *qref; - int len, baselen, ch; - - baselen = strlen(base); - len = baselen + 1; - for (cp = ref; (ch = *cp) != 0; cp++, len++) - if (needs_quote(ch)) - len += 2; /* extra two hex plus replacement % */ - qref = xmalloc(len); - memcpy(qref, base, baselen); - for (cp = ref, dp = qref + baselen; (ch = *cp) != 0; cp++) { - if (needs_quote(ch)) { - *dp++ = '%'; - *dp++ = hex((ch >> 4) & 0xF); - *dp++ = hex(ch & 0xF); - } - else - *dp++ = ch; - } - *dp = 0; - - return qref; -} - -int fetch_ref(char *ref, unsigned char *sha1) -{ - char *url; - char hex[42]; - struct buffer buffer; - char *base = remote->url; - struct active_request_slot *slot; - struct slot_results results; - buffer.size = 41; - buffer.posn = 0; - buffer.buffer = hex; - hex[41] = '\0'; - - url = quote_ref_url(base, ref); - slot = get_active_slot(); - slot->results = &results; - curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); - curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); - curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL); - curl_easy_setopt(slot->curl, CURLOPT_URL, url); - if (start_active_slot(slot)) { - run_active_slot(slot); - if (results.curl_result != CURLE_OK) - return error("Couldn't get %s for %s\n%s", - url, ref, curl_errorstr); - } else { - return error("Unable to start request"); - } - - hex[40] = '\0'; - get_sha1_hex(hex, sha1); - return 0; -} - static void one_remote_object(const char *hex) { unsigned char sha1[20]; @@ -1267,10 +1187,8 @@ static struct remote_lock *lock_remote(const char *path, long timeout) { struct active_request_slot *slot; struct slot_results results; - struct buffer out_buffer; - struct buffer in_buffer; - char *out_data; - char *in_data; + struct buffer out_buffer = { STRBUF_INIT, 0 }; + struct strbuf in_buffer = STRBUF_INIT; char *url; char *ep; char timeout_header[25]; @@ -1310,16 +1228,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout) ep = strchr(ep + 1, '/'); } - out_buffer.size = strlen(LOCK_REQUEST) + strlen(git_default_email) - 2; - out_data = xmalloc(out_buffer.size + 1); - snprintf(out_data, out_buffer.size + 1, LOCK_REQUEST, git_default_email); - out_buffer.posn = 0; - out_buffer.buffer = out_data; - - in_buffer.size = 4096; - in_data = xmalloc(in_buffer.size); - in_buffer.posn = 0; - in_buffer.buffer = in_data; + strbuf_addf(&out_buffer.buf, LOCK_REQUEST, git_default_email); sprintf(timeout_header, "Timeout: Second-%ld", timeout); dav_headers = curl_slist_append(dav_headers, timeout_header); @@ -1328,7 +1237,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout) slot = get_active_slot(); slot->results = &results; curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer); - curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size); + curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len); curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); @@ -1354,8 +1263,8 @@ static struct remote_lock *lock_remote(const char *path, long timeout) XML_SetElementHandler(parser, xml_start_tag, xml_end_tag); XML_SetCharacterDataHandler(parser, xml_cdata); - result = XML_Parse(parser, in_buffer.buffer, - in_buffer.posn, 1); + result = XML_Parse(parser, in_buffer.buf, + in_buffer.len, 1); free(ctx.name); if (result != XML_STATUS_OK) { fprintf(stderr, "XML error: %s\n", @@ -1370,8 +1279,8 @@ static struct remote_lock *lock_remote(const char *path, long timeout) } curl_slist_free_all(dav_headers); - free(out_data); - free(in_data); + strbuf_release(&out_buffer.buf); + strbuf_release(&in_buffer); if (lock->token == NULL || lock->timeout <= 0) { if (lock->token != NULL) @@ -1522,10 +1431,8 @@ static void remote_ls(const char *path, int flags, char *url = xmalloc(strlen(remote->url) + strlen(path) + 1); struct active_request_slot *slot; struct slot_results results; - struct buffer in_buffer; - struct buffer out_buffer; - char *in_data; - char *out_data; + struct strbuf in_buffer = STRBUF_INIT; + struct buffer out_buffer = { STRBUF_INIT, 0 }; struct curl_slist *dav_headers = NULL; struct xml_ctx ctx; struct remote_ls_ctx ls; @@ -1539,16 +1446,7 @@ static void remote_ls(const char *path, int flags, sprintf(url, "%s%s", remote->url, path); - out_buffer.size = strlen(PROPFIND_ALL_REQUEST); - out_data = xmalloc(out_buffer.size + 1); - snprintf(out_data, out_buffer.size + 1, PROPFIND_ALL_REQUEST); - out_buffer.posn = 0; - out_buffer.buffer = out_data; - - in_buffer.size = 4096; - in_data = xmalloc(in_buffer.size); - in_buffer.posn = 0; - in_buffer.buffer = in_data; + strbuf_addf(&out_buffer.buf, PROPFIND_ALL_REQUEST); dav_headers = curl_slist_append(dav_headers, "Depth: 1"); dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml"); @@ -1556,7 +1454,7 @@ static void remote_ls(const char *path, int flags, slot = get_active_slot(); slot->results = &results; curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer); - curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size); + curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len); curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); @@ -1579,8 +1477,8 @@ static void remote_ls(const char *path, int flags, XML_SetElementHandler(parser, xml_start_tag, xml_end_tag); XML_SetCharacterDataHandler(parser, xml_cdata); - result = XML_Parse(parser, in_buffer.buffer, - in_buffer.posn, 1); + result = XML_Parse(parser, in_buffer.buf, + in_buffer.len, 1); free(ctx.name); if (result != XML_STATUS_OK) { @@ -1596,8 +1494,8 @@ static void remote_ls(const char *path, int flags, free(ls.path); free(url); - free(out_data); - free(in_buffer.buffer); + strbuf_release(&out_buffer.buf); + strbuf_release(&in_buffer); curl_slist_free_all(dav_headers); } @@ -1618,27 +1516,13 @@ static int locking_available(void) { struct active_request_slot *slot; struct slot_results results; - struct buffer in_buffer; - struct buffer out_buffer; - char *in_data; - char *out_data; + struct strbuf in_buffer = STRBUF_INIT; + struct buffer out_buffer = { STRBUF_INIT, 0 }; struct curl_slist *dav_headers = NULL; struct xml_ctx ctx; int lock_flags = 0; - out_buffer.size = - strlen(PROPFIND_SUPPORTEDLOCK_REQUEST) + - strlen(remote->url) - 2; - out_data = xmalloc(out_buffer.size + 1); - snprintf(out_data, out_buffer.size + 1, - PROPFIND_SUPPORTEDLOCK_REQUEST, remote->url); - out_buffer.posn = 0; - out_buffer.buffer = out_data; - - in_buffer.size = 4096; - in_data = xmalloc(in_buffer.size); - in_buffer.posn = 0; - in_buffer.buffer = in_data; + strbuf_addf(&out_buffer.buf, PROPFIND_SUPPORTEDLOCK_REQUEST, remote->url); dav_headers = curl_slist_append(dav_headers, "Depth: 0"); dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml"); @@ -1646,7 +1530,7 @@ static int locking_available(void) slot = get_active_slot(); slot->results = &results; curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer); - curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size); + curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len); curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); @@ -1668,8 +1552,8 @@ static int locking_available(void) XML_SetUserData(parser, &ctx); XML_SetElementHandler(parser, xml_start_tag, xml_end_tag); - result = XML_Parse(parser, in_buffer.buffer, - in_buffer.posn, 1); + result = XML_Parse(parser, in_buffer.buf, + in_buffer.len, 1); free(ctx.name); if (result != XML_STATUS_OK) { @@ -1684,8 +1568,8 @@ static int locking_available(void) fprintf(stderr, "Unable to start PROPFIND request\n"); } - free(out_data); - free(in_buffer.buffer); + strbuf_release(&out_buffer.buf); + strbuf_release(&in_buffer); curl_slist_free_all(dav_headers); return lock_flags; @@ -1803,30 +1687,20 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock) { struct active_request_slot *slot; struct slot_results results; - char *out_data; char *if_header; - struct buffer out_buffer; + struct buffer out_buffer = { STRBUF_INIT, 0 }; struct curl_slist *dav_headers = NULL; - int i; if_header = xmalloc(strlen(lock->token) + 25); sprintf(if_header, "If: ()", lock->token); dav_headers = curl_slist_append(dav_headers, if_header); - out_buffer.size = 41; - out_data = xmalloc(out_buffer.size + 1); - i = snprintf(out_data, out_buffer.size + 1, "%s\n", sha1_to_hex(sha1)); - if (i != out_buffer.size) { - fprintf(stderr, "Unable to initialize PUT request body\n"); - return 0; - } - out_buffer.posn = 0; - out_buffer.buffer = out_data; + strbuf_addf(&out_buffer.buf, "%s\n", sha1_to_hex(sha1)); slot = get_active_slot(); slot->results = &results; curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer); - curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size); + curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len); curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null); curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT); @@ -1837,7 +1711,7 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock) if (start_active_slot(slot)) { run_active_slot(slot); - free(out_data); + strbuf_release(&out_buffer.buf); free(if_header); if (results.curl_result != CURLE_OK) { fprintf(stderr, @@ -1847,7 +1721,7 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock) return 0; } } else { - free(out_data); + strbuf_release(&out_buffer.buf); free(if_header); fprintf(stderr, "Unable to start PUT request\n"); return 0; @@ -1878,7 +1752,8 @@ static void one_remote_ref(char *refname) struct object *obj; int len = strlen(refname) + 1; - if (fetch_ref(refname, remote_sha1) != 0) { + if (http_fetch_ref(remote->url, refname + 5 /* "refs/" */, + remote_sha1) != 0) { fprintf(stderr, "Unable to fetch ref %s from %s\n", refname, remote->url); @@ -2004,13 +1879,14 @@ static void mark_edges_uninteresting(struct commit_list *list) static void add_remote_info_ref(struct remote_ls_ctx *ls) { - struct buffer *buf = (struct buffer *)ls->userData; + struct strbuf *buf = (struct strbuf *)ls->userData; unsigned char remote_sha1[20]; struct object *o; int len; char *ref_info; - if (fetch_ref(ls->dentry_name, remote_sha1) != 0) { + if (http_fetch_ref(remote->url, ls->dentry_name + 5 /* "refs/" */, + remote_sha1) != 0) { fprintf(stderr, "Unable to fetch ref %s from %s\n", ls->dentry_name, remote->url); @@ -2049,17 +1925,14 @@ static void add_remote_info_ref(struct remote_ls_ctx *ls) static void update_remote_info_refs(struct remote_lock *lock) { - struct buffer buffer; + struct buffer buffer = { STRBUF_INIT, 0 }; struct active_request_slot *slot; struct slot_results results; char *if_header; struct curl_slist *dav_headers = NULL; - buffer.buffer = xcalloc(1, 4096); - buffer.size = 4096; - buffer.posn = 0; remote_ls("refs/", (PROCESS_FILES | RECURSIVE), - add_remote_info_ref, &buffer); + add_remote_info_ref, &buffer.buf); if (!aborted) { if_header = xmalloc(strlen(lock->token) + 25); sprintf(if_header, "If: ()", lock->token); @@ -2068,7 +1941,7 @@ static void update_remote_info_refs(struct remote_lock *lock) slot = get_active_slot(); slot->results = &results; curl_easy_setopt(slot->curl, CURLOPT_INFILE, &buffer); - curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, buffer.posn); + curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, buffer.buf.len); curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null); curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT); @@ -2077,8 +1950,6 @@ static void update_remote_info_refs(struct remote_lock *lock) curl_easy_setopt(slot->curl, CURLOPT_PUT, 1); curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url); - buffer.posn = 0; - if (start_active_slot(slot)) { run_active_slot(slot); if (results.curl_result != CURLE_OK) { @@ -2089,7 +1960,7 @@ static void update_remote_info_refs(struct remote_lock *lock) } free(if_header); } - free(buffer.buffer); + strbuf_release(&buffer.buf); } static int remote_exists(const char *path) @@ -2097,43 +1968,43 @@ static int remote_exists(const char *path) char *url = xmalloc(strlen(remote->url) + strlen(path) + 1); struct active_request_slot *slot; struct slot_results results; + int ret = -1; sprintf(url, "%s%s", remote->url, path); - slot = get_active_slot(); + slot = get_active_slot(); slot->results = &results; - curl_easy_setopt(slot->curl, CURLOPT_URL, url); - curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1); + curl_easy_setopt(slot->curl, CURLOPT_URL, url); + curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1); - if (start_active_slot(slot)) { + if (start_active_slot(slot)) { run_active_slot(slot); + free(url); if (results.http_code == 404) - return 0; + ret = 0; else if (results.curl_result == CURLE_OK) - return 1; + ret = 1; else fprintf(stderr, "HEAD HTTP error %ld\n", results.http_code); } else { + free(url); fprintf(stderr, "Unable to start HEAD request\n"); } - return -1; + free(url); + return ret; } static void fetch_symref(const char *path, char **symref, unsigned char *sha1) { char *url; - struct buffer buffer; + struct strbuf buffer = STRBUF_INIT; struct active_request_slot *slot; struct slot_results results; url = xmalloc(strlen(remote->url) + strlen(path) + 1); sprintf(url, "%s%s", remote->url, path); - buffer.size = 4096; - buffer.posn = 0; - buffer.buffer = xmalloc(buffer.size); - slot = get_active_slot(); slot->results = &results; curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); @@ -2156,17 +2027,17 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1) *symref = NULL; hashclr(sha1); - if (buffer.posn == 0) + if (buffer.len == 0) return; /* If it's a symref, set the refname; otherwise try for a sha1 */ - if (!prefixcmp((char *)buffer.buffer, "ref: ")) { - *symref = xmemdupz((char *)buffer.buffer + 5, buffer.posn - 6); + if (!prefixcmp((char *)buffer.buf, "ref: ")) { + *symref = xmemdupz((char *)buffer.buf + 5, buffer.len - 6); } else { - get_sha1_hex(buffer.buffer, sha1); + get_sha1_hex(buffer.buf, sha1); } - free(buffer.buffer); + strbuf_release(&buffer); } static int verify_merge_base(unsigned char *head_sha1, unsigned char *branch_sha1) @@ -2354,11 +2225,6 @@ int main(int argc, char **argv) http_init(); no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:"); - default_headers = curl_slist_append(default_headers, "Range:"); - default_headers = curl_slist_append(default_headers, "Destination:"); - default_headers = curl_slist_append(default_headers, "If:"); - default_headers = curl_slist_append(default_headers, - "Pragma: no-cache"); /* Verify DAV compliance/lock support */ if (!locking_available()) { @@ -2538,7 +2404,6 @@ int main(int argc, char **argv) free(remote); curl_slist_free_all(no_pragma_header); - curl_slist_free_all(default_headers); http_cleanup(); diff --git a/http-walker.c b/http-walker.c index a3fb596542..2c3786870e 100644 --- a/http-walker.c +++ b/http-walker.c @@ -48,7 +48,7 @@ struct alternates_request { struct walker *walker; const char *base; char *url; - struct buffer *buffer; + struct strbuf *buffer; struct active_request_slot *slot; int http_specific; }; @@ -90,19 +90,6 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb, return size; } -static int missing__target(int code, int result) -{ - return /* file:// URL -- do we ever use one??? */ - (result == CURLE_FILE_COULDNT_READ_FILE) || - /* http:// and https:// URL */ - (code == 404 && result == CURLE_HTTP_RETURNED_ERROR) || - /* ftp:// URL */ - (code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE) - ; -} - -#define missing_target(a) missing__target((a)->http_code, (a)->curl_result) - static void fetch_alternates(struct walker *walker, const char *base); static void process_object_response(void *callback_data); @@ -475,7 +462,7 @@ static void process_alternates_response(void *callback_data) if (alt_req->http_specific) { if (slot->curl_result != CURLE_OK || - !alt_req->buffer->posn) { + !alt_req->buffer->len) { /* Try reusing the slot to get non-http alternates */ alt_req->http_specific = 0; @@ -503,12 +490,12 @@ static void process_alternates_response(void *callback_data) } fwrite_buffer(&null_byte, 1, 1, alt_req->buffer); - alt_req->buffer->posn--; - data = alt_req->buffer->buffer; + alt_req->buffer->len--; + data = alt_req->buffer->buf; - while (i < alt_req->buffer->posn) { + while (i < alt_req->buffer->len) { int posn = i; - while (posn < alt_req->buffer->posn && data[posn] != '\n') + while (posn < alt_req->buffer->len && data[posn] != '\n') posn++; if (data[posn] == '\n') { int okay = 0; @@ -596,9 +583,8 @@ static void process_alternates_response(void *callback_data) static void fetch_alternates(struct walker *walker, const char *base) { - struct buffer buffer; + struct strbuf buffer = STRBUF_INIT; char *url; - char *data; struct active_request_slot *slot; struct alternates_request alt_req; struct walker_data *cdata = walker->data; @@ -619,11 +605,6 @@ static void fetch_alternates(struct walker *walker, const char *base) /* Start the fetch */ cdata->got_alternates = 0; - data = xmalloc(4096); - buffer.size = 4096; - buffer.posn = 0; - buffer.buffer = data; - if (walker->get_verbosely) fprintf(stderr, "Getting alternates list for %s\n", base); @@ -652,7 +633,7 @@ static void fetch_alternates(struct walker *walker, const char *base) else cdata->got_alternates = -1; - free(data); + strbuf_release(&buffer); free(url); } @@ -660,9 +641,10 @@ static int fetch_indices(struct walker *walker, struct alt_base *repo) { unsigned char sha1[20]; char *url; - struct buffer buffer; + struct strbuf buffer = STRBUF_INIT; char *data; int i = 0; + int ret = 0; struct active_request_slot *slot; struct slot_results results; @@ -670,11 +652,6 @@ static int fetch_indices(struct walker *walker, struct alt_base *repo) if (repo->got_indices) return 0; - data = xmalloc(4096); - buffer.size = 4096; - buffer.posn = 0; - buffer.buffer = data; - if (walker->get_verbosely) fprintf(stderr, "Getting pack list for %s\n", repo->base); @@ -692,26 +669,25 @@ static int fetch_indices(struct walker *walker, struct alt_base *repo) if (results.curl_result != CURLE_OK) { if (missing_target(&results)) { repo->got_indices = 1; - free(buffer.buffer); - return 0; + goto cleanup; } else { repo->got_indices = 0; - free(buffer.buffer); - return error("%s", curl_errorstr); + ret = error("%s", curl_errorstr); + goto cleanup; } } } else { repo->got_indices = 0; - free(buffer.buffer); - return error("Unable to start request"); + ret = error("Unable to start request"); + goto cleanup; } - data = buffer.buffer; - while (i < buffer.posn) { + data = buffer.buf; + while (i < buffer.len) { switch (data[i]) { case 'P': i++; - if (i + 52 <= buffer.posn && + if (i + 52 <= buffer.len && !prefixcmp(data + i, " pack-") && !prefixcmp(data + i + 46, ".pack\n")) { get_sha1_hex(data + i + 6, sha1); @@ -720,15 +696,17 @@ static int fetch_indices(struct walker *walker, struct alt_base *repo) break; } default: - while (i < buffer.posn && data[i] != '\n') + while (i < buffer.len && data[i] != '\n') i++; } i++; } - free(buffer.buffer); repo->got_indices = 1; - return 0; +cleanup: + strbuf_release(&buffer); + free(url); + return ret; } static int fetch_pack(struct walker *walker, struct alt_base *repo, unsigned char *sha1) @@ -910,85 +888,10 @@ static int fetch(struct walker *walker, unsigned char *sha1) data->alt->base); } -static inline int needs_quote(int ch) -{ - if (((ch >= 'A') && (ch <= 'Z')) - || ((ch >= 'a') && (ch <= 'z')) - || ((ch >= '0') && (ch <= '9')) - || (ch == '/') - || (ch == '-') - || (ch == '.')) - return 0; - return 1; -} - -static inline int hex(int v) -{ - if (v < 10) return '0' + v; - else return 'A' + v - 10; -} - -static char *quote_ref_url(const char *base, const char *ref) -{ - const char *cp; - char *dp, *qref; - int len, baselen, ch; - - baselen = strlen(base); - len = baselen + 7; /* "/refs/" + NUL */ - for (cp = ref; (ch = *cp) != 0; cp++, len++) - if (needs_quote(ch)) - len += 2; /* extra two hex plus replacement % */ - qref = xmalloc(len); - memcpy(qref, base, baselen); - memcpy(qref + baselen, "/refs/", 6); - for (cp = ref, dp = qref + baselen + 6; (ch = *cp) != 0; cp++) { - if (needs_quote(ch)) { - *dp++ = '%'; - *dp++ = hex((ch >> 4) & 0xF); - *dp++ = hex(ch & 0xF); - } - else - *dp++ = ch; - } - *dp = 0; - - return qref; -} - static int fetch_ref(struct walker *walker, char *ref, unsigned char *sha1) { - char *url; - char hex[42]; - struct buffer buffer; struct walker_data *data = walker->data; - const char *base = data->alt->base; - struct active_request_slot *slot; - struct slot_results results; - buffer.size = 41; - buffer.posn = 0; - buffer.buffer = hex; - hex[41] = '\0'; - - url = quote_ref_url(base, ref); - slot = get_active_slot(); - slot->results = &results; - curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); - curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); - curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL); - curl_easy_setopt(slot->curl, CURLOPT_URL, url); - if (start_active_slot(slot)) { - run_active_slot(slot); - if (results.curl_result != CURLE_OK) - return error("Couldn't get %s for %s\n%s", - url, ref, curl_errorstr); - } else { - return error("Unable to start request"); - } - - hex[40] = '\0'; - get_sha1_hex(hex, sha1); - return 0; + return http_fetch_ref(data->alt->base, ref, sha1); } static void cleanup(struct walker *walker) diff --git a/http.c b/http.c index 146f62609d..d2c11aee90 100644 --- a/http.c +++ b/http.c @@ -34,31 +34,25 @@ size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, struct buffer *buffer) { size_t size = eltsize * nmemb; - if (size > buffer->size - buffer->posn) - size = buffer->size - buffer->posn; - memcpy(ptr, (char *) buffer->buffer + buffer->posn, size); + if (size > buffer->buf.len - buffer->posn) + size = buffer->buf.len - buffer->posn; + memcpy(ptr, buffer->buf.buf + buffer->posn, size); buffer->posn += size; + return size; } size_t fwrite_buffer(const void *ptr, size_t eltsize, - size_t nmemb, struct buffer *buffer) + size_t nmemb, struct strbuf *buffer) { size_t size = eltsize * nmemb; - if (size > buffer->size - buffer->posn) { - buffer->size = buffer->size * 3 / 2; - if (buffer->size < buffer->posn + size) - buffer->size = buffer->posn + size; - buffer->buffer = xrealloc(buffer->buffer, buffer->size); - } - memcpy((char *) buffer->buffer + buffer->posn, ptr, size); - buffer->posn += size; + strbuf_add(buffer, ptr, size); data_received++; return size; } size_t fwrite_null(const void *ptr, size_t eltsize, - size_t nmemb, struct buffer *buffer) + size_t nmemb, struct strbuf *buffer) { data_received++; return eltsize * nmemb; @@ -370,7 +364,6 @@ struct active_request_slot *get_active_slot(void) slot->finished = NULL; slot->callback_data = NULL; slot->callback_func = NULL; - curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header); curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr); curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL); @@ -508,8 +501,8 @@ void run_active_slot(struct active_request_slot *slot) static void closedown_active_slot(struct active_request_slot *slot) { - active_requests--; - slot->in_use = 0; + active_requests--; + slot->in_use = 0; } void release_active_slot(struct active_request_slot *slot) @@ -530,7 +523,7 @@ void release_active_slot(struct active_request_slot *slot) static void finish_active_slot(struct active_request_slot *slot) { closedown_active_slot(slot); - curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code); + curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code); if (slot->finished != NULL) (*slot->finished) = 1; @@ -541,10 +534,10 @@ static void finish_active_slot(struct active_request_slot *slot) slot->results->http_code = slot->http_code; } - /* Run callback if appropriate */ - if (slot->callback_func != NULL) { - slot->callback_func(slot->callback_data); - } + /* Run callback if appropriate */ + if (slot->callback_func != NULL) { + slot->callback_func(slot->callback_data); + } } void finish_all_active_slots(void) @@ -559,3 +552,85 @@ void finish_all_active_slots(void) slot = slot->next; } } + +static inline int needs_quote(int ch) +{ + if (((ch >= 'A') && (ch <= 'Z')) + || ((ch >= 'a') && (ch <= 'z')) + || ((ch >= '0') && (ch <= '9')) + || (ch == '/') + || (ch == '-') + || (ch == '.')) + return 0; + return 1; +} + +static inline int hex(int v) +{ + if (v < 10) return '0' + v; + else return 'A' + v - 10; +} + +static char *quote_ref_url(const char *base, const char *ref) +{ + const char *cp; + char *dp, *qref; + int len, baselen, ch; + + baselen = strlen(base); + len = baselen + 7; /* "/refs/" + NUL */ + for (cp = ref; (ch = *cp) != 0; cp++, len++) + if (needs_quote(ch)) + len += 2; /* extra two hex plus replacement % */ + qref = xmalloc(len); + memcpy(qref, base, baselen); + memcpy(qref + baselen, "/refs/", 6); + for (cp = ref, dp = qref + baselen + 6; (ch = *cp) != 0; cp++) { + if (needs_quote(ch)) { + *dp++ = '%'; + *dp++ = hex((ch >> 4) & 0xF); + *dp++ = hex(ch & 0xF); + } + else + *dp++ = ch; + } + *dp = 0; + + return qref; +} + +int http_fetch_ref(const char *base, const char *ref, unsigned char *sha1) +{ + char *url; + struct strbuf buffer = STRBUF_INIT; + struct active_request_slot *slot; + struct slot_results results; + int ret; + + url = quote_ref_url(base, ref); + slot = get_active_slot(); + slot->results = &results; + curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); + curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); + curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL); + curl_easy_setopt(slot->curl, CURLOPT_URL, url); + if (start_active_slot(slot)) { + run_active_slot(slot); + if (results.curl_result == CURLE_OK) { + strbuf_rtrim(&buffer); + if (buffer.len == 40) + ret = get_sha1_hex(buffer.buf, sha1); + else + ret = 1; + } else { + ret = error("Couldn't get %s for %s\n%s", + url, ref, curl_errorstr); + } + } else { + ret = error("Unable to start request"); + } + + strbuf_release(&buffer); + free(url); + return ret; +} diff --git a/http.h b/http.h index fe1b0d153b..aeba9301f8 100644 --- a/http.h +++ b/http.h @@ -6,6 +6,8 @@ #include #include +#include "strbuf.h" + #if LIBCURL_VERSION_NUM >= 0x071000 #define USE_CURL_MULTI #define DEFAULT_MAX_REQUESTS 5 @@ -48,18 +50,17 @@ struct active_request_slot struct buffer { - size_t posn; - size_t size; - void *buffer; + struct strbuf buf; + size_t posn; }; /* Curl request read/write callbacks */ extern size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, struct buffer *buffer); extern size_t fwrite_buffer(const void *ptr, size_t eltsize, - size_t nmemb, struct buffer *buffer); + size_t nmemb, struct strbuf *buffer); extern size_t fwrite_null(const void *ptr, size_t eltsize, - size_t nmemb, struct buffer *buffer); + size_t nmemb, struct strbuf *buffer); /* Slot lifecycle functions */ extern struct active_request_slot *get_active_slot(void); @@ -82,4 +83,19 @@ extern int active_requests; extern char curl_errorstr[CURL_ERROR_SIZE]; +static inline int missing__target(int code, int result) +{ + return /* file:// URL -- do we ever use one??? */ + (result == CURLE_FILE_COULDNT_READ_FILE) || + /* http:// and https:// URL */ + (code == 404 && result == CURLE_HTTP_RETURNED_ERROR) || + /* ftp:// URL */ + (code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE) + ; +} + +#define missing_target(a) missing__target((a)->http_code, (a)->curl_result) + +extern int http_fetch_ref(const char *base, const char *ref, unsigned char *sha1); + #endif /* HTTP_H */ diff --git a/merge-file.c b/merge-file.c index 1e031eafe0..2a939c9dd8 100644 --- a/merge-file.c +++ b/merge-file.c @@ -71,7 +71,7 @@ static int generate_common_file(mmfile_t *res, mmfile_t *f1, mmfile_t *f2) res->size = 0; ecb.priv = res; - return xdl_diff(f1, f2, &xpp, &xecfg, &ecb); + return xdi_diff(f1, f2, &xpp, &xecfg, &ecb); } void *merge_file(struct blob *base, struct blob *our, struct blob *their, unsigned long *size) diff --git a/merge-recursive.c b/merge-recursive.c index 2a58dad3f4..33ccc40ecd 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -549,6 +549,10 @@ static void update_file_flags(const unsigned char *sha, void *buf; unsigned long size; + if (S_ISGITLINK(mode)) + die("cannot read object %s '%s': It is a submodule!", + sha1_to_hex(sha), path); + buf = read_sha1_file(sha, &type, &size); if (!buf) die("cannot read object %s '%s'", sha1_to_hex(sha), path); @@ -1463,10 +1467,13 @@ static int process_entry(const char *path, struct stage_data *entry, mfi = merge_file(&o, &a, &b, branch1, branch2); + clean_merge = mfi.clean; if (mfi.clean) update_file(1, mfi.sha, mfi.mode, path); + else if (S_ISGITLINK(mfi.mode)) + output(1, "CONFLICT (submodule): Merge conflict in %s " + "- needs %s", path, sha1_to_hex(b.sha1)); else { - clean_merge = 0; output(1, "CONFLICT (%s): Merge conflict in %s", reason, path); diff --git a/merge-tree.c b/merge-tree.c index 7d4f628444..e08324686c 100644 --- a/merge-tree.c +++ b/merge-tree.c @@ -119,7 +119,7 @@ static void show_diff(struct merge_list *entry) if (!dst.ptr) size = 0; dst.size = size; - xdl_diff(&src, &dst, &xpp, &xecfg, &ecb); + xdi_diff(&src, &dst, &xpp, &xecfg, &ecb); free(src.ptr); free(dst.ptr); } diff --git a/remote.c b/remote.c index 3fb0f99b29..0e006804ef 100644 --- a/remote.c +++ b/remote.c @@ -220,11 +220,11 @@ static int handle_config(const char *key, const char *value) if (!prefixcmp(key, "branch.")) { name = key + 7; subkey = strrchr(name, '.'); - branch = make_branch(name, subkey - name); if (!subkey) return 0; if (!value) return 0; + branch = make_branch(name, subkey - name); if (!strcmp(subkey, ".remote")) { branch->remote_name = xstrdup(value); if (branch == current_branch) diff --git a/t/diff-lib.sh b/t/diff-lib.sh index 4624fe654c..7dc6d7eb1e 100644 --- a/t/diff-lib.sh +++ b/t/diff-lib.sh @@ -21,8 +21,8 @@ compare_diff_raw_z () { # Also we do not check SHA1 hash generation in this test, which # is a job for t0000-basic.sh - tr '\0' '\012' <"$1" | sed -e "$sanitize_diff_raw_z" >.tmp-1 - tr '\0' '\012' <"$2" | sed -e "$sanitize_diff_raw_z" >.tmp-2 + tr '\000' '\012' <"$1" | sed -e "$sanitize_diff_raw_z" >.tmp-1 + tr '\000' '\012' <"$2" | sed -e "$sanitize_diff_raw_z" >.tmp-2 git diff .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2 } diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh index 62bc4bb077..89baebdfa6 100755 --- a/t/t0020-crlf.sh +++ b/t/t0020-crlf.sh @@ -5,7 +5,7 @@ test_description='CRLF conversion' . ./test-lib.sh q_to_nul () { - tr Q '\0' + tr Q '\000' } append_cr () { diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 12285d674b..d175d6b074 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -591,12 +591,12 @@ Qsection.sub=section.val4 Qsection.sub=section.val5Q EOF -git config --null --list | tr '[\000]' 'Q' > result +git config --null --list | tr '\000' 'Q' > result echo >>result test_expect_success '--null --list' 'cmp result expect' -git config --null --get-regexp 'val[0-9]' | tr '[\000]' 'Q' > result +git config --null --get-regexp 'val[0-9]' | tr '\000' 'Q' > result echo >>result test_expect_success '--null --get-regexp' 'cmp result expect' diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh index dc8c369310..98c133db50 100755 --- a/t/t3300-funny-names.sh +++ b/t/t3300-funny-names.sh @@ -54,7 +54,7 @@ echo 'just space no-funny tabs ," (dq) and spaces' >expected test_expect_success 'git ls-files -z with-funny' \ - 'git ls-files -z | tr \\0 \\012 >current && + 'git ls-files -z | tr \\000 \\012 >current && git diff expected current' t1=`git write-tree` @@ -83,11 +83,11 @@ test_expect_success 'git diff-tree with-funny' \ echo 'A tabs ," (dq) and spaces' >expected test_expect_success 'git diff-index -z with-funny' \ - 'git diff-index -z --name-status $t0 | tr \\0 \\012 >current && + 'git diff-index -z --name-status $t0 | tr \\000 \\012 >current && git diff expected current' test_expect_success 'git diff-tree -z with-funny' \ - 'git diff-tree -z --name-status $t0 $t1 | tr \\0 \\012 >current && + 'git diff-tree -z --name-status $t0 $t1 | tr \\000 \\012 >current && git diff expected current' cat > expected <<\EOF diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 907c7f9f6b..74a7eb30f8 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -184,6 +184,12 @@ test_expect_success 'retain authorship when squashing' ' git show HEAD | grep "^Author: Twerp Snog" ' +test_expect_success '-p handles "no changes" gracefully' ' + HEAD=$(git rev-parse HEAD) && + git rebase -i -p HEAD^ && + test $HEAD = $(git rev-parse HEAD) +' + test_expect_success 'preserve merges with -p' ' git checkout -b to-be-preserved master^ && : > unrelated-file && diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh index 6adf9d11d0..d30169fbdc 100755 --- a/t/t4015-diff-whitespace.sh +++ b/t/t4015-diff-whitespace.sh @@ -117,13 +117,213 @@ EOF git diff -b > out test_expect_success 'another test, with -b' 'git diff expect out' - test_expect_success 'check mixed spaces and tabs in indent' ' # This is indented with SP HT SP. echo " foo();" > x && - git diff --check | grep "space before tab" + git diff --check | grep "space before tab in indent" ' +test_expect_success 'check mixed tabs and spaces in indent' ' + + # This is indented with HT SP HT. + echo " foo();" > x && + git diff --check | grep "space before tab in indent" + +' + +test_expect_success 'check with no whitespace errors' ' + + git commit -m "snapshot" && + echo "foo();" > x && + git diff --check + +' + +test_expect_success 'check with trailing whitespace' ' + + echo "foo(); " > x && + ! git diff --check + +' + +test_expect_success 'check with space before tab in indent' ' + + # indent has space followed by hard tab + echo " foo();" > x && + ! git diff --check + +' + +test_expect_success '--check and --exit-code are not exclusive' ' + + git checkout x && + git diff --check --exit-code + +' + +test_expect_success '--check and --quiet are not exclusive' ' + + git diff --check --quiet + +' + +test_expect_success 'check staged with no whitespace errors' ' + + echo "foo();" > x && + git add x && + git diff --cached --check + +' + +test_expect_success 'check staged with trailing whitespace' ' + + echo "foo(); " > x && + git add x && + ! git diff --cached --check + +' + +test_expect_success 'check staged with space before tab in indent' ' + + # indent has space followed by hard tab + echo " foo();" > x && + git add x && + ! git diff --cached --check + +' + +test_expect_success 'check with no whitespace errors (diff-index)' ' + + echo "foo();" > x && + git add x && + git diff-index --check HEAD + +' + +test_expect_success 'check with trailing whitespace (diff-index)' ' + + echo "foo(); " > x && + git add x && + ! git diff-index --check HEAD + +' + +test_expect_success 'check with space before tab in indent (diff-index)' ' + + # indent has space followed by hard tab + echo " foo();" > x && + git add x && + ! git diff-index --check HEAD + +' + +test_expect_success 'check staged with no whitespace errors (diff-index)' ' + + echo "foo();" > x && + git add x && + git diff-index --cached --check HEAD + +' + +test_expect_success 'check staged with trailing whitespace (diff-index)' ' + + echo "foo(); " > x && + git add x && + ! git diff-index --cached --check HEAD + +' + +test_expect_success 'check staged with space before tab in indent (diff-index)' ' + + # indent has space followed by hard tab + echo " foo();" > x && + git add x && + ! git diff-index --cached --check HEAD + +' + +test_expect_success 'check with no whitespace errors (diff-tree)' ' + + echo "foo();" > x && + git commit -m "new commit" x && + git diff-tree --check HEAD^ HEAD + +' + +test_expect_success 'check with trailing whitespace (diff-tree)' ' + + echo "foo(); " > x && + git commit -m "another commit" x && + ! git diff-tree --check HEAD^ HEAD + +' + +test_expect_success 'check with space before tab in indent (diff-tree)' ' + + # indent has space followed by hard tab + echo " foo();" > x && + git commit -m "yet another" x && + ! git diff-tree --check HEAD^ HEAD + +' + +test_expect_success 'check trailing whitespace (trailing-space: off)' ' + + git config core.whitespace "-trailing-space" && + echo "foo (); " > x && + git diff --check + +' + +test_expect_success 'check trailing whitespace (trailing-space: on)' ' + + git config core.whitespace "trailing-space" && + echo "foo (); " > x && + ! git diff --check + +' + +test_expect_success 'check space before tab in indent (space-before-tab: off)' ' + + # indent contains space followed by HT + git config core.whitespace "-space-before-tab" && + echo " foo ();" > x && + git diff --check + +' + +test_expect_success 'check space before tab in indent (space-before-tab: on)' ' + + # indent contains space followed by HT + git config core.whitespace "space-before-tab" && + echo " foo (); " > x && + ! git diff --check + +' + +test_expect_success 'check spaces as indentation (indent-with-non-tab: off)' ' + + git config core.whitespace "-indent-with-non-tab" + echo " foo ();" > x && + git diff --check + +' + +test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' ' + + git config core.whitespace "indent-with-non-tab" && + echo " foo ();" > x && + ! git diff --check + +' + +test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: on)' ' + + git config core.whitespace "indent-with-non-tab" && + echo " foo ();" > x && + ! git diff --check + +' test_done diff --git a/t/t4017-diff-retval.sh b/t/t4017-diff-retval.sh index 68731908be..dc0b7126cc 100755 --- a/t/t4017-diff-retval.sh +++ b/t/t4017-diff-retval.sh @@ -76,4 +76,33 @@ test_expect_success 'git diff-index --cached HEAD' ' } ' +test_expect_success '--check --exit-code returns 0 for no difference' ' + + git diff --check --exit-code + +' + +test_expect_success '--check --exit-code returns 1 for a clean difference' ' + + echo "good" > a && + git diff --check --exit-code + test $? = 1 + +' + +test_expect_success '--check --exit-code returns 3 for a dirty difference' ' + + echo "bad " >> a && + git diff --check --exit-code + test $? = 3 + +' + +test_expect_success '--check with --no-pager returns 2 for dirty difference' ' + + git --no-pager diff --check + test $? = 2 + +' + test_done diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh index ed3bd5b3fe..888293361d 100755 --- a/t/t4020-diff-external.sh +++ b/t/t4020-diff-external.sh @@ -99,7 +99,7 @@ test_expect_success 'no diff with -diff' ' git diff | grep Binary ' -echo NULZbetweenZwords | tr Z '\0' > file +echo NULZbetweenZwords | tr Z '\000' > file test_expect_success 'force diff with "diff"' ' echo >.gitattributes "file diff" && diff --git a/t/t4024-diff-optimize-common.sh b/t/t4024-diff-optimize-common.sh new file mode 100755 index 0000000000..20fe87b7dd --- /dev/null +++ b/t/t4024-diff-optimize-common.sh @@ -0,0 +1,69 @@ +#!/bin/sh + +test_description='common tail optimization' + +. ./test-lib.sh + +z=zzzzzzzz ;# 8 +z="$z$z$z$z$z$z$z$z" ;# 64 +z="$z$z$z$z$z$z$z$z" ;# 512 +z="$z$z$z$z" ;# 2048 +z2047=$(expr "$z" : '.\(.*\)') ; #2047 + +test_expect_success setup ' + + echo "a$z2047" >file-a && + echo "b" >file-b && + echo "$z2047" >>file-b && + echo "c$z2047" | tr -d "\012" >file-c && + echo "d" >file-d && + echo "$z2047" | tr -d "\012" >>file-d && + + git add file-a file-b file-c file-d && + + echo "A$z2047" >file-a && + echo "B" >file-b && + echo "$z2047" >>file-b && + echo "C$z2047" | tr -d "\012" >file-c && + echo "D" >file-d && + echo "$z2047" | tr -d "\012" >>file-d + +' + +cat >expect <<\EOF +diff --git a/file-a b/file-a +--- a/file-a ++++ b/file-a +@@ -1 +1 @@ +-aZ ++AZ +diff --git a/file-b b/file-b +--- a/file-b ++++ b/file-b +@@ -1 +1 @@ +-b ++B +diff --git a/file-c b/file-c +--- a/file-c ++++ b/file-c +@@ -1 +1 @@ +-cZ +\ No newline at end of file ++CZ +\ No newline at end of file +diff --git a/file-d b/file-d +--- a/file-d ++++ b/file-d +@@ -1 +1 @@ +-d ++D +EOF + +test_expect_success 'diff -U0' ' + + git diff -U0 | sed -e "/^index/d" -e "s/$z2047/Z/g" >actual && + diff -u expect actual + +' + +test_done diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh index 011126f336..74f06ec730 100755 --- a/t/t4103-apply-binary.sh +++ b/t/t4103-apply-binary.sh @@ -24,10 +24,10 @@ git update-index --add --remove file1 file2 file4 git-commit -m 'Initial Version' 2>/dev/null git-checkout -b binary -tr 'x' '\0' file3 +tr 'x' '\000' file3 cat file3 >file4 git add file2 -tr '\0' 'v' file1 +tr '\000' 'v' file1 rm -f file2 git update-index --add --remove file1 file2 file3 file4 git-commit -m 'Second Version' diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh index 359f347b2c..28208d5bad 100755 --- a/t/t4116-apply-reverse.sh +++ b/t/t4116-apply-reverse.sh @@ -12,14 +12,14 @@ test_description='git apply in reverse test_expect_success setup ' for i in a b c d e f g h i j k l m n; do echo $i; done >file1 && - tr "[ijk]" '\''[\0\1\2]'\'' file2 && + tr "ijk" '\''\000\001\002'\'' file2 && git add file1 file2 && git commit -m initial && git tag initial && for i in a b c g h i J K L m o n p q; do echo $i; done >file1 && - tr "[mon]" '\''[\0\1\2]'\'' file2 && + tr "mon" '\''\000\001\002'\'' file2 && git commit -a -m second && git tag second && diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh index cfcdb69dc8..eeff3c9c07 100755 --- a/t/t4200-rerere.sh +++ b/t/t4200-rerere.sh @@ -129,7 +129,7 @@ test_expect_success 'rerere kicked in' "! grep ======= a1" test_expect_success 'rerere prefers first change' 'git diff a1 expect' rm $rr/postimage -echo "$sha1 a1" | tr '\012' '\0' > .git/rr-cache/MERGE_RR +echo "$sha1 a1" | tr '\012' '\000' > .git/rr-cache/MERGE_RR test_expect_success 'rerere clear' 'git rerere clear' diff --git a/t/t5702-clone-options.sh b/t/t5702-clone-options.sh new file mode 100755 index 0000000000..328e4d9a33 --- /dev/null +++ b/t/t5702-clone-options.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +test_description='basic clone options' +. ./test-lib.sh + +test_expect_success 'setup' ' + + mkdir parent && + (cd parent && git init && + echo one >file && git add file && + git commit -m one) + +' + +test_expect_success 'clone -o' ' + + git clone -o foo parent clone-o && + (cd clone-o && git rev-parse --verify refs/remotes/foo/master) + +' + +test_done diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index ccf8dbd341..afe5bbc849 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -311,4 +311,21 @@ test_expect_success 'same tree (merge and amend merge)' ' ' +test_expect_success 'amend using the message from another commit' ' + + git reset --hard && + test_tick && + git commit --allow-empty -m "old commit" && + old=$(git rev-parse --verify HEAD) && + test_tick && + git commit --allow-empty -m "new commit" && + new=$(git rev-parse --verify HEAD) && + test_tick && + git commit --allow-empty --amend -C "$old" && + git show --pretty="format:%ad %s" "$old" >expected && + git show --pretty="format:%ad %s" HEAD >actual && + diff -u expected actual + +' + test_done diff --git a/t/t9103-git-svn-tracked-directory-removed.sh b/t/t9103-git-svn-tracked-directory-removed.sh new file mode 100755 index 0000000000..0f0b0fd2c6 --- /dev/null +++ b/t/t9103-git-svn-tracked-directory-removed.sh @@ -0,0 +1,39 @@ +#!/bin/sh +# +# Copyright (c) 2007 Eric Wong +# + +test_description='git-svn tracking removed top-level path' +. ./lib-git-svn.sh + +test_expect_success 'make history for tracking' ' + mkdir import && + mkdir import/trunk && + echo hello >> import/trunk/README && + svn import -m initial import $svnrepo && + rm -rf import && + svn co $svnrepo/trunk trunk && + echo bye bye >> trunk/README && + svn rm -m "gone" $svnrepo/trunk && + rm -rf trunk && + mkdir trunk && + echo "new" > trunk/FOLLOWME && + svn import -m "new trunk" trunk $svnrepo/trunk +' + +test_expect_success 'clone repo with git' ' + git svn clone -s $svnrepo x && + test -f x/FOLLOWME && + test ! -f x/README +' + +test_expect_success 'make sure r2 still has old file' ' + cd x && + test -n "$(git svn find-rev r1)" && + git reset --hard $(git svn find-rev r1) && + test -f README && + test ! -f FOLLOWME && + test x$(git svn find-rev r2) = x +' + +test_done diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index 35fff3ddba..796cd7dba0 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -557,6 +557,31 @@ test_expect_success \ 'gitweb_run "p=.git;a=tree;opt=--no-merges"' test_debug 'cat gitweb.log' +# ---------------------------------------------------------------------- +# testing config_to_multi / cloneurl + +test_expect_success \ + 'URL: no project URLs, no base URL' \ + 'gitweb_run "p=.git;a=summary"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'URL: project URLs via gitweb.url' \ + 'git config --add gitweb.url git://example.com/git/trash.git && + git config --add gitweb.url http://example.com/git/trash.git && + gitweb_run "p=.git;a=summary"' +test_debug 'cat gitweb.log' + +cat >.git/cloneurl <<\EOF +git://example.com/git/trash.git +http://example.com/git/trash.git +EOF + +test_expect_success \ + 'URL: project URLs via cloneurl file' \ + 'gitweb_run "p=.git;a=summary"' +test_debug 'cat gitweb.log' + # ---------------------------------------------------------------------- # gitweb config and repo config diff --git a/test-sha1.sh b/test-sha1.sh index 640856af5a..bf526c8f5e 100755 --- a/test-sha1.sh +++ b/test-sha1.sh @@ -10,7 +10,7 @@ do { test -z "$pfx" || echo "$pfx" dd if=/dev/zero bs=1048576 count=$cnt 2>/dev/null | - tr '[\0]' '[g]' + tr '\000' 'g' } | ./test-sha1 $cnt ` if test "$expect" = "$actual" @@ -55,7 +55,7 @@ do { test -z "$pfx" || echo "$pfx" dd if=/dev/zero bs=1048576 count=$cnt 2>/dev/null | - tr '[\0]' '[g]' + tr '\000' 'g' } | sha1sum | sed -e 's/ .*//' ` diff --git a/transport.c b/transport.c index 58e66f6c11..4e151a9e87 100644 --- a/transport.c +++ b/transport.c @@ -426,22 +426,9 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons return !!err; } -static int missing__target(int code, int result) -{ - return /* file:// URL -- do we ever use one??? */ - (result == CURLE_FILE_COULDNT_READ_FILE) || - /* http:// and https:// URL */ - (code == 404 && result == CURLE_HTTP_RETURNED_ERROR) || - /* ftp:// URL */ - (code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE) - ; -} - -#define missing_target(a) missing__target((a)->http_code, (a)->curl_result) - static struct ref *get_refs_via_curl(struct transport *transport) { - struct buffer buffer; + struct strbuf buffer = STRBUF_INIT; char *data, *start, *mid; char *ref_name; char *refs_url; @@ -454,11 +441,6 @@ static struct ref *get_refs_via_curl(struct transport *transport) struct ref *ref = NULL; struct ref *last_ref = NULL; - data = xmalloc(4096); - buffer.size = 4096; - buffer.posn = 0; - buffer.buffer = data; - refs_url = xmalloc(strlen(transport->url) + 11); sprintf(refs_url, "%s/info/refs", transport->url); @@ -477,27 +459,26 @@ static struct ref *get_refs_via_curl(struct transport *transport) if (start_active_slot(slot)) { run_active_slot(slot); if (results.curl_result != CURLE_OK) { + strbuf_release(&buffer); if (missing_target(&results)) { - free(buffer.buffer); return NULL; } else { - free(buffer.buffer); error("%s", curl_errorstr); return NULL; } } } else { - free(buffer.buffer); + strbuf_release(&buffer); error("Unable to start request"); return NULL; } http_cleanup(); - data = buffer.buffer; + data = buffer.buf; start = NULL; mid = data; - while (i < buffer.posn) { + while (i < buffer.len) { if (!start) start = &data[i]; if (data[i] == '\t') @@ -520,7 +501,7 @@ static struct ref *get_refs_via_curl(struct transport *transport) i++; } - free(buffer.buffer); + strbuf_release(&buffer); return refs; } diff --git a/unpack-trees.c b/unpack-trees.c index e9eb795d64..aa2513ed79 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -590,7 +590,7 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old, * a match. */ if (same(old, merge)) { - *merge = *old; + memcpy(merge, old, offsetof(struct cache_entry, name)); } else { verify_uptodate(old, o); invalidate_ce_path(old); diff --git a/walker.c b/walker.c index 397b80de9e..adc3e80ce1 100644 --- a/walker.c +++ b/walker.c @@ -274,7 +274,7 @@ int walker_fetch(struct walker *walker, int targets, char **target, for (i = 0; i < targets; i++) { if (interpret_target(walker, target[i], &sha1[20 * i])) { - error("Could not interpret %s as something to pull", target[i]); + error("Could not interpret response from server '%s' as something to pull", target[i]); goto unlock_and_fail; } if (process(walker, lookup_unknown_object(&sha1[20 * i]))) diff --git a/ws.c b/ws.c index 52c10caf35..d09b9df89a 100644 --- a/ws.c +++ b/ws.c @@ -94,3 +94,110 @@ unsigned whitespace_rule(const char *pathname) return whitespace_rule_cfg; } } + +/* The returned string should be freed by the caller. */ +char *whitespace_error_string(unsigned ws) +{ + struct strbuf err; + strbuf_init(&err, 0); + if (ws & WS_TRAILING_SPACE) + strbuf_addstr(&err, "trailing whitespace"); + if (ws & WS_SPACE_BEFORE_TAB) { + if (err.len) + strbuf_addstr(&err, ", "); + strbuf_addstr(&err, "space before tab in indent"); + } + if (ws & WS_INDENT_WITH_NON_TAB) { + if (err.len) + strbuf_addstr(&err, ", "); + strbuf_addstr(&err, "indent with spaces"); + } + return strbuf_detach(&err, NULL); +} + +/* If stream is non-NULL, emits the line after checking. */ +unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule, + FILE *stream, const char *set, + const char *reset, const char *ws) +{ + unsigned result = 0; + int written = 0; + int trailing_whitespace = -1; + int trailing_newline = 0; + int i; + + /* Logic is simpler if we temporarily ignore the trailing newline. */ + if (len > 0 && line[len - 1] == '\n') { + trailing_newline = 1; + len--; + } + + /* Check for trailing whitespace. */ + if (ws_rule & WS_TRAILING_SPACE) { + for (i = len - 1; i >= 0; i--) { + if (isspace(line[i])) { + trailing_whitespace = i; + result |= WS_TRAILING_SPACE; + } + else + break; + } + } + + /* Check for space before tab in initial indent. */ + for (i = 0; i < len; i++) { + if (line[i] == ' ') + continue; + if (line[i] != '\t') + break; + if ((ws_rule & WS_SPACE_BEFORE_TAB) && written < i) { + result |= WS_SPACE_BEFORE_TAB; + if (stream) { + fputs(ws, stream); + fwrite(line + written, i - written, 1, stream); + fputs(reset, stream); + } + } else if (stream) + fwrite(line + written, i - written, 1, stream); + if (stream) + fwrite(line + i, 1, 1, stream); + written = i + 1; + } + + /* Check for indent using non-tab. */ + if ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= 8) { + result |= WS_INDENT_WITH_NON_TAB; + if (stream) { + fputs(ws, stream); + fwrite(line + written, i - written, 1, stream); + fputs(reset, stream); + } + written = i; + } + + if (stream) { + /* Now the rest of the line starts at written. + * The non-highlighted part ends at trailing_whitespace. */ + if (trailing_whitespace == -1) + trailing_whitespace = len; + + /* Emit non-highlighted (middle) segment. */ + if (trailing_whitespace - written > 0) { + fputs(set, stream); + fwrite(line + written, + trailing_whitespace - written, 1, stream); + fputs(reset, stream); + } + + /* Highlight errors in trailing whitespace. */ + if (trailing_whitespace != len) { + fputs(ws, stream); + fwrite(line + trailing_whitespace, + len - trailing_whitespace, 1, stream); + fputs(reset, stream); + } + if (trailing_newline) + fputc('\n', stream); + } + return result; +} diff --git a/wt-status.c b/wt-status.c index 9a2d3c65b7..a772b936b9 100644 --- a/wt-status.c +++ b/wt-status.c @@ -383,6 +383,8 @@ void wt_status_print(struct wt_status *s) if (!s->commitable) { if (s->amend) fprintf(s->fp, "# No changes\n"); + else if (s->nowarn) + ; /* nothing */ else if (s->workdir_dirty) printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"); else if (s->workdir_untracked) diff --git a/wt-status.h b/wt-status.h index 63d50f2871..02afaa60ee 100644 --- a/wt-status.h +++ b/wt-status.h @@ -17,6 +17,7 @@ struct wt_status { int verbose; int amend; int untracked; + int nowarn; /* These are computed during processing of the individual sections */ int commitable; int workdir_dirty; diff --git a/xdiff-interface.c b/xdiff-interface.c index be866d12d3..9ee877c6f4 100644 --- a/xdiff-interface.c +++ b/xdiff-interface.c @@ -103,6 +103,41 @@ int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf) return 0; } +/* + * Trim down common substring at the end of the buffers, + * but leave at least ctx lines at the end. + */ +static void trim_common_tail(mmfile_t *a, mmfile_t *b, long ctx) +{ + const int blk = 1024; + long trimmed = 0, recovered = 0; + char *ap = a->ptr + a->size; + char *bp = b->ptr + b->size; + long smaller = (a->size < b->size) ? a->size : b->size; + + while (blk + trimmed <= smaller && !memcmp(ap - blk, bp - blk, blk)) { + trimmed += blk; + ap -= blk; + bp -= blk; + } + + while (recovered < trimmed && 0 <= ctx) + if (ap[recovered++] == '\n') + ctx--; + a->size -= (trimmed - recovered); + b->size -= (trimmed - recovered); +} + +int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *xecb) +{ + mmfile_t a = *mf1; + mmfile_t b = *mf2; + + trim_common_tail(&a, &b, xecfg->ctxlen); + + return xdl_diff(&a, &b, xpp, xecfg, xecb); +} + int read_mmfile(mmfile_t *ptr, const char *filename) { struct stat st; diff --git a/xdiff-interface.h b/xdiff-interface.h index fb742dbb6b..f7f791d96b 100644 --- a/xdiff-interface.h +++ b/xdiff-interface.h @@ -13,6 +13,7 @@ struct xdiff_emit_state { unsigned long remainder_size; }; +int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb); int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf); int parse_hunk_header(char *line, int len, int *ob, int *on,