diff options
220 files changed, 7531 insertions, 1826 deletions
diff --git a/.gitignore b/.gitignore index 41c0b20a76..10808e3a73 100644 --- a/.gitignore +++ b/.gitignore @@ -105,6 +105,7 @@ git-reflog git-relink git-remote git-repack +git-replace git-repo-config git-request-pull git-rerere diff --git a/Documentation/RelNotes-1.6.5.txt b/Documentation/RelNotes-1.6.5.txt new file mode 100644 index 0000000000..84a84519d4 --- /dev/null +++ b/Documentation/RelNotes-1.6.5.txt @@ -0,0 +1,109 @@ +GIT v1.6.5 Release Notes +======================== + +In git 1.7.0, which is planned to be the release after 1.6.5, "git push" +into a branch that is currently checked out will be refused by default. + +You can choose what should happen upon such a push by setting the +configuration variable receive.denyCurrentBranch in the receiving +repository. + +Also, "git push $there :$killed" to delete the branch $killed in a remote +repository $there, when $killed branch is the current branch pointed at by +its HEAD, will be refused by default. + +You can choose what should happen upon such a push by setting the +configuration variable receive.denyDeleteCurrent in the receiving +repository. + +To ease the transition plan, the receiving repository of such a +push running this release will issue a big warning when the +configuration variable is missing. Please refer to: + + http://git.or.cz/gitwiki/GitFaq#non-bare + http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007 + +for more details on the reason why this change is needed and the +transition plan. + +Updates since v1.6.4 +-------------------- + +(subsystems) + + * various updates to git-svn and gitweb. + +(portability) + + * more improvements on mingw port. + +(performance) + + * On major platforms, the system can be compiled to use with Linus's + block-sha1 implementation of the SHA-1 hash algorithm, which + outperforms the default fallback implementation we borrowed from + Mozzilla. + +(usability, bells and whistles) + + * refs/replace/ hierarchy is designed to be usable as a replacement + of the "grafts" mechanism, with the added advantage that it can be + transferred across repositories. + + * "git am" learned to optionally ignore whitespace differences. + + * "git am" handles input e-mail files that has CRLF line endings sensibly. + + * "git commit --dry-run $args" is a new recommended way to ask "what would + happen if I try to commit with these arguments." + + * "git cvsimport" now supports password-protected pserver access. + + * "git fast-export" learned --no-data option that can be useful when + reordering commits and trees without touching the contents of + blobs. + + * "git init" learned to mkdir/chdir into a directory when given an + extra argument (i.e. "git init this"). + + * "git instaweb" optionally can use mongoose as the web server. + + * "git log --decorate" can optionally be told with --decorate=full to + give the reference name in full. + + * "git push" can be told to be --quiet. + + * informational output from "git reset" that lists the locally modified + paths is made consistent with that of "git checkout $another_branch". + + * "git status" gives more descriptive output for unmerged paths. + + * "git submodule" learned to give submodule name to scripts run with + "foreach" subcommand. + + * various subcommands to "git submodule" learned --recursive option. + + * "git submodule summary" learned --files option to compare the work + tree vs the commit bound at submodule path, instead of comparing + the index. + +(developers) + + * With GIT_TEST_OPTS="--root=/p/a/t/h", tests can be run outside the + source directory; using tmpfs may give faster turnaround. + + +Fixes since v1.6.4 +------------------ + +# All of the fixes in v1.6.4.X maintenance series are included in this +# release, unless otherwise noted. + +# Here are fixes that this release has, but have not been backported to +# v1.6.4.X series. + +-- +exec >/var/tmp/1 +O=v1.6.4.1-266-g235db15 +echo O=$(git describe master) +git shortlog --no-merges $O..master --not maint diff --git a/Documentation/config.txt b/Documentation/config.txt index 2632c5149e..5256c7fb81 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -461,6 +461,14 @@ it will be treated as a shell command. For example, defining executed from the top-level directory of a repository, which may not necessarily be the current directory. +apply.ignorewhitespace:: + When set to 'change', tells 'git-apply' to ignore changes in + whitespace, in the same way as the '--ignore-space-change' + option. + When set to one of: no, none, never, false tells 'git-apply' to + respect all whitespace differences. + See linkgit:git-apply[1]. + apply.whitespace:: Tells 'git-apply' how to handle whitespaces, in the same way as the '--whitespace' option. See linkgit:git-apply[1]. diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt index 32e689b2bf..fcacc94650 100644 --- a/Documentation/git-am.txt +++ b/Documentation/git-am.txt @@ -11,7 +11,7 @@ SYNOPSIS [verse] 'git am' [--signoff] [--keep] [--utf8 | --no-utf8] [--3way] [--interactive] [--committer-date-is-author-date] - [--ignore-date] + [--ignore-date] [--ignore-space-change | --ignore-whitespace] [--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>] [--reject] [-q | --quiet] [<mbox> | <Maildir>...] @@ -65,6 +65,9 @@ default. You can use `--no-utf8` to override this. it is supposed to apply to and we have those blobs available locally. +--ignore-date:: +--ignore-space-change:: +--ignore-whitespace:: --whitespace=<option>:: -C<n>:: -p<n>:: diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt index 735374d7df..5ee8c91f2d 100644 --- a/Documentation/git-apply.txt +++ b/Documentation/git-apply.txt @@ -13,6 +13,7 @@ SYNOPSIS [--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse] [--allow-binary-replacement | --binary] [--reject] [-z] [-pNUM] [-CNUM] [--inaccurate-eof] [--recount] [--cached] + [--ignore-space-change | --ignore-whitespace ] [--whitespace=<nowarn|warn|fix|error|error-all>] [--exclude=PATH] [--include=PATH] [--directory=<root>] [--verbose] [<patch>...] @@ -149,6 +150,14 @@ patch to each path is used. A patch to a path that does not match any include/exclude pattern is used by default if there is no include pattern on the command line, and ignored if there is any include pattern. +--ignore-space-change:: +--ignore-whitespace:: + When applying a patch, ignore changes in whitespace in context + lines if necessary. + Context lines will preserve their whitespace, and they will not + undergo whitespace fixing regardless of the value of the + `--whitespace` option. New lines will still be fixed, though. + --whitespace=<action>:: When applying a patch, detect a new or modified line that has whitespace errors. What are considered whitespace errors is @@ -205,6 +214,10 @@ running `git apply --directory=modules/git-gui`. Configuration ------------- +apply.ignorewhitespace:: + Set to 'change' if you want changes in whitespace to be ignored by default. + Set to one of: no, none, never, false if you want changes in + whitespace to be significant. apply.whitespace:: When no `--whitespace` flag is given from the command line, this configuration item is used as the default. diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index ae201deb7a..aad71dc59a 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -76,6 +76,7 @@ OPTIONS based sha1 expressions such as "<branchname>@\{yesterday}". -f:: +--force:: Reset <branchname> to <startpoint> if <branchname> exists already. Without `-f` 'git-branch' refuses to change an existing branch. @@ -209,6 +210,12 @@ but different purposes: - `--no-merged` is used to find branches which are candidates for merging into HEAD, since those branches are not fully contained by HEAD. +SEE ALSO +-------- +linkgit:git-check-ref-format[1], +linkgit:git-fetch[1], +linkgit:git-remote[1]. + Author ------ Written by Linus Torvalds <torvalds@osdl.org> and Junio C Hamano <gitster@pobox.com> diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index ad4b31e892..b1314b5614 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -45,9 +45,11 @@ file can be discarded to recreate the original conflicted merge result. OPTIONS ------- -q:: +--quiet:: Quiet, suppress feedback messages. -f:: +--force:: When switching branches, proceed even if the index or the working tree differs from HEAD. This is used to throw away local changes. diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt index ae8938b2de..9d291bdd26 100644 --- a/Documentation/git-clean.txt +++ b/Documentation/git-clean.txt @@ -32,6 +32,7 @@ OPTIONS if you really want to remove such a directory. -f:: +--force:: If the git configuration specifies clean.requireForce as true, 'git-clean' will refuse to run unless given -f or -n. diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 1709a2dbd0..2e0785e1de 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -12,7 +12,7 @@ SYNOPSIS 'git clone' [--template=<template_directory>] [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror] [-o <name>] [-u <upload-pack>] [--reference <repository>] - [--depth <depth>] [--] <repository> [<directory>] + [--depth <depth>] [--recursive] [--] <repository> [<directory>] DESCRIPTION ----------- @@ -147,6 +147,14 @@ objects from the source repository into a pack in the cloned repository. with a long history, and would want to send in fixes as patches. +--recursive:: + After the clone is created, initialize all submodules within, + using their default settings. This is equivalent to running + 'git submodule update --init --recursive' immediately after + the clone is finished. This option is ignored if the cloned + repository does not have a worktree/checkout (i.e. if any of + `--no-checkout`/`-n`, `--bare`, or `--mirror` is given) + <repository>:: The (possibly remote) repository to clone from. See the <<URLS,URLS>> section below for more information on specifying diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index b5d81be7ec..64f94cfe12 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -8,8 +8,8 @@ git-commit - Record changes to the repository SYNOPSIS -------- [verse] -'git commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend] - [(-c | -C) <commit>] [-F <file> | -m <msg>] +'git commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend] [--dry-run] + [(-c | -C) <commit>] [-F <file> | -m <msg>] [--dry-run] [--allow-empty] [--no-verify] [-e] [--author=<author>] [--cleanup=<mode>] [--] [[-i | -o ]<file>...] @@ -42,10 +42,9 @@ The content to be added can be specified in several ways: by one which files should be part of the commit, before finalizing the operation. Currently, this is done by invoking 'git-add --interactive'. -The 'git-status' command can be used to obtain a +The `--dry-run` option can be used to obtain a summary of what is included by any of the above for the next -commit by giving the same set of parameters you would give to -this command. +commit by giving the same set of parameters (options and paths). If you make a commit and then find a mistake immediately after that, you can recover from it with 'git-reset'. @@ -70,6 +69,12 @@ OPTIONS Like '-C', but with '-c' the editor is invoked, so that the user can further edit the commit message. +--dry-run:: + Do not actually make a commit, but show the list of paths + with updates in the index, paths with changes in the work tree, + and paths that are untracked, similar to the one that is given + in the commit log editor. + -F <file>:: --file=<file>:: Take the commit message from the given file. Use '-' to @@ -198,6 +203,11 @@ specified. --quiet:: Suppress commit summary message. +--dry-run:: + Do not create a commit, but show a list of paths that are + to be committed, paths with local changes that will be left + uncommitted and paths that are untracked. + \--:: Do not interpret any more arguments as options. diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt index af2328d401..75b06f33e7 100644 --- a/Documentation/git-fast-export.txt +++ b/Documentation/git-fast-export.txt @@ -82,6 +82,14 @@ marks the same across runs. allow that. So fake a tagger to be able to fast-import the output. +--no-data:: + Skip output of blob objects and instead refer to blobs via + their original SHA-1 hash. This is useful when rewriting the + directory structure or history of a repository without + touching the contents of individual files. Note that the + resulting stream can only be used by a repository which + already contains the necessary objects. + [git-rev-list-args...]:: A list of arguments, acceptable to 'git-rev-parse' and 'git-rev-list', that specifies the specific objects and references diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt index b753c9d76f..8c700200f5 100644 --- a/Documentation/git-grep.txt +++ b/Documentation/git-grep.txt @@ -17,6 +17,7 @@ SYNOPSIS [-l | --files-with-matches] [-L | --files-without-match] [-z | --null] [-c | --count] [--all-match] + [--max-depth <depth>] [--color | --no-color] [-A <post-context>] [-B <pre-context>] [-C <context>] [-f <file>] [-e] <pattern> @@ -47,6 +48,10 @@ OPTIONS -I:: Don't match the pattern in binary files. +--max-depth <depth>:: + For each pathspec given on command line, descend at most <depth> + levels of directories. A negative value means no limit. + -w:: --word-regexp:: Match the pattern only at word boundary (either begin at the diff --git a/Documentation/git-init-db.txt b/Documentation/git-init-db.txt index 1fd0ff2610..eba3cb4998 100644 --- a/Documentation/git-init-db.txt +++ b/Documentation/git-init-db.txt @@ -8,7 +8,7 @@ git-init-db - Creates an empty git repository SYNOPSIS -------- -'git init-db' [-q | --quiet] [--template=<template_directory>] [--shared[=<permissions>]] +'git init-db' [-q | --quiet] [--bare] [--template=<template_directory>] [--shared[=<permissions>]] DESCRIPTION diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt index 7151d12f34..f081b24d9d 100644 --- a/Documentation/git-init.txt +++ b/Documentation/git-init.txt @@ -8,7 +8,7 @@ git-init - Create an empty git repository or reinitialize an existing one SYNOPSIS -------- -'git init' [-q | --quiet] [--bare] [--template=<template_directory>] [--shared[=<permissions>]] +'git init' [-q | --quiet] [--bare] [--template=<template_directory>] [--shared[=<permissions>]] [directory] OPTIONS @@ -74,6 +74,9 @@ By default, the configuration flag receive.denyNonFastForwards is enabled in shared repositories, so that you cannot force a non fast-forwarding push into it. +If you name a (possibly non-existent) directory at the end of the command +line, the command is run inside the directory (possibly after creating it). + -- diff --git a/Documentation/git-instaweb.txt b/Documentation/git-instaweb.txt index 22da21a54f..0771f25443 100644 --- a/Documentation/git-instaweb.txt +++ b/Documentation/git-instaweb.txt @@ -29,7 +29,7 @@ OPTIONS The HTTP daemon command-line that will be executed. Command-line options may be specified here, and the configuration file will be added at the end of the command-line. - Currently lighttpd, apache2 and webrick are supported. + Currently apache2, lighttpd, mongoose and webrick are supported. (Default: lighttpd) -m:: diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index 057a021eb5..021066e95d 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -44,7 +44,7 @@ OPTIONS -o:: --others:: - Show other files in the output + Show other (i.e. untracked) files in the output -i:: --ignored:: diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt index 767486c770..ce5b369985 100644 --- a/Documentation/git-merge-base.txt +++ b/Documentation/git-merge-base.txt @@ -8,12 +8,12 @@ git-merge-base - Find as good common ancestors as possible for a merge SYNOPSIS -------- -'git merge-base' [--all] <commit> <commit>... +'git merge-base' [-a|--all] <commit> <commit>... DESCRIPTION ----------- -'git-merge-base' finds best common ancestor(s) between two commits to use +'git merge-base' finds best common ancestor(s) between two commits to use in a three-way merge. One common ancestor is 'better' than another common ancestor if the latter is an ancestor of the former. A common ancestor that does not have any better common ancestor is a 'best common @@ -27,8 +27,13 @@ commits on the command line. As the most common special case, specifying only two commits on the command line means computing the merge base between the given two commits. +As a consequence, the 'merge base' is not necessarily contained in each of the +commit arguments if more than two commits are specified. This is different +from linkgit:git-show-branch[1] when used with the `--merge-base` option. + OPTIONS ------- +-a:: --all:: Output all merge bases for the commits, instead of just one. diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt index 9c5660275b..bdcb58526e 100644 --- a/Documentation/git-mv.txt +++ b/Documentation/git-mv.txt @@ -28,6 +28,7 @@ committed. OPTIONS ------- -f:: +--force:: Force renaming or moving of a file even if the target exists -k:: Skip move or rename actions which would lead to an error diff --git a/Documentation/git-prune-packed.txt b/Documentation/git-prune-packed.txt index b5f26cee13..abfc6b6ead 100644 --- a/Documentation/git-prune-packed.txt +++ b/Documentation/git-prune-packed.txt @@ -8,7 +8,7 @@ git-prune-packed - Remove extra objects that are already in pack files SYNOPSIS -------- -'git prune-packed' [-n] [-q] +'git prune-packed' [-n|--dry-run] [-q|--quiet] DESCRIPTION @@ -28,10 +28,12 @@ disk storage, etc. OPTIONS ------- -n:: +--dry-run:: Don't actually remove any objects, only show those that would have been removed. -q:: +--quiet:: Squelch the progress indicator. Author diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt index 7160fa1536..4a932b08c6 100644 --- a/Documentation/git-read-tree.txt +++ b/Documentation/git-read-tree.txt @@ -8,7 +8,10 @@ git-read-tree - Reads tree information into the index SYNOPSIS -------- -'git read-tree' (<tree-ish> | [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]]) +'git read-tree' [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] + [-u [--exclude-per-directory=<gitignore>] | -i]] + [--index-output=<file>] + <tree-ish1> [<tree-ish2> [<tree-ish3>]] DESCRIPTION diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index db1b71d248..0aefc34d0d 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -268,8 +268,9 @@ OPTIONS exit with the message "Current branch is up to date" in such a situation. +--ignore-whitespace:: --whitespace=<option>:: - This flag is passed to the 'git-apply' program + These flag are passed to the 'git-apply' program (see linkgit:git-apply[1]) that applies the patch. Incompatible with the --interactive option. diff --git a/Documentation/git-replace.txt b/Documentation/git-replace.txt new file mode 100644 index 0000000000..915cb77b29 --- /dev/null +++ b/Documentation/git-replace.txt @@ -0,0 +1,71 @@ +git-replace(1) +============== + +NAME +---- +git-replace - Create, list, delete refs to replace objects + +SYNOPSIS +-------- +[verse] +'git replace' [-f] <object> <replacement> +'git replace' -d <object>... +'git replace' -l [<pattern>] + +DESCRIPTION +----------- +Adds a 'replace' reference in `.git/refs/replace/` + +The name of the 'replace' reference is the SHA1 of the object that is +replaced. The content of the replace reference is the SHA1 of the +replacement object. + +Unless `-f` is given, the replace reference must not yet exist in +`.git/refs/replace/` directory. + +OPTIONS +------- +-f:: + If an existing replace ref for the same object exists, it will + be overwritten (instead of failing). + +-d:: + Delete existing replace refs for the given objects. + +-l <pattern>:: + List replace refs for objects that match the given pattern (or + all if no pattern is given). + Typing "git replace" without arguments, also lists all replace + refs. + +BUGS +---- +Comparing blobs or trees that have been replaced with those that +replace them will not work properly. And using 'git reset --hard' to +go back to a replaced commit will move the branch to the replacement +commit instead of the replaced commit. + +There may be other problems when using 'git rev-list' related to +pending objects. And of course things may break if an object of one +type is replaced by an object of another type (for example a blob +replaced by a commit). + +SEE ALSO +-------- +linkgit:git-tag[1] +linkgit:git-branch[1] + +Author +------ +Written by Christian Couder <chriscool@tuxfamily.org> and Junio C +Hamano <gitster@pobox.com>, based on 'git tag' by Kristian Hogsberg +<krh@redhat.com> and Carlos Rica <jasampler@gmail.com>. + +Documentation +-------------- +Documentation by Christian Couder <chriscool@tuxfamily.org> and the +git-list <git@vger.kernel.org>, based on 'git tag' documentation. + +GIT +--- +Part of the linkgit:git[1] suite diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index 974d9f527f..3341d1b62f 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -51,20 +51,26 @@ SYNOPSIS DESCRIPTION ----------- -Lists commit objects in reverse chronological order starting at the -given commit(s), taking ancestry relationship into account. This is -useful to produce human-readable log output. +List commits that are reachable by following the `parent` links from the +given commit(s), but exclude commits that are reachable from the one(s) +given with a '{caret}' in front of them. The output is given in reverse +chronological order by default. -Commits which are stated with a preceding '{caret}' cause listing to -stop at that point. Their parents are implied. Thus the following -command: +You can think of this as a set operation. Commits given on the command +line form a set of commits that are reachable from any of them, and then +commits reachable from any of the ones given with '{caret}' in front are +subtracted from that set. The remaining commits are what comes out in the +command's output. Various other options and paths parameters can be used +to further limit the result. + +Thus, the following command: ----------------------------------------------------------------------- $ git rev-list foo bar ^baz ----------------------------------------------------------------------- -means "list all the commits which are included in 'foo' and 'bar', but -not in 'baz'". +means "list all the commits which are reachable from 'foo' or 'bar', but +not from 'baz'". A special notation "'<commit1>'..'<commit2>'" can be used as a short-hand for "{caret}'<commit1>' '<commit2>'". For example, either of diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index d6b192b7b9..767cf4d4bd 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -142,8 +142,9 @@ user is prompted for a password while the input is masked for privacy. --smtp-server-port=<port>:: Specifies a port different from the default port (SMTP - servers typically listen to smtp port 25 and ssmtp port - 465); symbolic port names (e.g. "submission" instead of 465) + servers typically listen to smtp port 25, but may also listen to + submission port 587, or the common SSL smtp port 465); + symbolic port names (e.g. "submission" instead of 587) are also accepted. The port can also be set with the 'sendemail.smtpserverport' configuration variable. diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt index 89ec5364ec..734336119c 100644 --- a/Documentation/git-show-branch.txt +++ b/Documentation/git-show-branch.txt @@ -8,11 +8,12 @@ git-show-branch - Show branches and their commits SYNOPSIS -------- [verse] -'git show-branch' [--all] [--remotes] [--topo-order | --date-order] - [--current] [--color | --no-color] +'git show-branch' [-a|--all] [-r|--remotes] [--topo-order | --date-order] + [--current] [--color | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [<rev> | <glob>]... + 'git show-branch' (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>] DESCRIPTION @@ -81,9 +82,11 @@ OPTIONS Synonym to `--more=-1` --merge-base:: - Instead of showing the commit list, just act like the - 'git-merge-base -a' command, except that it can accept - more than two heads. + Instead of showing the commit list, determine possible + merge bases for the specified commits. All merge bases + will be contained in all specified commits. This is + different from how linkgit:git-merge-base[1] handles + the case of three or more commits. --independent:: Among the <reference>s given, display only the ones that diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 1c64a02fe5..2f5ca7b1a3 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -114,7 +114,8 @@ no conflicts. clear:: Remove all the stashed states. Note that those states will then - be subject to pruning, and may be difficult or impossible to recover. + be subject to pruning, and may be impossible to recover (see + 'Examples' below for a possible strategy). drop [-q|--quiet] [<stash>]:: @@ -217,6 +218,20 @@ $ edit/build/test remaining parts $ git commit foo -m 'Remaining parts' ---------------------------------------------------------------- +Recovering stashes that were cleared/dropped erroneously:: + +If you mistakenly drop or clear stashes, they cannot be recovered +through the normal safety mechanisms. However, you can try the +following incantation to get a list of stashes that are still in your +repository, but not reachable any more: ++ +---------------------------------------------------------------- +git fsck --unreachable | +grep commit | cut -d\ -f3 | +xargs git log --merges --no-walk --grep=WIP +---------------------------------------------------------------- + + SEE ALSO -------- linkgit:git-checkout[1], diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 7dd73ae14e..5ccdd18c89 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -11,12 +11,12 @@ SYNOPSIS [verse] 'git submodule' [--quiet] add [-b branch] [--reference <repository>] [--] <repository> <path> -'git submodule' [--quiet] status [--cached] [--] [<path>...] +'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...] 'git submodule' [--quiet] init [--] [<path>...] 'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase] - [--reference <repository>] [--merge] [--] [<path>...] -'git submodule' [--quiet] summary [--cached] [--summary-limit <n>] [commit] [--] [<path>...] -'git submodule' [--quiet] foreach <command> + [--reference <repository>] [--merge] [--recursive] [--] [<path>...] +'git submodule' [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...] +'git submodule' [--quiet] foreach [--recursive] <command> 'git submodule' [--quiet] sync [--] [<path>...] @@ -100,6 +100,9 @@ status:: initialized and `+` if the currently checked out submodule commit does not match the SHA-1 found in the index of the containing repository. This command is the default command for 'git-submodule'. ++ +If '--recursive' is specified, this command will recurse into nested +submodules, and show their status as well. init:: Initialize the submodules, i.e. register each submodule name @@ -122,21 +125,31 @@ update:: If the submodule is not yet initialized, and you just want to use the setting as stored in .gitmodules, you can automatically initialize the submodule with the --init option. ++ +If '--recursive' is specified, this command will recurse into the +registered submodules, and update any nested submodules within. summary:: Show commit summary between the given commit (defaults to HEAD) and working tree/index. For a submodule in question, a series of commits in the submodule between the given super project commit and the - index or working tree (switched by --cached) are shown. + index or working tree (switched by --cached) are shown. If the option + --files is given, show the series of commits in the submodule between + the index of the super project and the working tree of the submodule + (this option doesn't allow to use the --cached option or to provide an + explicit commit). foreach:: Evaluates an arbitrary shell command in each checked out submodule. - The command has access to the variables $path and $sha1: + The command has access to the variables $name, $path and $sha1: + $name is the name of the relevant submodule section in .gitmodules, $path is the name of the submodule directory relative to the superproject, and $sha1 is the commit as recorded in the superproject. Any submodules defined in the superproject but not checked out are ignored by this command. Unless given --quiet, foreach prints the name of each submodule before evaluating the command. + If --recursive is given, submodules are traversed recursively (i.e. + the given shell command is evaluated in nested submodules as well). A non-zero return from the command in any submodule causes the processing to terminate. This can be overridden by adding '|| :' to the end of the command. @@ -169,6 +182,11 @@ OPTIONS commands typically use the commit found in the submodule HEAD, but with this option, the commit stored in the index is used instead. +--files:: + This option is only valid for the summary command. This command + compares the commit in the index with that in the submodule HEAD + when this option is used. + -n:: --summary-limit:: This option is only valid for the summary command. @@ -209,6 +227,12 @@ OPTIONS *NOTE*: Do *not* use this option unless you have read the note for linkgit:git-clone[1]'s --reference and --shared options carefully. +--recursive:: + This option is only valid for foreach, update and status commands. + Traverse submodules recursively. The operation is performed not + only in the submodules of the current repo, but also + in any nested submodules inside those submodules (and so on). + <path>...:: Paths to submodule(s). When specified this will restrict the command to only operate on the submodules found at the specified paths. diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 22a0389f1e..1812890a7e 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -102,9 +102,6 @@ COMMANDS Store Git commit times in the local timezone instead of UTC. This makes 'git log' (even without --date=local) show the same times that `svn log` would in the local timezone. - ---parent;; - Fetch only from the SVN parent of the current HEAD. + This doesn't interfere with interoperating with the Subversion repository you cloned from, but if you wish for your local Git @@ -112,6 +109,9 @@ repository to be able to interoperate with someone else's local Git repository, either don't use this option or you should both use it in the same local timezone. +--parent;; + Fetch only from the SVN parent of the current HEAD. + --ignore-paths=<regex>;; This allows one to specify a Perl regular expression that will cause skipping of all matching paths from checkout from SVN. diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index fa733214ab..299b04f726 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -10,14 +10,15 @@ SYNOPSIS -------- [verse] 'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] - <name> [<commit> | <object>] -'git tag' -d <name>... + <tagname> [<commit> | <object>] +'git tag' -d <tagname>... 'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>] -'git tag' -v <name>... +'git tag' -v <tagname>... DESCRIPTION ----------- -Adds a 'tag' reference in `.git/refs/tags/` + +Adds a tag reference in `.git/refs/tags/`. Unless `-f` is given, the tag must not yet exist in `.git/refs/tags/` directory. @@ -50,6 +51,7 @@ OPTIONS Make a GPG-signed tag, using the given key -f:: +--force:: Replace an existing tag with the given name (instead of failing) -d:: @@ -85,6 +87,12 @@ OPTIONS Implies `-a` if none of `-a`, `-s`, or `-u <key-id>` is given. +<tagname>:: + The name of the tag to create, delete, or describe. + The new tag name must pass all checks defined by + linkgit:git-check-ref-format[1]. Some of these checks + may restrict the characters allowed in a tag name. + CONFIGURATION ------------- By default, 'git-tag' in sign-with-default mode (-s) will use your @@ -249,6 +257,10 @@ $ GIT_COMMITTER_DATE="2006-10-02 10:31" git tag -s v1.0.1 ------------ +SEE ALSO +-------- +linkgit:git-check-ref-format[1]. + Author ------ Written by Linus Torvalds <torvalds@osdl.org>, diff --git a/Documentation/git-verify-pack.txt b/Documentation/git-verify-pack.txt index c8611632d1..97f7f9165e 100644 --- a/Documentation/git-verify-pack.txt +++ b/Documentation/git-verify-pack.txt @@ -8,7 +8,7 @@ git-verify-pack - Validate packed git archive files SYNOPSIS -------- -'git verify-pack' [-v] [--] <pack>.idx ... +'git verify-pack' [-v|--verbose] [--] <pack>.idx ... DESCRIPTION @@ -23,8 +23,15 @@ OPTIONS The idx files to verify. -v:: +--verbose:: After verifying the pack, show list of objects contained - in the pack. + in the pack and a histogram of delta chain length. + +-s:: +--stat-only:: + Do not verify the pack contents; only show the histogram of delta + chain length. With `--verbose`, list of objects is also shown. + \--:: Do not interpret any more arguments as options. diff --git a/Documentation/pt_BR/gittutorial.txt b/Documentation/pt_BR/gittutorial.txt new file mode 100644 index 0000000000..81e7ad7df4 --- /dev/null +++ b/Documentation/pt_BR/gittutorial.txt @@ -0,0 +1,675 @@ +gittutorial(7) +============== + +NAME +---- +gittutorial - Um tutorial de introdução ao git (para versão 1.5.1 ou mais nova) + +SYNOPSIS +-------- +git * + +DESCRIPTION +----------- + +Este tutorial explica como importar um novo projeto para o git, +adicionar mudanças a ele, e compartilhar mudanças com outros +desenvolvedores. + +Se, ao invés disso, você está interessado primariamente em usar git para +obter um projeto, por exemplo, para testar a última versão, você pode +preferir começar com os primeiros dois capÃtulos de +link:user-manual.html[O Manual do Usuário Git]. + +Primeiro, note que você pode obter documentação para um comando como +`git log --graph` com: + +------------------------------------------------ +$ man git-log +------------------------------------------------ + +ou: + +------------------------------------------------ +$ git help log +------------------------------------------------ + +Com a última forma, você pode usar o visualizador de manual de sua +escolha; veja linkgit:git-help[1] para maior informação. + +É uma boa idéia informar ao git seu nome e endereço público de email +antes de fazer qualquer operação. A maneira mais fácil de fazê-lo é: + +------------------------------------------------ +$ git config --global user.name "Seu Nome Vem Aqui" +$ git config --global user.email voce@seudominio.exemplo.com +------------------------------------------------ + + +Importando um novo projeto +----------------------- + +Assuma que você tem um tarball project.tar.gz com seu trabalho inicial. +Você pode colocá-lo sob controle de revisão git da seguinte forma: + +------------------------------------------------ +$ tar xzf project.tar.gz +$ cd project +$ git init +------------------------------------------------ + +Git irá responder + +------------------------------------------------ +Initialized empty Git repository in .git/ +------------------------------------------------ + +Você agora iniciou seu diretório de trabalho--você deve ter notado um +novo diretório criado, com o nome de ".git". + +A seguir, diga ao git para gravar um instantâneo do conteúdo de todos os +arquivos sob o diretório corrente (note o '.'), com 'git-add': + +------------------------------------------------ +$ git add . +------------------------------------------------ + +Este instantâneo está agora armazenado em uma área temporária que o git +chama de "index" ou Ãndice. Você pode armazenar permanentemente o +conteúdo do Ãndice no repositório com 'git-commit': + +------------------------------------------------ +$ git commit +------------------------------------------------ + +Isto vai te pedir por uma mensagem de commit. Você agora gravou sua +primeira versão de seu projeto no git. + +Fazendo mudanças +-------------- + +Modifique alguns arquivos, e, então, adicione seu conteúdo atualizado ao +Ãndice: + +------------------------------------------------ +$ git add file1 file2 file3 +------------------------------------------------ + +Você está agora pronto para fazer o commit. Você pode ver o que está +para ser gravado usando 'git-diff' com a opção --cached: + +------------------------------------------------ +$ git diff --cached +------------------------------------------------ + +(Sem --cached, o comando 'git-diff' irá te mostrar quaisquer mudanças +que você tenha feito mas ainda não adicionou ao Ãndice.) Você também +pode obter um breve sumário da situação com 'git-status': + +------------------------------------------------ +$ git status +# On branch master +# Changes to be committed: +# (use "git reset HEAD <file>..." to unstage) +# +# modified: file1 +# modified: file2 +# modified: file3 +# +------------------------------------------------ + +Se você precisar fazer qualquer outro ajuste, faça-o agora, e, então, +adicione qualquer conteúdo modificado ao Ãndice. Finalmente, grave suas +mudanças com: + +------------------------------------------------ +$ git commit +------------------------------------------------ + +Isto irá novamente te pedir por uma mensagem descrevendo a mudança, e, +então, gravar a nova versão do projeto. + +Alternativamente, ao invés de executar 'git-add' antes, você pode usar + +------------------------------------------------ +$ git commit -a +------------------------------------------------ + +o que irá automaticamente notar quaisquer arquivos modificados (mas não +novos), adicioná-los ao Ãndices, e gravar, tudo em um único passo. + +Uma nota em mensagens de commit: Apesar de não ser exigido, é uma boa +idéia começar a mensagem com uma simples e curta (menos de 50 +caracteres) linha sumarizando a mudança, seguida de uma linha em branco +e, então, uma descrição mais detalhada. Ferramentas que transformam +commits em email, por exemplo, usam a primeira linha no campo de +cabeçalho Subject: e o resto no corpo. + +Git rastreia conteúdo, não arquivos +---------------------------- + +Muitos sistemas de controle de revisão provêem um comando `add` que diz +ao sistema para começar a rastrear mudanças em um novo arquivo. O +comando `add` do git faz algo mais simples e mais poderoso: 'git-add' é +usado tanto para arquivos novos e arquivos recentemente modificados, e +em ambos os casos, ele tira o instantâneo dos arquivos dados e armazena +o conteúdo no Ãndice, pronto para inclusão do próximo commit. + +Visualizando história do projeto +----------------------- + +Em qualquer ponto você pode visualizar a história das suas mudanças +usando + +------------------------------------------------ +$ git log +------------------------------------------------ + +Se você também quer ver a diferença completa a cada passo, use + +------------------------------------------------ +$ git log -p +------------------------------------------------ + +Geralmente, uma visão geral da mudança é útil para ter a sensação de +cada passo + +------------------------------------------------ +$ git log --stat --summary +------------------------------------------------ + +Gerenciando "branches"/ramos +----------------- + +Um simples repositório git pode manter múltiplos ramos de +desenvolvimento. Para criar um novo ramo chamado "experimental", use + +------------------------------------------------ +$ git branch experimental +------------------------------------------------ + +Se você executar agora + +------------------------------------------------ +$ git branch +------------------------------------------------ + +você vai obter uma lista de todos os ramos existentes: + +------------------------------------------------ + experimental +* master +------------------------------------------------ + +O ramo "experimental" é o que você acaba de criar, e o ramo "master" é o +ramo padrão que foi criado pra você automaticamente. O asterisco marca +o ramo em que você está atualmente; digite + +------------------------------------------------ +$ git checkout experimental +------------------------------------------------ + +para mudar para o ramo experimental. Agora edite um arquivo, grave a +mudança, e mude de volta para o ramo master: + +------------------------------------------------ +(edita arquivo) +$ git commit -a +$ git checkout master +------------------------------------------------ + +Verifique que a mudança que você fez não está mais visÃvel, já que ela +foi feita no ramo experimental e você está de volta ao ramo master. + +Você pode fazer uma mudança diferente no ramo master: + +------------------------------------------------ +(edit file) +$ git commit -a +------------------------------------------------ + +neste ponto, os dois ramos divergiram, com diferentes mudanças feitas em +cada um. Para unificar as mudanças feitas no experimental para o +master, execute + +------------------------------------------------ +$ git merge experimental +------------------------------------------------ + +Se as mudanças não conflitarem, estará pronto. Se existirem conflitos, +marcadores serão deixados nos arquivos problemáticos exibindo o +conflito; + +------------------------------------------------ +$ git diff +------------------------------------------------ + +vai exibir isto. Após você editar os arquivos para resolver os +conflitos, + +------------------------------------------------ +$ git commit -a +------------------------------------------------ + +irá gravar o resultado da unificação. Finalmente, + +------------------------------------------------ +$ gitk +------------------------------------------------ + +vai mostrar uma bela representação gráfica da história resultante. + +Neste ponto você pode remover seu ramo experimental com + +------------------------------------------------ +$ git branch -d experimental +------------------------------------------------ + +Este comando garante que as mudanças no ramo experimental já estão no +ramo atual. + +Se você desenvolve em um ramo ideia-louca, e se arrepende, você pode +sempre remover o ramo com + +------------------------------------- +$ git branch -D ideia-louca +------------------------------------- + +Ramos são baratos e fáceis, então isto é uma boa maneira de experimentar +alguma coisa. + +Usando git para colaboração +--------------------------- + +Suponha que Alice começou um novo projeto com um repositório git em +/home/alice/project, e que Bob, que tem um diretório home na mesma +máquina, quer contribuir. + +Bob começa com: + +------------------------------------------------ +bob$ git clone /home/alice/project myrepo +------------------------------------------------ + +Isso cria um novo diretório "myrepo" contendo um clone do repositório de +Alice. O clone está no mesmo pé que o projeto original, possuindo sua +própria cópia da história do projeto original. + +Bob então faz algumas mudanças e as grava: + +------------------------------------------------ +(editar arquivos) +bob$ git commit -a +(repetir conforme necessário) +------------------------------------------------ + +Quanto está pronto, ele diz a Alice para puxar as mudanças do +repositório em /home/bob/myrepo. Ela o faz com: + +------------------------------------------------ +alice$ cd /home/alice/project +alice$ git pull /home/bob/myrepo master +------------------------------------------------ + +Isto unifica as mudanças do ramo "master" do Bob ao ramo atual de Alice. +Se Alice fez suas próprias mudanças no intervalo, ela, então, pode +precisar corrigir manualmente quaisquer conflitos. (Note que o argumento +"master" no comando acima é, de fato, desnecessário, já que é o padrão.) + +O comando "pull" executa, então, duas operações: ele obtém mudanças de +um ramo remoto, e, então, as unifica no ramo atual. + +Note que, em geral, Alice gostaria que suas mudanças locais fossem +gravadas antes de iniciar este "pull". Se o trabalho de Bob conflita +com o que Alice fez desde que suas histórias se ramificaram, Alice irá +usar seu diretório de trabalho e o Ãndice para resolver conflitos, e +mudanças locais existentes irão interferir com o processo de resolução +de conflitos (git ainda irá realizar a obtenção mas irá se recusar a +unificar --- Alice terá que se livrar de suas mudanças locais de alguma +forma e puxar de novo quando isso acontecer). + +Alice pode espiar o que Bob fez sem unificar primeiro, usando o comando +"fetch"; isto permite Alice inspecionar o que Bob fez, usando um sÃmbolo +especial "FETCH_HEAD", com o fim de determinar se ele tem alguma coisa +que vale puxar, assim: + +------------------------------------------------ +alice$ git fetch /home/bob/myrepo master +alice$ git log -p HEAD..FETCH_HEAD +------------------------------------------------ + +Esta operação é segura mesmo se Alice tem mudanças locais não gravadas. +A notação de intervalo "HEAD..FETCH_HEAD" significa mostrar tudo que é +alcançável de FETCH_HEAD mas exclua tudo o que é alcançável de HEAD. +Alice já sabe tudo que leva a seu estado atual (HEAD), e revisa o que Bob +tem em seu estado (FETCH_HEAD) que ela ainda não viu com esse comando. + +Se Alice quer visualizar o que Bob fez desde que suas histórias se +ramificaram, ela pode disparar o seguinte comando: + +------------------------------------------------ +$ gitk HEAD..FETCH_HEAD +------------------------------------------------ + +Isto usa a mesma notação de intervalo que vimos antes com 'git log'. + +Alice pode querer ver o que ambos fizeram desde que ramificaram. Ela +pode usar a forma com três pontos ao invés da forma com dois pontos: + +------------------------------------------------ +$ gitk HEAD...FETCH_HEAD +------------------------------------------------ + +Isto significa "mostre tudo que é alcançável de qualquer um deles, mas +exclua tudo que é alcançável a partir de ambos". + +Por favor, note que essas notações de intervalo podem ser usadas tanto +com gitk quanto com "git log". + +Após inspecionar o que Bob fez, se não há nada urgente, Alice pode +decidir continuar trabalhando sem puxar de Bob. Se a história de Bob +tem alguma coisa que Alice precisa imediatamente, Alice pode optar por +separar seu trabalho em progresso primeiro, fazer um "pull", e, então, +finalmente, retomar seu trabalho em progresso em cima da história +resultante. + +Quando você está trabalhando em um pequeno grupo unido, não é incomum +interagir com o mesmo repositório várias e várias vezes. Definindo um +repositório remoto antes de tudo, você pode fazê-lo mais facilmente: + +------------------------------------------------ +alice$ git remote add bob /home/bob/myrepo +------------------------------------------------ + +Com isso, Alice pode executar a primeira parte da operação "pull" usando +o comando 'git-fetch' sem unificar suas mudanças com seu próprio ramo, +usando: + +------------------------------------- +alice$ git fetch bob +------------------------------------- + +Diferente da forma longa, quando Alice obteve de Bob usando um +repositório remoto antes definido com 'git-remote', o que foi obtido é +armazenado em um ramo remoto, neste caso `bob/master`. Então, após isso: + +------------------------------------- +alice$ git log -p master..bob/master +------------------------------------- + +mostra uma lista de todas as mudanças que Bob fez desde que ramificou do +ramo master de Alice. + +Após examinar essas mudanças, Alice pode unificá-las em seu ramo master: + +------------------------------------- +alice$ git merge bob/master +------------------------------------- + +Esse `merge` pode também ser feito puxando de seu próprio ramo remoto, +assim: + +------------------------------------- +alice$ git pull . remotes/bob/master +------------------------------------- + +Note que 'git pull' sempre unifica ao ramo atual, independente do que +mais foi passado na linha de comando. + +Depois, Bob pode atualizar seu repositório com as últimas mudanças de +Alice, usando + +------------------------------------- +bob$ git pull +------------------------------------- + +Note que ele não precisa dar o caminho do repositório de Alice; quando +Bob clonou seu repositório, o git armazenou a localização de seu +repositório na configuração do mesmo, e essa localização é usada +para puxar: + +------------------------------------- +bob$ git config --get remote.origin.url +/home/alice/project +------------------------------------- + +(A configuração completa criada por 'git-clone' é visÃvel usando `git +config -l`, e a página de manual linkgit:git-config[1] explica o +significado de cada opção.) + +Git também mantém uma cópia limpa do ramo master de Alice sob o nome +"origin/master": + +------------------------------------- +bob$ git branch -r + origin/master +------------------------------------- + +Se Bob decidir depois em trabalhar em um host diferente, ele ainda pode +executar clones e puxar usando o protocolo ssh: + +------------------------------------- +bob$ git clone alice.org:/home/alice/project myrepo +------------------------------------- + +Alternativamente, o git tem um protocolo nativo, ou pode usar rsync ou +http; veja linkgit:git-pull[1] para detalhes. + +Git pode também ser usado em um modo parecido com CVS, com um +repositório central para o qual vários usuários empurram modificações; +veja linkgit:git-push[1] e linkgit:gitcvs-migration[7]. + +Explorando história +----------------- + +A história no git é representada como uma série de commits +interrelacionados. Nós já vimos que o comando 'git-log' pode listar +esses commits. Note que a primeira linha de cada entrada no log também +dá o nome para o commit: + +------------------------------------- +$ git log +commit c82a22c39cbc32576f64f5c6b3f24b99ea8149c7 +Author: Junio C Hamano <junkio@cox.net> +Date: Tue May 16 17:18:22 2006 -0700 + + merge-base: Clarify the comments on post processing. +------------------------------------- + +Nós podemos dar este nome ao 'git-show' para ver os detalhes sobre este +commit. + +------------------------------------- +$ git show c82a22c39cbc32576f64f5c6b3f24b99ea8149c7 +------------------------------------- + +Mas há outras formas de se referir aos commits. Você pode usar qualquer +parte inicial do nome que seja longo o bastante para identificar +unicamente o commit: + +------------------------------------- +$ git show c82a22c39c # os primeiros caracteres do nome são o bastante + # usualmente +$ git show HEAD # a ponta do ramo atual +$ git show experimental # a ponta do ramo "experimental" +------------------------------------- + +Todo commit normalmente tem um commit "pai" que aponta para o estado +anterior do projeto: + +------------------------------------- +$ git show HEAD^ # para ver o pai de HEAD +$ git show HEAD^^ # para ver o avô de HEAD +$ git show HEAD~4 # para ver o trisavô de HEAD +------------------------------------- + +Note que commits de unificação podem ter mais de um pai: + +------------------------------------- +$ git show HEAD^1 # mostra o primeiro pai de HEAD (o mesmo que HEAD^) +$ git show HEAD^2 # mostra o segundo pai de HEAD +------------------------------------- + +Você também pode dar aos commits nomes à sua escolha; após executar + +------------------------------------- +$ git tag v2.5 1b2e1d63ff +------------------------------------- + +você pode se referir a 1b2e1d63ff pelo nome "v2.5". Se você pretende +compartilhar esse nome com outras pessoas (por exemplo, para identificar +uma versão de lançamento), você deveria criar um objeto "tag", e talvez +assiná-lo; veja linkgit:git-tag[1] para detalhes. + +Qualquer comando git que precise conhecer um commit pode receber +quaisquer desses nomes. Por exemplo: + +------------------------------------- +$ git diff v2.5 HEAD # compara o HEAD atual com v2.5 +$ git branch stable v2.5 # inicia um novo ramo chamado "stable" baseado + # em v2.5 +$ git reset --hard HEAD^ # reseta seu ramo atual e seu diretório de + # trabalho a seu estado em HEAD^ +------------------------------------- + +Seja cuidadoso com o último comando: além de perder quaisquer mudanças +em seu diretório de trabalho, ele também remove todos os commits +posteriores desse ramo. Se esse ramo é o único ramo contendo esses +commits, eles serão perdidos. Também, não use 'git-reset' num ramo +publicamente visÃvel de onde outros desenvolvedores puxam, já que vai +forçar unificações desnecessárias para que outros desenvolvedores limpem +a história. Se você precisa desfazer mudanças que você empurrou, use +'git-revert' no lugar. + +O comando 'git-grep' pode buscar strings em qualquer versão de seu +projeto, então + +------------------------------------- +$ git grep "hello" v2.5 +------------------------------------- + +procura por todas as ocorrências de "hello" em v2.5. + +Se você deixar de fora o nome do commit, 'git-grep' irá procurar +quaisquer dos arquivos que ele gerencia no diretório corrente. Então + +------------------------------------- +$ git grep "hello" +------------------------------------- + +é uma forma rápida de buscar somente os arquivos que são rastreados pelo +git. + +Muitos comandos git também recebem um conjunto de commits, o que pode +ser especificado de várias formas. Aqui estão alguns exemplos com 'git-log': + +------------------------------------- +$ git log v2.5..v2.6 # commits entre v2.5 e v2.6 +$ git log v2.5.. # commits desde v2.5 +$ git log --since="2 weeks ago" # commits das últimas 2 semanas +$ git log v2.5.. Makefile # commits desde v2.5 que modificam + # Makefile +------------------------------------- + +Você também pode dar ao 'git-log' um "intervalo" de commits onde o +primeiro não é necessariamente um ancestral do segundo; por exemplo, se +as pontas dos ramos "stable" e "master" divergiram de um commit +comum algum tempo atrás, então + +------------------------------------- +$ git log stable..master +------------------------------------- + +irá listar os commits feitos no ramo "master" mas não no ramo +"stable", enquanto + +------------------------------------- +$ git log master..stable +------------------------------------- + +irá listar a lista de commits feitos no ramo "stable" mas não no ramo +"master". + +O comando 'git-log' tem uma fraqueza: ele precisa mostrar os commits em +uma lista. Quando a história tem linhas de desenvolvimento que +divergiram e então foram unificadas novamente, a ordem em que 'git-log' +apresenta essas mudanças é irrelevante. + +A maioria dos projetos com múltiplos contribuidores (como o kernel +Linux, ou o próprio git) tem unificações frequentes, e 'gitk' faz um +trabalho melhor de visualizar sua história. Por exemplo, + +------------------------------------- +$ gitk --since="2 weeks ago" drivers/ +------------------------------------- + +permite a você navegar em quaisquer commits desde as últimas duas semanas +de commits que modificaram arquivos sob o diretório "drivers". (Nota: +você pode ajustar as fontes do gitk segurando a tecla control enquanto +pressiona "-" ou "+".) + +Finalmente, a maioria dos comandos que recebem nomes de arquivo permitirão +também, opcionalmente, preceder qualquer nome de arquivo por um +commit, para especificar uma versão particular do arquivo: + +------------------------------------- +$ git diff v2.5:Makefile HEAD:Makefile.in +------------------------------------- + +Você pode usar 'git-show' para ver tal arquivo: + +------------------------------------- +$ git show v2.5:Makefile +------------------------------------- + +Próximos passos +---------- + +Este tutorial deve ser o bastante para operar controle de revisão +distribuÃdo básico para seus projetos. No entanto, para entender +plenamente a profundidade e o poder do git você precisa entender duas +idéias simples nas quais ele se baseia: + + * A base de objetos é um sistema bem elegante usado para armazenar a + história de seu projeto--arquivos, diretórios, e commits. + + * O arquivo de Ãndice é um cache do estado de uma árvore de diretório, + usado para criar commits, restaurar diretórios de trabalho, e + armazenar as várias árvores envolvidas em uma unificação. + +A parte dois deste tutorial explica a base de objetos, o arquivo de +Ãndice, e algumas outras coisinhas que você vai precisar pra usar o +máximo do git. Você pode encontrá-la em linkgit:gittutorial-2[7]. + +Se você não quiser continuar com o tutorial agora nesse momento, algumas +outras digressões que podem ser interessantes neste ponto são: + + * linkgit:git-format-patch[1], linkgit:git-am[1]: Estes convertem + séries de commits em patches para email, e vice-versa, úteis para + projetos como o kernel Linux que dependem fortemente de patches + enviados por email. + + * linkgit:git-bisect[1]: Quando há uma regressão em seu projeto, uma + forma de rastrear um bug é procurando pela história para encontrar o + commit culpado. Git bisect pode ajudar a executar uma busca binária + por esse commit. Ele é inteligente o bastante para executar uma + busca próxima da ótima mesmo no caso de uma história complexa + não-linear com muitos ramos unificados. + + * link:everyday.html[GIT diariamente com 20 e tantos comandos] + + * linkgit:gitcvs-migration[7]: Git para usuários de CVS. + +VEJA TAMBÉM +-------- +linkgit:gittutorial-2[7], +linkgit:gitcvs-migration[7], +linkgit:gitcore-tutorial[7], +linkgit:gitglossary[7], +linkgit:git-help[1], +link:everyday.html[git diariamente], +link:user-manual.html[O Manual do Usuário git] + +GIT +--- +Parte da suite linkgit:git[1]. diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt index 2efe7a40be..b26c28133c 100644 --- a/Documentation/technical/api-run-command.txt +++ b/Documentation/technical/api-run-command.txt @@ -35,12 +35,32 @@ Functions Convenience functions that encapsulate a sequence of start_command() followed by finish_command(). The argument argv specifies the program and its arguments. The argument opt is zero - or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`, or - `RUN_COMMAND_STDOUT_TO_STDERR` that correspond to the members - .no_stdin, .git_cmd, .stdout_to_stderr of `struct child_process`. + or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`, + `RUN_COMMAND_STDOUT_TO_STDERR`, or `RUN_SILENT_EXEC_FAILURE` + that correspond to the members .no_stdin, .git_cmd, + .stdout_to_stderr, .silent_exec_failure of `struct child_process`. The argument dir corresponds the member .dir. The argument env corresponds to the member .env. +The functions above do the following: + +. If a system call failed, errno is set and -1 is returned. A diagnostic + is printed. + +. If the program was not found, then -1 is returned and errno is set to + ENOENT; a diagnostic is printed only if .silent_exec_failure is 0. + +. Otherwise, the program is run. If it terminates regularly, its exit + code is returned. No diagnistic is printed, even if the exit code is + non-zero. + +. If the program terminated due to a signal, then the return value is the + signal number - 128, ie. it is negative and so indicates an unusual + condition; a diagnostic is printed. This return value can be passed to + exit(2), which will report the same code to the parent process that a + POSIX shell's $? would report for a program that died from the signal. + + `start_async`:: Run a function asynchronously. Takes a pointer to a `struct @@ -143,6 +163,11 @@ string pointers (NULL terminated) in .env: To specify a new initial working directory for the sub-process, specify it in the .dir member. +If the program cannot be found, the functions return -1 and set +errno to ENOENT. Normally, an error message is printed, but if +.silent_exec_failure is set to 1, no message is printed for this +special error condition. + * `struct async` diff --git a/Documentation/technical/api-tree-walking.txt b/Documentation/technical/api-tree-walking.txt index e3ddf91284..55b728632c 100644 --- a/Documentation/technical/api-tree-walking.txt +++ b/Documentation/technical/api-tree-walking.txt @@ -1,12 +1,145 @@ tree walking API ================ -Talk about <tree-walk.h>, things like +The tree walking API is used to traverse and inspect trees. -* struct tree_desc -* init_tree_desc -* tree_entry_extract -* update_tree_entry -* get_tree_entry +Data Structures +--------------- -(JC, Linus) +`struct name_entry`:: + + An entry in a tree. Each entry has a sha1 identifier, pathname, and + mode. + +`struct tree_desc`:: + + A semi-opaque data structure used to maintain the current state of the + walk. ++ +* `buffer` is a pointer into the memory representation of the tree. It always +points at the current entry being visited. + +* `size` counts the number of bytes left in the `buffer`. + +* `entry` points to the current entry being visited. + +`struct traverse_info`:: + + A structure used to maintain the state of a traversal. ++ +* `prev` points to the traverse_info which was used to descend into the +current tree. If this is the top-level tree `prev` will point to +a dummy traverse_info. + +* `name` is the entry for the current tree (if the tree is a subtree). + +* `pathlen` is the length of the full path for the current tree. + +* `conflicts` can be used by callbacks to maintain directory-file conflicts. + +* `fn` is a callback called for each entry in the tree. See Traversing for more +information. + +* `data` can be anything the `fn` callback would want to use. + +Initializing +------------ + +`init_tree_desc`:: + + Initialize a `tree_desc` and decode its first entry. The buffer and + size parameters are assumed to be the same as the buffer and size + members of `struct tree`. + +`fill_tree_descriptor`:: + + Initialize a `tree_desc` and decode its first entry given the sha1 of + a tree. Returns the `buffer` member if the sha1 is a valid tree + identifier and NULL otherwise. + +`setup_traverse_info`:: + + Initialize a `traverse_info` given the pathname of the tree to start + traversing from. The `base` argument is assumed to be the `path` + member of the `name_entry` being recursed into unless the tree is a + top-level tree in which case the empty string ("") is used. + +Walking +------- + +`tree_entry`:: + + Visit the next entry in a tree. Returns 1 when there are more entries + left to visit and 0 when all entries have been visited. This is + commonly used in the test of a while loop. + +`tree_entry_len`:: + + Calculate the length of a tree entry's pathname. This utilizes the + memory structure of a tree entry to avoid the overhead of using a + generic strlen(). + +`update_tree_entry`:: + + Walk to the next entry in a tree. This is commonly used in conjunction + with `tree_entry_extract` to inspect the current entry. + +`tree_entry_extract`:: + + Decode the entry currently being visited (the one pointed to by + `tree_desc's` `entry` member) and return the sha1 of the entry. The + `pathp` and `modep` arguments are set to the entry's pathname and mode + respectively. + +`get_tree_entry`:: + + Find an entry in a tree given a pathname and the sha1 of a tree to + search. Returns 0 if the entry is found and -1 otherwise. The third + and fourth parameters are set to the entry's sha1 and mode + respectively. + +Traversing +---------- + +`traverse_trees`:: + + Traverse `n` number of trees in parallel. The `fn` callback member of + `traverse_info` is called once for each tree entry. + +`traverse_callback_t`:: + The arguments passed to the traverse callback are as follows: ++ +* `n` counts the number of trees being traversed. + +* `mask` has its nth bit set if something exists in the nth entry. + +* `dirmask` has its nth bit set if the nth tree's entry is a directory. + +* `entry` is an array of size `n` where the nth entry is from the nth tree. + +* `info` maintains the state of the traversal. + ++ +Returning a negative value will terminate the traversal. Otherwise the +return value is treated as an update mask. If the nth bit is set the nth tree +will be updated and if the bit is not set the nth tree entry will be the +same in the next callback invocation. + +`make_traverse_path`:: + + Generate the full pathname of a tree entry based from the root of the + traversal. For example, if the traversal has recursed into another + tree named "bar" the pathname of an entry "baz" in the "bar" + tree would be "bar/baz". + +`traverse_path_len`:: + + Calculate the length of a pathname returned by `make_traverse_path`. + This utilizes the memory structure of a tree entry to avoid the + overhead of using a generic strlen(). + +Authors +------- + +Written by Junio C Hamano <gitster@pobox.com> and Linus Torvalds +<torvalds@linux-foundation.org> diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 76c6dd2fef..d7d9a9a063 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.6.4.2 +DEF_VER=v1.6.4.GIT LF=' ' @@ -16,7 +16,7 @@ all:: # when attempting to read from an fopen'ed directory. # # Define NO_OPENSSL environment variable if you do not have OpenSSL. -# This also implies MOZILLA_SHA1. +# This also implies BLK_SHA1. # # Define NO_CURL if you do not have libcurl installed. git-http-pull and # git-http-push are not built, and you cannot use http:// and https:// @@ -84,17 +84,13 @@ all:: # specify your own (or DarwinPort's) include directories and # library directories by defining CFLAGS and LDFLAGS appropriately. # +# Define BLK_SHA1 environment variable if you want the C version +# of the SHA1 that assumes you can do unaligned 32-bit loads and +# have a fast htonl() function. +# # Define PPC_SHA1 environment variable when running make to make use of # a bundled SHA1 routine optimized for PowerPC. # -# Define ARM_SHA1 environment variable when running make to make use of -# a bundled SHA1 routine optimized for ARM. -# -# Define MOZILLA_SHA1 environment variable when running make to make use of -# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast -# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default -# choice) has very fast version optimized for i586. -# # Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin). # # Define NEEDS_LIBICONV if linking with libc is not enough (Darwin). @@ -361,7 +357,6 @@ PROGRAMS += git-patch-id$X PROGRAMS += git-shell$X PROGRAMS += git-show-index$X PROGRAMS += git-unpack-file$X -PROGRAMS += git-update-server-info$X PROGRAMS += git-upload-pack$X PROGRAMS += git-var$X @@ -532,6 +527,7 @@ LIB_OBJS += read-cache.o LIB_OBJS += reflog-walk.o LIB_OBJS += refs.o LIB_OBJS += remote.o +LIB_OBJS += replace_object.o LIB_OBJS += rerere.o LIB_OBJS += revision.o LIB_OBJS += run-command.o @@ -621,6 +617,7 @@ BUILTIN_OBJS += builtin-read-tree.o BUILTIN_OBJS += builtin-receive-pack.o BUILTIN_OBJS += builtin-reflog.o BUILTIN_OBJS += builtin-remote.o +BUILTIN_OBJS += builtin-replace.o BUILTIN_OBJS += builtin-rerere.o BUILTIN_OBJS += builtin-reset.o BUILTIN_OBJS += builtin-rev-list.o @@ -638,6 +635,7 @@ BUILTIN_OBJS += builtin-tar-tree.o BUILTIN_OBJS += builtin-unpack-objects.o BUILTIN_OBJS += builtin-update-index.o BUILTIN_OBJS += builtin-update-ref.o +BUILTIN_OBJS += builtin-update-server-info.o BUILTIN_OBJS += builtin-upload-archive.o BUILTIN_OBJS += builtin-verify-pack.o BUILTIN_OBJS += builtin-verify-tag.o @@ -751,9 +749,6 @@ ifeq ($(uname_S),SunOS) NO_C99_FORMAT = YesPlease NO_STRTOUMAX = YesPlease endif - ifdef NO_IPV6 - NEEDS_RESOLV = YesPlease - endif INSTALL = /usr/ucb/install TAR = gtar BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__ -DHAVE_ALLOCA_H @@ -922,10 +917,6 @@ else NO_PTHREADS = YesPlease endif endif -ifneq (,$(findstring arm,$(uname_M))) - ARM_SHA1 = YesPlease - NO_MKSTEMPS = YesPlease -endif -include config.mak.autogen -include config.mak @@ -1018,7 +1009,7 @@ ifndef NO_OPENSSL endif else BASIC_CFLAGS += -DNO_OPENSSL - MOZILLA_SHA1 = 1 + BLK_SHA1 = 1 OPENSSL_LIBSSL = endif ifdef NEEDS_SSL_WITH_CRYPTO @@ -1167,23 +1158,18 @@ ifdef NO_DEFLATE_BOUND BASIC_CFLAGS += -DNO_DEFLATE_BOUND endif +ifdef BLK_SHA1 + SHA1_HEADER = "block-sha1/sha1.h" + LIB_OBJS += block-sha1/sha1.o +else ifdef PPC_SHA1 SHA1_HEADER = "ppc/sha1.h" LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o else -ifdef ARM_SHA1 - SHA1_HEADER = "arm/sha1.h" - LIB_OBJS += arm/sha1.o arm/sha1_arm.o -else -ifdef MOZILLA_SHA1 - SHA1_HEADER = "mozilla-sha1/sha1.h" - LIB_OBJS += mozilla-sha1/sha1.o -else SHA1_HEADER = <openssl/sha.h> EXTLIBS += $(LIB_4_CRYPTO) endif endif -endif ifdef NO_PERL_MAKEMAKER export NO_PERL_MAKEMAKER endif @@ -1 +1 @@ -Documentation/RelNotes-1.6.4.2.txt
\ No newline at end of file +Documentation/RelNotes-1.6.5.txt
\ No newline at end of file @@ -18,7 +18,7 @@ const char *make_absolute_path(const char *path) { static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1]; char cwd[1024] = ""; - int buf_index = 1, len; + int buf_index = 1; int depth = MAXDEPTH; char *last_elem = NULL; @@ -50,7 +50,7 @@ const char *make_absolute_path(const char *path) die_errno ("Could not get current working directory"); if (last_elem) { - int len = strlen(buf); + size_t len = strlen(buf); if (len + strlen(last_elem) + 2 > PATH_MAX) die ("Too long path name: '%s/%s'", buf, last_elem); @@ -61,7 +61,7 @@ const char *make_absolute_path(const char *path) } if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) { - len = readlink(buf, next_buf, PATH_MAX); + ssize_t len = readlink(buf, next_buf, PATH_MAX); if (len < 0) die_errno ("Invalid symlink '%s'", buf); if (PATH_MAX <= len) diff --git a/arm/sha1.c b/arm/sha1.c deleted file mode 100644 index c61ad4aff9..0000000000 --- a/arm/sha1.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * SHA-1 implementation optimized for ARM - * - * Copyright: (C) 2005 by Nicolas Pitre <nico@cam.org> - * Created: September 17, 2005 - */ - -#include <string.h> -#include "sha1.h" - -extern void arm_sha_transform(uint32_t *hash, const unsigned char *data, uint32_t *W); - -void arm_SHA1_Init(arm_SHA_CTX *c) -{ - c->len = 0; - c->hash[0] = 0x67452301; - c->hash[1] = 0xefcdab89; - c->hash[2] = 0x98badcfe; - c->hash[3] = 0x10325476; - c->hash[4] = 0xc3d2e1f0; -} - -void arm_SHA1_Update(arm_SHA_CTX *c, const void *p, unsigned long n) -{ - uint32_t workspace[80]; - unsigned int partial; - unsigned long done; - - partial = c->len & 0x3f; - c->len += n; - if ((partial + n) >= 64) { - if (partial) { - done = 64 - partial; - memcpy(c->buffer + partial, p, done); - arm_sha_transform(c->hash, c->buffer, workspace); - partial = 0; - } else - done = 0; - while (n >= done + 64) { - arm_sha_transform(c->hash, p + done, workspace); - done += 64; - } - } else - done = 0; - if (n - done) - memcpy(c->buffer + partial, p + done, n - done); -} - -void arm_SHA1_Final(unsigned char *hash, arm_SHA_CTX *c) -{ - uint64_t bitlen; - uint32_t bitlen_hi, bitlen_lo; - unsigned int i, offset, padlen; - unsigned char bits[8]; - static const unsigned char padding[64] = { 0x80, }; - - bitlen = c->len << 3; - offset = c->len & 0x3f; - padlen = ((offset < 56) ? 56 : (64 + 56)) - offset; - arm_SHA1_Update(c, padding, padlen); - - bitlen_hi = bitlen >> 32; - bitlen_lo = bitlen & 0xffffffff; - bits[0] = bitlen_hi >> 24; - bits[1] = bitlen_hi >> 16; - bits[2] = bitlen_hi >> 8; - bits[3] = bitlen_hi; - bits[4] = bitlen_lo >> 24; - bits[5] = bitlen_lo >> 16; - bits[6] = bitlen_lo >> 8; - bits[7] = bitlen_lo; - arm_SHA1_Update(c, bits, 8); - - for (i = 0; i < 5; i++) { - uint32_t v = c->hash[i]; - hash[0] = v >> 24; - hash[1] = v >> 16; - hash[2] = v >> 8; - hash[3] = v; - hash += 4; - } -} diff --git a/arm/sha1.h b/arm/sha1.h deleted file mode 100644 index b61b618486..0000000000 --- a/arm/sha1.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SHA-1 implementation optimized for ARM - * - * Copyright: (C) 2005 by Nicolas Pitre <nico@cam.org> - * Created: September 17, 2005 - */ - -#include <stdint.h> - -typedef struct { - uint64_t len; - uint32_t hash[5]; - unsigned char buffer[64]; -} arm_SHA_CTX; - -void arm_SHA1_Init(arm_SHA_CTX *c); -void arm_SHA1_Update(arm_SHA_CTX *c, const void *p, unsigned long n); -void arm_SHA1_Final(unsigned char *hash, arm_SHA_CTX *c); - -#define git_SHA_CTX arm_SHA_CTX -#define git_SHA1_Init arm_SHA1_Init -#define git_SHA1_Update arm_SHA1_Update -#define git_SHA1_Final arm_SHA1_Final diff --git a/arm/sha1_arm.S b/arm/sha1_arm.S deleted file mode 100644 index 41e92636e0..0000000000 --- a/arm/sha1_arm.S +++ /dev/null @@ -1,183 +0,0 @@ -/* - * SHA transform optimized for ARM - * - * Copyright: (C) 2005 by Nicolas Pitre <nico@cam.org> - * Created: September 17, 2005 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - - .text - .globl arm_sha_transform - -/* - * void sha_transform(uint32_t *hash, const unsigned char *data, uint32_t *W); - * - * note: the "data" pointer may be unaligned. - */ - -arm_sha_transform: - - stmfd sp!, {r4 - r8, lr} - - @ for (i = 0; i < 16; i++) - @ W[i] = ntohl(((uint32_t *)data)[i]); - -#ifdef __ARMEB__ - mov r4, r0 - mov r0, r2 - mov r2, #64 - bl memcpy - mov r2, r0 - mov r0, r4 -#else - mov r3, r2 - mov lr, #16 -1: ldrb r4, [r1], #1 - ldrb r5, [r1], #1 - ldrb r6, [r1], #1 - ldrb r7, [r1], #1 - subs lr, lr, #1 - orr r5, r5, r4, lsl #8 - orr r6, r6, r5, lsl #8 - orr r7, r7, r6, lsl #8 - str r7, [r3], #4 - bne 1b -#endif - - @ for (i = 0; i < 64; i++) - @ W[i+16] = ror(W[i+13] ^ W[i+8] ^ W[i+2] ^ W[i], 31); - - sub r3, r2, #4 - mov lr, #64 -2: ldr r4, [r3, #4]! - subs lr, lr, #1 - ldr r5, [r3, #8] - ldr r6, [r3, #32] - ldr r7, [r3, #52] - eor r4, r4, r5 - eor r4, r4, r6 - eor r4, r4, r7 - mov r4, r4, ror #31 - str r4, [r3, #64] - bne 2b - - /* - * The SHA functions are: - * - * f1(B,C,D) = (D ^ (B & (C ^ D))) - * f2(B,C,D) = (B ^ C ^ D) - * f3(B,C,D) = ((B & C) | (D & (B | C))) - * - * Then the sub-blocks are processed as follows: - * - * A' = ror(A, 27) + f(B,C,D) + E + K + *W++ - * B' = A - * C' = ror(B, 2) - * D' = C - * E' = D - * - * We therefore unroll each loop 5 times to avoid register shuffling. - * Also the ror for C (and also D and E which are successivelyderived - * from it) is applied in place to cut on an additional mov insn for - * each round. - */ - - .macro sha_f1, A, B, C, D, E - ldr r3, [r2], #4 - eor ip, \C, \D - add \E, r1, \E, ror #2 - and ip, \B, ip, ror #2 - add \E, \E, \A, ror #27 - eor ip, ip, \D, ror #2 - add \E, \E, r3 - add \E, \E, ip - .endm - - .macro sha_f2, A, B, C, D, E - ldr r3, [r2], #4 - add \E, r1, \E, ror #2 - eor ip, \B, \C, ror #2 - add \E, \E, \A, ror #27 - eor ip, ip, \D, ror #2 - add \E, \E, r3 - add \E, \E, ip - .endm - - .macro sha_f3, A, B, C, D, E - ldr r3, [r2], #4 - add \E, r1, \E, ror #2 - orr ip, \B, \C, ror #2 - add \E, \E, \A, ror #27 - and ip, ip, \D, ror #2 - add \E, \E, r3 - and r3, \B, \C, ror #2 - orr ip, ip, r3 - add \E, \E, ip - .endm - - ldmia r0, {r4 - r8} - - mov lr, #4 - ldr r1, .L_sha_K + 0 - - /* adjust initial values */ - mov r6, r6, ror #30 - mov r7, r7, ror #30 - mov r8, r8, ror #30 - -3: subs lr, lr, #1 - sha_f1 r4, r5, r6, r7, r8 - sha_f1 r8, r4, r5, r6, r7 - sha_f1 r7, r8, r4, r5, r6 - sha_f1 r6, r7, r8, r4, r5 - sha_f1 r5, r6, r7, r8, r4 - bne 3b - - ldr r1, .L_sha_K + 4 - mov lr, #4 - -4: subs lr, lr, #1 - sha_f2 r4, r5, r6, r7, r8 - sha_f2 r8, r4, r5, r6, r7 - sha_f2 r7, r8, r4, r5, r6 - sha_f2 r6, r7, r8, r4, r5 - sha_f2 r5, r6, r7, r8, r4 - bne 4b - - ldr r1, .L_sha_K + 8 - mov lr, #4 - -5: subs lr, lr, #1 - sha_f3 r4, r5, r6, r7, r8 - sha_f3 r8, r4, r5, r6, r7 - sha_f3 r7, r8, r4, r5, r6 - sha_f3 r6, r7, r8, r4, r5 - sha_f3 r5, r6, r7, r8, r4 - bne 5b - - ldr r1, .L_sha_K + 12 - mov lr, #4 - -6: subs lr, lr, #1 - sha_f2 r4, r5, r6, r7, r8 - sha_f2 r8, r4, r5, r6, r7 - sha_f2 r7, r8, r4, r5, r6 - sha_f2 r6, r7, r8, r4, r5 - sha_f2 r5, r6, r7, r8, r4 - bne 6b - - ldmia r0, {r1, r2, r3, ip, lr} - add r4, r1, r4 - add r5, r2, r5 - add r6, r3, r6, ror #2 - add r7, ip, r7, ror #2 - add r8, lr, r8, ror #2 - stmia r0, {r4 - r8} - - ldmfd sp!, {r4 - r8, pc} - -.L_sha_K: - .word 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c new file mode 100644 index 0000000000..d8934757a5 --- /dev/null +++ b/block-sha1/sha1.c @@ -0,0 +1,282 @@ +/* + * SHA1 routine optimized to do word accesses rather than byte accesses, + * and to avoid unnecessary copies into the context array. + * + * This was initially based on the Mozilla SHA1 implementation, although + * none of the original Mozilla code remains. + */ + +/* this is only to get definitions for memcpy(), ntohl() and htonl() */ +#include "../git-compat-util.h" + +#include "sha1.h" + +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +/* + * Force usage of rol or ror by selecting the one with the smaller constant. + * It _can_ generate slightly smaller code (a constant of 1 is special), but + * perhaps more importantly it's possibly faster on any uarch that does a + * rotate with a loop. + */ + +#define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; }) +#define SHA_ROL(x,n) SHA_ASM("rol", x, n) +#define SHA_ROR(x,n) SHA_ASM("ror", x, n) + +#else + +#define SHA_ROT(X,l,r) (((X) << (l)) | ((X) >> (r))) +#define SHA_ROL(X,n) SHA_ROT(X,n,32-(n)) +#define SHA_ROR(X,n) SHA_ROT(X,32-(n),n) + +#endif + +/* + * If you have 32 registers or more, the compiler can (and should) + * try to change the array[] accesses into registers. However, on + * machines with less than ~25 registers, that won't really work, + * and at least gcc will make an unholy mess of it. + * + * So to avoid that mess which just slows things down, we force + * the stores to memory to actually happen (we might be better off + * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as + * suggested by Artur Skawina - that will also make gcc unable to + * try to do the silly "optimize away loads" part because it won't + * see what the value will be). + * + * Ben Herrenschmidt reports that on PPC, the C version comes close + * to the optimized asm with this (ie on PPC you don't want that + * 'volatile', since there are lots of registers). + * + * On ARM we get the best code generation by forcing a full memory barrier + * between each SHA_ROUND, otherwise gcc happily get wild with spilling and + * the stack frame size simply explode and performance goes down the drain. + */ + +#if defined(__i386__) || defined(__x86_64__) + #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val)) +#elif defined(__GNUC__) && defined(__arm__) + #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0) +#else + #define setW(x, val) (W(x) = (val)) +#endif + +/* + * Performance might be improved if the CPU architecture is OK with + * unaligned 32-bit loads and a fast ntohl() is available. + * Otherwise fall back to byte loads and shifts which is portable, + * and is faster on architectures with memory alignment issues. + */ + +#if defined(__i386__) || defined(__x86_64__) || \ + defined(__ppc__) || defined(__ppc64__) || \ + defined(__powerpc__) || defined(__powerpc64__) || \ + defined(__s390__) || defined(__s390x__) + +#define get_be32(p) ntohl(*(unsigned int *)(p)) +#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0) + +#else + +#define get_be32(p) ( \ + (*((unsigned char *)(p) + 0) << 24) | \ + (*((unsigned char *)(p) + 1) << 16) | \ + (*((unsigned char *)(p) + 2) << 8) | \ + (*((unsigned char *)(p) + 3) << 0) ) +#define put_be32(p, v) do { \ + unsigned int __v = (v); \ + *((unsigned char *)(p) + 0) = __v >> 24; \ + *((unsigned char *)(p) + 1) = __v >> 16; \ + *((unsigned char *)(p) + 2) = __v >> 8; \ + *((unsigned char *)(p) + 3) = __v >> 0; } while (0) + +#endif + +/* This "rolls" over the 512-bit array */ +#define W(x) (array[(x)&15]) + +/* + * Where do we get the source from? The first 16 iterations get it from + * the input data, the next mix it from the 512-bit array. + */ +#define SHA_SRC(t) get_be32(data + t) +#define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1) + +#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \ + unsigned int TEMP = input(t); setW(t, TEMP); \ + E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \ + B = SHA_ROR(B, 2); } while (0) + +#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) +#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) +#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E ) +#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E ) +#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E ) + +static void blk_SHA1_Block(blk_SHA_CTX *ctx, const unsigned int *data) +{ + unsigned int A,B,C,D,E; + unsigned int array[16]; + + A = ctx->H[0]; + B = ctx->H[1]; + C = ctx->H[2]; + D = ctx->H[3]; + E = ctx->H[4]; + + /* Round 1 - iterations 0-16 take their input from 'data' */ + T_0_15( 0, A, B, C, D, E); + T_0_15( 1, E, A, B, C, D); + T_0_15( 2, D, E, A, B, C); + T_0_15( 3, C, D, E, A, B); + T_0_15( 4, B, C, D, E, A); + T_0_15( 5, A, B, C, D, E); + T_0_15( 6, E, A, B, C, D); + T_0_15( 7, D, E, A, B, C); + T_0_15( 8, C, D, E, A, B); + T_0_15( 9, B, C, D, E, A); + T_0_15(10, A, B, C, D, E); + T_0_15(11, E, A, B, C, D); + T_0_15(12, D, E, A, B, C); + T_0_15(13, C, D, E, A, B); + T_0_15(14, B, C, D, E, A); + T_0_15(15, A, B, C, D, E); + + /* Round 1 - tail. Input from 512-bit mixing array */ + T_16_19(16, E, A, B, C, D); + T_16_19(17, D, E, A, B, C); + T_16_19(18, C, D, E, A, B); + T_16_19(19, B, C, D, E, A); + + /* Round 2 */ + T_20_39(20, A, B, C, D, E); + T_20_39(21, E, A, B, C, D); + T_20_39(22, D, E, A, B, C); + T_20_39(23, C, D, E, A, B); + T_20_39(24, B, C, D, E, A); + T_20_39(25, A, B, C, D, E); + T_20_39(26, E, A, B, C, D); + T_20_39(27, D, E, A, B, C); + T_20_39(28, C, D, E, A, B); + T_20_39(29, B, C, D, E, A); + T_20_39(30, A, B, C, D, E); + T_20_39(31, E, A, B, C, D); + T_20_39(32, D, E, A, B, C); + T_20_39(33, C, D, E, A, B); + T_20_39(34, B, C, D, E, A); + T_20_39(35, A, B, C, D, E); + T_20_39(36, E, A, B, C, D); + T_20_39(37, D, E, A, B, C); + T_20_39(38, C, D, E, A, B); + T_20_39(39, B, C, D, E, A); + + /* Round 3 */ + T_40_59(40, A, B, C, D, E); + T_40_59(41, E, A, B, C, D); + T_40_59(42, D, E, A, B, C); + T_40_59(43, C, D, E, A, B); + T_40_59(44, B, C, D, E, A); + T_40_59(45, A, B, C, D, E); + T_40_59(46, E, A, B, C, D); + T_40_59(47, D, E, A, B, C); + T_40_59(48, C, D, E, A, B); + T_40_59(49, B, C, D, E, A); + T_40_59(50, A, B, C, D, E); + T_40_59(51, E, A, B, C, D); + T_40_59(52, D, E, A, B, C); + T_40_59(53, C, D, E, A, B); + T_40_59(54, B, C, D, E, A); + T_40_59(55, A, B, C, D, E); + T_40_59(56, E, A, B, C, D); + T_40_59(57, D, E, A, B, C); + T_40_59(58, C, D, E, A, B); + T_40_59(59, B, C, D, E, A); + + /* Round 4 */ + T_60_79(60, A, B, C, D, E); + T_60_79(61, E, A, B, C, D); + T_60_79(62, D, E, A, B, C); + T_60_79(63, C, D, E, A, B); + T_60_79(64, B, C, D, E, A); + T_60_79(65, A, B, C, D, E); + T_60_79(66, E, A, B, C, D); + T_60_79(67, D, E, A, B, C); + T_60_79(68, C, D, E, A, B); + T_60_79(69, B, C, D, E, A); + T_60_79(70, A, B, C, D, E); + T_60_79(71, E, A, B, C, D); + T_60_79(72, D, E, A, B, C); + T_60_79(73, C, D, E, A, B); + T_60_79(74, B, C, D, E, A); + T_60_79(75, A, B, C, D, E); + T_60_79(76, E, A, B, C, D); + T_60_79(77, D, E, A, B, C); + T_60_79(78, C, D, E, A, B); + T_60_79(79, B, C, D, E, A); + + ctx->H[0] += A; + ctx->H[1] += B; + ctx->H[2] += C; + ctx->H[3] += D; + ctx->H[4] += E; +} + +void blk_SHA1_Init(blk_SHA_CTX *ctx) +{ + ctx->size = 0; + + /* Initialize H with the magic constants (see FIPS180 for constants) */ + ctx->H[0] = 0x67452301; + ctx->H[1] = 0xefcdab89; + ctx->H[2] = 0x98badcfe; + ctx->H[3] = 0x10325476; + ctx->H[4] = 0xc3d2e1f0; +} + +void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len) +{ + int lenW = ctx->size & 63; + + ctx->size += len; + + /* Read the data into W and process blocks as they get full */ + if (lenW) { + int left = 64 - lenW; + if (len < left) + left = len; + memcpy(lenW + (char *)ctx->W, data, left); + lenW = (lenW + left) & 63; + len -= left; + data = ((const char *)data + left); + if (lenW) + return; + blk_SHA1_Block(ctx, ctx->W); + } + while (len >= 64) { + blk_SHA1_Block(ctx, data); + data = ((const char *)data + 64); + len -= 64; + } + if (len) + memcpy(ctx->W, data, len); +} + +void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) +{ + static const unsigned char pad[64] = { 0x80 }; + unsigned int padlen[2]; + int i; + + /* Pad with a binary 1 (ie 0x80), then zeroes, then length */ + padlen[0] = htonl(ctx->size >> 29); + padlen[1] = htonl(ctx->size << 3); + + i = ctx->size & 63; + blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i))); + blk_SHA1_Update(ctx, padlen, 8); + + /* Output hash */ + for (i = 0; i < 5; i++) + put_be32(hashout + i*4, ctx->H[i]); +} diff --git a/block-sha1/sha1.h b/block-sha1/sha1.h new file mode 100644 index 0000000000..b864df623e --- /dev/null +++ b/block-sha1/sha1.h @@ -0,0 +1,22 @@ +/* + * SHA1 routine optimized to do word accesses rather than byte accesses, + * and to avoid unnecessary copies into the context array. + * + * This was initially based on the Mozilla SHA1 implementation, although + * none of the original Mozilla code remains. + */ + +typedef struct { + unsigned long long size; + unsigned int H[5]; + unsigned int W[16]; +} blk_SHA_CTX; + +void blk_SHA1_Init(blk_SHA_CTX *ctx); +void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, unsigned long len); +void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx); + +#define git_SHA_CTX blk_SHA_CTX +#define git_SHA1_Init blk_SHA1_Init +#define git_SHA1_Update blk_SHA1_Update +#define git_SHA1_Final blk_SHA1_Final diff --git a/builtin-add.c b/builtin-add.c index 581a2a1748..006fd08769 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -105,8 +105,8 @@ static void refresh(int verbose, const char **pathspec) for (specs = 0; pathspec[specs]; specs++) /* nothing */; seen = xcalloc(specs, 1); - refresh_index(&the_index, verbose ? REFRESH_SAY_CHANGED : REFRESH_QUIET, - pathspec, seen); + refresh_index(&the_index, verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET, + pathspec, seen, "Unstaged changes after refreshing the index:"); for (i = 0; i < specs; i++) { if (!seen[i]) die("pathspec '%s' did not match any files", pathspec[i]); diff --git a/builtin-apply.c b/builtin-apply.c index 39dc96ae02..c8372a0a80 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -61,6 +61,13 @@ static enum ws_error_action { static int whitespace_error; static int squelch_whitespace_errors = 5; static int applied_after_fixing_ws; + +static enum ws_ignore { + ignore_ws_none, + ignore_ws_change, +} ws_ignore_action = ignore_ws_none; + + static const char *patch_input_file; static const char *root; static int root_len; @@ -97,6 +104,21 @@ static void parse_whitespace_option(const char *option) die("unrecognized whitespace option '%s'", option); } +static void parse_ignorewhitespace_option(const char *option) +{ + if (!option || !strcmp(option, "no") || + !strcmp(option, "false") || !strcmp(option, "never") || + !strcmp(option, "none")) { + ws_ignore_action = ignore_ws_none; + return; + } + if (!strcmp(option, "change")) { + ws_ignore_action = ignore_ws_change; + return; + } + die("unrecognized whitespace ignore option '%s'", option); +} + static void set_default_whitespace_mode(const char *whitespace_option) { if (!whitespace_option && !apply_default_whitespace) @@ -214,6 +236,62 @@ static uint32_t hash_line(const char *cp, size_t len) return h; } +/* + * Compare lines s1 of length n1 and s2 of length n2, ignoring + * whitespace difference. Returns 1 if they match, 0 otherwise + */ +static int fuzzy_matchlines(const char *s1, size_t n1, + const char *s2, size_t n2) +{ + const char *last1 = s1 + n1 - 1; + const char *last2 = s2 + n2 - 1; + int result = 0; + + if (n1 < 0 || n2 < 0) + return 0; + + /* ignore line endings */ + while ((*last1 == '\r') || (*last1 == '\n')) + last1--; + while ((*last2 == '\r') || (*last2 == '\n')) + last2--; + + /* skip leading whitespace */ + while (isspace(*s1) && (s1 <= last1)) + s1++; + while (isspace(*s2) && (s2 <= last2)) + s2++; + /* early return if both lines are empty */ + if ((s1 > last1) && (s2 > last2)) + return 1; + while (!result) { + result = *s1++ - *s2++; + /* + * Skip whitespace inside. We check for whitespace on + * both buffers because we don't want "a b" to match + * "ab" + */ + if (isspace(*s1) && isspace(*s2)) { + while (isspace(*s1) && s1 <= last1) + s1++; + while (isspace(*s2) && s2 <= last2) + s2++; + } + /* + * If we reached the end on one side only, + * lines don't match + */ + if ( + ((s2 > last2) && (s1 <= last1)) || + ((s1 > last1) && (s2 <= last2))) + return 0; + if ((s1 > last1) && (s2 > last2)) + break; + } + + return !result; +} + static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag) { ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc); @@ -1672,10 +1750,17 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf) } } +/* + * Update the preimage, and the common lines in postimage, + * from buffer buf of length len. If postlen is 0 the postimage + * is updated in place, otherwise it's updated on a new buffer + * of length postlen + */ + static void update_pre_post_images(struct image *preimage, struct image *postimage, char *buf, - size_t len) + size_t len, size_t postlen) { int i, ctx; char *new, *old, *fixed; @@ -1694,11 +1779,19 @@ static void update_pre_post_images(struct image *preimage, *preimage = fixed_preimage; /* - * Adjust the common context lines in postimage, in place. - * This is possible because whitespace fixing does not make - * the string grow. + * Adjust the common context lines in postimage. This can be + * done in-place when we are just doing whitespace fixing, + * which does not make the string grow, but needs a new buffer + * when ignoring whitespace causes the update, since in this case + * we could have e.g. tabs converted to multiple spaces. + * We trust the caller to tell us if the update can be done + * in place (postlen==0) or not. */ - new = old = postimage->buf; + old = postimage->buf; + if (postlen) + new = postimage->buf = xmalloc(postlen); + else + new = old; fixed = preimage->buf; for (i = ctx = 0; i < postimage->nr; i++) { size_t len = postimage->line[i].len; @@ -1773,12 +1866,56 @@ static int match_fragment(struct image *img, !memcmp(img->buf + try, preimage->buf, preimage->len)) return 1; + /* + * No exact match. If we are ignoring whitespace, run a line-by-line + * fuzzy matching. We collect all the line length information because + * we need it to adjust whitespace if we match. + */ + if (ws_ignore_action == ignore_ws_change) { + size_t imgoff = 0; + size_t preoff = 0; + size_t postlen = postimage->len; + for (i = 0; i < preimage->nr; i++) { + size_t prelen = preimage->line[i].len; + size_t imglen = img->line[try_lno+i].len; + + if (!fuzzy_matchlines(img->buf + try + imgoff, imglen, + preimage->buf + preoff, prelen)) + return 0; + if (preimage->line[i].flag & LINE_COMMON) + postlen += imglen - prelen; + imgoff += imglen; + preoff += prelen; + } + + /* + * Ok, the preimage matches with whitespace fuzz. Update it and + * the common postimage lines to use the same whitespace as the + * target. imgoff now holds the true length of the target that + * matches the preimage, and we need to update the line lengths + * of the preimage to match the target ones. + */ + fixed_buf = xmalloc(imgoff); + memcpy(fixed_buf, img->buf + try, imgoff); + for (i = 0; i < preimage->nr; i++) + preimage->line[i].len = img->line[try_lno+i].len; + + /* + * Update the preimage buffer and the postimage context lines. + */ + update_pre_post_images(preimage, postimage, + fixed_buf, imgoff, postlen); + return 1; + } + if (ws_error_action != correct_ws_error) return 0; /* * The hunk does not apply byte-by-byte, but the hash says - * it might with whitespace fuzz. + * it might with whitespace fuzz. We haven't been asked to + * ignore whitespace, we were asked to correct whitespace + * errors, so let's try matching after whitespace correction. */ fixed_buf = xmalloc(preimage->len + 1); buf = fixed_buf; @@ -1830,7 +1967,7 @@ static int match_fragment(struct image *img, * hunk match. Update the context lines in the postimage. */ update_pre_post_images(preimage, postimage, - fixed_buf, buf - fixed_buf); + fixed_buf, buf - fixed_buf, 0); return 1; unmatch_exit: @@ -3272,6 +3409,8 @@ static int git_apply_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "apply.whitespace")) return git_config_string(&apply_default_whitespace, var, value); + else if (!strcmp(var, "apply.ignorewhitespace")) + return git_config_string(&apply_default_ignorewhitespace, var, value); return git_default_config(var, value, cb); } @@ -3308,6 +3447,16 @@ static int option_parse_z(const struct option *opt, return 0; } +static int option_parse_space_change(const struct option *opt, + const char *arg, int unset) +{ + if (unset) + ws_ignore_action = ignore_ws_none; + else + ws_ignore_action = ignore_ws_change; + return 0; +} + static int option_parse_whitespace(const struct option *opt, const char *arg, int unset) { @@ -3384,6 +3533,12 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, "action", "detect new or modified lines that have whitespace errors", 0, option_parse_whitespace }, + { OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL, + "ignore changes in whitespace when finding context", + PARSE_OPT_NOARG, option_parse_space_change }, + { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL, + "ignore changes in whitespace when finding context", + PARSE_OPT_NOARG, option_parse_space_change }, OPT_BOOLEAN('R', "reverse", &apply_in_reverse, "apply the patch in reverse"), OPT_BOOLEAN(0, "unidiff-zero", &unidiff_zero, @@ -3408,6 +3563,8 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) git_config(git_apply_config, NULL); if (apply_default_whitespace) parse_whitespace_option(apply_default_whitespace); + if (apply_default_ignorewhitespace) + parse_ignorewhitespace_option(apply_default_ignorewhitespace); argc = parse_options(argc, argv, prefix, builtin_apply_options, apply_usage, 0); diff --git a/builtin-blame.c b/builtin-blame.c index fd6ca51eeb..7512773b40 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -1348,7 +1348,7 @@ static void get_ac_line(const char *inbuf, const char *what, /* * Now, convert both name and e-mail using mailmap */ - if(map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) { + if (map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) { /* Add a trailing '>' to email, since map_user returns plain emails Note: It already has '<', since we replace from mail+1 */ mailpos = memchr(mail, '\0', mail_len); diff --git a/builtin-branch.c b/builtin-branch.c index 1a03d5f356..9f57992062 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -586,7 +586,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT_BIT('m', NULL, &rename, "move/rename a branch and its reflog", 1), OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2), OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"), - OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"), + OPT_BOOLEAN('f', "force", &force_create, "force creation (when already exists)"), { OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref, "commit", "print only not merged branches", diff --git a/builtin-checkout.c b/builtin-checkout.c index 8a9a474218..36e2116ea2 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -402,7 +402,9 @@ static int merge_working_tree(struct checkout_opts *opts, topts.dir = xcalloc(1, sizeof(*topts.dir)); topts.dir->flags |= DIR_SHOW_IGNORED; topts.dir->exclude_per_dir = ".gitignore"; - tree = parse_tree_indirect(old->commit->object.sha1); + tree = parse_tree_indirect(old->commit ? + old->commit->object.sha1 : + (unsigned char *)EMPTY_TREE_SHA1_BIN); init_tree_desc(&trees[0], tree->buffer, tree->size); tree = parse_tree_indirect(new->commit->object.sha1); init_tree_desc(&trees[1], tree->buffer, tree->size); @@ -541,14 +543,6 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) parse_commit(new->commit); } - if (!old.commit && !opts->force) { - if (!opts->quiet) { - warning("You appear to be on a branch yet to be born."); - warning("Forcing checkout of %s.", new->name); - } - opts->force = 1; - } - ret = merge_working_tree(opts, &old, new); if (ret) return ret; @@ -590,7 +584,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) 2), OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage", 3), - OPT_BOOLEAN('f', NULL, &opts.force, "force"), + OPT_BOOLEAN('f', "force", &opts.force, "force"), OPT_BOOLEAN('m', "merge", &opts.merge, "merge"), OPT_STRING(0, "conflict", &conflict_style, "style", "conflict style (merge or diff3)"), diff --git a/builtin-clean.c b/builtin-clean.c index 05c763cbec..28cdcd0274 100644 --- a/builtin-clean.c +++ b/builtin-clean.c @@ -41,7 +41,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) struct option options[] = { OPT__QUIET(&quiet), OPT__DRY_RUN(&show_only), - OPT_BOOLEAN('f', NULL, &force, "force"), + OPT_BOOLEAN('f', "force", &force, "force"), OPT_BOOLEAN('d', NULL, &remove_directories, "remove whole directories"), OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"), diff --git a/builtin-clone.c b/builtin-clone.c index e51978a157..0f231d8af5 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -38,7 +38,7 @@ static const char * const builtin_clone_usage[] = { }; static int option_quiet, option_no_checkout, option_bare, option_mirror; -static int option_local, option_no_hardlinks, option_shared; +static int option_local, option_no_hardlinks, option_shared, option_recursive; static char *option_template, *option_reference, *option_depth; static char *option_origin = NULL; static char *option_upload_pack = "git-upload-pack"; @@ -59,6 +59,8 @@ static struct option builtin_clone_options[] = { "don't use local hardlinks, always copy"), OPT_BOOLEAN('s', "shared", &option_shared, "setup as shared repository"), + OPT_BOOLEAN(0, "recursive", &option_recursive, + "setup as shared repository"), OPT_STRING(0, "template", &option_template, "path", "path the template repository"), OPT_STRING(0, "reference", &option_reference, "repo", @@ -73,6 +75,10 @@ static struct option builtin_clone_options[] = { OPT_END() }; +static const char *argv_submodule[] = { + "submodule", "update", "--init", "--recursive", NULL +}; + static char *get_repo_path(const char *repo, int *is_bundle) { static char *suffix[] = { "/.git", ".git", "" }; @@ -509,7 +515,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) option_upload_pack); refs = transport_get_remote_refs(transport); - if(refs) + if (refs) transport_fetch_refs(transport, refs); } @@ -610,6 +616,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1), sha1_to_hex(remote_head->old_sha1), "1", NULL); + + if (!err && option_recursive) + err = run_command_v_opt(argv_submodule, RUN_GIT_CMD); } strbuf_release(&reflog_msg); diff --git a/builtin-commit.c b/builtin-commit.c index 4bcce06fbf..200ffdaad4 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -51,7 +51,7 @@ static const char *template_file; static char *edit_message, *use_message; static char *author_name, *author_email, *author_date; static int all, edit_flag, also, interactive, only, amend, signoff; -static int quiet, verbose, no_verify, allow_empty; +static int quiet, verbose, no_verify, allow_empty, dry_run; static char *untracked_files_arg; /* * The default commit message cleanup mode will remove the lines @@ -103,6 +103,7 @@ static struct option builtin_commit_options[] = { OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"), OPT_BOOLEAN('o', "only", &only, "commit only specified files"), OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"), + OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"), OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"), { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"), @@ -217,12 +218,15 @@ static void create_base_index(void) exit(128); /* We've already reported the error, finish dying */ } -static char *prepare_index(int argc, const char **argv, const char *prefix) +static char *prepare_index(int argc, const char **argv, const char *prefix, int is_status) { int fd; struct string_list partial; const char **pathspec = NULL; + int refresh_flags = REFRESH_QUIET; + if (is_status) + refresh_flags |= REFRESH_UNMERGED; if (interactive) { if (interactive_add(argc, argv, prefix) != 0) die("interactive add failed"); @@ -253,7 +257,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix) if (all || (also && pathspec && *pathspec)) { int fd = hold_locked_index(&index_lock, 1); add_files_to_cache(also ? prefix : NULL, pathspec, 0); - refresh_cache(REFRESH_QUIET); + refresh_cache(refresh_flags); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) die("unable to write new_index file"); @@ -272,7 +276,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix) */ if (!pathspec || !*pathspec) { fd = hold_locked_index(&index_lock, 1); - refresh_cache(REFRESH_QUIET); + refresh_cache(refresh_flags); if (write_cache(fd, active_cache, active_nr) || commit_locked_index(&index_lock)) die("unable to write new_index file"); @@ -339,27 +343,24 @@ 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, int nowarn) +static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn, + struct wt_status *s) { - struct wt_status s; - - wt_status_prepare(&s); - if (wt_status_relative_paths) - s.prefix = prefix; + if (s->relative_paths) + s->prefix = prefix; if (amend) { - s.amend = 1; - s.reference = "HEAD^1"; + s->amend = 1; + s->reference = "HEAD^1"; } - s.verbose = verbose; - s.untracked = (show_untracked_files == SHOW_ALL_UNTRACKED_FILES); - s.index_file = index_file; - s.fp = fp; - s.nowarn = nowarn; + s->verbose = verbose; + s->index_file = index_file; + s->fp = fp; + s->nowarn = nowarn; - wt_status_print(&s); + wt_status_print(s); - return s.commitable; + return s->commitable; } static int is_a_merge(const unsigned char *sha1) @@ -413,7 +414,8 @@ static void determine_author_info(void) author_date = date; } -static int prepare_to_commit(const char *index_file, const char *prefix) +static int prepare_to_commit(const char *index_file, const char *prefix, + struct wt_status *s) { struct stat statbuf; int commitable, saved_color_setting; @@ -555,10 +557,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix) if (ident_shown) fprintf(fp, "#\n"); - saved_color_setting = wt_status_use_color; - wt_status_use_color = 0; - commitable = run_status(fp, index_file, prefix, 1); - wt_status_use_color = saved_color_setting; + saved_color_setting = s->use_color; + s->use_color = 0; + commitable = run_status(fp, index_file, prefix, 1, s); + s->use_color = saved_color_setting; } else { unsigned char sha1[20]; const char *parent = "HEAD"; @@ -579,7 +581,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix) if (!commitable && !in_merge && !allow_empty && !(amend && is_a_merge(head_sha1))) { - run_status(stdout, index_file, prefix, 0); + run_status(stdout, index_file, prefix, 0, s); return 0; } @@ -691,7 +693,8 @@ static const char *find_author_by_nickname(const char *name) static int parse_and_validate_options(int argc, const char *argv[], const char * const usage[], - const char *prefix) + const char *prefix, + struct wt_status *s) { int f = 0; @@ -794,11 +797,11 @@ static int parse_and_validate_options(int argc, const char *argv[], if (!untracked_files_arg) ; /* default already initialized */ else if (!strcmp(untracked_files_arg, "no")) - show_untracked_files = SHOW_NO_UNTRACKED_FILES; + s->show_untracked_files = SHOW_NO_UNTRACKED_FILES; else if (!strcmp(untracked_files_arg, "normal")) - show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; + s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; else if (!strcmp(untracked_files_arg, "all")) - show_untracked_files = SHOW_ALL_UNTRACKED_FILES; + s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES; else die("Invalid untracked files mode '%s'", untracked_files_arg); @@ -810,28 +813,93 @@ static int parse_and_validate_options(int argc, const char *argv[], return argc; } -int cmd_status(int argc, const char **argv, const char *prefix) +static int dry_run_commit(int argc, const char **argv, const char *prefix, + struct wt_status *s) { - const char *index_file; int commitable; + const char *index_file; - git_config(git_status_config, NULL); + index_file = prepare_index(argc, argv, prefix, 1); + commitable = run_status(stdout, index_file, prefix, 0, s); + rollback_index_files(); - if (wt_status_use_color == -1) - wt_status_use_color = git_use_color_default; + return commitable ? 0 : 1; +} - if (diff_use_color_default == -1) - diff_use_color_default = git_use_color_default; +static int parse_status_slot(const char *var, int offset) +{ + if (!strcasecmp(var+offset, "header")) + return WT_STATUS_HEADER; + if (!strcasecmp(var+offset, "updated") + || !strcasecmp(var+offset, "added")) + return WT_STATUS_UPDATED; + if (!strcasecmp(var+offset, "changed")) + return WT_STATUS_CHANGED; + if (!strcasecmp(var+offset, "untracked")) + return WT_STATUS_UNTRACKED; + if (!strcasecmp(var+offset, "nobranch")) + return WT_STATUS_NOBRANCH; + if (!strcasecmp(var+offset, "unmerged")) + return WT_STATUS_UNMERGED; + die("bad config variable '%s'", var); +} - argc = parse_and_validate_options(argc, argv, builtin_status_usage, prefix); +static int git_status_config(const char *k, const char *v, void *cb) +{ + struct wt_status *s = cb; - index_file = prepare_index(argc, argv, prefix); + if (!strcmp(k, "status.submodulesummary")) { + int is_bool; + s->submodule_summary = git_config_bool_or_int(k, v, &is_bool); + if (is_bool && s->submodule_summary) + s->submodule_summary = -1; + return 0; + } + if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) { + s->use_color = git_config_colorbool(k, v, -1); + return 0; + } + if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) { + int slot = parse_status_slot(k, 13); + if (!v) + return config_error_nonbool(k); + color_parse(v, k, s->color_palette[slot]); + return 0; + } + if (!strcmp(k, "status.relativepaths")) { + s->relative_paths = git_config_bool(k, v); + return 0; + } + if (!strcmp(k, "status.showuntrackedfiles")) { + if (!v) + return config_error_nonbool(k); + else if (!strcmp(v, "no")) + s->show_untracked_files = SHOW_NO_UNTRACKED_FILES; + else if (!strcmp(v, "normal")) + s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; + else if (!strcmp(v, "all")) + s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES; + else + return error("Invalid untracked files mode '%s'", v); + return 0; + } + return git_diff_ui_config(k, v, NULL); +} - commitable = run_status(stdout, index_file, prefix, 0); +int cmd_status(int argc, const char **argv, const char *prefix) +{ + struct wt_status s; - rollback_index_files(); + wt_status_prepare(&s); + git_config(git_status_config, &s); + if (s.use_color == -1) + s.use_color = git_use_color_default; + if (diff_use_color_default == -1) + diff_use_color_default = git_use_color_default; - return commitable ? 0 : 1; + argc = parse_and_validate_options(argc, argv, builtin_status_usage, + prefix, &s); + return dry_run_commit(argc, argv, prefix, &s); } static void print_summary(const char *prefix, const unsigned char *sha1) @@ -883,10 +951,12 @@ static void print_summary(const char *prefix, const unsigned char *sha1) static int git_commit_config(const char *k, const char *v, void *cb) { + struct wt_status *s = cb; + if (!strcmp(k, "commit.template")) return git_config_string(&template_file, k, v); - return git_status_config(k, v, cb); + return git_status_config(k, v, s); } int cmd_commit(int argc, const char **argv, const char *prefix) @@ -899,19 +969,26 @@ int cmd_commit(int argc, const char **argv, const char *prefix) struct commit_list *parents = NULL, **pptr = &parents; struct stat statbuf; int allow_fast_forward = 1; + struct wt_status s; - git_config(git_commit_config, NULL); - - if (wt_status_use_color == -1) - wt_status_use_color = git_use_color_default; + wt_status_prepare(&s); + git_config(git_commit_config, &s); - argc = parse_and_validate_options(argc, argv, builtin_commit_usage, prefix); + if (s.use_color == -1) + s.use_color = git_use_color_default; - index_file = prepare_index(argc, argv, prefix); + argc = parse_and_validate_options(argc, argv, builtin_commit_usage, + prefix, &s); + if (dry_run) { + if (diff_use_color_default == -1) + diff_use_color_default = git_use_color_default; + return dry_run_commit(argc, argv, prefix, &s); + } + index_file = prepare_index(argc, argv, prefix, 0); /* Set up everything for writing the commit object. This includes running hooks, writing the trees, and interacting with the user. */ - if (!prepare_to_commit(index_file, prefix)) { + if (!prepare_to_commit(index_file, prefix, &s)) { rollback_index_files(); return 1; } diff --git a/builtin-describe.c b/builtin-describe.c index 7a662980d1..df67a733ae 100644 --- a/builtin-describe.c +++ b/builtin-describe.c @@ -20,6 +20,7 @@ static int tags; /* Allow lightweight tags */ static int longformat; static int abbrev = DEFAULT_ABBREV; static int max_candidates = 10; +static int found_names; static const char *pattern; static int always; @@ -49,6 +50,7 @@ static void add_to_known_names(const char *path, memcpy(e->path, path, len); commit->util = e; } + found_names = 1; } static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data) @@ -195,6 +197,9 @@ static void describe(const char *arg, int last_one) for_each_ref(get_name, NULL); } + if (!found_names) + die("cannot describe '%s'", sha1_to_hex(sha1)); + n = cmit->util; if (n) { /* diff --git a/builtin-diff.c b/builtin-diff.c index 2e51f408f9..ffcdd055ca 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -218,6 +218,8 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv revs->max_count = 3; else if (!strcmp(argv[1], "-q")) options |= DIFF_SILENT_ON_REMOVED; + else if (!strcmp(argv[1], "-h")) + usage(builtin_diff_usage); else return error("invalid option: %s", argv[1]); argv++; argc--; diff --git a/builtin-fast-export.c b/builtin-fast-export.c index c48c18d0c8..b0a4029c94 100644 --- a/builtin-fast-export.c +++ b/builtin-fast-export.c @@ -26,6 +26,7 @@ static int progress; static enum { ABORT, VERBATIM, WARN, STRIP } signed_tag_mode = ABORT; static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ABORT; static int fake_missing_tagger; +static int no_data; static int parse_opt_signed_tag_mode(const struct option *opt, const char *arg, int unset) @@ -116,6 +117,9 @@ static void handle_object(const unsigned char *sha1) char *buf; struct object *object; + if (no_data) + return; + if (is_null_sha1(sha1)) return; @@ -173,7 +177,7 @@ static void show_filemodify(struct diff_queue_struct *q, * Links refer to objects in another repositories; * output the SHA-1 verbatim. */ - if (S_ISGITLINK(spec->mode)) + if (no_data || S_ISGITLINK(spec->mode)) printf("M %06o %s %s\n", spec->mode, sha1_to_hex(spec->sha1), spec->path); else { @@ -580,6 +584,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) "Import marks from this file"), OPT_BOOLEAN(0, "fake-missing-tagger", &fake_missing_tagger, "Fake a tagger when tags lack one"), + { OPTION_NEGBIT, 0, "data", &no_data, NULL, + "Skip output of blob data", + PARSE_OPT_NOARG | PARSE_OPT_NEGHELP, NULL, 1 }, OPT_END() }; diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index d7cc8cafbf..a5a83f1469 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -576,7 +576,7 @@ static void populate_value(struct refinfo *ref) if (!prefixcmp(name, "refname")) refname = ref->refname; - else if(!prefixcmp(name, "upstream")) { + else if (!prefixcmp(name, "upstream")) { struct branch *branch; /* only local branches may have an upstream */ if (prefixcmp(ref->refname, "refs/heads/")) diff --git a/builtin-fsck.c b/builtin-fsck.c index b3d38fa277..c58b0e337e 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -589,6 +589,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) struct alternate_object_database *alt; errors_found = 0; + read_replace_refs = 0; argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0); if (write_lost_and_found) { diff --git a/builtin-grep.c b/builtin-grep.c index f477659100..ad0e0a5385 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -53,25 +53,57 @@ static int grep_config(const char *var, const char *value, void *cb) } /* + * Return non-zero if max_depth is negative or path has no more then max_depth + * slashes. + */ +static int accept_subdir(const char *path, int max_depth) +{ + if (max_depth < 0) + return 1; + + while ((path = strchr(path, '/')) != NULL) { + max_depth--; + if (max_depth < 0) + return 0; + path++; + } + return 1; +} + +/* + * Return non-zero if name is a subdirectory of match and is not too deep. + */ +static int is_subdir(const char *name, int namelen, + const char *match, int matchlen, int max_depth) +{ + if (matchlen > namelen || strncmp(name, match, matchlen)) + return 0; + + if (name[matchlen] == '\0') /* exact match */ + return 1; + + if (!matchlen || match[matchlen-1] == '/' || name[matchlen] == '/') + return accept_subdir(name + matchlen + 1, max_depth); + + return 0; +} + +/* * git grep pathspecs are somewhat different from diff-tree pathspecs; * pathname wildcards are allowed. */ -static int pathspec_matches(const char **paths, const char *name) +static int pathspec_matches(const char **paths, const char *name, int max_depth) { int namelen, i; if (!paths || !*paths) - return 1; + return accept_subdir(name, max_depth); namelen = strlen(name); for (i = 0; paths[i]; i++) { const char *match = paths[i]; int matchlen = strlen(match); const char *cp, *meta; - if (!matchlen || - ((matchlen <= namelen) && - !strncmp(name, match, matchlen) && - (match[matchlen-1] == '/' || - name[matchlen] == '\0' || name[matchlen] == '/'))) + if (is_subdir(name, namelen, match, matchlen, max_depth)) return 1; if (!fnmatch(match, name, 0)) return 1; @@ -421,7 +453,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) int kept; if (!S_ISREG(ce->ce_mode)) continue; - if (!pathspec_matches(paths, ce->name)) + if (!pathspec_matches(paths, ce->name, opt->max_depth)) continue; name = ce->name; if (name[0] == '-') { @@ -478,7 +510,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached, struct cache_entry *ce = active_cache[nr]; if (!S_ISREG(ce->ce_mode)) continue; - if (!pathspec_matches(paths, ce->name)) + if (!pathspec_matches(paths, ce->name, opt->max_depth)) continue; /* * If CE_VALID is on, we assume worktree file and its cache entry @@ -538,7 +570,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths, strbuf_addch(&pathbuf, '/'); down = pathbuf.buf + tn_len; - if (!pathspec_matches(paths, down)) + if (!pathspec_matches(paths, down, opt->max_depth)) ; else if (S_ISREG(entry.mode)) hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len); @@ -692,6 +724,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix) OPT_SET_INT('I', NULL, &opt.binary, "don't match patterns in binary files", GREP_BINARY_NOMATCH), + { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, "depth", + "descend at most <depth> levels", PARSE_OPT_NONEG, + NULL, 1 }, OPT_GROUP(""), OPT_BIT('E', "extended-regexp", &opt.regflags, "use extended POSIX regular expressions", REG_EXTENDED), @@ -768,6 +803,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) opt.pathname = 1; opt.pattern_tail = &opt.pattern_list; opt.regflags = REG_NEWLINE; + opt.max_depth = -1; strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD); opt.color = -1; diff --git a/builtin-init-db.c b/builtin-init-db.c index 4a5600631c..dd84caecbc 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -6,6 +6,7 @@ #include "cache.h" #include "builtin.h" #include "exec_cmd.h" +#include "parse-options.h" #ifndef DEFAULT_GIT_TEMPLATE_DIR #define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates" @@ -370,8 +371,16 @@ static int guess_repository_type(const char *git_dir) return 1; } -static const char init_db_usage[] = -"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]]"; +static int shared_callback(const struct option *opt, const char *arg, int unset) +{ + *((int *) opt->value) = (arg) ? git_config_perm("arg", arg) : PERM_GROUP; + return 0; +} + +static const char *const init_db_usage[] = { + "git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [directory]", + NULL +}; /* * If you want to, you can share the DB area with any number of branches. @@ -384,25 +393,60 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) const char *git_dir; const char *template_dir = NULL; unsigned int flags = 0; - int i; - - for (i = 1; i < argc; i++, argv++) { - const char *arg = argv[1]; - if (!prefixcmp(arg, "--template=")) - template_dir = arg+11; - else if (!strcmp(arg, "--bare")) { - static char git_dir[PATH_MAX+1]; - is_bare_repository_cfg = 1; - setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, - sizeof(git_dir)), 0); - } else if (!strcmp(arg, "--shared")) - init_shared_repository = PERM_GROUP; - else if (!prefixcmp(arg, "--shared=")) - init_shared_repository = git_config_perm("arg", arg+9); - else if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet")) - flags |= INIT_DB_QUIET; - else - usage(init_db_usage); + const struct option init_db_options[] = { + OPT_STRING(0, "template", &template_dir, "template-directory", + "provide the directory from which templates will be used"), + OPT_SET_INT(0, "bare", &is_bare_repository_cfg, + "create a bare repository", 1), + { OPTION_CALLBACK, 0, "shared", &init_shared_repository, + "permissions", + "specify that the git repository is to be shared amongst several users", + PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0}, + OPT_BIT('q', "quiet", &flags, "be quiet", INIT_DB_QUIET), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0); + + if (argc == 1) { + int mkdir_tried = 0; + retry: + if (chdir(argv[0]) < 0) { + if (!mkdir_tried) { + int saved; + /* + * At this point we haven't read any configuration, + * and we know shared_repository should always be 0; + * but just in case we play safe. + */ + saved = shared_repository; + shared_repository = 0; + switch (safe_create_leading_directories_const(argv[0])) { + case -3: + errno = EEXIST; + /* fallthru */ + case -1: + die_errno("cannot mkdir %s", argv[0]); + break; + default: + break; + } + shared_repository = saved; + if (mkdir(argv[0], 0777) < 0) + die_errno("cannot mkdir %s", argv[0]); + mkdir_tried = 1; + goto retry; + } + die_errno("cannot chdir to %s", argv[0]); + } + } else if (0 < argc) { + usage(init_db_usage[0]); + } + if (is_bare_repository_cfg == 1) { + static char git_dir[PATH_MAX+1]; + + setenv(GIT_DIR_ENVIRONMENT, + getcwd(git_dir, sizeof(git_dir)), 0); } if (init_shared_repository != -1) diff --git a/builtin-log.c b/builtin-log.c index 8d93c1a2a4..25e21ed415 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -27,6 +27,10 @@ static int default_show_root = 1; static const char *fmt_patch_subject_prefix = "PATCH"; static const char *fmt_pretty; +static const char * const builtin_log_usage = + "git log [<options>] [<since>..<until>] [[--] <path>...]\n" + " or: git show [options] <object>..."; + static void cmd_log_init(int argc, const char **argv, const char *prefix, struct rev_info *rev) { @@ -69,6 +73,8 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix, die("invalid --decorate option: %s", arg); } else if (!strcmp(arg, "--source")) { rev->show_source = 1; + } else if (!strcmp(arg, "-h")) { + usage(builtin_log_usage); } else die("unrecognized argument: %s", arg); } diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index 92637ac0ba..b0b5d8f6cb 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -765,7 +765,6 @@ static void handle_filter(struct strbuf *line) static void handle_body(void) { - int len = 0; struct strbuf prev = STRBUF_INIT; /* Skip up to the first boundary */ @@ -775,8 +774,6 @@ static void handle_body(void) } do { - strbuf_setlen(&line, line.len + len); - /* process any boundary lines */ if (*content_top && is_multipart_boundary(&line)) { /* flush any leftover */ @@ -832,10 +829,7 @@ static void handle_body(void) handle_filter(&line); } - strbuf_reset(&line); - if (strbuf_avail(&line) < 100) - strbuf_grow(&line, 100); - } while ((len = read_line_with_nul(line.buf, strbuf_avail(&line), fin))); + } while (!strbuf_getwholeline(&line, fin, '\n')); handle_body_out: strbuf_release(&prev); diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c index ad5f6b593d..ee6ca0ebcd 100644 --- a/builtin-mailsplit.c +++ b/builtin-mailsplit.c @@ -7,6 +7,7 @@ #include "cache.h" #include "builtin.h" #include "string-list.h" +#include "strbuf.h" static const char git_mailsplit_usage[] = "git mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> [<mbox>|<Maildir>...]"; @@ -42,26 +43,8 @@ static int is_from_line(const char *line, int len) return 1; } -/* Could be as small as 64, enough to hold a Unix "From " line. */ -static char buf[4096]; - -/* We cannot use fgets() because our lines can contain NULs */ -int read_line_with_nul(char *buf, int size, FILE *in) -{ - int len = 0, c; - - for (;;) { - c = getc(in); - if (c == EOF) - break; - buf[len++] = c; - if (c == '\n' || len + 1 >= size) - break; - } - buf[len] = '\0'; - - return len; -} +static struct strbuf buf = STRBUF_INIT; +static int keep_cr; /* Called with the first line (potentially partial) * already in buf[] -- normally that should begin with @@ -71,10 +54,9 @@ int read_line_with_nul(char *buf, int size, FILE *in) static int split_one(FILE *mbox, const char *name, int allow_bare) { FILE *output = NULL; - int len = strlen(buf); int fd; int status = 0; - int is_bare = !is_from_line(buf, len); + int is_bare = !is_from_line(buf.buf, buf.len); if (is_bare && !allow_bare) goto corrupt; @@ -88,20 +70,23 @@ static int split_one(FILE *mbox, const char *name, int allow_bare) * "From " and having something that looks like a date format. */ for (;;) { - int is_partial = len && buf[len-1] != '\n'; + if (!keep_cr && buf.len > 1 && buf.buf[buf.len-1] == '\n' && + buf.buf[buf.len-2] == '\r') { + strbuf_setlen(&buf, buf.len-2); + strbuf_addch(&buf, '\n'); + } - if (fwrite(buf, 1, len, output) != len) + if (fwrite(buf.buf, 1, buf.len, output) != buf.len) die_errno("cannot write output"); - len = read_line_with_nul(buf, sizeof(buf), mbox); - if (len == 0) { + if (strbuf_getwholeline(&buf, mbox, '\n')) { if (feof(mbox)) { status = 1; break; } die_errno("cannot read mbox"); } - if (!is_partial && !is_bare && is_from_line(buf, len)) + if (!is_bare && is_from_line(buf.buf, buf.len)) break; /* done with one message */ } fclose(output); @@ -166,7 +151,7 @@ static int split_maildir(const char *maildir, const char *dir, goto out; } - if (fgets(buf, sizeof(buf), f) == NULL) { + if (strbuf_getwholeline(&buf, f, '\n')) { error("cannot read mail %s (%s)", file, strerror(errno)); goto out; } @@ -203,7 +188,7 @@ static int split_mbox(const char *file, const char *dir, int allow_bare, } while (isspace(peek)); ungetc(peek, f); - if (fgets(buf, sizeof(buf), f) == NULL) { + if (strbuf_getwholeline(&buf, f, '\n')) { /* empty stdin is OK */ if (f != stdin) { error("cannot read mbox %s", file); @@ -248,6 +233,8 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix) nr = strtol(arg+2, NULL, 10); } else if ( arg[1] == 'b' && !arg[2] ) { allow_bare = 1; + } else if (!strcmp(arg, "--keep-cr")) { + keep_cr = 1; } else if ( arg[1] == 'o' && arg[2] ) { dir = arg+2; } else if ( arg[1] == '-' && !arg[2] ) { diff --git a/builtin-merge-base.c b/builtin-merge-base.c index a6ec2f7ab7..54e7ec2237 100644 --- a/builtin-merge-base.c +++ b/builtin-merge-base.c @@ -23,7 +23,7 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all) } static const char * const merge_base_usage[] = { - "git merge-base [--all] <commit-id> <commit-id>...", + "git merge-base [-a|--all] <commit> <commit>...", NULL }; diff --git a/builtin-merge.c b/builtin-merge.c index f4de73fa9d..b6b84286b2 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -598,7 +598,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, discard_cache(); if (read_cache() < 0) die("failed to read the cache"); - return -ret; + return ret; } } diff --git a/builtin-mv.c b/builtin-mv.c index b592c367b2..1b20028c67 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -53,7 +53,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; struct option builtin_mv_options[] = { OPT__DRY_RUN(&show_only), - OPT_BOOLEAN('f', NULL, &force, "force move/rename even if target exists"), + OPT_BOOLEAN('f', "force", &force, "force move/rename even if target exists"), OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"), OPT_END(), }; diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 9cc8a8451d..1ff2ce6df4 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1599,7 +1599,7 @@ static void *threaded_find_deltas(void *arg) static void ll_find_deltas(struct object_entry **list, unsigned list_size, int window, int depth, unsigned *processed) { - struct thread_params p[delta_search_threads]; + struct thread_params *p; int i, ret, active_threads = 0; if (delta_search_threads <= 1) { @@ -1609,6 +1609,7 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size, if (progress > pack_to_stdout) fprintf(stderr, "Delta compression using up to %d threads.\n", delta_search_threads); + p = xcalloc(delta_search_threads, sizeof(*p)); /* Partition the work amongst work threads. */ for (i = 0; i < delta_search_threads; i++) { @@ -1717,6 +1718,7 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size, active_threads--; } } + free(p); } #else @@ -1808,7 +1810,7 @@ static void prepare_pack(int window, int depth) static int git_pack_config(const char *k, const char *v, void *cb) { - if(!strcmp(k, "pack.window")) { + if (!strcmp(k, "pack.window")) { window = git_config_int(k, v); return 0; } @@ -2098,6 +2100,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) int rp_ac_alloc = 64; int rp_ac; + read_replace_refs = 0; + rp_av = xcalloc(rp_ac_alloc, sizeof(*rp_av)); rp_av[0] = "pack-objects"; diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c index 00590b1c3c..be99eb0ac4 100644 --- a/builtin-prune-packed.c +++ b/builtin-prune-packed.c @@ -1,9 +1,12 @@ #include "builtin.h" #include "cache.h" #include "progress.h" +#include "parse-options.h" -static const char prune_packed_usage[] = -"git prune-packed [-n] [-q]"; +static const char * const prune_packed_usage[] = { + "git prune-packed [-n|--dry-run] [-q|--quiet]", + NULL +}; #define DRY_RUN 01 #define VERBOSE 02 @@ -68,24 +71,16 @@ void prune_packed_objects(int opts) int cmd_prune_packed(int argc, const char **argv, const char *prefix) { - int i; int opts = VERBOSE; + const struct option prune_packed_options[] = { + OPT_BIT('n', "dry-run", &opts, "dry run", DRY_RUN), + OPT_NEGBIT('q', "quiet", &opts, "be quiet", VERBOSE), + OPT_END() + }; - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; + argc = parse_options(argc, argv, prefix, prune_packed_options, + prune_packed_usage, 0); - if (*arg == '-') { - if (!strcmp(arg, "-n")) - opts |= DRY_RUN; - else if (!strcmp(arg, "-q")) - opts &= ~VERBOSE; - else - usage(prune_packed_usage); - continue; - } - /* Handle arguments here .. */ - usage(prune_packed_usage); - } prune_packed_objects(opts); return 0; } diff --git a/builtin-prune.c b/builtin-prune.c index 0ed9cce4a2..8459aec8e8 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -140,6 +140,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix) char *s; save_commit_buffer = 0; + read_replace_refs = 0; init_revisions(&revs, prefix); argc = parse_options(argc, argv, prefix, options, prune_usage, 0); diff --git a/builtin-push.c b/builtin-push.c index 50328f4b08..67f6d96fbe 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -175,6 +175,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) const char *repo = NULL; /* default repository */ struct option options[] = { + OPT_BIT('q', "quiet", &flags, "be quiet", TRANSPORT_PUSH_QUIET), OPT_BIT('v', "verbose", &flags, "be verbose", TRANSPORT_PUSH_VERBOSE), OPT_STRING( 0 , "repo", &repo, "repository", "repository"), OPT_BIT( 0 , "all", &flags, "push all refs", TRANSPORT_PUSH_ALL), diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 82e25eaa07..14c836b169 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -12,6 +12,7 @@ #include "unpack-trees.h" #include "dir.h" #include "builtin.h" +#include "parse-options.h" static int nr_trees; static struct tree *trees[MAX_UNPACK_TREES]; @@ -29,7 +30,39 @@ static int list_tree(unsigned char *sha1) return 0; } -static const char read_tree_usage[] = "git read-tree (<sha> | [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <sha1> [<sha2> [<sha3>]])"; +static const char * const read_tree_usage[] = { + "git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]] [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]]", + NULL +}; + +static int index_output_cb(const struct option *opt, const char *arg, + int unset) +{ + set_alternate_index_output(arg); + return 0; +} + +static int exclude_per_directory_cb(const struct option *opt, const char *arg, + int unset) +{ + struct dir_struct *dir; + struct unpack_trees_options *opts; + + opts = (struct unpack_trees_options *)opt->value; + + if (opts->dir) + die("more than one --exclude-per-directory given."); + + dir = xcalloc(1, sizeof(*opts->dir)); + dir->flags |= DIR_SHOW_IGNORED; + dir->exclude_per_dir = arg; + opts->dir = dir; + /* We do not need to nor want to do read-directory + * here; we are merely interested in reusing the + * per directory ignore stack mechanism. + */ + return 0; +} static struct lock_file lock_file; @@ -39,6 +72,34 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) unsigned char sha1[20]; struct tree_desc t[MAX_UNPACK_TREES]; struct unpack_trees_options opts; + int prefix_set = 0; + const struct option read_tree_options[] = { + { OPTION_CALLBACK, 0, "index-output", NULL, "FILE", + "write resulting index to <FILE>", + PARSE_OPT_NONEG, index_output_cb }, + OPT__VERBOSE(&opts.verbose_update), + OPT_GROUP("Merging"), + OPT_SET_INT('m', NULL, &opts.merge, + "perform a merge in addition to a read", 1), + OPT_SET_INT(0, "trivial", &opts.trivial_merges_only, + "3-way merge if no file level merging required", 1), + OPT_SET_INT(0, "aggressive", &opts.aggressive, + "3-way merge in presence of adds and removes", 1), + OPT_SET_INT(0, "reset", &opts.reset, + "same as -m, but discard unmerged entries", 1), + { OPTION_STRING, 0, "prefix", &opts.prefix, "<subdirectory>/", + "read the tree into the index under <subdirectory>/", + PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP }, + OPT_SET_INT('u', NULL, &opts.update, + "update working tree with merge result", 1), + { OPTION_CALLBACK, 0, "exclude-per-directory", &opts, + "gitignore", + "allow explicitly ignored files to be overwritten", + PARSE_OPT_NONEG, exclude_per_directory_cb }, + OPT_SET_INT('i', NULL, &opts.index_only, + "don't check the working tree after merging", 1), + OPT_END() + }; memset(&opts, 0, sizeof(opts)); opts.head_idx = -1; @@ -49,105 +110,21 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) newfd = hold_locked_index(&lock_file, 1); - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - /* "-u" means "update", meaning that a merge will update - * the working tree. - */ - if (!strcmp(arg, "-u")) { - opts.update = 1; - continue; - } + argc = parse_options(argc, argv, unused_prefix, read_tree_options, + read_tree_usage, 0); - if (!strcmp(arg, "-v")) { - opts.verbose_update = 1; - continue; - } + prefix_set = opts.prefix ? 1 : 0; + if (1 < opts.merge + opts.reset + prefix_set) + die("Which one? -m, --reset, or --prefix?"); - /* "-i" means "index only", meaning that a merge will - * not even look at the working tree. - */ - if (!strcmp(arg, "-i")) { - opts.index_only = 1; - continue; - } - - if (!prefixcmp(arg, "--index-output=")) { - set_alternate_index_output(arg + 15); - continue; - } - - /* "--prefix=<subdirectory>/" means keep the current index - * entries and put the entries from the tree under the - * given subdirectory. - */ - if (!prefixcmp(arg, "--prefix=")) { - if (stage || opts.merge || opts.prefix) - usage(read_tree_usage); - opts.prefix = arg + 9; - opts.merge = 1; - stage = 1; - if (read_cache_unmerged()) - die("you need to resolve your current index first"); - continue; - } - - /* This differs from "-m" in that we'll silently ignore - * unmerged entries and overwrite working tree files that - * correspond to them. - */ - if (!strcmp(arg, "--reset")) { - if (stage || opts.merge || opts.prefix) - usage(read_tree_usage); - opts.reset = 1; - opts.merge = 1; - stage = 1; - read_cache_unmerged(); - continue; - } - - if (!strcmp(arg, "--trivial")) { - opts.trivial_merges_only = 1; - continue; - } - - if (!strcmp(arg, "--aggressive")) { - opts.aggressive = 1; - continue; - } - - /* "-m" stands for "merge", meaning we start in stage 1 */ - if (!strcmp(arg, "-m")) { - if (stage || opts.merge || opts.prefix) - usage(read_tree_usage); - if (read_cache_unmerged()) - die("you need to resolve your current index first"); - stage = 1; - opts.merge = 1; - continue; - } - - if (!prefixcmp(arg, "--exclude-per-directory=")) { - struct dir_struct *dir; - - if (opts.dir) - die("more than one --exclude-per-directory are given."); - - dir = xcalloc(1, sizeof(*opts.dir)); - dir->flags |= DIR_SHOW_IGNORED; - dir->exclude_per_dir = arg + 24; - opts.dir = dir; - /* We do not need to nor want to do read-directory - * here; we are merely interested in reusing the - * per directory ignore stack mechanism. - */ - continue; - } + if (opts.reset || opts.merge || opts.prefix) { + if (read_cache_unmerged() && (opts.prefix || opts.merge)) + die("You need to resolve your current index first"); + stage = opts.merge = 1; + } - /* using -u and -i at the same time makes no sense */ - if (1 < opts.index_only + opts.update) - usage(read_tree_usage); + for (i = 0; i < argc; i++) { + const char *arg = argv[i]; if (get_sha1(arg, sha1)) die("Not a valid object name %s", arg); @@ -155,8 +132,11 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) die("failed to unpack tree object %s", arg); stage++; } + if (1 < opts.index_only + opts.update) + die("-u and -i at the same time makes no sense"); if ((opts.update||opts.index_only) && !opts.merge) - usage(read_tree_usage); + die("%s is meaningless without -m, --reset, or --prefix", + opts.update ? "-u" : "-i"); if ((opts.dir && !opts.update)) die("--exclude-per-directory is meaningless unless -u"); if (opts.merge && !opts.index_only) diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c index 6ec1d056e6..b771fe9b20 100644 --- a/builtin-receive-pack.c +++ b/builtin-receive-pack.c @@ -123,31 +123,6 @@ static struct command *commands; static const char pre_receive_hook[] = "hooks/pre-receive"; static const char post_receive_hook[] = "hooks/post-receive"; -static int run_status(int code, const char *cmd_name) -{ - switch (code) { - case 0: - return 0; - case -ERR_RUN_COMMAND_FORK: - return error("fork of %s failed", cmd_name); - case -ERR_RUN_COMMAND_EXEC: - return error("execute of %s failed", cmd_name); - case -ERR_RUN_COMMAND_PIPE: - return error("pipe failed"); - case -ERR_RUN_COMMAND_WAITPID: - return error("waitpid failed"); - case -ERR_RUN_COMMAND_WAITPID_WRONG_PID: - return error("waitpid is confused"); - case -ERR_RUN_COMMAND_WAITPID_SIGNAL: - return error("%s died of signal", cmd_name); - case -ERR_RUN_COMMAND_WAITPID_NOEXIT: - return error("%s died strangely", cmd_name); - default: - error("%s exited with error code %d", cmd_name, -code); - return -code; - } -} - static int run_receive_hook(const char *hook_name) { static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4]; @@ -174,7 +149,7 @@ static int run_receive_hook(const char *hook_name) code = start_command(&proc); if (code) - return run_status(code, hook_name); + return code; for (cmd = commands; cmd; cmd = cmd->next) { if (!cmd->error_string) { size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n", @@ -186,7 +161,7 @@ static int run_receive_hook(const char *hook_name) } } close(proc.in); - return run_status(finish_command(&proc), hook_name); + return finish_command(&proc); } static int run_update_hook(struct command *cmd) @@ -203,9 +178,8 @@ static int run_update_hook(struct command *cmd) argv[3] = sha1_to_hex(cmd->new_sha1); argv[4] = NULL; - return run_status(run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | - RUN_COMMAND_STDOUT_TO_STDERR), - update_hook); + return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | + RUN_COMMAND_STDOUT_TO_STDERR); } static int is_ref_checked_out(const char *ref) @@ -419,7 +393,6 @@ static void run_update_post_hook(struct command *cmd) argv[argc] = NULL; status = run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | RUN_COMMAND_STDOUT_TO_STDERR); - run_status(status, update_post_hook); } static void execute_commands(const char *unpacker_error) @@ -537,7 +510,6 @@ static const char *unpack(void) code = run_command_v_opt(unpacker, RUN_GIT_CMD); if (!code) return NULL; - run_status(code, unpacker[0]); return "unpack-objects abnormal exit"; } else { const char *keeper[7]; @@ -563,7 +535,6 @@ static const char *unpack(void) ip.git_cmd = 1; status = start_command(&ip); if (status) { - run_status(status, keeper[0]); return "index-pack fork failed"; } pack_lockfile = index_pack_lockfile(ip.out); @@ -573,7 +544,6 @@ static const char *unpack(void) reprepare_packed_git(); return NULL; } - run_status(status, keeper[0]); return "index-pack abnormal exit"; } } diff --git a/builtin-remote.c b/builtin-remote.c index 008abfe092..0777dd719b 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -385,7 +385,7 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0); matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"), fetch_map, 1); - for(ref = matches; ref; ref = ref->next) + for (ref = matches; ref; ref = ref->next) string_list_append(abbrev_branch(ref->name), &states->heads); free_refs(fetch_map); @@ -484,7 +484,7 @@ static int read_remote_branches(const char *refname, const char *symref; strbuf_addf(&buf, "refs/remotes/%s", rename->old); - if(!prefixcmp(refname, buf.buf)) { + if (!prefixcmp(refname, buf.buf)) { item = string_list_append(xstrdup(refname), rename->remote_branches); symref = resolve_ref(refname, orig_sha1, 1, &flag); if (flag & REF_ISSYMREF) diff --git a/builtin-replace.c b/builtin-replace.c new file mode 100644 index 0000000000..fe3a647a36 --- /dev/null +++ b/builtin-replace.c @@ -0,0 +1,159 @@ +/* + * Builtin "git replace" + * + * Copyright (c) 2008 Christian Couder <chriscool@tuxfamily.org> + * + * Based on builtin-tag.c by Kristian Høgsberg <krh@redhat.com> + * and Carlos Rica <jasampler@gmail.com> that was itself based on + * git-tag.sh and mktag.c by Linus Torvalds. + */ + +#include "cache.h" +#include "builtin.h" +#include "refs.h" +#include "parse-options.h" + +static const char * const git_replace_usage[] = { + "git replace [-f] <object> <replacement>", + "git replace -d <object>...", + "git replace -l [<pattern>]", + NULL +}; + +static int show_reference(const char *refname, const unsigned char *sha1, + int flag, void *cb_data) +{ + const char *pattern = cb_data; + + if (!fnmatch(pattern, refname, 0)) + printf("%s\n", refname); + + return 0; +} + +static int list_replace_refs(const char *pattern) +{ + if (pattern == NULL) + pattern = "*"; + + for_each_replace_ref(show_reference, (void *) pattern); + + return 0; +} + +typedef int (*each_replace_name_fn)(const char *name, const char *ref, + const unsigned char *sha1); + +static int for_each_replace_name(const char **argv, each_replace_name_fn fn) +{ + const char **p; + char ref[PATH_MAX]; + int had_error = 0; + unsigned char sha1[20]; + + for (p = argv; *p; p++) { + if (snprintf(ref, sizeof(ref), "refs/replace/%s", *p) + >= sizeof(ref)) { + error("replace ref name too long: %.*s...", 50, *p); + had_error = 1; + continue; + } + if (!resolve_ref(ref, sha1, 1, NULL)) { + error("replace ref '%s' not found.", *p); + had_error = 1; + continue; + } + if (fn(*p, ref, sha1)) + had_error = 1; + } + return had_error; +} + +static int delete_replace_ref(const char *name, const char *ref, + const unsigned char *sha1) +{ + if (delete_ref(ref, sha1, 0)) + return 1; + printf("Deleted replace ref '%s'\n", name); + return 0; +} + +static int replace_object(const char *object_ref, const char *replace_ref, + int force) +{ + unsigned char object[20], prev[20], repl[20]; + char ref[PATH_MAX]; + struct ref_lock *lock; + + if (get_sha1(object_ref, object)) + die("Failed to resolve '%s' as a valid ref.", object_ref); + if (get_sha1(replace_ref, repl)) + die("Failed to resolve '%s' as a valid ref.", replace_ref); + + if (snprintf(ref, sizeof(ref), + "refs/replace/%s", + sha1_to_hex(object)) > sizeof(ref) - 1) + die("replace ref name too long: %.*s...", 50, ref); + if (check_ref_format(ref)) + die("'%s' is not a valid ref name.", ref); + + if (!resolve_ref(ref, prev, 1, NULL)) + hashclr(prev); + else if (!force) + die("replace ref '%s' already exists", ref); + + lock = lock_any_ref_for_update(ref, prev, 0); + if (!lock) + die("%s: cannot lock the ref", ref); + if (write_ref_sha1(lock, repl, NULL) < 0) + die("%s: cannot update the ref", ref); + + return 0; +} + +int cmd_replace(int argc, const char **argv, const char *prefix) +{ + int list = 0, delete = 0, force = 0; + struct option options[] = { + OPT_BOOLEAN('l', NULL, &list, "list replace refs"), + OPT_BOOLEAN('d', NULL, &delete, "delete replace refs"), + OPT_BOOLEAN('f', NULL, &force, "replace the ref if it exists"), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0); + + if (list && delete) + usage_msg_opt("-l and -d cannot be used together", + git_replace_usage, options); + + if (force && (list || delete)) + usage_msg_opt("-f cannot be used with -d or -l", + git_replace_usage, options); + + /* Delete refs */ + if (delete) { + if (argc < 1) + usage_msg_opt("-d needs at least one argument", + git_replace_usage, options); + return for_each_replace_name(argv, delete_replace_ref); + } + + /* Replace object */ + if (!list && argc) { + if (argc != 2) + usage_msg_opt("bad number of arguments", + git_replace_usage, options); + return replace_object(argv[0], argv[1], force); + } + + /* List refs, even if "list" is not set */ + if (argc > 1) + usage_msg_opt("only one pattern can be given with -l", + git_replace_usage, options); + if (force) + usage_msg_opt("-f needs some arguments", + git_replace_usage, options); + + return list_replace_refs(argv[0]); +} diff --git a/builtin-reset.c b/builtin-reset.c index 5fa1789d0c..0fc0b0743f 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -108,7 +108,8 @@ static int update_index_refresh(int fd, struct lock_file *index_lock, int flags) if (read_cache() < 0) return error("Could not read index"); - result = refresh_cache(flags) ? 1 : 0; + result = refresh_index(&the_index, (flags), NULL, NULL, + "Unstaged changes after reset:") ? 1 : 0; if (write_cache(fd, active_cache, active_nr) || commit_locked_index(index_lock)) return error ("Could not refresh index"); @@ -261,7 +262,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) die("Cannot do %s reset with paths.", reset_type_names[reset_type]); return read_from_tree(prefix, argv + i, sha1, - quiet ? REFRESH_QUIET : REFRESH_SAY_CHANGED); + quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN); } if (reset_type == NONE) reset_type = MIXED; /* by default */ @@ -302,7 +303,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) break; case MIXED: /* Report what has not been updated. */ update_index_refresh(0, NULL, - quiet ? REFRESH_QUIET : REFRESH_SAY_CHANGED); + quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN); break; } diff --git a/builtin-send-pack.c b/builtin-send-pack.c index 47fb9f7baa..37e528e283 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -44,6 +44,7 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext NULL, NULL, NULL, + NULL, }; struct child_process po; int i; @@ -53,6 +54,8 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext argv[i++] = "--thin"; if (args->use_ofs_delta) argv[i++] = "--delta-base-offset"; + if (args->quiet) + argv[i++] = "-q"; memset(&po, 0, sizeof(po)); po.argv = argv; po.in = -1; diff --git a/builtin-shortlog.c b/builtin-shortlog.c index 6a3812ee18..4d4a3c82d6 100644 --- a/builtin-shortlog.c +++ b/builtin-shortlog.c @@ -56,7 +56,7 @@ static void insert_one_record(struct shortlog *log, /* copy author name to namebuf, to support matching on both name and email */ memcpy(namebuf, author, boemail - author); len = boemail - author; - while(len > 0 && isspace(namebuf[len-1])) + while (len > 0 && isspace(namebuf[len-1])) len--; namebuf[len] = 0; diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 01bea3b583..3510a86e38 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -6,8 +6,8 @@ #include "parse-options.h" static const char* show_branch_usage[] = { - "git show-branch [--sparse] [--current] [--all] [--remotes] [--topo-order] [--more=count | --list | --independent | --merge-base] [--topics] [--color] [<refs>...]", - "--reflog[=n[,b]] [--list] [--color] <branch>", + "git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [<rev> | <glob>]...", + "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]", NULL }; @@ -665,7 +665,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) OPT_BOOLEAN(0, "sha1-name", &sha1_name, "name commits with their object names"), OPT_BOOLEAN(0, "merge-base", &merge_base, - "act like git merge-base -a"), + "show possible merge bases"), OPT_BOOLEAN(0, "independent", &independent, "show refs unreachable from any other ref"), OPT_BOOLEAN(0, "topo-order", &lifo, diff --git a/builtin-tag.c b/builtin-tag.c index a51a6d1ea2..c4790185eb 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -390,7 +390,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"), OPT_STRING('u', NULL, &keyid, "key-id", "use another key to sign the tag"), - OPT_BOOLEAN('f', NULL, &force, "replace the tag if exists"), + OPT_BOOLEAN('f', "force", &force, "replace the tag if exists"), OPT_GROUP("Tag listing options"), { diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c index 557148a693..685566e0b5 100644 --- a/builtin-unpack-objects.c +++ b/builtin-unpack-objects.c @@ -181,10 +181,10 @@ static void write_cached_object(struct object *obj) static int check_object(struct object *obj, int type, void *data) { if (!obj) - return 0; + return 1; if (obj->flags & FLAG_WRITTEN) - return 1; + return 0; if (type != OBJ_ANY && obj->type != type) die("object type mismatch"); @@ -195,22 +195,24 @@ static int check_object(struct object *obj, int type, void *data) if (type != obj->type || type <= 0) die("object of unexpected type"); obj->flags |= FLAG_WRITTEN; - return 1; + return 0; } if (fsck_object(obj, 1, fsck_error_function)) die("Error in object"); - if (!fsck_walk(obj, check_object, NULL)) + if (fsck_walk(obj, check_object, NULL)) die("Error on reachable objects of %s", sha1_to_hex(obj->sha1)); write_cached_object(obj); - return 1; + return 0; } static void write_rest(void) { unsigned i; - for (i = 0; i < nr_objects; i++) - check_object(obj_list[i].obj, OBJ_ANY, NULL); + for (i = 0; i < nr_objects; i++) { + if (obj_list[i].obj) + check_object(obj_list[i].obj, OBJ_ANY, NULL); + } } static void added_object(unsigned nr, enum object_type type, @@ -495,6 +497,8 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix) int i; unsigned char sha1[20]; + read_replace_refs = 0; + git_config(git_default_config, NULL); quiet = !isatty(2); diff --git a/builtin-update-server-info.c b/builtin-update-server-info.c new file mode 100644 index 0000000000..2b3fddcc69 --- /dev/null +++ b/builtin-update-server-info.c @@ -0,0 +1,25 @@ +#include "cache.h" +#include "builtin.h" +#include "parse-options.h" + +static const char * const update_server_info_usage[] = { + "git update-server-info [--force]", + NULL +}; + +int cmd_update_server_info(int argc, const char **argv, const char *prefix) +{ + int force = 0; + struct option options[] = { + OPT_BOOLEAN('f', "force", &force, + "update the info files from scratch"), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, + update_server_info_usage, 0); + if (argc > 0) + usage_with_options(update_server_info_usage, options); + + return !!update_server_info(force); +} diff --git a/builtin-verify-pack.c b/builtin-verify-pack.c index a18df04cf9..b6079ae6cb 100644 --- a/builtin-verify-pack.c +++ b/builtin-verify-pack.c @@ -2,13 +2,18 @@ #include "cache.h" #include "pack.h" #include "pack-revindex.h" +#include "parse-options.h" #define MAX_CHAIN 50 -static void show_pack_info(struct packed_git *p) +#define VERIFY_PACK_VERBOSE 01 +#define VERIFY_PACK_STAT_ONLY 02 + +static void show_pack_info(struct packed_git *p, unsigned int flags) { uint32_t nr_objects, i; int cnt; + int stat_only = flags & VERIFY_PACK_STAT_ONLY; unsigned long chain_histogram[MAX_CHAIN+1], baseobjects; nr_objects = p->num_objects; @@ -31,16 +36,19 @@ static void show_pack_info(struct packed_git *p) type = packed_object_info_detail(p, offset, &size, &store_size, &delta_chain_length, base_sha1); - printf("%s ", sha1_to_hex(sha1)); + if (!stat_only) + printf("%s ", sha1_to_hex(sha1)); if (!delta_chain_length) { - printf("%-6s %lu %lu %"PRIuMAX"\n", - type, size, store_size, (uintmax_t)offset); + if (!stat_only) + printf("%-6s %lu %lu %"PRIuMAX"\n", + type, size, store_size, (uintmax_t)offset); baseobjects++; } else { - printf("%-6s %lu %lu %"PRIuMAX" %u %s\n", - type, size, store_size, (uintmax_t)offset, - delta_chain_length, sha1_to_hex(base_sha1)); + if (!stat_only) + printf("%-6s %lu %lu %"PRIuMAX" %u %s\n", + type, size, store_size, (uintmax_t)offset, + delta_chain_length, sha1_to_hex(base_sha1)); if (delta_chain_length <= MAX_CHAIN) chain_histogram[delta_chain_length]++; else @@ -65,10 +73,12 @@ static void show_pack_info(struct packed_git *p) chain_histogram[0] > 1 ? "s" : ""); } -static int verify_one_pack(const char *path, int verbose) +static int verify_one_pack(const char *path, unsigned int flags) { char arg[PATH_MAX]; int len; + int verbose = flags & VERIFY_PACK_VERBOSE; + int stat_only = flags & VERIFY_PACK_STAT_ONLY; struct packed_git *pack; int err; @@ -104,50 +114,53 @@ static int verify_one_pack(const char *path, int verbose) return error("packfile %s not found.", arg); install_packed_git(pack); - err = verify_pack(pack); - if (verbose) { + if (!stat_only) + err = verify_pack(pack); + else + err = open_pack_index(pack); + + if (verbose || stat_only) { if (err) printf("%s: bad\n", pack->pack_name); else { - show_pack_info(pack); - printf("%s: ok\n", pack->pack_name); + show_pack_info(pack, flags); + if (!stat_only) + printf("%s: ok\n", pack->pack_name); } } return err; } -static const char verify_pack_usage[] = "git verify-pack [-v] <pack>..."; +static const char * const verify_pack_usage[] = { + "git verify-pack [-v|--verbose] [-s|--stat-only] <pack>...", + NULL +}; int cmd_verify_pack(int argc, const char **argv, const char *prefix) { int err = 0; - int verbose = 0; - int no_more_options = 0; - int nothing_done = 1; + unsigned int flags = 0; + int i; + const struct option verify_pack_options[] = { + OPT_BIT('v', "verbose", &flags, "verbose", + VERIFY_PACK_VERBOSE), + OPT_BIT('s', "stat-only", &flags, "show statistics only", + VERIFY_PACK_STAT_ONLY), + OPT_END() + }; git_config(git_default_config, NULL); - while (1 < argc) { - if (!no_more_options && argv[1][0] == '-') { - if (!strcmp("-v", argv[1])) - verbose = 1; - else if (!strcmp("--", argv[1])) - no_more_options = 1; - else - usage(verify_pack_usage); - } - else { - if (verify_one_pack(argv[1], verbose)) - err = 1; - discard_revindex(); - nothing_done = 0; - } - argc--; argv++; + argc = parse_options(argc, argv, prefix, verify_pack_options, + verify_pack_usage, 0); + if (argc < 1) + usage_with_options(verify_pack_usage, verify_pack_options); + for (i = 0; i < argc; i++) { + if (verify_one_pack(argv[i], flags)) + err = 1; + discard_revindex(); } - if (nothing_done) - usage(verify_pack_usage); - return err; } diff --git a/builtin-verify-tag.c b/builtin-verify-tag.c index 7f7fda42f9..9f482c29f5 100644 --- a/builtin-verify-tag.c +++ b/builtin-verify-tag.c @@ -10,9 +10,12 @@ #include "tag.h" #include "run-command.h" #include <signal.h> +#include "parse-options.h" -static const char builtin_verify_tag_usage[] = - "git verify-tag [-v|--verbose] <tag>..."; +static const char * const verify_tag_usage[] = { + "git verify-tag [-v|--verbose] <tag>...", + NULL +}; #define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----" @@ -89,17 +92,17 @@ static int verify_tag(const char *name, int verbose) int cmd_verify_tag(int argc, const char **argv, const char *prefix) { int i = 1, verbose = 0, had_error = 0; + const struct option verify_tag_options[] = { + OPT__VERBOSE(&verbose), + OPT_END() + }; git_config(git_default_config, NULL); - if (argc > 1 && - (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))) { - verbose = 1; - i++; - } - + argc = parse_options(argc, argv, prefix, verify_tag_options, + verify_tag_usage, PARSE_OPT_KEEP_ARGV0); if (argc <= i) - usage(builtin_verify_tag_usage); + usage_with_options(verify_tag_usage, verify_tag_options); /* sometimes the program was terminated because this signal * was received in the process of writing the gpg input: */ diff --git a/builtin-write-tree.c b/builtin-write-tree.c index 3a24ce8157..b223af416f 100644 --- a/builtin-write-tree.c +++ b/builtin-write-tree.c @@ -7,9 +7,12 @@ #include "cache.h" #include "tree.h" #include "cache-tree.h" +#include "parse-options.h" -static const char write_tree_usage[] = -"git write-tree [--missing-ok] [--prefix=<prefix>/]"; +static const char * const write_tree_usage[] = { + "git write-tree [--missing-ok] [--prefix=<prefix>/]", + NULL +}; int cmd_write_tree(int argc, const char **argv, const char *unused_prefix) { @@ -17,27 +20,22 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix) const char *prefix = NULL; unsigned char sha1[20]; const char *me = "git-write-tree"; + struct option write_tree_options[] = { + OPT_BIT(0, "missing-ok", &flags, "allow missing objects", + WRITE_TREE_MISSING_OK), + { OPTION_STRING, 0, "prefix", &prefix, "<prefix>/", + "write tree object for a subdirectory <prefix>" , + PARSE_OPT_LITERAL_ARGHELP }, + { OPTION_BIT, 0, "ignore-cache-tree", &flags, NULL, + "only useful for debugging", + PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, NULL, + WRITE_TREE_IGNORE_CACHE_TREE }, + OPT_END() + }; git_config(git_default_config, NULL); - while (1 < argc) { - const char *arg = argv[1]; - if (!strcmp(arg, "--missing-ok")) - flags |= WRITE_TREE_MISSING_OK; - else if (!prefixcmp(arg, "--prefix=")) - prefix = arg + 9; - else if (!prefixcmp(arg, "--ignore-cache-tree")) - /* - * This is only useful for debugging, so I - * do not bother documenting it. - */ - flags |= WRITE_TREE_IGNORE_CACHE_TREE; - else - usage(write_tree_usage); - argc--; argv++; - } - - if (argc > 2) - die("too many options"); + argc = parse_options(argc, argv, unused_prefix, write_tree_options, + write_tree_usage, 0); ret = write_cache_as_tree(sha1, flags, prefix); switch (ret) { @@ -13,7 +13,6 @@ extern const char git_more_info_string[]; extern void list_common_cmds_help(void); extern const char *help_unknown_cmd(const char *cmd); extern void prune_packed_objects(int); -extern int read_line_with_nul(char *buf, int size, FILE *file); extern int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out); extern int commit_tree(const char *msg, unsigned char *tree, @@ -103,6 +102,7 @@ extern int cmd_tar_tree(int argc, const char **argv, const char *prefix); extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix); extern int cmd_update_index(int argc, const char **argv, const char *prefix); extern int cmd_update_ref(int argc, const char **argv, const char *prefix); +extern int cmd_update_server_info(int argc, const char **argv, const char *prefix); extern int cmd_upload_archive(int argc, const char **argv, const char *prefix); extern int cmd_upload_tar(int argc, const char **argv, const char *prefix); extern int cmd_verify_tag(int argc, const char **argv, const char *prefix); @@ -112,5 +112,6 @@ extern int cmd_write_tree(int argc, const char **argv, const char *prefix); extern int cmd_verify_pack(int argc, const char **argv, const char *prefix); extern int cmd_show_ref(int argc, const char **argv, const char *prefix); extern int cmd_pack_refs(int argc, const char **argv, const char *prefix); +extern int cmd_replace(int argc, const char **argv, const char *prefix); #endif @@ -330,7 +330,7 @@ static inline void remove_name_hash(struct cache_entry *ce) #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path)) #define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags)) #define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags)) -#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL) +#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL) #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options)) #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options)) #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase)) @@ -469,15 +469,15 @@ extern int index_path(unsigned char *sha1, const char *path, struct stat *st, in extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); /* "careful lstat()" */ -extern int check_path(const char *path, int len, struct stat *st); +extern int check_path(const char *path, int len, struct stat *st, int skiplen); #define REFRESH_REALLY 0x0001 /* ignore_valid */ #define REFRESH_UNMERGED 0x0002 /* allow unmerged */ #define REFRESH_QUIET 0x0004 /* be quiet about it */ #define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */ #define REFRESH_IGNORE_SUBMODULES 0x0010 /* ignore submodules */ -#define REFRESH_SAY_CHANGED 0x0020 /* say "changed" not "needs update" */ -extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen); +#define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */ +extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, char *header_msg); struct lock_file { struct lock_file *next; @@ -512,6 +512,7 @@ extern int log_all_ref_updates; extern int warn_ambiguous_refs; extern int shared_repository; extern const char *apply_default_whitespace; +extern const char *apply_default_ignorewhitespace; extern int zlib_compression_level; extern int core_compression_level; extern int core_compression_seen; @@ -519,6 +520,7 @@ extern size_t packed_git_window_size; extern size_t packed_git_limit; extern size_t delta_base_cache_limit; extern int auto_crlf; +extern int read_replace_refs; extern int fsync_object_files; extern int core_preload_index; @@ -655,7 +657,11 @@ char *strip_path_suffix(const char *path, const char *suffix); /* Read and unpack a sha1 file into memory, write memory to a sha1 file */ extern int sha1_object_info(const unsigned char *, unsigned long *); -extern void * read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size); +extern void *read_sha1_file_repl(const unsigned char *sha1, enum object_type *type, unsigned long *size, const unsigned char **replacement); +static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size) +{ + return read_sha1_file_repl(sha1, type, size, NULL); +} extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1); extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1); extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *); @@ -725,9 +731,14 @@ enum date_mode { }; const char *show_date(unsigned long time, int timezone, enum date_mode mode); +const char *show_date_relative(unsigned long time, int tz, + const struct timeval *now, + char *timebuf, + size_t timebuf_size); int parse_date(const char *date, char *buf, int bufsize); void datestamp(char *buf, int bufsize); unsigned long approxidate(const char *); +unsigned long approxidate_relative(const char *date, const struct timeval *now); enum date_mode parse_date_format(const char *format); #define IDENT_WARN_ON_NO_NAME 1 @@ -564,13 +564,13 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co while (interesting(list)) { struct commit *commit; struct commit_list *parents; - struct commit_list *n; + struct commit_list *next; int flags; commit = list->item; - n = list->next; + next = list->next; free(list); - list = n; + list = next; flags = commit->object.flags & (PARENT1 | PARENT2 | STALE); if (flags == (PARENT1 | PARENT2)) { @@ -598,11 +598,11 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co free_commit_list(list); list = result; result = NULL; while (list) { - struct commit_list *n = list->next; + struct commit_list *next = list->next; if (!(list->item->object.flags & STALE)) insert_by_date(list->item, &result); free(list); - list = n; + list = next; } return result; } @@ -123,6 +123,8 @@ struct commit_graft *read_graft_line(char *buf, int len); int register_commit_graft(struct commit_graft *, int); struct commit_graft *lookup_commit_graft(const unsigned char *sha1); +const unsigned char *lookup_replace_object(const unsigned char *sha1); + extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup); extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup); extern struct commit_list *get_octopus_merge_bases(struct commit_list *in); diff --git a/compat/bswap.h b/compat/bswap.h new file mode 100644 index 0000000000..7246a12c6e --- /dev/null +++ b/compat/bswap.h @@ -0,0 +1,36 @@ +/* + * Let's make sure we always have a sane definition for ntohl()/htonl(). + * Some libraries define those as a function call, just to perform byte + * shifting, bringing significant overhead to what should be a simple + * operation. + */ + +/* + * Default version that the compiler ought to optimize properly with + * constant values. + */ +static inline unsigned int default_swab32(unsigned int val) +{ + return (((val & 0xff000000) >> 24) | + ((val & 0x00ff0000) >> 8) | + ((val & 0x0000ff00) << 8) | + ((val & 0x000000ff) << 24)); +} + +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +#define bswap32(x) ({ \ + unsigned int __res; \ + if (__builtin_constant_p(x)) { \ + __res = default_swab32(x); \ + } else { \ + __asm__("bswap %0" : "=r" (__res) : "0" (x)); \ + } \ + __res; }) + +#undef ntohl +#undef htonl +#define ntohl(x) bswap32(x) +#define htonl(x) bswap32(x) + +#endif diff --git a/compat/mingw.h b/compat/mingw.h index c1859c5480..948de66eb5 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -17,9 +17,10 @@ typedef int pid_t; #define S_IROTH 0 #define S_IXOTH 0 -#define WIFEXITED(x) ((unsigned)(x) < 259) /* STILL_ACTIVE */ +#define WIFEXITED(x) 1 +#define WIFSIGNALED(x) 0 #define WEXITSTATUS(x) ((x) & 0xff) -#define WIFSIGNALED(x) ((unsigned)(x) > 259) +#define WTERMSIG(x) SIGTERM #define SIGHUP 1 #define SIGQUIT 3 diff --git a/compat/snprintf.c b/compat/snprintf.c index 6c0fb056a5..4d07087abd 100644 --- a/compat/snprintf.c +++ b/compat/snprintf.c @@ -3,7 +3,8 @@ /* * The size parameter specifies the available space, i.e. includes * the trailing NUL byte; but Windows's vsnprintf expects the - * number of characters to write without the trailing NUL. + * number of characters to write, and does not necessarily write the + * trailing NUL. */ #ifndef SNPRINTF_SIZE_CORR #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ < 4 diff --git a/configure.ac b/configure.ac index 3f1922d0bf..b09b8e446f 100644 --- a/configure.ac +++ b/configure.ac @@ -156,19 +156,11 @@ AC_MSG_NOTICE([CHECKS for site configuration]) # tests. These tests take up a significant amount of the total test time # but are not needed unless you plan to talk to SVN repos. # -# Define MOZILLA_SHA1 environment variable when running make to make use of -# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast -# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default -# choice) has very fast version optimized for i586. -# # Define PPC_SHA1 environment variable when running make to make use of # a bundled SHA1 routine optimized for PowerPC. # -# Define ARM_SHA1 environment variable when running make to make use of -# a bundled SHA1 routine optimized for ARM. -# # Define NO_OPENSSL environment variable if you do not have OpenSSL. -# This also implies MOZILLA_SHA1. +# This also implies BLK_SHA1. # # Define OPENSSLDIR=/foo/bar if your openssl header and library files are in # /foo/bar/include and /foo/bar/lib directories. @@ -513,7 +513,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig, signal(SIGCHLD, SIG_DFL); host = strstr(url, "://"); - if(host) { + if (host) { *host = '\0'; protocol = get_protocol(url); host += 3; diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 745b5fb78b..bf688e12e6 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -674,6 +674,7 @@ _git_am () --*) __gitcomp " --3way --committer-date-is-author-date --ignore-date + --ignore-whitespace --ignore-space-change --interactive --keep --no-utf8 --signoff --utf8 --whitespace= " @@ -695,6 +696,7 @@ _git_apply () --stat --numstat --summary --check --index --cached --index-info --reverse --reject --unidiff-zero --apply --no-add --exclude= + --ignore-whitespace --ignore-space-change --whitespace= --inaccurate-eof --verbose " return @@ -1047,6 +1049,7 @@ _git_grep () --extended-regexp --basic-regexp --fixed-strings --files-with-matches --name-only --files-without-match + --max-depth --count --and --or --not --all-match " @@ -1536,6 +1539,7 @@ _git_config () __gitcomp " add.ignore-errors alias. + apply.ignorewhitespace apply.whitespace branch.autosetupmerge branch.autosetuprebase diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index eace9c18eb..8c70ad8b7f 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -429,16 +429,19 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)." (git-get-string-sha1 (git-call-process-string-display-error "write-tree")))) -(defun git-commit-tree (buffer tree head) - "Call git-commit-tree with buffer as input and return the resulting commit SHA1." +(defun git-commit-tree (buffer tree parent) + "Create a commit and possibly update HEAD. +Create a commit with the message in BUFFER using the tree with hash TREE. +Use PARENT as the parent of the new commit. If PARENT is the current \"HEAD\", +update the \"HEAD\" reference to the new commit." (let ((author-name (git-get-committer-name)) (author-email (git-get-committer-email)) (subject "commit (initial): ") author-date log-start log-end args coding-system-for-write) - (when head + (when parent (setq subject "commit: ") (push "-p" args) - (push head args)) + (push parent args)) (with-current-buffer buffer (goto-char (point-min)) (if @@ -474,7 +477,7 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)." (apply #'git-run-command-region buffer log-start log-end env "commit-tree" tree (nreverse args)))))) - (when commit (git-update-ref "HEAD" commit head subject)) + (when commit (git-update-ref "HEAD" commit parent subject)) commit))) (defun git-empty-db-p () diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 342529db30..38438f3c4a 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -201,7 +201,7 @@ def isModeExec(mode): def isModeExecChanged(src_mode, dst_mode): return isModeExec(src_mode) != isModeExec(dst_mode) -def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): +def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None): cmd = p4_build_cmd("-G %s" % (cmd)) if verbose: sys.stderr.write("Opening pipe: %s\n" % cmd) @@ -224,7 +224,10 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): try: while True: entry = marshal.load(p4.stdout) - result.append(entry) + if cb is not None: + cb(entry) + else: + result.append(entry) except EOFError: pass exitCode = p4.wait() @@ -950,10 +953,84 @@ class P4Sync(Command): return branches - ## Should move this out, doesn't use SELF. - def readP4Files(self, files): + # output one file from the P4 stream + # - helper for streamP4Files + + def streamOneP4File(self, file, contents): + if file["type"] == "apple": + print "\nfile %s is a strange apple file that forks. Ignoring" % \ + file['depotFile'] + return + + relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes) + if verbose: + sys.stderr.write("%s\n" % relPath) + + mode = "644" + if isP4Exec(file["type"]): + mode = "755" + elif file["type"] == "symlink": + mode = "120000" + # p4 print on a symlink contains "target\n", so strip it off + last = contents.pop() + last = last[:-1] + contents.append(last) + + if self.isWindows and file["type"].endswith("text"): + mangled = [] + for data in contents: + data = data.replace("\r\n", "\n") + mangled.append(data) + contents = mangled + + if file['type'] in ('text+ko', 'unicode+ko', 'binary+ko'): + contents = map(lambda text: re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text), contents) + elif file['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'): + contents = map(lambda text: re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text), contents) + + self.gitStream.write("M %s inline %s\n" % (mode, relPath)) + + # total length... + length = 0 + for d in contents: + length = length + len(d) + + self.gitStream.write("data %d\n" % length) + for d in contents: + self.gitStream.write(d) + self.gitStream.write("\n") + + def streamOneP4Deletion(self, file): + relPath = self.stripRepoPath(file['path'], self.branchPrefixes) + if verbose: + sys.stderr.write("delete %s\n" % relPath) + self.gitStream.write("D %s\n" % relPath) + + # handle another chunk of streaming data + def streamP4FilesCb(self, marshalled): + + if marshalled.has_key('depotFile') and self.stream_have_file_info: + # start of a new file - output the old one first + self.streamOneP4File(self.stream_file, self.stream_contents) + self.stream_file = {} + self.stream_contents = [] + self.stream_have_file_info = False + + # pick up the new file information... for the + # 'data' field we need to append to our array + for k in marshalled.keys(): + if k == 'data': + self.stream_contents.append(marshalled['data']) + else: + self.stream_file[k] = marshalled[k] + + self.stream_have_file_info = True + + # Stream directly from "p4 files" into "git fast-import" + def streamP4Files(self, files): filesForCommit = [] filesToRead = [] + filesToDelete = [] for f in files: includeFile = True @@ -967,50 +1044,35 @@ class P4Sync(Command): filesForCommit.append(f) if f['action'] not in ('delete', 'purge'): filesToRead.append(f) + else: + filesToDelete.append(f) - filedata = [] - if len(filesToRead) > 0: - filedata = p4CmdList('-x - print', - stdin='\n'.join(['%s#%s' % (f['path'], f['rev']) - for f in filesToRead]), - stdin_mode='w+') - - if "p4ExitCode" in filedata[0]: - die("Problems executing p4. Error: [%d]." - % (filedata[0]['p4ExitCode'])); - - j = 0; - contents = {} - while j < len(filedata): - stat = filedata[j] - j += 1 - text = '' - while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'): - text += filedata[j]['data'] - del filedata[j]['data'] - j += 1 - - if not stat.has_key('depotFile'): - sys.stderr.write("p4 print fails with: %s\n" % repr(stat)) - continue + # deleted files... + for f in filesToDelete: + self.streamOneP4Deletion(f) - if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'): - text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text) - elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'): - text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text) + if len(filesToRead) > 0: + self.stream_file = {} + self.stream_contents = [] + self.stream_have_file_info = False - contents[stat['depotFile']] = text + # curry self argument + def streamP4FilesCbSelf(entry): + self.streamP4FilesCb(entry) - for f in filesForCommit: - path = f['path'] - if contents.has_key(path): - f['data'] = contents[path] + p4CmdList("-x - print", + '\n'.join(['%s#%s' % (f['path'], f['rev']) + for f in filesToRead]), + cb=streamP4FilesCbSelf) - return filesForCommit + # do the last chunk + if self.stream_file.has_key('depotFile'): + self.streamOneP4File(self.stream_file, self.stream_contents) def commit(self, details, files, branch, branchPrefixes, parent = ""): epoch = details["time"] author = details["user"] + self.branchPrefixes = branchPrefixes if self.verbose: print "commit into %s" % branch @@ -1023,7 +1085,6 @@ class P4Sync(Command): new_files.append (f) else: sys.stderr.write("Ignoring file outside of prefix: %s\n" % path) - files = self.readP4Files(new_files) self.gitStream.write("commit %s\n" % branch) # gitStream.write("mark :%s\n" % details["change"]) @@ -1051,33 +1112,7 @@ class P4Sync(Command): print "parent %s" % parent self.gitStream.write("from %s\n" % parent) - for file in files: - if file["type"] == "apple": - print "\nfile %s is a strange apple file that forks. Ignoring!" % file['path'] - continue - - relPath = self.stripRepoPath(file['path'], branchPrefixes) - if file["action"] in ("delete", "purge"): - self.gitStream.write("D %s\n" % relPath) - else: - data = file['data'] - - mode = "644" - if isP4Exec(file["type"]): - mode = "755" - elif file["type"] == "symlink": - mode = "120000" - # p4 print on a symlink contains "target\n", so strip it off - data = data[:-1] - - if self.isWindows and file["type"].endswith("text"): - data = data.replace("\r\n", "\n") - - self.gitStream.write("M %s inline %s\n" % (mode, relPath)) - self.gitStream.write("data %s\n" % len(data)) - self.gitStream.write(data) - self.gitStream.write("\n") - + self.streamP4Files(new_files) self.gitStream.write("\n") change = int(details["change"]) @@ -267,7 +267,7 @@ static int filter_buffer(int fd, void *data) status = finish_command(&child_process); if (status) - error("external filter %s failed %d", params->cmd, -status); + error("external filter %s failed %d", params->cmd, status); return (write_err || status); } @@ -24,6 +24,8 @@ time_t tm_to_time_t(const struct tm *tm) return -1; if (month < 2 || (year + 2) % 4) day--; + if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0) + return -1; return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL + tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec; } @@ -84,6 +86,67 @@ static int local_tzoffset(unsigned long time) return offset * eastwest; } +const char *show_date_relative(unsigned long time, int tz, + const struct timeval *now, + char *timebuf, + size_t timebuf_size) +{ + unsigned long diff; + if (now->tv_sec < time) + return "in the future"; + diff = now->tv_sec - time; + if (diff < 90) { + snprintf(timebuf, timebuf_size, "%lu seconds ago", diff); + return timebuf; + } + /* Turn it into minutes */ + diff = (diff + 30) / 60; + if (diff < 90) { + snprintf(timebuf, timebuf_size, "%lu minutes ago", diff); + return timebuf; + } + /* Turn it into hours */ + diff = (diff + 30) / 60; + if (diff < 36) { + snprintf(timebuf, timebuf_size, "%lu hours ago", diff); + return timebuf; + } + /* We deal with number of days from here on */ + diff = (diff + 12) / 24; + if (diff < 14) { + snprintf(timebuf, timebuf_size, "%lu days ago", diff); + return timebuf; + } + /* Say weeks for the past 10 weeks or so */ + if (diff < 70) { + snprintf(timebuf, timebuf_size, "%lu weeks ago", (diff + 3) / 7); + return timebuf; + } + /* Say months for the past 12 months or so */ + if (diff < 360) { + snprintf(timebuf, timebuf_size, "%lu months ago", (diff + 15) / 30); + return timebuf; + } + /* Give years and months for 5 years or so */ + if (diff < 1825) { + unsigned long years = diff / 365; + unsigned long months = (diff % 365 + 15) / 30; + int n; + n = snprintf(timebuf, timebuf_size, "%lu year%s", + years, (years > 1 ? "s" : "")); + if (months) + snprintf(timebuf + n, timebuf_size - n, + ", %lu month%s ago", + months, (months > 1 ? "s" : "")); + else + snprintf(timebuf + n, timebuf_size - n, " ago"); + return timebuf; + } + /* Otherwise, just years. Centuries is probably overkill. */ + snprintf(timebuf, timebuf_size, "%lu years ago", (diff + 183) / 365); + return timebuf; +} + const char *show_date(unsigned long time, int tz, enum date_mode mode) { struct tm *tm; @@ -95,63 +158,10 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode) } if (mode == DATE_RELATIVE) { - unsigned long diff; struct timeval now; gettimeofday(&now, NULL); - if (now.tv_sec < time) - return "in the future"; - diff = now.tv_sec - time; - if (diff < 90) { - snprintf(timebuf, sizeof(timebuf), "%lu seconds ago", diff); - return timebuf; - } - /* Turn it into minutes */ - diff = (diff + 30) / 60; - if (diff < 90) { - snprintf(timebuf, sizeof(timebuf), "%lu minutes ago", diff); - return timebuf; - } - /* Turn it into hours */ - diff = (diff + 30) / 60; - if (diff < 36) { - snprintf(timebuf, sizeof(timebuf), "%lu hours ago", diff); - return timebuf; - } - /* We deal with number of days from here on */ - diff = (diff + 12) / 24; - if (diff < 14) { - snprintf(timebuf, sizeof(timebuf), "%lu days ago", diff); - return timebuf; - } - /* Say weeks for the past 10 weeks or so */ - if (diff < 70) { - snprintf(timebuf, sizeof(timebuf), "%lu weeks ago", (diff + 3) / 7); - return timebuf; - } - /* Say months for the past 12 months or so */ - if (diff < 360) { - snprintf(timebuf, sizeof(timebuf), "%lu months ago", (diff + 15) / 30); - return timebuf; - } - /* Give years and months for 5 years or so */ - if (diff < 1825) { - unsigned long years = diff / 365; - unsigned long months = (diff % 365 + 15) / 30; - int n; - n = snprintf(timebuf, sizeof(timebuf), "%lu year%s", - years, (years > 1 ? "s" : "")); - if (months) - snprintf(timebuf + n, sizeof(timebuf) - n, - ", %lu month%s ago", - months, (months > 1 ? "s" : "")); - else - snprintf(timebuf + n, sizeof(timebuf) - n, - " ago"); - return timebuf; - } - /* Otherwise, just years. Centuries is probably overkill. */ - snprintf(timebuf, sizeof(timebuf), "%lu years ago", (diff + 183) / 365); - return timebuf; + return show_date_relative(time, tz, &now, + timebuf, sizeof(timebuf)); } if (mode == DATE_LOCAL) @@ -425,13 +435,19 @@ static int match_multi_number(unsigned long num, char c, const char *date, char return end - date; } -/* Have we filled in any part of the time/date yet? */ +/* + * Have we filled in any part of the time/date yet? + * We just do a binary 'and' to see if the sign bit + * is set in all the values. + */ static inline int nodate(struct tm *tm) { - return tm->tm_year < 0 && - tm->tm_mon < 0 && - tm->tm_mday < 0 && - !(tm->tm_hour | tm->tm_min | tm->tm_sec); + return (tm->tm_year & + tm->tm_mon & + tm->tm_mday & + tm->tm_hour & + tm->tm_min & + tm->tm_sec) < 0; } /* @@ -525,11 +541,8 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt } } - if (num > 0 && num < 32) { - tm->tm_mday = num; - } else if (num > 0 && num < 13) { + if (num > 0 && num < 13 && tm->tm_mon < 0) tm->tm_mon = num-1; - } return n; } @@ -583,6 +596,9 @@ int parse_date(const char *date, char *result, int maxlen) tm.tm_mon = -1; tm.tm_mday = -1; tm.tm_isdst = -1; + tm.tm_hour = -1; + tm.tm_min = -1; + tm.tm_sec = -1; offset = -1; tm_gmt = 0; @@ -657,42 +673,59 @@ void datestamp(char *buf, int bufsize) date_string(now, offset, buf, bufsize); } -static void update_tm(struct tm *tm, unsigned long sec) +/* + * Relative time update (eg "2 days ago"). If we haven't set the time + * yet, we need to set it from current time. + */ +static unsigned long update_tm(struct tm *tm, struct tm *now, unsigned long sec) { - time_t n = mktime(tm) - sec; + time_t n; + + if (tm->tm_mday < 0) + tm->tm_mday = now->tm_mday; + if (tm->tm_mon < 0) + tm->tm_mon = now->tm_mon; + if (tm->tm_year < 0) { + tm->tm_year = now->tm_year; + if (tm->tm_mon > now->tm_mon) + tm->tm_year--; + } + + n = mktime(tm) - sec; localtime_r(&n, tm); + return n; } -static void date_yesterday(struct tm *tm, int *num) +static void date_yesterday(struct tm *tm, struct tm *now, int *num) { - update_tm(tm, 24*60*60); + update_tm(tm, now, 24*60*60); } -static void date_time(struct tm *tm, int hour) +static void date_time(struct tm *tm, struct tm *now, int hour) { if (tm->tm_hour < hour) - date_yesterday(tm, NULL); + date_yesterday(tm, now, NULL); tm->tm_hour = hour; tm->tm_min = 0; tm->tm_sec = 0; } -static void date_midnight(struct tm *tm, int *num) +static void date_midnight(struct tm *tm, struct tm *now, int *num) { - date_time(tm, 0); + date_time(tm, now, 0); } -static void date_noon(struct tm *tm, int *num) +static void date_noon(struct tm *tm, struct tm *now, int *num) { - date_time(tm, 12); + date_time(tm, now, 12); } -static void date_tea(struct tm *tm, int *num) +static void date_tea(struct tm *tm, struct tm *now, int *num) { - date_time(tm, 17); + date_time(tm, now, 17); } -static void date_pm(struct tm *tm, int *num) +static void date_pm(struct tm *tm, struct tm *now, int *num) { int hour, n = *num; *num = 0; @@ -706,7 +739,7 @@ static void date_pm(struct tm *tm, int *num) tm->tm_hour = (hour % 12) + 12; } -static void date_am(struct tm *tm, int *num) +static void date_am(struct tm *tm, struct tm *now, int *num) { int hour, n = *num; *num = 0; @@ -720,7 +753,7 @@ static void date_am(struct tm *tm, int *num) tm->tm_hour = (hour % 12); } -static void date_never(struct tm *tm, int *num) +static void date_never(struct tm *tm, struct tm *now, int *num) { time_t n = 0; localtime_r(&n, tm); @@ -728,7 +761,7 @@ static void date_never(struct tm *tm, int *num) static const struct special { const char *name; - void (*fn)(struct tm *, int *); + void (*fn)(struct tm *, struct tm *, int *); } special[] = { { "yesterday", date_yesterday }, { "noon", date_noon }, @@ -757,7 +790,7 @@ static const struct typelen { { NULL } }; -static const char *approxidate_alpha(const char *date, struct tm *tm, int *num) +static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num) { const struct typelen *tl; const struct special *s; @@ -778,7 +811,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, int *num) for (s = special; s->name; s++) { int len = strlen(s->name); if (match_string(date, s->name) == len) { - s->fn(tm, num); + s->fn(tm, now, num); return end; } } @@ -800,7 +833,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, int *num) while (tl->type) { int len = strlen(tl->type); if (match_string(date, tl->type) >= len-1) { - update_tm(tm, tl->length * *num); + update_tm(tm, now, tl->length * *num); *num = 0; return end; } @@ -818,13 +851,15 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, int *num) n++; diff += 7*n; - update_tm(tm, diff * 24 * 60 * 60); + update_tm(tm, now, diff * 24 * 60 * 60); return end; } } if (match_string(date, "months") >= 5) { - int n = tm->tm_mon - *num; + int n; + update_tm(tm, now, 0); /* fill in date fields if needed */ + n = tm->tm_mon - *num; *num = 0; while (n < 0) { n += 12; @@ -835,6 +870,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, int *num) } if (match_string(date, "years") >= 4) { + update_tm(tm, now, 0); /* fill in date fields if needed */ tm->tm_year -= *num; *num = 0; return end; @@ -866,36 +902,82 @@ static const char *approxidate_digit(const char *date, struct tm *tm, int *num) return end; } -unsigned long approxidate(const char *date) +/* + * Do we have a pending number at the end, or when + * we see a new one? Let's assume it's a month day, + * as in "Dec 6, 1992" + */ +static void pending_number(struct tm *tm, int *num) +{ + int number = *num; + + if (number) { + *num = 0; + if (tm->tm_mday < 0 && number < 32) + tm->tm_mday = number; + else if (tm->tm_mon < 0 && number < 13) + tm->tm_mon = number-1; + else if (tm->tm_year < 0) { + if (number > 1969 && number < 2100) + tm->tm_year = number - 1900; + else if (number > 69 && number < 100) + tm->tm_year = number; + else if (number < 38) + tm->tm_year = 100 + number; + /* We screw up for number = 00 ? */ + } + } +} + +static unsigned long approxidate_str(const char *date, const struct timeval *tv) { int number = 0; struct tm tm, now; - struct timeval tv; time_t time_sec; - char buffer[50]; - - if (parse_date(date, buffer, sizeof(buffer)) > 0) - return strtoul(buffer, NULL, 10); - gettimeofday(&tv, NULL); - time_sec = tv.tv_sec; + time_sec = tv->tv_sec; localtime_r(&time_sec, &tm); now = tm; + + tm.tm_year = -1; + tm.tm_mon = -1; + tm.tm_mday = -1; + for (;;) { unsigned char c = *date; if (!c) break; date++; if (isdigit(c)) { + pending_number(&tm, &number); date = approxidate_digit(date-1, &tm, &number); continue; } if (isalpha(c)) - date = approxidate_alpha(date-1, &tm, &number); + date = approxidate_alpha(date-1, &tm, &now, &number); } - if (number > 0 && number < 32) - tm.tm_mday = number; - if (tm.tm_mon > now.tm_mon && tm.tm_year == now.tm_year) - tm.tm_year--; - return mktime(&tm); + pending_number(&tm, &number); + return update_tm(&tm, &now, 0); +} + +unsigned long approxidate_relative(const char *date, const struct timeval *tv) +{ + char buffer[50]; + + if (parse_date(date, buffer, sizeof(buffer)) > 0) + return strtoul(buffer, NULL, 0); + + return approxidate_str(date, tv); +} + +unsigned long approxidate(const char *date) +{ + struct timeval tv; + char buffer[50]; + + if (parse_date(date, buffer, sizeof(buffer)) > 0) + return strtoul(buffer, NULL, 0); + + gettimeofday(&tv, NULL); + return approxidate_str(date, &tv); } diff --git a/diff-lib.c b/diff-lib.c index ad2a4cde74..0c74ef5cbe 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -162,7 +162,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option) if (ce_uptodate(ce)) continue; - changed = check_removed(ce, &st); + /* If CE_VALID is set, don't look at workdir for file removal */ + changed = (ce->ce_flags & CE_VALID) ? 0 : check_removed(ce, &st); if (changed) { if (changed < 0) { perror(ce->name); @@ -309,22 +310,6 @@ static int show_modified(struct rev_info *revs, } /* - * This turns all merge entries into "stage 3". That guarantees that - * when we read in the new tree (into "stage 1"), we won't lose sight - * of the fact that we had unmerged entries. - */ -static void mark_merge_entries(void) -{ - int i; - for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; - if (!ce_stage(ce)) - continue; - ce->ce_flags |= CE_STAGEMASK; - } -} - -/* * This gets a mix of an existing index and a tree, one pathname entry * at a time. The index entry may be a single stage-0 one, but it could * also be multiple unmerged entries (in which case idx_pos/idx_nr will @@ -337,6 +322,8 @@ static void do_oneway_diff(struct unpack_trees_options *o, struct rev_info *revs = o->unpack_data; int match_missing, cached; + /* if the entry is not checked out, don't examine work tree */ + cached = o->index_only || (idx && (idx->ce_flags & CE_VALID)); /* * Backward compatibility wart - "diff-index -m" does * not mean "do not ignore merges", but "match_missing". @@ -344,12 +331,11 @@ static void do_oneway_diff(struct unpack_trees_options *o, * But with the revision flag parsing, that's found in * "!revs->ignore_merges". */ - cached = o->index_only; match_missing = !revs->ignore_merges; if (cached && idx && ce_stage(idx)) { - if (tree) - diff_unmerge(&revs->diffopt, idx->name, idx->ce_mode, idx->sha1); + diff_unmerge(&revs->diffopt, idx->name, idx->ce_mode, + idx->sha1); return; } @@ -435,8 +421,6 @@ int run_diff_index(struct rev_info *revs, int cached) struct unpack_trees_options opts; struct tree_desc t; - mark_merge_entries(); - ent = revs->pending.objects[0].item; tree_name = revs->pending.objects[0].name; tree = parse_tree_indirect(ent->sha1); @@ -2691,7 +2691,7 @@ static int parse_num(const char **cp_p) num = 0; scale = 1; dot = 0; - for(;;) { + for (;;) { ch = *cp; if ( !dot && ch == '.' ) { scale = 1; @@ -177,11 +177,15 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout /* * This is like 'lstat()', except it refuses to follow symlinks - * in the path. + * in the path, after skipping "skiplen". */ -int check_path(const char *path, int len, struct stat *st) +int check_path(const char *path, int len, struct stat *st, int skiplen) { - if (has_symlink_leading_path(path, len)) { + const char *slash = path + len; + + while (path < slash && *slash != '/') + slash--; + if (!has_dirs_only_path(path, slash - path, skiplen)) { errno = ENOENT; return -1; } @@ -201,7 +205,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t strcpy(path + len, ce->name); len += ce_namelen(ce); - if (!check_path(path, len, &st)) { + if (!check_path(path, len, &st, state->base_dir_len)) { unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID); if (!changed) return 0; diff --git a/environment.c b/environment.c index 8f5eaa7dd8..5de6837840 100644 --- a/environment.c +++ b/environment.c @@ -26,6 +26,7 @@ const char *git_commit_encoding; const char *git_log_output_encoding; int shared_repository = PERM_UMASK; const char *apply_default_whitespace; +const char *apply_default_ignorewhitespace; int zlib_compression_level = Z_BEST_SPEED; int core_compression_level; int core_compression_seen; @@ -38,6 +39,7 @@ int pager_use_color = 1; const char *editor_program; const char *excludes_file; int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */ +int read_replace_refs = 1; enum safe_crlf safe_crlf = SAFE_CRLF_WARN; unsigned whitespace_rule_cfg = WS_DEFAULT_RULE; enum branch_track git_branch_track = BRANCH_TRACK_REMOTE; @@ -16,6 +16,8 @@ s,signoff add a Signed-off-by line to the commit message u,utf8 recode into utf8 (default) k,keep pass -k flag to git-mailinfo whitespace= pass it through git-apply +ignore-space-change pass it through git-apply +ignore-whitespace pass it through git-apply directory= pass it through git-apply C= pass it through git-apply p= pass it through git-apply @@ -211,7 +213,13 @@ check_patch_format () { split_patches () { case "$patch_format" in mbox) - git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" || + case "$rebasing" in + '') + keep_cr= ;; + ?*) + keep_cr=--keep-cr ;; + esac + git mailsplit -d"$prec" -o"$dotest" -b $keep_cr -- "$@" > "$dotest/last" || clean_abort ;; stgit-series) @@ -321,7 +329,7 @@ do git_apply_opt="$git_apply_opt $(sq "$1$2")"; shift ;; --patch-format) shift ; patch_format="$1" ;; - --reject) + --reject|--ignore-whitespace|--ignore-space-change) git_apply_opt="$git_apply_opt $1" ;; --committer-date-is-author-date) committer_date_is_author_date=t ;; diff --git a/git-compat-util.h b/git-compat-util.h index 9f941e42b1..e5e9f39c56 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -57,10 +57,8 @@ # endif #elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && !defined(_M_UNIX) && !defined(sgi) #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */ -#ifndef __sun__ #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */ #endif -#endif #define _ALL_SOURCE 1 #define _GNU_SOURCE 1 #define _BSD_SOURCE 1 @@ -176,6 +174,8 @@ extern char *gitbasename(char *); #endif #endif +#include "compat/bswap.h" + /* General helper functions */ extern void usage(const char *err) NORETURN; extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2))); diff --git a/git-cvsimport.perl b/git-cvsimport.perl index e439202961..593832d813 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -252,7 +252,8 @@ sub conn { } }; } - $pass="A" unless $pass; + + $pass = $self->_scramble($pass); my ($s, $rep); if ($proxyhost) { @@ -484,6 +485,42 @@ sub _fetchfile { return $res; } +sub _scramble { + my ($self, $pass) = @_; + my $scrambled = "A"; + + return $scrambled unless $pass; + + my $pass_len = length($pass); + my @pass_arr = split("", $pass); + my $i; + + # from cvs/src/scramble.c + my @shifts = ( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87, + 111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105, + 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35, + 125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56, + 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48, + 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223, + 225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190, + 199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193, + 174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212, + 207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246, + 192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176, + 227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127, + 182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195, + 243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152 + ); + + for ($i = 0; $i < $pass_len; $i++) { + $scrambled .= pack("C", $shifts[ord($pass_arr[$i])]); + } + + return $scrambled; +} package main; diff --git a/git-instaweb.sh b/git-instaweb.sh index 5f5cac75ea..d96eddbe56 100755 --- a/git-instaweb.sh +++ b/git-instaweb.sh @@ -77,11 +77,30 @@ start_httpd () { resolve_full_httpd # don't quote $full_httpd, there can be arguments to it (-f) - $full_httpd "$fqgitdir/gitweb/httpd.conf" - if test $? != 0; then - echo "Could not execute http daemon $httpd." - exit 1 - fi + case "$httpd" in + *mongoose*) + #The mongoose server doesn't have a daemon mode so we'll have to fork it + $full_httpd "$fqgitdir/gitweb/httpd.conf" & + #Save the pid before doing anything else (we'll print it later) + pid=$! + + if test $? != 0; then + echo "Could not execute http daemon $httpd." + exit 1 + fi + + cat > "$fqgitdir/pid" <<EOF +$pid +EOF + ;; + *) + $full_httpd "$fqgitdir/gitweb/httpd.conf" + if test $? != 0; then + echo "Could not execute http daemon $httpd." + exit 1 + fi + ;; + esac } stop_httpd () { @@ -308,6 +327,31 @@ EOF fi } +mongoose_conf() { + cat > "$conf" <<EOF +# Mongoose web server configuration file. +# Lines starting with '#' and empty lines are ignored. +# For detailed description of every option, visit +# http://code.google.com/p/mongoose/wiki/MongooseManual + +root $fqgitdir/gitweb +ports $port +index_files gitweb.cgi +#ssl_cert $fqgitdir/gitweb/ssl_cert.pem +error_log $fqgitdir/gitweb/error.log +access_log $fqgitdir/gitweb/access.log + +#cgi setup +cgi_env PATH=/usr/local/bin:/usr/bin:/bin,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH +cgi_interp $PERL +cgi_ext cgi,pl + +# mimetype mapping +mime_types .gz=application/x-gzip,.tar.gz=application/x-tgz,.tgz=application/x-tgz,.tar=application/x-tar,.zip=application/zip,.gif=image/gif,.jpg=image/jpeg,.jpeg=image/jpeg,.png=image/png,.css=text/css,.html=text/html,.htm=text/html,.js=text/javascript,.c=text/plain,.cpp=text/plain,.log=text/plain,.conf=text/plain,.text=text/plain,.txt=text/plain,.dtd=text/xml,.bz2=application/x-bzip,.tbz=application/x-bzip-compressed-tar,.tar.bz2=application/x-bzip-compressed-tar +EOF +} + + script=' s#^(my|our) \$projectroot =.*#$1 \$projectroot = "'$(dirname "$fqgitdir")'";#; s#(my|our) \$gitbin =.*#$1 \$gitbin = "'$GIT_EXEC_PATH'";#; @@ -344,6 +388,9 @@ case "$httpd" in webrick) webrick_conf ;; +*mongoose*) + mongoose_conf + ;; *) echo "Unknown httpd specified: $httpd" exit 1 diff --git a/git-pull.sh b/git-pull.sh index 0f24182974..0bbd5bf7df 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -119,11 +119,19 @@ error_on_no_merge_candidates () { } test true = "$rebase" && { - git update-index --ignore-submodules --refresh && - git diff-files --ignore-submodules --quiet && - git diff-index --ignore-submodules --cached --quiet HEAD -- || - die "refusing to pull with rebase: your working tree is not up-to-date" - + if ! git rev-parse -q --verify HEAD >/dev/null + then + # On an unborn branch + if test -f "$GIT_DIR/index" + then + die "updating an unborn branch with changes added to the index" + fi + else + git update-index --ignore-submodules --refresh && + git diff-files --ignore-submodules --quiet && + git diff-index --ignore-submodules --cached --quiet HEAD -- || + die "refusing to pull with rebase: your working tree is not up-to-date" + fi oldremoteref= && . git-parse-remote && remoteref="$(get_remote_merge_branch "$@" 2>/dev/null)" && diff --git a/git-rebase.sh b/git-rebase.sh index 18bc6946cf..2315d95a9f 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -333,6 +333,9 @@ do ;; esac ;; + --ignore-whitespace) + git_am_opt="$git_am_opt $1" + ;; --committer-date-is-author-date|--ignore-date) git_am_opt="$git_am_opt $1" force_rebase=t @@ -382,8 +385,10 @@ else fi # The tree must be really really clean. -if ! git update-index --ignore-submodules --refresh; then - die "cannot rebase: you have unstaged changes" +if ! git update-index --ignore-submodules --refresh > /dev/null; then + echo >&2 "cannot rebase: you have unstaged changes" + git diff --name-status -r --ignore-submodules -- >&2 + exit 1 fi diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --) case "$diff" in diff --git a/git-request-pull.sh b/git-request-pull.sh index fd95beadab..630ceddf03 100755 --- a/git-request-pull.sh +++ b/git-request-pull.sh @@ -8,13 +8,33 @@ USAGE='<start> <url> [<end>]' LONG_USAGE='Summarizes the changes between two commits to the standard output, and includes the given URL in the generated summary.' SUBDIRECTORY_OK='Yes' -OPTIONS_SPEC= +OPTIONS_SPEC='git request-pull [options] start url [end] +-- +p show patch text as well +' + . git-sh-setup . git-parse-remote GIT_PAGER= export GIT_PAGER +patch= +while case "$#" in 0) break ;; esac +do + case "$1" in + -p) + patch=-p ;; + --) + shift; break ;; + -*) + usage ;; + *) + break ;; + esac + shift +done + base=$1 url=$2 head=${3-HEAD} @@ -54,5 +74,5 @@ echo " $url $branch" echo git shortlog ^$baserev $headrev -git diff -M --stat --summary $merge_base $headrev +git diff -M --stat --summary $patch $merge_base..$headrev exit $status diff --git a/git-stash.sh b/git-stash.sh index 03e589f764..d61c9d03bc 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -162,10 +162,6 @@ show_stash () { } apply_stash () { - git update-index -q --refresh && - git diff-files --quiet --ignore-submodules || - die 'Cannot apply to a dirty working tree, please stage your changes' - unstash_index= while test $# != 0 @@ -184,18 +180,27 @@ apply_stash () { shift done - # current index state - c_tree=$(git write-tree) || - die 'Cannot apply a stash in the middle of a merge' + if test $# = 0 + then + have_stash || die 'Nothing to apply' + fi # stash records the work tree, and is a merge between the # base commit (first parent) and the index tree (second parent). - s=$(git rev-parse --verify --default $ref_stash "$@") && - w_tree=$(git rev-parse --verify "$s:") && - b_tree=$(git rev-parse --verify "$s^1:") && - i_tree=$(git rev-parse --verify "$s^2:") || + s=$(git rev-parse --quiet --verify --default $ref_stash "$@") && + w_tree=$(git rev-parse --quiet --verify "$s:") && + b_tree=$(git rev-parse --quiet --verify "$s^1:") && + i_tree=$(git rev-parse --quiet --verify "$s^2:") || die "$*: no valid stashed state found" + git update-index -q --refresh && + git diff-files --quiet --ignore-submodules || + die 'Cannot apply to a dirty working tree, please stage your changes' + + # current index state + c_tree=$(git write-tree) || + die 'Cannot apply a stash in the middle of a merge' + unstashed_index_tree= if test -n "$unstash_index" && test "$b_tree" != "$i_tree" && test "$c_tree" != "$i_tree" diff --git a/git-submodule.sh b/git-submodule.sh index ebed711da4..bfbd36b6f4 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -4,9 +4,14 @@ # # Copyright (c) 2007 Lars Hjemli -USAGE="[--quiet] [--cached] \ -[add [-b branch] <repo> <path>]|[status|init|update [-i|--init] [-N|--no-fetch] [--rebase|--merge]|summary [-n|--summary-limit <n>] [<commit>]] \ -[--] [<path>...]|[foreach <command>]|[sync [--] [<path>...]]" +dashless=$(basename "$0" | sed -e 's/-/ /') +USAGE="[--quiet] add [-b branch] [--reference <repository>] [--] <repository> <path> + or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...] + or: $dashless [--quiet] init [--] [<path>...] + or: $dashless [--quiet] update [--init] [-N|--no-fetch] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...] + or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...] + or: $dashless [--quiet] foreach [--recursive] <command> + or: $dashless [--quiet] sync [--] [<path>...]" OPTIONS_SPEC= . git-sh-setup . git-parse-remote @@ -16,8 +21,10 @@ command= branch= reference= cached= +files= nofetch= update= +prefix= # Resolve relative url by appending to parent's url resolve_relative_url () @@ -237,13 +244,43 @@ cmd_add() # cmd_foreach() { + # parse $args after "submodule ... foreach". + while test $# -ne 0 + do + case "$1" in + -q|--quiet) + GIT_QUIET=1 + ;; + --recursive) + recursive=1 + ;; + -*) + usage + ;; + *) + break + ;; + esac + shift + done + module_list | while read mode sha1 stage path do if test -e "$path"/.git then - say "Entering '$path'" - (cd "$path" && eval "$@") || + say "Entering '$prefix$path'" + name=$(module_name "$path") + ( + prefix="$prefix$path/" + unset GIT_DIR + cd "$path" && + eval "$@" && + if test -n "$recursive" + then + cmd_foreach "--recursive" "$@" + fi + ) || die "Stopping at '$path'; script returned non-zero status." fi done @@ -316,6 +353,7 @@ cmd_init() cmd_update() { # parse $args after "submodule ... update". + orig_args="$@" while test $# -ne 0 do case "$1" in @@ -348,6 +386,10 @@ cmd_update() shift update="merge" ;; + --recursive) + shift + recursive=1 + ;; --) shift break @@ -434,6 +476,12 @@ cmd_update() die "Unable to $action '$sha1' in submodule path '$path'" say "Submodule path '$path': $msg '$sha1'" fi + + if test -n "$recursive" + then + (unset GIT_DIR; cd "$path" && cmd_update $orig_args) || + die "Failed to recurse into submodule path '$path'" + fi done } @@ -460,6 +508,7 @@ set_name_rev () { cmd_summary() { summary_limit=-1 for_status= + diff_cmd=diff-index # parse $args after "submodule ... summary". while test $# -ne 0 @@ -468,6 +517,9 @@ cmd_summary() { --cached) cached="$1" ;; + --files) + files="$1" + ;; --for-status) for_status="$1" ;; @@ -504,9 +556,17 @@ cmd_summary() { head=HEAD fi + if [ -n "$files" ] + then + test -n "$cached" && + die "--cached cannot be used with --files" + diff_cmd=diff-files + head= + fi + cd_to_toplevel # Get modified modules cared by user - modules=$(git diff-index $cached --raw $head -- "$@" | + modules=$(git $diff_cmd $cached --raw $head -- "$@" | egrep '^:([0-7]* )?160000' | while read mod_src mod_dst sha1_src sha1_dst status name do @@ -520,7 +580,7 @@ cmd_summary() { test -z "$modules" && return - git diff-index $cached --raw $head -- $modules | + git $diff_cmd $cached --raw $head -- $modules | egrep '^:([0-7]* )?160000' | cut -c2- | while read mod_src mod_dst sha1_src sha1_dst status name @@ -643,6 +703,7 @@ cmd_summary() { cmd_status() { # parse $args after "submodule ... status". + orig_args="$@" while test $# -ne 0 do case "$1" in @@ -652,6 +713,9 @@ cmd_status() --cached) cached=1 ;; + --recursive) + recursive=1 + ;; --) shift break @@ -671,22 +735,34 @@ cmd_status() do name=$(module_name "$path") || exit url=$(git config submodule."$name".url) + displaypath="$prefix$path" if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git then - say "-$sha1 $path" + say "-$sha1 $displaypath" continue; fi set_name_rev "$path" "$sha1" if git diff-files --quiet -- "$path" then - say " $sha1 $path$revname" + say " $sha1 $displaypath$revname" else if test -z "$cached" then sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD) set_name_rev "$path" "$sha1" fi - say "+$sha1 $path$revname" + say "+$sha1 $displaypath$revname" + fi + + if test -n "$recursive" + then + ( + prefix="$displaypath/" + unset GIT_DIR + cd "$path" && + cmd_status $orig_args + ) || + die "Failed to recurse into submodule path '$path'" fi done } diff --git a/git-svn.perl b/git-svn.perl index d075810724..ce4fef9d34 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -21,6 +21,15 @@ $Git::SVN::default_ref_id = $ENV{GIT_SVN_ID} || 'git-svn'; $Git::SVN::Ra::_log_window_size = 100; $Git::SVN::_minimize_url = 'unset'; +if (! exists $ENV{SVN_SSH}) { + if (exists $ENV{GIT_SSH}) { + $ENV{SVN_SSH} = $ENV{GIT_SSH}; + if ($^O eq 'msys') { + $ENV{SVN_SSH} =~ s/\\/\\\\/g; + } + } +} + $Git::SVN::Log::TZ = $ENV{TZ}; $ENV{TZ} = 'UTC'; $| = 1; # unbuffer STDOUT @@ -764,6 +773,7 @@ sub cmd_show_ignore { print STDOUT "\n# $path\n"; my $s = $props->{'svn:ignore'} or return; $s =~ s/[\r\n]+/\n/g; + $s =~ s/^\n+//; chomp $s; $s =~ s#^#$path#gm; print STDOUT "$s\n"; @@ -801,6 +811,7 @@ sub cmd_create_ignore { open(GITIGNORE, '>', $ignore) or fatal("Failed to open `$ignore' for writing: $!"); $s =~ s/[\r\n]+/\n/g; + $s =~ s/^\n+//; chomp $s; # Prefix all patterns so that the ignore doesn't apply # to sub-directories. @@ -907,7 +918,7 @@ sub cmd_multi_init { } do_git_init_db(); if (defined $_trunk) { - my $trunk_ref = $_prefix . 'trunk'; + my $trunk_ref = 'refs/remotes/' . $_prefix . 'trunk'; # try both old-style and new-style lookups: my $gs_trunk = eval { Git::SVN->new($trunk_ref) }; unless ($gs_trunk) { @@ -1154,6 +1165,17 @@ sub post_fetch_checkout { my $gs = $Git::SVN::_head or return; return if verify_ref('refs/heads/master^0'); + # look for "trunk" ref if it exists + my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}}; + my $fetch = $remote->{fetch}; + if ($fetch) { + foreach my $p (keys %$fetch) { + basename($fetch->{$p}) eq 'trunk' or next; + $gs = Git::SVN->new($fetch->{$p}, $gs->{repo_id}, $p); + last; + } + } + my $valid_head = verify_ref('HEAD^0'); command_noisy(qw(update-ref refs/heads/master), $gs->refname); return if ($valid_head || !verify_ref('HEAD^0')); @@ -1209,6 +1231,7 @@ sub complete_url_ls_init { } command_oneline('config', $k, $gs->{url}) unless $orig_url; my $remote_path = "$gs->{path}/$repo_path"; + $remote_path =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg; $remote_path =~ s#/+#/#g; $remote_path =~ s#^/##g; $remote_path .= "/*" if $remote_path !~ /\*/; @@ -1641,23 +1664,23 @@ sub resolve_local_globs { return unless defined $glob_spec; my $ref = $glob_spec->{ref}; my $path = $glob_spec->{path}; - foreach (command(qw#for-each-ref --format=%(refname) refs/remotes#)) { - next unless m#^refs/remotes/$ref->{regex}$#; + foreach (command(qw#for-each-ref --format=%(refname) refs/#)) { + next unless m#^$ref->{regex}$#; my $p = $1; my $pathname = desanitize_refname($path->full_path($p)); my $refname = desanitize_refname($ref->full_path($p)); if (my $existing = $fetch->{$pathname}) { if ($existing ne $refname) { die "Refspec conflict:\n", - "existing: refs/remotes/$existing\n", - " globbed: refs/remotes/$refname\n"; + "existing: $existing\n", + " globbed: $refname\n"; } - my $u = (::cmt_metadata("refs/remotes/$refname"))[0]; + my $u = (::cmt_metadata("$refname"))[0]; $u =~ s!^\Q$url\E(/|$)!! or die - "refs/remotes/$refname: '$url' not found in '$u'\n"; + "$refname: '$url' not found in '$u'\n"; if ($pathname ne $u) { warn "W: Refspec glob conflict ", - "(ref: refs/remotes/$refname):\n", + "(ref: $refname):\n", "expected path: $pathname\n", " real path: $u\n", "Continuing ahead with $u\n"; @@ -1735,33 +1758,35 @@ sub read_all_remotes { my $use_svm_props = eval { command_oneline(qw/config --bool svn.useSvmProps/) }; $use_svm_props = $use_svm_props eq 'true' if $use_svm_props; + my $svn_refspec = qr{\s*/?(.*?)\s*:\s*(.+?)\s*}; foreach (grep { s/^svn-remote\.// } command(qw/config -l/)) { - if (m!^(.+)\.fetch=\s*(.*)\s*:\s*(.+)\s*$!) { - my ($remote, $local_ref, $_remote_ref) = ($1, $2, $3); - die("svn-remote.$remote: remote ref '$_remote_ref' " - . "must start with 'refs/remotes/'\n") - unless $_remote_ref =~ m{^refs/remotes/(.+)}; - my $remote_ref = $1; - $local_ref =~ s{^/}{}; + if (m!^(.+)\.fetch=$svn_refspec$!) { + my ($remote, $local_ref, $remote_ref) = ($1, $2, $3); + die("svn-remote.$remote: remote ref '$remote_ref' " + . "must start with 'refs/'\n") + unless $remote_ref =~ m{^refs/}; $r->{$remote}->{fetch}->{$local_ref} = $remote_ref; $r->{$remote}->{svm} = {} if $use_svm_props; } elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) { $r->{$1}->{svm} = {}; } elsif (m!^(.+)\.url=\s*(.*)\s*$!) { $r->{$1}->{url} = $2; - } elsif (m!^(.+)\.(branches|tags)= - (.*):refs/remotes/(.+)\s*$/!x) { - my ($p, $g) = ($3, $4); + } elsif (m!^(.+)\.(branches|tags)=$svn_refspec$!) { + my ($remote, $t, $local_ref, $remote_ref) = + ($1, $2, $3, $4); + die("svn-remote.$remote: remote ref '$remote_ref' ($t) " + . "must start with 'refs/'\n") + unless $remote_ref =~ m{^refs/}; my $rs = { - t => $2, - remote => $1, - path => Git::SVN::GlobSpec->new($p), - ref => Git::SVN::GlobSpec->new($g) }; + t => $t, + remote => $remote, + path => Git::SVN::GlobSpec->new($local_ref), + ref => Git::SVN::GlobSpec->new($remote_ref) }; if (length($rs->{ref}->{right}) != 0) { die "The '*' glob character must be the last ", - "character of '$g'\n"; + "character of '$remote_ref'\n"; } - push @{ $r->{$1}->{$2} }, $rs; + push @{ $r->{$remote}->{$t} }, $rs; } } @@ -1869,14 +1894,15 @@ sub init_remote_config { } } my ($xrepo_id, $xpath) = find_ref($self->refname); - if (defined $xpath) { + if (!$no_write && defined $xpath) { die "svn-remote.$xrepo_id.fetch already set to track ", - "$xpath:refs/remotes/", $self->refname, "\n"; + "$xpath:", $self->refname, "\n"; } unless ($no_write) { command_noisy('config', "svn-remote.$self->{repo_id}.url", $url); $self->{path} =~ s{^/}{}; + $self->{path} =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg; command_noisy('config', '--add', "svn-remote.$self->{repo_id}.fetch", "$self->{path}:".$self->refname); @@ -1946,7 +1972,7 @@ sub find_ref { my ($ref_id) = @_; foreach (command(qw/config -l/)) { next unless m!^svn-remote\.(.+)\.fetch= - \s*(.*)\s*:\s*refs/remotes/(.+)\s*$!x; + \s*/?(.*?)\s*:\s*(.+?)\s*$!x; my ($repo_id, $path, $ref) = ($1, $2, $3); if ($ref eq $ref_id) { $path = '' if ($path =~ m#^\./?#); @@ -1963,16 +1989,16 @@ sub new { if (!defined $repo_id) { die "Could not find a \"svn-remote.*.fetch\" key ", "in the repository configuration matching: ", - "refs/remotes/$ref_id\n"; + "$ref_id\n"; } } my $self = _new($class, $repo_id, $ref_id, $path); if (!defined $self->{path} || !length $self->{path}) { my $fetch = command_oneline('config', '--get', "svn-remote.$repo_id.fetch", - ":refs/remotes/$ref_id\$") or + ":$ref_id\$") or die "Failed to read \"svn-remote.$repo_id.fetch\" ", - "\":refs/remotes/$ref_id\$\" in config\n"; + "\":$ref_id\$\" in config\n"; ($self->{path}, undef) = split(/\s*:\s*/, $fetch); } $self->{url} = command_oneline('config', '--get', @@ -1983,7 +2009,7 @@ sub new { } sub refname { - my ($refname) = "refs/remotes/$_[0]->{ref_id}" ; + my ($refname) = $_[0]->{ref_id} ; # It cannot end with a slash /, we'll throw up on this because # SVN can't have directories with a slash in their name, either: @@ -3262,7 +3288,7 @@ sub _rev_map_get { my $i = int(($l/24 + $u/24) / 2) * 24; sysseek($fh, $i, SEEK_SET) or croak "seek: $!"; sysread($fh, my $buf, 24) == 24 or croak "read: $!"; - my ($r, $c) = unpack('NH40', $buf); + my ($r, $c) = unpack(rev_map_fmt, $buf); if ($r < $rev) { $l = $i + 24; @@ -3317,12 +3343,24 @@ sub _new { $repo_id = $Git::SVN::default_repo_id; } unless (defined $ref_id && length $ref_id) { - $_[2] = $ref_id = $Git::SVN::default_ref_id; + $_prefix = '' unless defined($_prefix); + $_[2] = $ref_id = + "refs/remotes/$_prefix$Git::SVN::default_ref_id"; } $_[1] = $repo_id; my $dir = "$ENV{GIT_DIR}/svn/$ref_id"; + + # Older repos imported by us used $GIT_DIR/svn/foo instead of + # $GIT_DIR/svn/refs/remotes/foo when tracking refs/remotes/foo + if ($ref_id =~ m{^refs/remotes/(.*)}) { + my $old_dir = "$ENV{GIT_DIR}/svn/$1"; + if (-d $old_dir && ! -d $dir) { + $dir = $old_dir; + } + } + $_[3] = $path = '' unless (defined $path); - mkpath(["$ENV{GIT_DIR}/svn"]); + mkpath([$dir]); bless { ref_id => $ref_id, dir => $dir, index => "$dir/index", path => $path, config => "$ENV{GIT_DIR}/svn/config", @@ -5495,7 +5533,7 @@ sub minimize_connections { my $pfx = "svn-remote.$x->{old_repo_id}"; my $old_fetch = quotemeta("$x->{old_path}:". - "refs/remotes/$x->{ref_id}"); + "$x->{ref_id}"); command_noisy(qw/config --unset/, "$pfx.fetch", '^'. $old_fetch . '$'); delete $r->{$x->{old_repo_id}}-> @@ -5564,7 +5602,7 @@ sub new { my ($class, $glob) = @_; my $re = $glob; $re =~ s!/+$!!g; # no need for trailing slashes - $re =~ m!^([^*]*)(\*(?:/\*)*)([^*]*)$!; + $re =~ m!^([^*]*)(\*(?:/\*)*)(.*)$!; my $temp = $re; my ($left, $right) = ($1, $3); $re = $2; @@ -339,6 +339,7 @@ static void handle_internal_command(int argc, const char **argv) { "receive-pack", cmd_receive_pack }, { "reflog", cmd_reflog, RUN_SETUP }, { "remote", cmd_remote, RUN_SETUP }, + { "replace", cmd_replace, RUN_SETUP }, { "repo-config", cmd_config }, { "rerere", cmd_rerere, RUN_SETUP }, { "reset", cmd_reset, RUN_SETUP }, @@ -358,6 +359,7 @@ static void handle_internal_command(int argc, const char **argv) { "unpack-objects", cmd_unpack_objects, RUN_SETUP }, { "update-index", cmd_update_index, RUN_SETUP }, { "update-ref", cmd_update_ref, RUN_SETUP }, + { "update-server-info", cmd_update_server_info, RUN_SETUP }, { "upload-archive", cmd_upload_archive }, { "verify-tag", cmd_verify_tag, RUN_SETUP }, { "version", cmd_version }, @@ -416,13 +418,9 @@ static void execv_dashed_external(const char **argv) * if we fail because the command is not found, it is * OK to return. Otherwise, we just pass along the status code. */ - status = run_command_v_opt(argv, 0); - if (status != -ERR_RUN_COMMAND_EXEC) { - if (IS_RUN_COMMAND_ERR(status)) - die("unable to run '%s'", argv[0]); - exit(-status); - } - errno = ENOENT; /* as if we called execvp */ + status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE); + if (status >= 0 || errno != ENOENT) + exit(status); argv[0] = tmp; diff --git a/gitk-git/gitk b/gitk-git/gitk index 4604c831fe..8c08310e6d 100644 --- a/gitk-git/gitk +++ b/gitk-git/gitk @@ -288,7 +288,7 @@ proc parseviewrevs {view revs} { if {$sdm != 2} { lappend ret $id } else { - lset ret end [lindex $ret end]...$id + lset ret end $id...[lindex $ret end] } lappend pos $id } @@ -1677,6 +1677,7 @@ proc readrefs {} { global tagids idtags headids idheads tagobjid global otherrefids idotherrefs mainhead mainheadid global selecthead selectheadid + global hideremotes foreach v {tagids idtags headids idheads otherrefids idotherrefs} { catch {unset $v} @@ -1689,7 +1690,7 @@ proc readrefs {} { if {![string match "refs/*" $ref]} continue set name [string range $ref 5 end] if {[string match "remotes/*" $name]} { - if {![string match "*/HEAD" $name]} { + if {![string match "*/HEAD" $name] && !$hideremotes} { set headids($name) $id lappend idheads($id) $name } @@ -2520,6 +2521,7 @@ proc savestuff {w} { global cmitmode wrapcomment datetimeformat limitdiffs global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor global autoselect extdifftool perfile_attrs markbgcolor + global hideremotes if {$stuffsaved} return if {![winfo viewable .]} return @@ -2539,6 +2541,7 @@ proc savestuff {w} { puts $f [list set wrapcomment $wrapcomment] puts $f [list set autoselect $autoselect] puts $f [list set showneartags $showneartags] + puts $f [list set hideremotes $hideremotes] puts $f [list set showlocalchanges $showlocalchanges] puts $f [list set datetimeformat $datetimeformat] puts $f [list set limitdiffs $limitdiffs] @@ -7906,6 +7909,11 @@ proc gotocommit {} { } set id [lindex $matches 0] } + } else { + if {[catch {set id [exec git rev-parse --verify $sha1string]}]} { + error_popup [mc "Revision %s is not known" $sha1string] + return + } } } if {[commitinview $id $curview]} { @@ -7915,7 +7923,7 @@ proc gotocommit {} { if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} { set msg [mc "SHA1 id %s is not known" $sha1string] } else { - set msg [mc "Tag/Head %s is not known" $sha1string] + set msg [mc "Revision %s is not in the current view" $sha1string] } error_popup $msg } @@ -10383,6 +10391,7 @@ proc doprefs {} { global oldprefs prefstop showneartags showlocalchanges global bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor global tabstop limitdiffs autoselect extdifftool perfile_attrs + global hideremotes set top .gitkprefs set prefstop $top @@ -10391,7 +10400,7 @@ proc doprefs {} { return } foreach v {maxwidth maxgraphpct showneartags showlocalchanges \ - limitdiffs tabstop perfile_attrs} { + limitdiffs tabstop perfile_attrs hideremotes} { set oldprefs($v) [set $v] } toplevel $top @@ -10423,6 +10432,9 @@ proc doprefs {} { checkbutton $top.ntag -text [mc "Display nearby tags"] \ -font optionfont -variable showneartags grid x $top.ntag -sticky w + checkbutton $top.hideremotes -text [mc "Hide remote refs"] \ + -font optionfont -variable hideremotes + grid x $top.hideremotes -sticky w checkbutton $top.ldiff -text [mc "Limit diffs to listed paths"] \ -font optionfont -variable limitdiffs grid x $top.ldiff -sticky w @@ -10547,7 +10559,7 @@ proc prefscan {} { global oldprefs prefstop foreach v {maxwidth maxgraphpct showneartags showlocalchanges \ - limitdiffs tabstop perfile_attrs} { + limitdiffs tabstop perfile_attrs hideremotes} { global $v set $v $oldprefs($v) } @@ -10561,6 +10573,7 @@ proc prefsok {} { global oldprefs prefstop showneartags showlocalchanges global fontpref mainfont textfont uifont global limitdiffs treediffs perfile_attrs + global hideremotes catch {destroy $prefstop} unset prefstop @@ -10606,6 +10619,9 @@ proc prefsok {} { $limitdiffs != $oldprefs(limitdiffs)} { reselectline } + if {$hideremotes != $oldprefs(hideremotes)} { + rereadrefs + } } proc formatdate {d} { @@ -10901,7 +10917,7 @@ proc gitattr {path attr default} { } else { set r "unspecified" if {![catch {set line [exec git check-attr $attr -- $path]}]} { - regexp "(.*): encoding: (.*)" $line m f r + regexp "(.*): $attr: (.*)" $line m f r } set path_attr_cache($attr,$path) $r } @@ -10929,7 +10945,7 @@ proc cache_gitattr {attr pathlist} { set newlist [lrange $newlist $lim end] if {![catch {set rlist [eval exec git check-attr $attr -- $head]}]} { foreach row [split $rlist "\n"] { - if {[regexp "(.*): encoding: (.*)" $row m path value]} { + if {[regexp "(.*): $attr: (.*)" $row m path value]} { if {[string index $path 0] eq "\""} { set path [encoding convertfrom [lindex $path 0]] } @@ -11011,6 +11027,7 @@ set mingaplen 100 set cmitmode "patch" set wrapcomment "none" set showneartags 1 +set hideremotes 0 set maxrefs 20 set maxlinelen 200 set showlocalchanges 1 diff --git a/gitweb/INSTALL b/gitweb/INSTALL index 18c9ce35e8..b76a0cffff 100644 --- a/gitweb/INSTALL +++ b/gitweb/INSTALL @@ -123,6 +123,15 @@ GITWEB_CONFIG file: $feature{'snapshot'}{'default'} = ['zip', 'tgz']; $feature{'snapshot'}{'override'} = 1; +If you allow overriding for the snapshot feature, you can specify which +snapshot formats are globally disabled. You can also add any command line +options you want (such as setting the compression level). For instance, +you can disable Zip compressed snapshots and set GZip to run at level 6 by +adding the following lines to your $GITWEB_CONFIG: + + $known_snapshot_formats{'zip'}{'disabled'} = 1; + $known_snapshot_formats{'tgz'}{'compressor'} = ['gzip','-6']; + Gitweb repositories ------------------- diff --git a/gitweb/git-favicon.png b/gitweb/git-favicon.png Binary files differindex de637c0608..aae35a70e7 100644 --- a/gitweb/git-favicon.png +++ b/gitweb/git-favicon.png diff --git a/gitweb/git-logo.png b/gitweb/git-logo.png Binary files differindex 16ae8d5382..f4ede2e944 100644 --- a/gitweb/git-logo.png +++ b/gitweb/git-logo.png diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index d05bc37646..8f68fe3091 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -226,22 +226,30 @@ th { text-align: left; } -tr.light:hover { - background-color: #edece6; -} - -tr.dark { - background-color: #f6f6f0; +/* do not change row style on hover for 'blame' view */ +tr.light, +table.blame .light:hover { + background-color: #ffffff; } -tr.dark2 { +tr.dark, +table.blame .dark:hover { background-color: #f6f6f0; } +/* currently both use the same, but it can change */ +tr.light:hover, tr.dark:hover { background-color: #edece6; } +/* boundary commits in 'blame' view */ +/* and commits without "previous" */ +tr.boundary td.sha1, +tr.no-previous td.linenr { + font-weight: bold; +} + td { padding: 2px 5px; font-size: 100%; @@ -262,7 +270,7 @@ td.sha1 { font-family: monospace; } -td.error { +.error { color: red; background-color: yellow; } diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 2cb832753a..24b219310a 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -160,7 +160,8 @@ our %known_snapshot_formats = ( # 'suffix' => filename suffix, # 'format' => --format for git-archive, # 'compressor' => [compressor command and arguments] - # (array reference, optional)} + # (array reference, optional) + # 'disabled' => boolean (optional)} # 'tgz' => { 'display' => 'tar.gz', @@ -176,6 +177,14 @@ our %known_snapshot_formats = ( 'format' => 'tar', 'compressor' => ['bzip2']}, + 'txz' => { + 'display' => 'tar.xz', + 'type' => 'application/x-xz', + 'suffix' => '.tar.xz', + 'format' => 'tar', + 'compressor' => ['xz'], + 'disabled' => 1}, + 'zip' => { 'display' => 'zip', 'type' => 'application/x-zip', @@ -188,6 +197,7 @@ our %known_snapshot_formats = ( our %known_snapshot_format_aliases = ( 'gzip' => 'tgz', 'bzip2' => 'tbz2', + 'xz' => 'txz', # backward compatibility: legacy gitweb config support 'x-gzip' => undef, 'gz' => undef, @@ -494,7 +504,8 @@ sub filter_snapshot_fmts { exists $known_snapshot_format_aliases{$_} ? $known_snapshot_format_aliases{$_} : $_} @fmts; @fmts = grep { - exists $known_snapshot_formats{$_} } @fmts; + exists $known_snapshot_formats{$_} && + !$known_snapshot_formats{$_}{'disabled'}} @fmts; } our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++"; @@ -940,10 +951,13 @@ sub href { if (defined $params{'hash_parent_base'}) { $href .= esc_url($params{'hash_parent_base'}); # skip the file_parent if it's the same as the file_name - delete $params{'file_parent'} if $params{'file_parent'} eq $params{'file_name'}; - if (defined $params{'file_parent'} && $params{'file_parent'} !~ /\.\./) { - $href .= ":/".esc_url($params{'file_parent'}); - delete $params{'file_parent'}; + if (defined $params{'file_parent'}) { + if (defined $params{'file_name'} && $params{'file_parent'} eq $params{'file_name'}) { + delete $params{'file_parent'}; + } elsif ($params{'file_parent'} !~ /\.\./) { + $href .= ":/".esc_url($params{'file_parent'}); + delete $params{'file_parent'}; + } } $href .= ".."; delete $params{'hash_parent'}; @@ -1510,10 +1524,10 @@ sub format_subject_html { $long =~ s/[[:cntrl:]]/?/g; return $cgi->a({-href => $href, -class => "list subject", -title => to_utf8($long)}, - esc_html($short) . $extra); + esc_html($short)) . $extra; } else { return $cgi->a({-href => $href, -class => "list subject"}, - esc_html($long) . $extra); + esc_html($long)) . $extra; } } @@ -4800,7 +4814,7 @@ sub git_blame { git_print_page_path($file_name, $ftype, $hash_base); # page body - my @rev_color = qw(light2 dark2); + my @rev_color = qw(light dark); my $num_colors = scalar(@rev_color); my $current_color = 0; my %metainfo = (); @@ -4818,15 +4832,18 @@ HTML my ($full_rev, $orig_lineno, $lineno, $group_size) = ($line =~ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/); if (!exists $metainfo{$full_rev}) { - $metainfo{$full_rev} = {}; + $metainfo{$full_rev} = { 'nprevious' => 0 }; } my $meta = $metainfo{$full_rev}; my $data; while ($data = <$fd>) { chomp $data; last if ($data =~ s/^\t//); # contents of line - if ($data =~ /^(\S+) (.*)$/) { - $meta->{$1} = $2; + if ($data =~ /^(\S+)(?: (.*))?$/) { + $meta->{$1} = $2 unless exists $meta->{$1}; + } + if ($data =~ /^previous /) { + $meta->{'nprevious'}++; } } my $short_rev = substr($full_rev, 0, 8); @@ -4837,7 +4854,11 @@ HTML if ($group_size) { $current_color = ($current_color + 1) % $num_colors; } - print "<tr id=\"l$lineno\" class=\"$rev_color[$current_color]\">\n"; + my $tr_class = $rev_color[$current_color]; + $tr_class .= ' boundary' if (exists $meta->{'boundary'}); + $tr_class .= ' no-previous' if ($meta->{'nprevious'} == 0); + $tr_class .= ' multiple-previous' if ($meta->{'nprevious'} > 1); + print "<tr id=\"l$lineno\" class=\"$tr_class\">\n"; if ($group_size) { print "<td class=\"sha1\""; print " title=\"". esc_html($author) . ", $date\""; @@ -4847,22 +4868,31 @@ HTML hash=>$full_rev, file_name=>$file_name)}, esc_html($short_rev)); + if ($group_size >= 2) { + my @author_initials = ($author =~ /\b([[:upper:]])\B/g); + if (@author_initials) { + print "<br />" . + esc_html(join('', @author_initials)); + # or join('.', ...) + } + } print "</td>\n"; } - my $parent_commit; - if (!exists $meta->{'parent'}) { - open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^") - or die_error(500, "Open git-rev-parse failed"); - $parent_commit = <$dd>; - close $dd; - chomp($parent_commit); - $meta->{'parent'} = $parent_commit; - } else { - $parent_commit = $meta->{'parent'}; - } + # 'previous' <sha1 of parent commit> <filename at commit> + if (exists $meta->{'previous'} && + $meta->{'previous'} =~ /^([a-fA-F0-9]{40}) (.*)$/) { + $meta->{'parent'} = $1; + $meta->{'file_parent'} = unquote($2); + } + my $linenr_commit = + exists($meta->{'parent'}) ? + $meta->{'parent'} : $full_rev; + my $linenr_filename = + exists($meta->{'file_parent'}) ? + $meta->{'file_parent'} : unquote($meta->{'filename'}); my $blamed = href(action => 'blame', - file_name => $meta->{'filename'}, - hash_base => $parent_commit); + file_name => $linenr_filename, + hash_base => $linenr_commit); print "<td class=\"linenr\">"; print $cgi->a({ -href => "$blamed#l$orig_lineno", -class => "linenr" }, @@ -5160,6 +5190,8 @@ sub git_snapshot { die_error(400, "Invalid snapshot format parameter"); } elsif (!exists($known_snapshot_formats{$format})) { die_error(400, "Unknown snapshot format"); + } elsif ($known_snapshot_formats{$format}{'disabled'}) { + die_error(403, "Snapshot format not allowed"); } elsif (!grep($_ eq $format, @snapshot_fmts)) { die_error(403, "Unsupported snapshot format"); } @@ -225,7 +225,12 @@ struct git_graph *graph_init(struct rev_info *opt) graph->num_columns = 0; graph->num_new_columns = 0; graph->mapping_size = 0; - graph->default_column_color = 0; + /* + * Start the column color at the maximum value, since we'll + * always increment it for the first commit we output. + * This way we start at 0 for the first commit. + */ + graph->default_column_color = COLUMN_COLORS_MAX - 1; /* * Allocate a reasonably large default number of columns @@ -286,9 +291,10 @@ static int graph_is_interesting(struct git_graph *graph, struct commit *commit) } /* - * Uninteresting and pruned commits won't be printed + * Otherwise, use get_commit_action() to see if this commit is + * interesting */ - return (commit->object.flags & (UNINTERESTING | TREESAME)) ? 0 : 1; + return get_commit_action(graph->revs, commit) == commit_show; } static struct commit_list *next_interesting_parent(struct git_graph *graph, @@ -499,11 +505,14 @@ static void graph_update_columns(struct git_graph *graph) parent; parent = next_interesting_parent(graph, parent)) { /* - * If this is a merge increment the current + * If this is a merge, or the start of a new + * childless column, increment the current * color. */ - if (graph->num_parents > 1) + if (graph->num_parents > 1 || + !is_commit_in_columns) { graph_increment_column_color(graph); + } graph_insert_into_new_columns(graph, parent->item, &mapping_idx); @@ -79,6 +79,7 @@ struct grep_opt { int pathname; int null_following_name; int color; + int max_depth; int funcname; char color_match[COLOR_MAXLEN]; const char *color_external; @@ -1006,7 +1006,6 @@ int finish_http_pack_request(struct http_pack_request *preq) struct http_pack_request *new_http_pack_request( struct packed_git *target, const char *base_url) { - char *url; char *filename; long prev_posn = 0; char range[RANGE_HEADER_SIZE]; @@ -1020,8 +1019,7 @@ struct http_pack_request *new_http_pack_request( end_url_with_slash(&buf, base_url); strbuf_addf(&buf, "objects/pack/pack-%s.pack", sha1_to_hex(target->sha1)); - url = strbuf_detach(&buf, NULL); - preq->url = xstrdup(url); + preq->url = strbuf_detach(&buf, NULL); filename = sha1_pack_name(target->sha1); snprintf(preq->filename, sizeof(preq->filename), "%s", filename); @@ -1037,7 +1035,7 @@ struct http_pack_request *new_http_pack_request( preq->slot->local = preq->packfile; curl_easy_setopt(preq->slot->curl, CURLOPT_FILE, preq->packfile); curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite); - curl_easy_setopt(preq->slot->curl, CURLOPT_URL, url); + curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url); curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header); @@ -1061,6 +1059,8 @@ struct http_pack_request *new_http_pack_request( abort: free(filename); + free(preq->url); + free(preq); return NULL; } @@ -1100,7 +1100,6 @@ struct http_object_request *new_http_object_request(const char *base_url, char *hex = sha1_to_hex(sha1); char *filename; char prevfile[PATH_MAX]; - char *url; int prevlocal; unsigned char prev_buf[PREV_BUF_SIZE]; ssize_t prev_read = 0; @@ -1154,8 +1153,7 @@ struct http_object_request *new_http_object_request(const char *base_url, git_SHA1_Init(&freq->c); - url = get_remote_object_url(base_url, hex, 0); - freq->url = xstrdup(url); + freq->url = get_remote_object_url(base_url, hex, 0); /* * If a previous temp file is present, process what was already @@ -1191,7 +1189,11 @@ struct http_object_request *new_http_object_request(const char *base_url, if (prev_posn>0) { prev_posn = 0; lseek(freq->localfile, 0, SEEK_SET); - ftruncate(freq->localfile, 0); + if (ftruncate(freq->localfile, 0) < 0) { + error("Couldn't truncate temporary file %s for %s: %s", + freq->tmpfile, freq->filename, strerror(errno)); + goto abort; + } } } @@ -1200,7 +1202,7 @@ struct http_object_request *new_http_object_request(const char *base_url, curl_easy_setopt(freq->slot->curl, CURLOPT_FILE, freq); curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file); curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr); - curl_easy_setopt(freq->slot->curl, CURLOPT_URL, url); + curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url); curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header); /* @@ -1220,9 +1222,9 @@ struct http_object_request *new_http_object_request(const char *base_url, return freq; - free(url); abort: free(filename); + free(freq->url); free(freq); return NULL; } diff --git a/ll-merge.c b/ll-merge.c index 0571564ddf..2d6b6d6cb1 100644 --- a/ll-merge.c +++ b/ll-merge.c @@ -192,10 +192,6 @@ static int ll_ext_merge(const struct ll_merge_driver *fn, args[2] = cmd.buf; status = run_command_v_opt(args, 0); - if (status < -ERR_RUN_COMMAND_FORK) - ; /* failure in run-command */ - else - status = -status; fd = open(temp[1], O_RDONLY); if (fd < 0) goto bad; @@ -19,16 +19,17 @@ /* * We refuse to tag something we can't verify. Just because. */ -static int verify_object(unsigned char *sha1, const char *expected_type) +static int verify_object(const unsigned char *sha1, const char *expected_type) { int ret = -1; enum object_type type; unsigned long size; - void *buffer = read_sha1_file(sha1, &type, &size); + const unsigned char *repl; + void *buffer = read_sha1_file_repl(sha1, &type, &size, &repl); if (buffer) { if (type == type_from_string(expected_type)) - ret = check_sha1_signature(sha1, buffer, size, expected_type); + ret = check_sha1_signature(repl, buffer, size, expected_type); free(buffer); } return ret; diff --git a/mozilla-sha1/sha1.c b/mozilla-sha1/sha1.c deleted file mode 100644 index 95a4ebf496..0000000000 --- a/mozilla-sha1/sha1.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is SHA 180-1 Reference Implementation (Compact version) - * - * The Initial Developer of the Original Code is Paul Kocher of - * Cryptography Research. Portions created by Paul Kocher are - * Copyright (C) 1995-9 by Cryptography Research, Inc. All - * Rights Reserved. - * - * Contributor(s): - * - * Paul Kocher - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -#include "sha1.h" - -static void shaHashBlock(moz_SHA_CTX *ctx); - -void moz_SHA1_Init(moz_SHA_CTX *ctx) { - int i; - - ctx->lenW = 0; - ctx->sizeHi = ctx->sizeLo = 0; - - /* Initialize H with the magic constants (see FIPS180 for constants) - */ - ctx->H[0] = 0x67452301; - ctx->H[1] = 0xefcdab89; - ctx->H[2] = 0x98badcfe; - ctx->H[3] = 0x10325476; - ctx->H[4] = 0xc3d2e1f0; - - for (i = 0; i < 80; i++) - ctx->W[i] = 0; -} - - -void moz_SHA1_Update(moz_SHA_CTX *ctx, const void *_dataIn, int len) { - const unsigned char *dataIn = _dataIn; - int i; - - /* Read the data into W and process blocks as they get full - */ - for (i = 0; i < len; i++) { - ctx->W[ctx->lenW / 4] <<= 8; - ctx->W[ctx->lenW / 4] |= (unsigned int)dataIn[i]; - if ((++ctx->lenW) % 64 == 0) { - shaHashBlock(ctx); - ctx->lenW = 0; - } - ctx->sizeLo += 8; - ctx->sizeHi += (ctx->sizeLo < 8); - } -} - - -void moz_SHA1_Final(unsigned char hashout[20], moz_SHA_CTX *ctx) { - unsigned char pad0x80 = 0x80; - unsigned char pad0x00 = 0x00; - unsigned char padlen[8]; - int i; - - /* Pad with a binary 1 (e.g. 0x80), then zeroes, then length - */ - padlen[0] = (unsigned char)((ctx->sizeHi >> 24) & 255); - padlen[1] = (unsigned char)((ctx->sizeHi >> 16) & 255); - padlen[2] = (unsigned char)((ctx->sizeHi >> 8) & 255); - padlen[3] = (unsigned char)((ctx->sizeHi >> 0) & 255); - padlen[4] = (unsigned char)((ctx->sizeLo >> 24) & 255); - padlen[5] = (unsigned char)((ctx->sizeLo >> 16) & 255); - padlen[6] = (unsigned char)((ctx->sizeLo >> 8) & 255); - padlen[7] = (unsigned char)((ctx->sizeLo >> 0) & 255); - moz_SHA1_Update(ctx, &pad0x80, 1); - while (ctx->lenW != 56) - moz_SHA1_Update(ctx, &pad0x00, 1); - moz_SHA1_Update(ctx, padlen, 8); - - /* Output hash - */ - for (i = 0; i < 20; i++) { - hashout[i] = (unsigned char)(ctx->H[i / 4] >> 24); - ctx->H[i / 4] <<= 8; - } - - /* - * Re-initialize the context (also zeroizes contents) - */ - moz_SHA1_Init(ctx); -} - - -#define SHA_ROT(X,n) (((X) << (n)) | ((X) >> (32-(n)))) - -static void shaHashBlock(moz_SHA_CTX *ctx) { - int t; - unsigned int A,B,C,D,E,TEMP; - - for (t = 16; t <= 79; t++) - ctx->W[t] = - SHA_ROT(ctx->W[t-3] ^ ctx->W[t-8] ^ ctx->W[t-14] ^ ctx->W[t-16], 1); - - A = ctx->H[0]; - B = ctx->H[1]; - C = ctx->H[2]; - D = ctx->H[3]; - E = ctx->H[4]; - - for (t = 0; t <= 19; t++) { - TEMP = SHA_ROT(A,5) + (((C^D)&B)^D) + E + ctx->W[t] + 0x5a827999; - E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP; - } - for (t = 20; t <= 39; t++) { - TEMP = SHA_ROT(A,5) + (B^C^D) + E + ctx->W[t] + 0x6ed9eba1; - E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP; - } - for (t = 40; t <= 59; t++) { - TEMP = SHA_ROT(A,5) + ((B&C)|(D&(B|C))) + E + ctx->W[t] + 0x8f1bbcdc; - E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP; - } - for (t = 60; t <= 79; t++) { - TEMP = SHA_ROT(A,5) + (B^C^D) + E + ctx->W[t] + 0xca62c1d6; - E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP; - } - - ctx->H[0] += A; - ctx->H[1] += B; - ctx->H[2] += C; - ctx->H[3] += D; - ctx->H[4] += E; -} diff --git a/mozilla-sha1/sha1.h b/mozilla-sha1/sha1.h deleted file mode 100644 index aa48a46cf7..0000000000 --- a/mozilla-sha1/sha1.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is SHA 180-1 Header File - * - * The Initial Developer of the Original Code is Paul Kocher of - * Cryptography Research. Portions created by Paul Kocher are - * Copyright (C) 1995-9 by Cryptography Research, Inc. All - * Rights Reserved. - * - * Contributor(s): - * - * Paul Kocher - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -typedef struct { - unsigned int H[5]; - unsigned int W[80]; - int lenW; - unsigned int sizeHi,sizeLo; -} moz_SHA_CTX; - -void moz_SHA1_Init(moz_SHA_CTX *ctx); -void moz_SHA1_Update(moz_SHA_CTX *ctx, const void *dataIn, int len); -void moz_SHA1_Final(unsigned char hashout[20], moz_SHA_CTX *ctx); - -#define git_SHA_CTX moz_SHA_CTX -#define git_SHA1_Init moz_SHA1_Init -#define git_SHA1_Update moz_SHA1_Update -#define git_SHA1_Final moz_SHA1_Final @@ -188,17 +188,18 @@ struct object *parse_object(const unsigned char *sha1) unsigned long size; enum object_type type; int eaten; - void *buffer = read_sha1_file(sha1, &type, &size); + const unsigned char *repl; + void *buffer = read_sha1_file_repl(sha1, &type, &size, &repl); if (buffer) { struct object *obj; - if (check_sha1_signature(sha1, buffer, size, typename(type)) < 0) { + if (check_sha1_signature(repl, buffer, size, typename(type)) < 0) { free(buffer); - error("sha1 mismatch %s\n", sha1_to_hex(sha1)); + error("sha1 mismatch %s\n", sha1_to_hex(repl)); return NULL; } - obj = parse_object_buffer(sha1, type, size, buffer, &eaten); + obj = parse_object_buffer(repl, type, size, buffer, &eaten); if (!eaten) free(buffer); return obj; diff --git a/pack-redundant.c b/pack-redundant.c index 48a12bc135..69a7ab2e27 100644 --- a/pack-redundant.c +++ b/pack-redundant.c @@ -55,16 +55,15 @@ static inline struct llist_item *llist_item_get(void) } else { int i = 1; new = xmalloc(sizeof(struct llist_item) * BLKSIZE); - for(;i < BLKSIZE; i++) { + for (; i < BLKSIZE; i++) llist_item_put(&new[i]); - } } return new; } static void llist_free(struct llist *list) { - while((list->back = list->front)) { + while ((list->back = list->front)) { list->front = list->front->next; llist_item_put(list->back); } @@ -146,7 +145,7 @@ static inline struct llist_item *llist_insert_sorted_unique(struct llist *list, if (cmp > 0) { /* we insert before this entry */ return llist_insert(list, prev, sha1); } - if(!cmp) { /* already exists */ + if (!cmp) { /* already exists */ return l; } prev = l; @@ -168,7 +167,7 @@ redo_from_start: int cmp = hashcmp(l->sha1, sha1); if (cmp > 0) /* not in list, since sorted */ return prev; - if(!cmp) { /* found */ + if (!cmp) { /* found */ if (prev == NULL) { if (hint != NULL && hint != list->front) { /* we don't know the previous element */ @@ -218,7 +217,7 @@ static inline struct pack_list * pack_list_insert(struct pack_list **pl, static inline size_t pack_list_size(struct pack_list *pl) { size_t ret = 0; - while(pl) { + while (pl) { ret++; pl = pl->next; } @@ -396,7 +395,7 @@ static size_t get_pack_redundancy(struct pack_list *pl) return 0; while ((subset = pl->next)) { - while(subset) { + while (subset) { ret += sizeof_union(pl->pack, subset->pack); subset = subset->next; } @@ -427,7 +426,7 @@ static void minimize(struct pack_list **min) pl = local_packs; while (pl) { - if(pl->unique_objects->size) + if (pl->unique_objects->size) pack_list_insert(&unique, pl); else pack_list_insert(&non_unique, pl); @@ -479,7 +478,7 @@ static void minimize(struct pack_list **min) *min = min_perm; /* add the unique packs to the list */ pl = unique; - while(pl) { + while (pl) { pack_list_insert(min, pl); pl = pl->next; } @@ -516,7 +515,7 @@ static void cmp_local_packs(void) struct pack_list *subset, *pl = local_packs; while ((subset = pl)) { - while((subset = subset->next)) + while ((subset = subset->next)) cmp_two_packs(pl, subset); pl = pl->next; } @@ -608,23 +607,23 @@ int main(int argc, char **argv) for (i = 1; i < argc; i++) { const char *arg = argv[i]; - if(!strcmp(arg, "--")) { + if (!strcmp(arg, "--")) { i++; break; } - if(!strcmp(arg, "--all")) { + if (!strcmp(arg, "--all")) { load_all_packs = 1; continue; } - if(!strcmp(arg, "--verbose")) { + if (!strcmp(arg, "--verbose")) { verbose = 1; continue; } - if(!strcmp(arg, "--alt-odb")) { + if (!strcmp(arg, "--alt-odb")) { alt_odb = 1; continue; } - if(*arg == '-') + if (*arg == '-') usage(pack_redundant_usage); else break; diff --git a/parse-options.c b/parse-options.c index f7ce523a61..a64a4d6ee2 100644 --- a/parse-options.c +++ b/parse-options.c @@ -511,7 +511,7 @@ static int usage_with_options_internal(const char * const *usagestr, continue; pos = fprintf(stderr, " "); - if (opts->short_name) { + if (opts->short_name && !(opts->flags & PARSE_OPT_NEGHELP)) { if (opts->flags & PARSE_OPT_NODASH) pos += fprintf(stderr, "%c", opts->short_name); else @@ -520,7 +520,9 @@ static int usage_with_options_internal(const char * const *usagestr, if (opts->long_name && opts->short_name) pos += fprintf(stderr, ", "); if (opts->long_name) - pos += fprintf(stderr, "--%s", opts->long_name); + pos += fprintf(stderr, "--%s%s", + (opts->flags & PARSE_OPT_NEGHELP) ? "no-" : "", + opts->long_name); if (opts->type == OPTION_NUMBER) pos += fprintf(stderr, "-NUM"); @@ -547,6 +549,14 @@ void usage_with_options(const char * const *usagestr, exit(129); } +void usage_msg_opt(const char *msg, + const char * const *usagestr, + const struct option *options) +{ + fprintf(stderr, "%s\n\n", msg); + usage_with_options(usagestr, options); +} + int parse_options_usage(const char * const *usagestr, const struct option *opts) { diff --git a/parse-options.h b/parse-options.h index aba30671dc..f295a2cf85 100644 --- a/parse-options.h +++ b/parse-options.h @@ -36,6 +36,7 @@ enum parse_opt_option_flags { PARSE_OPT_LASTARG_DEFAULT = 16, PARSE_OPT_NODASH = 32, PARSE_OPT_LITERAL_ARGHELP = 64, + PARSE_OPT_NEGHELP = 128, }; struct option; @@ -80,6 +81,9 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset); * PARSE_OPT_LITERAL_ARGHELP: says that argh shouldn't be enclosed in brackets * (i.e. '<argh>') in the help message. * Useful for options with multiple parameters. + * PARSE_OPT_NEGHELP: says that the long option should always be shown with + * the --no prefix in the usage message. Sometimes + * useful for users of OPTION_NEGBIT. * * `callback`:: * pointer to the callback to use for OPTION_CALLBACK. @@ -141,6 +145,10 @@ extern int parse_options(int argc, const char **argv, const char *prefix, extern NORETURN void usage_with_options(const char * const *usagestr, const struct option *options); +extern NORETURN void usage_msg_opt(const char *msg, + const char * const *usagestr, + const struct option *options); + /*----- incremental advanced APIs -----*/ enum { diff --git a/read-cache.c b/read-cache.c index 4e3e272ee4..1bbaf1cffb 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1065,7 +1065,18 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate, return updated; } -int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec, char *seen) +static void show_file(const char * fmt, const char * name, int in_porcelain, + int * first, char *header_msg) +{ + if (in_porcelain && *first && header_msg) { + printf("%s\n", header_msg); + *first=0; + } + printf(fmt, name); +} + +int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec, + char *seen, char *header_msg) { int i; int has_errors = 0; @@ -1074,11 +1085,14 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p int quiet = (flags & REFRESH_QUIET) != 0; int not_new = (flags & REFRESH_IGNORE_MISSING) != 0; int ignore_submodules = (flags & REFRESH_IGNORE_SUBMODULES) != 0; + int first = 1; + int in_porcelain = (flags & REFRESH_IN_PORCELAIN); unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0; - const char *needs_update_message; + const char *needs_update_fmt; + const char *needs_merge_fmt; - needs_update_message = ((flags & REFRESH_SAY_CHANGED) - ? "locally modified" : "needs update"); + needs_update_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n"); + needs_merge_fmt = (in_porcelain ? "U\t%s\n" : "%s: needs merge\n"); for (i = 0; i < istate->cache_nr; i++) { struct cache_entry *ce, *new; int cache_errno = 0; @@ -1094,7 +1108,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p i--; if (allow_unmerged) continue; - printf("%s: needs merge\n", ce->name); + show_file(needs_merge_fmt, ce->name, in_porcelain, &first, header_msg); has_errors = 1; continue; } @@ -1117,7 +1131,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p } if (quiet) continue; - printf("%s: %s\n", ce->name, needs_update_message); + show_file(needs_update_fmt, ce->name, in_porcelain, &first, header_msg); has_errors = 1; continue; } @@ -668,6 +668,11 @@ int for_each_remote_ref(each_ref_fn fn, void *cb_data) return for_each_ref_in("refs/remotes/", fn, cb_data); } +int for_each_replace_ref(each_ref_fn fn, void *cb_data) +{ + return do_for_each_ref("refs/replace/", fn, 13, 0, cb_data); +} + int for_each_rawref(each_ref_fn fn, void *cb_data) { return do_for_each_ref("refs/", fn, 0, @@ -24,6 +24,7 @@ extern int for_each_ref_in(const char *, each_ref_fn, void *); extern int for_each_tag_ref(each_ref_fn, void *); extern int for_each_branch_ref(each_ref_fn, void *); extern int for_each_remote_ref(each_ref_fn, void *); +extern int for_each_replace_ref(each_ref_fn, void *); /* can be used to learn about broken ref and symref */ extern int for_each_rawref(each_ref_fn, void *); @@ -1038,7 +1038,7 @@ static int match_explicit(struct ref *src, struct ref *dst, case 0: if (!memcmp(dst_value, "refs/", 5)) matched_dst = make_linked_ref(dst_value, dst_tail); - else if((dst_guess = guess_ref(dst_value, matched_src))) + else if ((dst_guess = guess_ref(dst_value, matched_src))) matched_dst = make_linked_ref(dst_guess, dst_tail); else error("unable to push to unqualified destination: %s\n" diff --git a/replace_object.c b/replace_object.c new file mode 100644 index 0000000000..eb59604fd3 --- /dev/null +++ b/replace_object.c @@ -0,0 +1,114 @@ +#include "cache.h" +#include "sha1-lookup.h" +#include "refs.h" + +static struct replace_object { + unsigned char sha1[2][20]; +} **replace_object; + +static int replace_object_alloc, replace_object_nr; + +static const unsigned char *replace_sha1_access(size_t index, void *table) +{ + struct replace_object **replace = table; + return replace[index]->sha1[0]; +} + +static int replace_object_pos(const unsigned char *sha1) +{ + return sha1_pos(sha1, replace_object, replace_object_nr, + replace_sha1_access); +} + +static int register_replace_object(struct replace_object *replace, + int ignore_dups) +{ + int pos = replace_object_pos(replace->sha1[0]); + + if (0 <= pos) { + if (ignore_dups) + free(replace); + else { + free(replace_object[pos]); + replace_object[pos] = replace; + } + return 1; + } + pos = -pos - 1; + if (replace_object_alloc <= ++replace_object_nr) { + replace_object_alloc = alloc_nr(replace_object_alloc); + replace_object = xrealloc(replace_object, + sizeof(*replace_object) * + replace_object_alloc); + } + if (pos < replace_object_nr) + memmove(replace_object + pos + 1, + replace_object + pos, + (replace_object_nr - pos - 1) * + sizeof(*replace_object)); + replace_object[pos] = replace; + return 0; +} + +static int register_replace_ref(const char *refname, + const unsigned char *sha1, + int flag, void *cb_data) +{ + /* Get sha1 from refname */ + const char *slash = strrchr(refname, '/'); + const char *hash = slash ? slash + 1 : refname; + struct replace_object *repl_obj = xmalloc(sizeof(*repl_obj)); + + if (strlen(hash) != 40 || get_sha1_hex(hash, repl_obj->sha1[0])) { + free(repl_obj); + warning("bad replace ref name: %s", refname); + return 0; + } + + /* Copy sha1 from the read ref */ + hashcpy(repl_obj->sha1[1], sha1); + + /* Register new object */ + if (register_replace_object(repl_obj, 1)) + die("duplicate replace ref: %s", refname); + + return 0; +} + +static void prepare_replace_object(void) +{ + static int replace_object_prepared; + + if (replace_object_prepared) + return; + + for_each_replace_ref(register_replace_ref, NULL); + replace_object_prepared = 1; +} + +/* We allow "recursive" replacement. Only within reason, though */ +#define MAXREPLACEDEPTH 5 + +const unsigned char *lookup_replace_object(const unsigned char *sha1) +{ + int pos, depth = MAXREPLACEDEPTH; + const unsigned char *cur = sha1; + + if (!read_replace_refs) + return sha1; + + prepare_replace_object(); + + /* Try to recursively replace the object */ + do { + if (--depth < 0) + die("replace depth too high for object %s", + sha1_to_hex(sha1)); + + pos = replace_object_pos(cur); + if (0 <= pos) + cur = replace_object[pos]->sha1[1]; + } while (0 <= pos); + + return cur; +} diff --git a/revision.c b/revision.c index ce24ad9a8d..35eca4a361 100644 --- a/revision.c +++ b/revision.c @@ -1664,7 +1664,7 @@ static inline int want_ancestry(struct rev_info *revs) return (revs->rewrite_parents || revs->children.name); } -enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit) +enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit) { if (commit->object.flags & SHOWN) return commit_ignore; @@ -1692,12 +1692,23 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit) if (!commit->parents || !commit->parents->next) return commit_ignore; } - if (want_ancestry(revs) && rewrite_parents(revs, commit) < 0) - return commit_error; } return commit_show; } +enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit) +{ + enum commit_action action = get_commit_action(revs, commit); + + if (action == commit_show && + !revs->show_all && + revs->prune && revs->dense && want_ancestry(revs)) { + if (rewrite_parents(revs, commit) < 0) + return commit_error; + } + return action; +} + static struct commit *get_revision_1(struct rev_info *revs) { if (!revs->commits) diff --git a/revision.h b/revision.h index b10984b603..9d0dddbcbc 100644 --- a/revision.h +++ b/revision.h @@ -168,6 +168,7 @@ enum commit_action { commit_error }; +extern enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit); extern enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit); #endif diff --git a/run-command.c b/run-command.c index ff3d8e2d8b..f3e7abb7de 100644 --- a/run-command.c +++ b/run-command.c @@ -19,6 +19,7 @@ int start_command(struct child_process *cmd) { int need_in, need_out, need_err; int fdin[2], fdout[2], fderr[2]; + int failed_errno = failed_errno; /* * In case of errors we must keep the promise to close FDs @@ -28,9 +29,10 @@ int start_command(struct child_process *cmd) need_in = !cmd->no_stdin && cmd->in < 0; if (need_in) { if (pipe(fdin) < 0) { + failed_errno = errno; if (cmd->out > 0) close(cmd->out); - return -ERR_RUN_COMMAND_PIPE; + goto fail_pipe; } cmd->in = fdin[1]; } @@ -40,11 +42,12 @@ int start_command(struct child_process *cmd) && cmd->out < 0; if (need_out) { if (pipe(fdout) < 0) { + failed_errno = errno; if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); - return -ERR_RUN_COMMAND_PIPE; + goto fail_pipe; } cmd->out = fdout[0]; } @@ -52,6 +55,7 @@ int start_command(struct child_process *cmd) need_err = !cmd->no_stderr && cmd->err < 0; if (need_err) { if (pipe(fderr) < 0) { + failed_errno = errno; if (need_in) close_pair(fdin); else if (cmd->in) @@ -60,7 +64,11 @@ int start_command(struct child_process *cmd) close_pair(fdout); else if (cmd->out) close(cmd->out); - return -ERR_RUN_COMMAND_PIPE; +fail_pipe: + error("cannot create pipe for %s: %s", + cmd->argv[0], strerror(failed_errno)); + errno = failed_errno; + return -1; } cmd->err = fderr[0]; } @@ -122,6 +130,9 @@ int start_command(struct child_process *cmd) strerror(errno)); exit(127); } + if (cmd->pid < 0) + error("cannot fork() for %s: %s", cmd->argv[0], + strerror(failed_errno = errno)); #else int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */ const char **sargv = cmd->argv; @@ -173,6 +184,9 @@ int start_command(struct child_process *cmd) } cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env); + failed_errno = errno; + if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT)) + error("cannot spawn %s: %s", cmd->argv[0], strerror(errno)); if (cmd->env) free_environ(env); @@ -189,7 +203,6 @@ int start_command(struct child_process *cmd) #endif if (cmd->pid < 0) { - int err = errno; if (need_in) close_pair(fdin); else if (cmd->in) @@ -200,9 +213,8 @@ int start_command(struct child_process *cmd) close(cmd->out); if (need_err) close_pair(fderr); - return err == ENOENT ? - -ERR_RUN_COMMAND_EXEC : - -ERR_RUN_COMMAND_FORK; + errno = failed_errno; + return -1; } if (need_in) @@ -221,40 +233,51 @@ int start_command(struct child_process *cmd) return 0; } -static int wait_or_whine(pid_t pid) +static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure) { - for (;;) { - int status, code; - pid_t waiting = waitpid(pid, &status, 0); - - if (waiting < 0) { - if (errno == EINTR) - continue; - error("waitpid failed (%s)", strerror(errno)); - return -ERR_RUN_COMMAND_WAITPID; - } - if (waiting != pid) - return -ERR_RUN_COMMAND_WAITPID_WRONG_PID; - if (WIFSIGNALED(status)) - return -ERR_RUN_COMMAND_WAITPID_SIGNAL; - - if (!WIFEXITED(status)) - return -ERR_RUN_COMMAND_WAITPID_NOEXIT; + int status, code = -1; + pid_t waiting; + int failed_errno = 0; + + while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR) + ; /* nothing */ + + if (waiting < 0) { + failed_errno = errno; + error("waitpid for %s failed: %s", argv0, strerror(errno)); + } else if (waiting != pid) { + error("waitpid is confused (%s)", argv0); + } else if (WIFSIGNALED(status)) { + code = WTERMSIG(status); + error("%s died of signal %d", argv0, code); + /* + * This return value is chosen so that code & 0xff + * mimics the exit code that a POSIX shell would report for + * a program that died from this signal. + */ + code -= 128; + } else if (WIFEXITED(status)) { code = WEXITSTATUS(status); - switch (code) { - case 127: - return -ERR_RUN_COMMAND_EXEC; - case 0: - return 0; - default: - return -code; + /* + * Convert special exit code when execvp failed. + */ + if (code == 127) { + code = -1; + failed_errno = ENOENT; + if (!silent_exec_failure) + error("cannot run %s: %s", argv0, + strerror(ENOENT)); } + } else { + error("waitpid is confused (%s)", argv0); } + errno = failed_errno; + return code; } int finish_command(struct child_process *cmd) { - return wait_or_whine(cmd->pid); + return wait_or_whine(cmd->pid, cmd->argv[0], cmd->silent_exec_failure); } int run_command(struct child_process *cmd) @@ -274,6 +297,7 @@ static void prepare_run_command_v_opt(struct child_process *cmd, cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0; cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0; cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; + cmd->silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0; } int run_command_v_opt(const char **argv, int opt) @@ -338,10 +362,7 @@ int start_async(struct async *async) int finish_async(struct async *async) { #ifndef __MINGW32__ - int ret = 0; - - if (wait_or_whine(async->pid)) - ret = error("waitpid (async) failed"); + int ret = wait_or_whine(async->pid, "child process", 0); #else DWORD ret = 0; if (WaitForSingleObject(async->tid, INFINITE) != WAIT_OBJECT_0) @@ -385,15 +406,7 @@ int run_hook(const char *index_file, const char *name, ...) hook.env = env; } - ret = start_command(&hook); + ret = run_command(&hook); free(argv); - if (ret) { - warning("Could not spawn %s", argv[0]); - return ret; - } - ret = finish_command(&hook); - if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL) - warning("%s exited due to uncaught signal", argv[0]); - return ret; } diff --git a/run-command.h b/run-command.h index e345502843..0c00b25ff2 100644 --- a/run-command.h +++ b/run-command.h @@ -1,17 +1,6 @@ #ifndef RUN_COMMAND_H #define RUN_COMMAND_H -enum { - ERR_RUN_COMMAND_FORK = 10000, - ERR_RUN_COMMAND_EXEC, - ERR_RUN_COMMAND_PIPE, - ERR_RUN_COMMAND_WAITPID, - ERR_RUN_COMMAND_WAITPID_WRONG_PID, - ERR_RUN_COMMAND_WAITPID_SIGNAL, - ERR_RUN_COMMAND_WAITPID_NOEXIT, -}; -#define IS_RUN_COMMAND_ERR(x) (-(x) >= ERR_RUN_COMMAND_FORK) - struct child_process { const char **argv; pid_t pid; @@ -42,6 +31,7 @@ struct child_process { unsigned no_stdout:1; unsigned no_stderr:1; unsigned git_cmd:1; /* if this is to be git sub-command */ + unsigned silent_exec_failure:1; unsigned stdout_to_stderr:1; void (*preexec_cb)(void); }; @@ -55,6 +45,7 @@ extern int run_hook(const char *index_file, const char *name, ...); #define RUN_COMMAND_NO_STDIN 1 #define RUN_GIT_CMD 2 /*If this is to be git sub-command */ #define RUN_COMMAND_STDOUT_TO_STDERR 4 +#define RUN_SILENT_EXEC_FAILURE 8 int run_command_v_opt(const char **argv, int opt); /* diff --git a/send-pack.h b/send-pack.h index 1d7b1b3b4f..8b3cf028ed 100644 --- a/send-pack.h +++ b/send-pack.h @@ -3,6 +3,7 @@ struct send_pack_args { unsigned verbose:1, + quiet:1, send_mirror:1, force_update:1, use_thin_pack:1, diff --git a/sha1_file.c b/sha1_file.c index 1d996a1990..4ea0b18d0a 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2144,13 +2144,26 @@ static void *read_object(const unsigned char *sha1, enum object_type *type, return read_packed_sha1(sha1, type, size); } -void *read_sha1_file(const unsigned char *sha1, enum object_type *type, - unsigned long *size) +void *read_sha1_file_repl(const unsigned char *sha1, + enum object_type *type, + unsigned long *size, + const unsigned char **replacement) { - void *data = read_object(sha1, type, size); + const unsigned char *repl = lookup_replace_object(sha1); + void *data = read_object(repl, type, size); + + /* die if we replaced an object with one that does not exist */ + if (!data && repl != sha1) + die("replacement %s not found for %s", + sha1_to_hex(repl), sha1_to_hex(sha1)); + /* legacy behavior is to die on corrupted objects */ - if (!data && (has_loose_object(sha1) || has_packed_and_bad(sha1))) - die("object %s is corrupted", sha1_to_hex(sha1)); + if (!data && (has_loose_object(repl) || has_packed_and_bad(repl))) + die("object %s is corrupted", sha1_to_hex(repl)); + + if (replacement) + *replacement = repl; + return data; } @@ -322,7 +322,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint) return -1; } -int strbuf_getline(struct strbuf *sb, FILE *fp, int term) +int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term) { int ch; @@ -332,10 +332,10 @@ int strbuf_getline(struct strbuf *sb, FILE *fp, int term) strbuf_reset(sb); while ((ch = fgetc(fp)) != EOF) { - if (ch == term) - break; strbuf_grow(sb, 1); sb->buf[sb->len++] = ch; + if (ch == term) + break; } if (ch == EOF && sb->len == 0) return EOF; @@ -344,6 +344,15 @@ int strbuf_getline(struct strbuf *sb, FILE *fp, int term) return 0; } +int strbuf_getline(struct strbuf *sb, FILE *fp, int term) +{ + if (strbuf_getwholeline(sb, fp, term)) + return EOF; + if (sb->buf[sb->len-1] == term) + strbuf_setlen(sb, sb->len-1); + return 0; +} + int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint) { int fd, len; @@ -126,6 +126,7 @@ extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint); extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint); extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint); +extern int strbuf_getwholeline(struct strbuf *, FILE *, int); extern int strbuf_getline(struct strbuf *, FILE *, int); extern void stripspace(struct strbuf *buf, int skip_comments); diff --git a/t/Makefile b/t/Makefile index bf816fc850..bd09390d32 100644 --- a/t/Makefile +++ b/t/Makefile @@ -3,6 +3,8 @@ # Copyright (c) 2005 Junio C Hamano # +-include ../config.mak + #GIT_TEST_OPTS=--verbose --debug SHELL_PATH ?= $(SHELL) TAR ?= $(TAR) diff --git a/t/gitweb-lib.sh b/t/gitweb-lib.sh new file mode 100644 index 0000000000..845253274b --- /dev/null +++ b/t/gitweb-lib.sh @@ -0,0 +1,73 @@ +#!/bin/sh +# +# Copyright (c) 2007 Jakub Narebski +# + +gitweb_init () { + safe_pwd="$(perl -MPOSIX=getcwd -e 'print quotemeta(getcwd)')" + cat >gitweb_config.perl <<EOF +#!/usr/bin/perl + +# gitweb configuration for tests + +our \$version = 'current'; +our \$GIT = 'git'; +our \$projectroot = "$safe_pwd"; +our \$project_maxdepth = 8; +our \$home_link_str = 'projects'; +our \$site_name = '[localhost]'; +our \$site_header = ''; +our \$site_footer = ''; +our \$home_text = 'indextext.html'; +our @stylesheets = ('file:///$TEST_DIRECTORY/../gitweb/gitweb.css'); +our \$logo = 'file:///$TEST_DIRECTORY/../gitweb/git-logo.png'; +our \$favicon = 'file:///$TEST_DIRECTORY/../gitweb/git-favicon.png'; +our \$projects_list = ''; +our \$export_ok = ''; +our \$strict_export = ''; + +EOF + + cat >.git/description <<EOF +$0 test repository +EOF +} + +gitweb_run () { + GATEWAY_INTERFACE='CGI/1.1' + HTTP_ACCEPT='*/*' + REQUEST_METHOD='GET' + SCRIPT_NAME="$TEST_DIRECTORY/../gitweb/gitweb.perl" + QUERY_STRING=""$1"" + PATH_INFO=""$2"" + export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD \ + SCRIPT_NAME QUERY_STRING PATH_INFO + + GITWEB_CONFIG=$(pwd)/gitweb_config.perl + export GITWEB_CONFIG + + # some of git commands write to STDERR on error, but this is not + # written to web server logs, so we are not interested in that: + # we are interested only in properly formatted errors/warnings + rm -f gitweb.log && + perl -- "$SCRIPT_NAME" \ + >gitweb.output 2>gitweb.log && + if grep '^[[]' gitweb.log >/dev/null 2>&1; then false; else true; fi + + # gitweb.log is left for debugging + # gitweb.output is used to parse http output +} + +. ./test-lib.sh + +if ! test_have_prereq PERL; then + say 'skipping gitweb tests, perl not available' + test_done +fi + +perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || { + say 'skipping gitweb tests, perl version is too old' + test_done +} + +gitweb_init diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh new file mode 100644 index 0000000000..4b3b793730 --- /dev/null +++ b/t/lib-cvs.sh @@ -0,0 +1,75 @@ +#!/bin/sh + +. ./test-lib.sh + +unset CVS_SERVER +# for clean cvsps cache +HOME=$(pwd) +export HOME + +if ! type cvs >/dev/null 2>&1 +then + say 'skipping cvsimport tests, cvs not found' + test_done +fi + +CVS="cvs -f" +export CVS + +cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'` +case "$cvsps_version" in +2.1 | 2.2*) + ;; +'') + say 'skipping cvsimport tests, cvsps not found' + test_done + ;; +*) + say 'skipping cvsimport tests, unsupported cvsps version' + test_done + ;; +esac + +test_cvs_co () { + # Usage: test_cvs_co BRANCH_NAME + rm -rf module-cvs-"$1" + if [ "$1" = "master" ] + then + $CVS co -P -d module-cvs-"$1" -A module + else + $CVS co -P -d module-cvs-"$1" -r "$1" module + fi +} + +test_git_co () { + # Usage: test_git_co BRANCH_NAME + (cd module-git && git checkout "$1") +} + +test_cmp_branch_file () { + # Usage: test_cmp_branch_file BRANCH_NAME PATH + # The branch must already be checked out of CVS and git. + test_cmp module-cvs-"$1"/"$2" module-git/"$2" +} + +test_cmp_branch_tree () { + # Usage: test_cmp_branch_tree BRANCH_NAME + # Check BRANCH_NAME out of CVS and git and make sure that all + # of the files and directories are identical. + + test_cvs_co "$1" && + test_git_co "$1" && + ( + cd module-cvs-"$1" + find . -type d -name CVS -prune -o -type f -print + ) | sort >module-cvs-"$1".list && + ( + cd module-git + find . -type d -name .git -prune -o -type f -print + ) | sort >module-git-"$1".list && + test_cmp module-cvs-"$1".list module-git-"$1".list && + cat module-cvs-"$1".list | while read f + do + test_cmp_branch_file "$1" "$f" || return 1 + done +} diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh index 5654962343..fd8631f906 100644 --- a/t/lib-git-svn.sh +++ b/t/lib-git-svn.sh @@ -14,7 +14,7 @@ if ! test_have_prereq PERL; then fi GIT_DIR=$PWD/.git -GIT_SVN_DIR=$GIT_DIR/svn/git-svn +GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn SVN_TREE=$GIT_SVN_DIR/svn-tree svn >/dev/null 2>&1 diff --git a/t/t0001-init.sh b/t/t0001-init.sh index e3d846420d..5386504790 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -208,4 +208,87 @@ test_expect_success 'init rejects insanely long --template' ' ) ' +test_expect_success 'init creates a new directory' ' + rm -fr newdir && + ( + git init newdir && + test -d newdir/.git/refs + ) +' + +test_expect_success 'init creates a new bare directory' ' + rm -fr newdir && + ( + git init --bare newdir && + test -d newdir/refs + ) +' + +test_expect_success 'init recreates a directory' ' + rm -fr newdir && + ( + mkdir newdir && + git init newdir && + test -d newdir/.git/refs + ) +' + +test_expect_success 'init recreates a new bare directory' ' + rm -fr newdir && + ( + mkdir newdir && + git init --bare newdir && + test -d newdir/refs + ) +' + +test_expect_success 'init creates a new deep directory' ' + rm -fr newdir && + git init newdir/a/b/c && + test -d newdir/a/b/c/.git/refs +' + +test_expect_success POSIXPERM 'init creates a new deep directory (umask vs. shared)' ' + rm -fr newdir && + ( + # Leading directories should honor umask while + # the repository itself should follow "shared" + umask 002 && + git init --bare --shared=0660 newdir/a/b/c && + test -d newdir/a/b/c/refs && + ls -ld newdir/a newdir/a/b > lsab.out && + ! grep -v "^drwxrw[sx]r-x" lsab.out && + ls -ld newdir/a/b/c > lsc.out && + ! grep -v "^drwxrw[sx]---" lsc.out + ) +' + +test_expect_success 'init notices EEXIST (1)' ' + rm -fr newdir && + ( + >newdir && + test_must_fail git init newdir && + test -f newdir + ) +' + +test_expect_success 'init notices EEXIST (2)' ' + rm -fr newdir && + ( + mkdir newdir && + >newdir/a + test_must_fail git init newdir/a/b && + test -f newdir/a + ) +' + +test_expect_success POSIXPERM 'init notices EPERM' ' + rm -fr newdir && + ( + mkdir newdir && + chmod -w newdir && + test_must_fail git init newdir/a/b + ) +' + test_done diff --git a/t/t0006-date.sh b/t/t0006-date.sh new file mode 100755 index 0000000000..a4d8fa8fa1 --- /dev/null +++ b/t/t0006-date.sh @@ -0,0 +1,75 @@ +#!/bin/sh + +test_description='test date parsing and printing' +. ./test-lib.sh + +# arbitrary reference time: 2009-08-30 19:20:00 +TEST_DATE_NOW=1251660000; export TEST_DATE_NOW + +check_show() { + t=$(($TEST_DATE_NOW - $1)) + echo "$t -> $2" >expect + test_expect_${3:-success} "relative date ($2)" " + test-date show $t >actual && + test_cmp expect actual + " +} + +check_show 5 '5 seconds ago' +check_show 300 '5 minutes ago' +check_show 18000 '5 hours ago' +check_show 432000 '5 days ago' +check_show 1728000 '3 weeks ago' +check_show 13000000 '5 months ago' +check_show 37500000 '1 year, 2 months ago' +check_show 55188000 '1 year, 9 months ago' +check_show 630000000 '20 years ago' + +check_parse() { + echo "$1 -> $2" >expect + test_expect_${3:-success} "parse date ($1)" " + test-date parse '$1' >actual && + test_cmp expect actual + " +} + +check_parse 2008 bad +check_parse 2008-02 bad +check_parse 2008-02-14 bad +check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000' + +check_approxidate() { + echo "$1 -> $2 +0000" >expect + test_expect_${3:-success} "parse approxidate ($1)" " + test-date approxidate '$1' >actual && + test_cmp expect actual + " +} + +check_approxidate now '2009-08-30 19:20:00' +check_approxidate '5 seconds ago' '2009-08-30 19:19:55' +check_approxidate 5.seconds.ago '2009-08-30 19:19:55' +check_approxidate 10.minutes.ago '2009-08-30 19:10:00' +check_approxidate yesterday '2009-08-29 19:20:00' +check_approxidate 3.days.ago '2009-08-27 19:20:00' +check_approxidate 3.weeks.ago '2009-08-09 19:20:00' +check_approxidate 3.months.ago '2009-05-30 19:20:00' +check_approxidate 2.years.3.months.ago '2007-05-30 19:20:00' + +check_approxidate '6am yesterday' '2009-08-29 06:00:00' +check_approxidate '6pm yesterday' '2009-08-29 18:00:00' +check_approxidate '3:00' '2009-08-30 03:00:00' +check_approxidate '15:00' '2009-08-30 15:00:00' +check_approxidate 'noon today' '2009-08-30 12:00:00' +check_approxidate 'noon yesterday' '2009-08-29 12:00:00' + +check_approxidate 'last tuesday' '2009-08-25 19:20:00' +check_approxidate 'July 5th' '2009-07-05 19:20:00' +check_approxidate '06/05/2009' '2009-06-05 19:20:00' +check_approxidate '06.05.2009' '2009-05-06 19:20:00' + +check_approxidate 'Jun 6, 5AM' '2009-06-06 05:00:00' +check_approxidate '5AM Jun 6' '2009-06-06 05:00:00' +check_approxidate '6AM, June 7, 2009' '2009-06-07 06:00:00' + +test_done diff --git a/t/t1009-read-tree-new-index.sh b/t/t1009-read-tree-new-index.sh new file mode 100755 index 0000000000..59b3aa4bc4 --- /dev/null +++ b/t/t1009-read-tree-new-index.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +test_description='test read-tree into a fresh index file' + +. ./test-lib.sh + +test_expect_success setup ' + echo one >a && + git add a && + git commit -m initial +' + +test_expect_success 'non-existent index file' ' + rm -f new-index && + GIT_INDEX_FILE=new-index git read-tree master +' + +test_expect_success 'empty index file' ' + rm -f new-index && + > new-index && + GIT_INDEX_FILE=new-index git read-tree master +' + +test_done + diff --git a/t/t2000-checkout-cache-clash.sh b/t/t2000-checkout-cache-clash.sh index f7e1a735ec..de3edb5d57 100755 --- a/t/t2000-checkout-cache-clash.sh +++ b/t/t2000-checkout-cache-clash.sh @@ -48,4 +48,13 @@ test_expect_success \ 'git checkout-index conflicting paths.' \ 'test -f path0 && test -d path1 && test -f path1/file1' +test_expect_success SYMLINKS 'checkout-index -f twice with --prefix' ' + mkdir -p tar/get && + ln -s tar/get there && + echo first && + git checkout-index -a -f --prefix=there/ && + echo second && + git checkout-index -a -f --prefix=there/ +' + test_done diff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh new file mode 100755 index 0000000000..c551d39a66 --- /dev/null +++ b/t/t2015-checkout-unborn.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +test_description='checkout from unborn branch protects contents' +. ./test-lib.sh + +test_expect_success 'setup' ' + mkdir parent && + (cd parent && + git init && + echo content >file && + git add file && + git commit -m base + ) && + git fetch parent master:origin +' + +test_expect_success 'checkout from unborn preserves untracked files' ' + echo precious >expect && + echo precious >file && + test_must_fail git checkout -b new origin && + test_cmp expect file +' + +test_expect_success 'checkout from unborn preserves index contents' ' + echo precious >expect && + echo precious >file && + git add file && + test_must_fail git checkout -b new origin && + test_cmp expect file && + git show :file >file && + test_cmp expect file +' + +test_expect_success 'checkout from unborn merges identical index contents' ' + echo content >file && + git add file && + git checkout -b new origin +' + +test_done diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh index c5c29ccc4f..4e6a44b623 100755 --- a/t/t3400-rebase.sh +++ b/t/t3400-rebase.sh @@ -3,9 +3,10 @@ # Copyright (c) 2005 Amos Waterland # -test_description='git rebase should not destroy author information +test_description='git rebase assorted tests -This test runs git rebase and checks that the author information is not lost. +This test runs git rebase and checks that the author information is not lost +among other things. ' . ./test-lib.sh @@ -133,4 +134,25 @@ test_expect_success 'rebase -q is quiet' ' test ! -s output.out ' +q_to_cr () { + tr Q '\015' +} + +test_expect_success 'Rebase a commit that sprinkles CRs in' ' + ( + echo "One" + echo "TwoQ" + echo "Three" + echo "FQur" + echo "Five" + ) | q_to_cr >CR && + git add CR && + test_tick && + git commit -a -m "A file with a line with CR" && + git tag file-with-cr && + git checkout HEAD^0 && + git rebase --onto HEAD^^ HEAD^ && + git diff --exit-code file-with-cr:CR HEAD:CR +' + test_done diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index a973628e8e..4cae019521 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -10,7 +10,7 @@ that the result still makes sense. ' . ./test-lib.sh -. ../lib-rebase.sh +. "$TEST_DIRECTORY"/lib-rebase.sh set_fake_editor diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh index 6533505218..14a23cd872 100755 --- a/t/t3411-rebase-preserve-around-merges.sh +++ b/t/t3411-rebase-preserve-around-merges.sh @@ -10,7 +10,7 @@ a merge to before the merge. ' . ./test-lib.sh -. ../lib-rebase.sh +. "$TEST_DIRECTORY"/lib-rebase.sh set_fake_editor diff --git a/t/t3414-rebase-preserve-onto.sh b/t/t3414-rebase-preserve-onto.sh index 80019ee072..ee0a6cccfd 100755 --- a/t/t3414-rebase-preserve-onto.sh +++ b/t/t3414-rebase-preserve-onto.sh @@ -10,7 +10,7 @@ aren'"'"'t on top of $ONTO, even if they are on top of $UPSTREAM. ' . ./test-lib.sh -. ../lib-rebase.sh +. "$TEST_DIRECTORY"/lib-rebase.sh # Set up branches like this: # A1---B1---E1---F1---G1 diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 922a8941ed..531f5b795c 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -493,13 +493,12 @@ test_expect_success 'format-patch from a subdirectory (2)' ' ' test_expect_success 'format-patch from a subdirectory (3)' ' - here="$TEST_DIRECTORY/$test" && rm -f 0* && filename=$( rm -rf sub && mkdir -p sub/dir && cd sub/dir && - git format-patch -1 -o "$here" + git format-patch -1 -o "$TRASH_DIRECTORY" ) && basename=$(expr "$filename" : ".*/\(.*\)") && test -f "$basename" diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh index 4ea42e00da..a7602cf923 100755 --- a/t/t4020-diff-external.sh +++ b/t/t4020-diff-external.sh @@ -166,7 +166,7 @@ test_expect_success 'diff --cached' ' git update-index --assume-unchanged file && echo second >file && git diff --cached >actual && - test_cmp ../t4020/diff.NUL actual + test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual ' test_done diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh new file mode 100755 index 0000000000..9d9498bd95 --- /dev/null +++ b/t/t4039-diff-assume-unchanged.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +test_description='diff with assume-unchanged entries' + +. ./test-lib.sh + +# external diff has been tested in t4020-diff-external.sh + +test_expect_success 'setup' ' + echo zero > zero && + git add zero && + git commit -m zero && + echo one > one && + echo two > two && + git add one two && + git commit -m onetwo && + git update-index --assume-unchanged one && + echo borked >> one && + test "$(git ls-files -v one)" = "h one" +' + +test_expect_success 'diff-index does not examine assume-unchanged entries' ' + git diff-index HEAD^ -- one | grep -q 5626abf0f72e58d7a153368ba57db4c673c0e171 +' + +test_expect_success 'diff-files does not examine assume-unchanged entries' ' + rm one && + test -z "$(git diff-files -- one)" +' + +test_done diff --git a/t/t4107-apply-ignore-whitespace.sh b/t/t4107-apply-ignore-whitespace.sh new file mode 100755 index 0000000000..484654d6e4 --- /dev/null +++ b/t/t4107-apply-ignore-whitespace.sh @@ -0,0 +1,185 @@ +#!/bin/sh +# +# Copyright (c) 2009 Giuseppe Bilotta +# + +test_description='git-apply --ignore-whitespace. + +' +. ./test-lib.sh + +# This primes main.c file that indents without using HT at all. +# Various patches with HT and other spaces are attempted in the test. + +cat > patch1.patch <<\EOF +diff --git a/main.c b/main.c +new file mode 100644 +--- /dev/null ++++ b/main.c +@@ -0,0 +1,22 @@ ++#include <stdio.h> ++ ++void print_int(int num); ++int func(int num); ++ ++int main() { ++ int i; ++ ++ for (i = 0; i < 10; i++) { ++ print_int(func(i)); /* stuff */ ++ } ++ ++ return 0; ++} ++ ++int func(int num) { ++ return num * num; ++} ++ ++void print_int(int num) { ++ printf("%d", num); ++} +EOF + +# Since whitespace is very significant and we want to prevent whitespace +# mangling when creating this test from a patch, we protect 'fixable' +# whitespace by replacing spaces with Z and replacing them at patch +# creation time, hence the sed trick. + +# This patch will fail unless whitespace differences are being ignored + +sed -e 's/Z/ /g' > patch2.patch <<\EOF +diff --git a/main.c b/main.c +--- a/main.c ++++ b/main.c +@@ -10,6 +10,8 @@ +Z print_int(func(i)); /* stuff */ +Z } +Z ++ printf("\n"); ++ +Z return 0; +Z} +Z +EOF + +# This patch will fail even if whitespace differences are being ignored, +# because of the missing string at EOL. TODO: this testcase should be +# improved by creating a line that has the same hash with and without +# the final string. + +sed -e 's/Z/ /g' > patch3.patch <<\EOF +diff --git a/main.c b/main.c +--- a/main.c ++++ b/main.c +@@ -10,3 +10,4 @@ +Z for (i = 0; i < 10; i++) { +Z print_int(func(i));Z ++ /* stuff */ +Z } +EOF + +# This patch will fail even if whitespace differences are being ignored, +# because of the missing EOL at EOF. + +sed -e 's/Z/ /g' > patch4.patch <<\EOF +diff --git a/main.c b/main.c +--- a/main.c ++++ b/main.c +@@ -21,1 +21,1 @@ +- };Z +\ No newline at end of file ++ }; +EOF + +# This patch will fail unless whitespace differences are being ignored. + +sed -e 's/Z/ /g' > patch5.patch <<\EOF +diff --git a/main.c b/main.c +--- a/main.c ++++ b/main.c +@@ -2,2 +2,3 @@ +Z void print_int(int num); ++ /* a comment */ +Z int func(int num); +EOF + +# And this is how the final output should be. Patches introduce +# HTs but the original SP indents are mostly kept. + +sed -e 's/T/ /g' > main.c.final <<\EOF +#include <stdio.h> + +void print_int(int num); +T/* a comment */ +int func(int num); + +int main() { + int i; + + for (i = 0; i < 10; i++) { + print_int(func(i)); /* stuff */ + } + +Tprintf("\n"); + + return 0; +} + +int func(int num) { + return num * num; +} + +void print_int(int num) { + printf("%d", num); +} +EOF + +test_expect_success 'file creation' ' + git-apply patch1.patch +' + +test_expect_success 'patch2 fails (retab)' ' + test_must_fail git-apply patch2.patch +' + +test_expect_success 'patch2 applies with --ignore-whitespace' ' + git-apply --ignore-whitespace patch2.patch +' + +test_expect_success 'patch2 reverse applies with --ignore-space-change' ' + git-apply -R --ignore-space-change patch2.patch +' + +git config apply.ignorewhitespace change + +test_expect_success 'patch2 applies (apply.ignorewhitespace = change)' ' + git-apply patch2.patch +' + +test_expect_success 'patch3 fails (missing string at EOL)' ' + test_must_fail git-apply patch3.patch +' + +test_expect_success 'patch4 fails (missing EOL at EOF)' ' + test_must_fail git-apply patch4.patch +' + +test_expect_success 'patch5 applies (leading whitespace)' ' + git-apply patch5.patch +' + +test_expect_success 'patches do not mangle whitespace' ' + test_cmp main.c main.c.final +' + +test_expect_success 're-create file (with --ignore-whitespace)' ' + rm -f main.c && + git-apply patch1.patch +' + +test_expect_success 'patch5 fails (--no-ignore-whitespace)' ' + test_must_fail git-apply --no-ignore-whitespace patch5.patch +' + +test_done diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index a8c2ca2a78..18376d6608 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -139,6 +139,36 @@ test_expect_success 'fsck in shallow repo' ' ) ' +test_expect_success 'simple fetch in shallow repo' ' + ( + cd shallow && + git fetch + ) +' + +test_expect_success 'no changes expected' ' + ( + cd shallow && + git count-objects -v + ) > count.shallow.2 && + cmp count.shallow count.shallow.2 +' + +test_expect_success 'fetch same depth in shallow repo' ' + ( + cd shallow && + git fetch --depth=2 + ) +' + +test_expect_success 'no changes expected' ' + ( + cd shallow && + git count-objects -v + ) > count.shallow.3 && + cmp count.shallow count.shallow.3 +' + test_expect_success 'add two more' ' add B66 $B65 && add B67 $B66 @@ -201,4 +231,21 @@ test_expect_success 'pull in shallow repo with missing merge base' ' ) ' +test_expect_success 'additional simple shallow deepenings' ' + ( + cd shallow && + git fetch --depth=8 && + git fetch --depth=10 && + git fetch --depth=11 + ) +' + +test_expect_success 'clone shallow object count' ' + ( + cd shallow && + git count-objects -v + ) > count.shallow && + grep "^count: 52" count.shallow +' + test_done diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index e78d40242a..dd2ee842e0 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -149,4 +149,15 @@ test_expect_success 'pull --rebase dies early with dirty working directory' ' ' +test_expect_success 'pull --rebase works on branch yet to be born' ' + git rev-parse master >expect && + mkdir empty_repo && + (cd empty_repo && + git init && + git pull --rebase .. master && + git rev-parse HEAD >../actual + ) && + test_cmp expect actual +' + test_done diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh index f5102b902a..a696b8791b 100755 --- a/t/t5530-upload-pack-error.sh +++ b/t/t5530-upload-pack-error.sh @@ -30,11 +30,12 @@ test_expect_success 'fsck fails' ' test_must_fail git fsck ' -test_expect_success 'upload-pack fails due to error in pack-objects' ' +test_expect_success 'upload-pack fails due to error in pack-objects packing' ' ! echo "0032want $(git rev-parse HEAD) 00000009done 0000" | git upload-pack . > /dev/null 2> output.err && + grep "unable to read" output.err && grep "pack-objects died" output.err ' @@ -51,9 +52,21 @@ test_expect_success 'fsck fails' ' test_expect_success 'upload-pack fails due to error in rev-list' ' ! echo "0032want $(git rev-parse HEAD) +0034shallow $(git rev-parse HEAD^)00000009done +0000" | git upload-pack . > /dev/null 2> output.err && + # pack-objects survived + grep "Total.*, reused" output.err && + # but there was an error, which must have been in rev-list + grep "bad tree object" output.err +' + +test_expect_success 'upload-pack fails due to error in pack-objects enumeration' ' + + ! echo "0032want $(git rev-parse HEAD) 00000009done 0000" | git upload-pack . > /dev/null 2> output.err && - grep "waitpid (async) failed" output.err + grep "bad tree object" output.err && + grep "pack-objects died" output.err ' test_expect_success 'create empty repository' ' diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh new file mode 100644 index 0000000000..65d8d474bc --- /dev/null +++ b/t/t5531-deep-submodule-push.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +test_description='unpack-objects' + +. ./test-lib.sh + +test_expect_success setup ' + mkdir pub.git && + GIT_DIR=pub.git git init --bare + GIT_DIR=pub.git git config receive.fsckobjects true && + mkdir work && + ( + cd work && + git init && + mkdir -p gar/bage && + ( + cd gar/bage && + git init && + >junk && + git add junk && + git commit -m "Initial junk" + ) && + git add gar/bage && + git commit -m "Initial superproject" + ) +' + +test_expect_success push ' + ( + cd work && + git push ../pub.git master + ) +' + +test_done diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh index 04e4b7c5c2..0144d9e858 100755 --- a/t/t6010-merge-base.sh +++ b/t/t6010-merge-base.sh @@ -110,6 +110,18 @@ test_expect_success 'compute merge-base (all)' \ # Another set to demonstrate base between one commit and a merge # in the documentation. +# +# * C (MMC) * B (MMB) * A (MMA) +# * o * o * o +# * o * o * o +# * o * o * o +# * o | _______/ +# | |/ +# | * 1 (MM1) +# | _______/ +# |/ +# * root (MMR) + test_expect_success 'merge-base for octopus-step (setup)' ' test_tick && git commit --allow-empty -m root && git tag MMR && @@ -137,6 +149,12 @@ test_expect_success 'merge-base A B C' ' test "$MM1" = "$MB" ' +test_expect_success 'merge-base A B C using show-branch' ' + MB=$(git show-branch --merge-base MMA MMB MMC) && + MMR=$(git rev-parse --verify MMR) && + test "$MMR" = "$MB" +' + test_expect_success 'criss-cross merge-base for octopus-step (setup)' ' git reset --hard MMR && test_tick && git commit --allow-empty -m 1 && git tag CC1 && diff --git a/t/t6015-rev-list-show-all-parents.sh b/t/t6015-rev-list-show-all-parents.sh new file mode 100755 index 0000000000..8b146fb432 --- /dev/null +++ b/t/t6015-rev-list-show-all-parents.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +test_description='--show-all --parents does not rewrite TREESAME commits' + +. ./test-lib.sh + +test_expect_success 'set up --show-all --parents test' ' + test_commit one foo.txt && + commit1=`git rev-list -1 HEAD` && + test_commit two bar.txt && + commit2=`git rev-list -1 HEAD` && + test_commit three foo.txt && + commit3=`git rev-list -1 HEAD` + ' + +test_expect_success '--parents rewrites TREESAME parents correctly' ' + echo $commit3 $commit1 > expected && + echo $commit1 >> expected && + git rev-list --parents HEAD -- foo.txt > actual && + test_cmp expected actual + ' + +test_expect_success '--parents --show-all does not rewrites TREESAME parents' ' + echo $commit3 $commit2 > expected && + echo $commit2 $commit1 >> expected && + echo $commit1 >> expected && + git rev-list --parents --show-all HEAD -- foo.txt > actual && + test_cmp expected actual + ' + +test_done diff --git a/t/t6016-rev-list-graph-simplify-history.sh b/t/t6016-rev-list-graph-simplify-history.sh new file mode 100755 index 0000000000..27fd52b7be --- /dev/null +++ b/t/t6016-rev-list-graph-simplify-history.sh @@ -0,0 +1,276 @@ +#!/bin/sh + +# There's more than one "correct" way to represent the history graphically. +# These tests depend on the current behavior of the graphing code. If the +# graphing code is ever changed to draw the output differently, these tests +# cases will need to be updated to know about the new layout. + +test_description='--graph and simplified history' + +. ./test-lib.sh + +test_expect_success 'set up rev-list --graph test' ' + # 3 commits on branch A + test_commit A1 foo.txt && + test_commit A2 bar.txt && + test_commit A3 bar.txt && + git branch -m master A && + + # 2 commits on branch B, started from A1 + git checkout -b B A1 && + test_commit B1 foo.txt && + test_commit B2 abc.txt && + + # 2 commits on branch C, started from A2 + git checkout -b C A2 && + test_commit C1 xyz.txt && + test_commit C2 xyz.txt && + + # Octopus merge B and C into branch A + git checkout A && + git merge B C && + git tag A4 + + test_commit A5 bar.txt && + + # More commits on C, then merge C into A + git checkout C && + test_commit C3 foo.txt && + test_commit C4 bar.txt && + git checkout A && + git merge -s ours C && + git tag A6 + + test_commit A7 bar.txt && + + # Store commit names in variables for later use + A1=$(git rev-parse --verify A1) && + A2=$(git rev-parse --verify A2) && + A3=$(git rev-parse --verify A3) && + A4=$(git rev-parse --verify A4) && + A5=$(git rev-parse --verify A5) && + A6=$(git rev-parse --verify A6) && + A7=$(git rev-parse --verify A7) && + B1=$(git rev-parse --verify B1) && + B2=$(git rev-parse --verify B2) && + C1=$(git rev-parse --verify C1) && + C2=$(git rev-parse --verify C2) && + C3=$(git rev-parse --verify C3) && + C4=$(git rev-parse --verify C4) + ' + +test_expect_success '--graph --all' ' + rm -f expected && + echo "* $A7" >> expected && + echo "* $A6" >> expected && + echo "|\\ " >> expected && + echo "| * $C4" >> expected && + echo "| * $C3" >> expected && + echo "* | $A5" >> expected && + echo "| | " >> expected && + echo "| \\ " >> expected && + echo "*-. \\ $A4" >> expected && + echo "|\\ \\ \\ " >> expected && + echo "| | |/ " >> expected && + echo "| | * $C2" >> expected && + echo "| | * $C1" >> expected && + echo "| * | $B2" >> expected && + echo "| * | $B1" >> expected && + echo "* | | $A3" >> expected && + echo "| |/ " >> expected && + echo "|/| " >> expected && + echo "* | $A2" >> expected && + echo "|/ " >> expected && + echo "* $A1" >> expected && + git rev-list --graph --all > actual && + test_cmp expected actual + ' + +# Make sure the graph_is_interesting() code still realizes +# that undecorated merges are interesting, even with --simplify-by-decoration +test_expect_success '--graph --simplify-by-decoration' ' + rm -f expected && + git tag -d A4 + echo "* $A7" >> expected && + echo "* $A6" >> expected && + echo "|\\ " >> expected && + echo "| * $C4" >> expected && + echo "| * $C3" >> expected && + echo "* | $A5" >> expected && + echo "| | " >> expected && + echo "| \\ " >> expected && + echo "*-. \\ $A4" >> expected && + echo "|\\ \\ \\ " >> expected && + echo "| | |/ " >> expected && + echo "| | * $C2" >> expected && + echo "| | * $C1" >> expected && + echo "| * | $B2" >> expected && + echo "| * | $B1" >> expected && + echo "* | | $A3" >> expected && + echo "| |/ " >> expected && + echo "|/| " >> expected && + echo "* | $A2" >> expected && + echo "|/ " >> expected && + echo "* $A1" >> expected && + git rev-list --graph --all --simplify-by-decoration > actual && + test_cmp expected actual + ' + +# Get rid of all decorations on branch B, and graph with it simplified away +test_expect_success '--graph --simplify-by-decoration prune branch B' ' + rm -f expected && + git tag -d B2 + git tag -d B1 + git branch -d B + echo "* $A7" >> expected && + echo "* $A6" >> expected && + echo "|\\ " >> expected && + echo "| * $C4" >> expected && + echo "| * $C3" >> expected && + echo "* | $A5" >> expected && + echo "* | $A4" >> expected && + echo "|\\ \\ " >> expected && + echo "| |/ " >> expected && + echo "| * $C2" >> expected && + echo "| * $C1" >> expected && + echo "* | $A3" >> expected && + echo "|/ " >> expected && + echo "* $A2" >> expected && + echo "* $A1" >> expected && + git rev-list --graph --simplify-by-decoration --all > actual && + test_cmp expected actual + ' + +test_expect_success '--graph --full-history -- bar.txt' ' + rm -f expected && + git tag -d B2 + git tag -d B1 + git branch -d B + echo "* $A7" >> expected && + echo "* $A6" >> expected && + echo "|\\ " >> expected && + echo "| * $C4" >> expected && + echo "* | $A5" >> expected && + echo "* | $A4" >> expected && + echo "|\\ \\ " >> expected && + echo "| |/ " >> expected && + echo "* | $A3" >> expected && + echo "|/ " >> expected && + echo "* $A2" >> expected && + git rev-list --graph --full-history --all -- bar.txt > actual && + test_cmp expected actual + ' + +test_expect_success '--graph --full-history --simplify-merges -- bar.txt' ' + rm -f expected && + git tag -d B2 + git tag -d B1 + git branch -d B + echo "* $A7" >> expected && + echo "* $A6" >> expected && + echo "|\\ " >> expected && + echo "| * $C4" >> expected && + echo "* | $A5" >> expected && + echo "* | $A3" >> expected && + echo "|/ " >> expected && + echo "* $A2" >> expected && + git rev-list --graph --full-history --simplify-merges --all \ + -- bar.txt > actual && + test_cmp expected actual + ' + +test_expect_success '--graph -- bar.txt' ' + rm -f expected && + git tag -d B2 + git tag -d B1 + git branch -d B + echo "* $A7" >> expected && + echo "* $A5" >> expected && + echo "* $A3" >> expected && + echo "| * $C4" >> expected && + echo "|/ " >> expected && + echo "* $A2" >> expected && + git rev-list --graph --all -- bar.txt > actual && + test_cmp expected actual + ' + +test_expect_success '--graph --sparse -- bar.txt' ' + rm -f expected && + git tag -d B2 + git tag -d B1 + git branch -d B + echo "* $A7" >> expected && + echo "* $A6" >> expected && + echo "* $A5" >> expected && + echo "* $A4" >> expected && + echo "* $A3" >> expected && + echo "| * $C4" >> expected && + echo "| * $C3" >> expected && + echo "| * $C2" >> expected && + echo "| * $C1" >> expected && + echo "|/ " >> expected && + echo "* $A2" >> expected && + echo "* $A1" >> expected && + git rev-list --graph --sparse --all -- bar.txt > actual && + test_cmp expected actual + ' + +test_expect_success '--graph ^C4' ' + rm -f expected && + echo "* $A7" >> expected && + echo "* $A6" >> expected && + echo "* $A5" >> expected && + echo "* $A4" >> expected && + echo "|\\ " >> expected && + echo "| * $B2" >> expected && + echo "| * $B1" >> expected && + echo "* $A3" >> expected && + git rev-list --graph --all ^C4 > actual && + test_cmp expected actual + ' + +test_expect_success '--graph ^C3' ' + rm -f expected && + echo "* $A7" >> expected && + echo "* $A6" >> expected && + echo "|\\ " >> expected && + echo "| * $C4" >> expected && + echo "* $A5" >> expected && + echo "* $A4" >> expected && + echo "|\\ " >> expected && + echo "| * $B2" >> expected && + echo "| * $B1" >> expected && + echo "* $A3" >> expected && + git rev-list --graph --all ^C3 > actual && + test_cmp expected actual + ' + +# I don't think the ordering of the boundary commits is really +# that important, but this test depends on it. If the ordering ever changes +# in the code, we'll need to update this test. +test_expect_success '--graph --boundary ^C3' ' + rm -f expected && + echo "* $A7" >> expected && + echo "* $A6" >> expected && + echo "|\\ " >> expected && + echo "| * $C4" >> expected && + echo "* | $A5" >> expected && + echo "| | " >> expected && + echo "| \\ " >> expected && + echo "*-. \\ $A4" >> expected && + echo "|\\ \\ \\ " >> expected && + echo "| * | | $B2" >> expected && + echo "| * | | $B1" >> expected && + echo "* | | | $A3" >> expected && + echo "o | | | $A2" >> expected && + echo "|/ / / " >> expected && + echo "o | | $A1" >> expected && + echo " / / " >> expected && + echo "| o $C3" >> expected && + echo "|/ " >> expected && + echo "o $C2" >> expected && + git rev-list --graph --boundary --all ^C3 > actual && + test_cmp expected actual + ' + +test_done diff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh index a19d49de28..e71c687f2b 100755 --- a/t/t6020-merge-df.sh +++ b/t/t6020-merge-df.sh @@ -22,4 +22,27 @@ git commit -m "File: dir"' test_expect_code 1 'Merge with d/f conflicts' 'git merge "merge msg" B master' +test_expect_failure 'F/D conflict' ' + git reset --hard && + git checkout master && + rm .git/index && + + mkdir before && + echo FILE >before/one && + echo FILE >after && + git add . && + git commit -m first && + + rm -f after && + git mv before after && + git commit -m move && + + git checkout -b para HEAD^ && + echo COMPLETELY ANOTHER FILE >another && + git add . && + git commit -m para && + + git merge master +' + test_done diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh new file mode 100755 index 0000000000..8b8bd81c09 --- /dev/null +++ b/t/t6050-replace.sh @@ -0,0 +1,200 @@ +#!/bin/sh +# +# Copyright (c) 2008 Christian Couder +# +test_description='Tests replace refs functionality' + +exec </dev/null + +. ./test-lib.sh + +add_and_commit_file() +{ + _file="$1" + _msg="$2" + + git add $_file || return $? + test_tick || return $? + git commit --quiet -m "$_file: $_msg" +} + +HASH1= +HASH2= +HASH3= +HASH4= +HASH5= +HASH6= +HASH7= + +test_expect_success 'set up buggy branch' ' + echo "line 1" >> hello && + echo "line 2" >> hello && + echo "line 3" >> hello && + echo "line 4" >> hello && + add_and_commit_file hello "4 lines" && + HASH1=$(git rev-parse --verify HEAD) && + echo "line BUG" >> hello && + echo "line 6" >> hello && + echo "line 7" >> hello && + echo "line 8" >> hello && + add_and_commit_file hello "4 more lines with a BUG" && + HASH2=$(git rev-parse --verify HEAD) && + echo "line 9" >> hello && + echo "line 10" >> hello && + add_and_commit_file hello "2 more lines" && + HASH3=$(git rev-parse --verify HEAD) && + echo "line 11" >> hello && + add_and_commit_file hello "1 more line" && + HASH4=$(git rev-parse --verify HEAD) && + sed -e "s/BUG/5/" hello > hello.new && + mv hello.new hello && + add_and_commit_file hello "BUG fixed" && + HASH5=$(git rev-parse --verify HEAD) && + echo "line 12" >> hello && + echo "line 13" >> hello && + add_and_commit_file hello "2 more lines" && + HASH6=$(git rev-parse --verify HEAD) + echo "line 14" >> hello && + echo "line 15" >> hello && + echo "line 16" >> hello && + add_and_commit_file hello "again 3 more lines" && + HASH7=$(git rev-parse --verify HEAD) +' + +test_expect_success 'replace the author' ' + git cat-file commit $HASH2 | grep "author A U Thor" && + R=$(git cat-file commit $HASH2 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) && + git cat-file commit $R | grep "author O Thor" && + git update-ref refs/replace/$HASH2 $R && + git show HEAD~5 | grep "O Thor" && + git show $HASH2 | grep "O Thor" +' + +cat >tag.sig <<EOF +object $HASH2 +type commit +tag mytag +tagger T A Gger <> 0 +0000 + +EOF + +test_expect_success 'tag replaced commit' ' + git mktag <tag.sig >.git/refs/tags/mytag 2>message +' + +test_expect_success '"git fsck" works' ' + git fsck master > fsck_master.out && + grep "dangling commit $R" fsck_master.out && + grep "dangling tag $(cat .git/refs/tags/mytag)" fsck_master.out && + test -z "$(git fsck)" +' + +test_expect_success 'repack, clone and fetch work' ' + git repack -a -d && + git clone --no-hardlinks . clone_dir && + cd clone_dir && + git show HEAD~5 | grep "A U Thor" && + git show $HASH2 | grep "A U Thor" && + git cat-file commit $R && + git repack -a -d && + test_must_fail git cat-file commit $R && + git fetch ../ "refs/replace/*:refs/replace/*" && + git show HEAD~5 | grep "O Thor" && + git show $HASH2 | grep "O Thor" && + git cat-file commit $R && + cd .. +' + +test_expect_success '"git replace" listing and deleting' ' + test "$HASH2" = "$(git replace -l)" && + test "$HASH2" = "$(git replace)" && + aa=${HASH2%??????????????????????????????????????} && + test "$HASH2" = "$(git replace -l "$aa*")" && + test_must_fail git replace -d $R && + test_must_fail git replace -d && + test_must_fail git replace -l -d $HASH2 && + git replace -d $HASH2 && + git show $HASH2 | grep "A U Thor" && + test -z "$(git replace -l)" +' + +test_expect_success '"git replace" replacing' ' + git replace $HASH2 $R && + git show $HASH2 | grep "O Thor" && + test_must_fail git replace $HASH2 $R && + git replace -f $HASH2 $R && + test_must_fail git replace -f && + test "$HASH2" = "$(git replace)" +' + +# This creates a side branch where the bug in H2 +# does not appear because P2 is created by applying +# H2 and squashing H5 into it. +# P3, P4 and P6 are created by cherry-picking H3, H4 +# and H6 respectively. +# +# At this point, we should have the following: +# +# P2--P3--P4--P6 +# / +# H1-H2-H3-H4-H5-H6-H7 +# +# Then we replace H6 with P6. +# +test_expect_success 'create parallel branch without the bug' ' + git replace -d $HASH2 && + git show $HASH2 | grep "A U Thor" && + git checkout $HASH1 && + git cherry-pick $HASH2 && + git show $HASH5 | git apply && + git commit --amend -m "hello: 4 more lines WITHOUT the bug" hello && + PARA2=$(git rev-parse --verify HEAD) && + git cherry-pick $HASH3 && + PARA3=$(git rev-parse --verify HEAD) && + git cherry-pick $HASH4 && + PARA4=$(git rev-parse --verify HEAD) && + git cherry-pick $HASH6 && + PARA6=$(git rev-parse --verify HEAD) && + git replace $HASH6 $PARA6 && + git checkout master && + cur=$(git rev-parse --verify HEAD) && + test "$cur" = "$HASH7" && + git log --pretty=oneline | grep $PARA2 && + git remote add cloned ./clone_dir +' + +test_expect_success 'push to cloned repo' ' + git push cloned $HASH6^:refs/heads/parallel && + cd clone_dir && + git checkout parallel && + git log --pretty=oneline | grep $PARA2 && + cd .. +' + +test_expect_success 'push branch with replacement' ' + git cat-file commit $PARA3 | grep "author A U Thor" && + S=$(git cat-file commit $PARA3 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) && + git cat-file commit $S | grep "author O Thor" && + git replace $PARA3 $S && + git show $HASH6~2 | grep "O Thor" && + git show $PARA3 | grep "O Thor" && + git push cloned $HASH6^:refs/heads/parallel2 && + cd clone_dir && + git checkout parallel2 && + git log --pretty=oneline | grep $PARA3 && + git show $PARA3 | grep "A U Thor" && + cd .. +' + +test_expect_success 'fetch branch with replacement' ' + git branch tofetch $HASH6 && + cd clone_dir && + git fetch origin refs/heads/tofetch:refs/heads/parallel3 + git log --pretty=oneline parallel3 | grep $PARA3 + git show $PARA3 | grep "A U Thor" + cd .. +' + +# +# +test_done diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh index b13aa7e89a..b4709e28b5 100755 --- a/t/t7002-grep.sh +++ b/t/t7002-grep.sh @@ -25,13 +25,17 @@ test_expect_success setup ' echo foo mmap bar_mmap echo foo_mmap bar mmap baz } >file && + echo vvv >v && echo ww w >w && echo x x xx x >x && echo y yy >y && echo zzz > z && mkdir t && echo test >t/t && - git add file w x y z t/t hello.c && + echo vvv >t/v && + mkdir t/a && + echo vvv >t/a/v && + git add . && test_tick && git commit -m initial ' @@ -132,6 +136,51 @@ do ! git grep -c test $H | grep /dev/null ' + test_expect_success "grep --max-depth -1 $L" ' + { + echo ${HC}t/a/v:1:vvv + echo ${HC}t/v:1:vvv + echo ${HC}v:1:vvv + } >expected && + git grep --max-depth -1 -n -e vvv $H >actual && + test_cmp expected actual + ' + + test_expect_success "grep --max-depth 0 $L" ' + { + echo ${HC}v:1:vvv + } >expected && + git grep --max-depth 0 -n -e vvv $H >actual && + test_cmp expected actual + ' + + test_expect_success "grep --max-depth 0 -- '*' $L" ' + { + echo ${HC}t/a/v:1:vvv + echo ${HC}t/v:1:vvv + echo ${HC}v:1:vvv + } >expected && + git grep --max-depth 0 -n -e vvv $H -- "*" >actual && + test_cmp expected actual + ' + + test_expect_success "grep --max-depth 1 $L" ' + { + echo ${HC}t/v:1:vvv + echo ${HC}v:1:vvv + } >expected && + git grep --max-depth 1 -n -e vvv $H >actual && + test_cmp expected actual + ' + + test_expect_success "grep --max-depth 0 -- t $L" ' + { + echo ${HC}t/v:1:vvv + } >expected && + git grep --max-depth 0 -n -e vvv $H -- t >actual && + test_cmp expected actual + ' + done cat >expected <<EOF diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh new file mode 100755 index 0000000000..1044aa6549 --- /dev/null +++ b/t/t7060-wtstatus.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +test_description='basic work tree status reporting' + +. ./test-lib.sh + +test_expect_success setup ' + test_commit A && + test_commit B oneside added && + git checkout A^0 && + test_commit C oneside created +' + +test_expect_success 'A/A conflict' ' + git checkout B^0 && + test_must_fail git merge C +' + +test_expect_success 'Report path with conflict' ' + git diff --cached --name-status >actual && + echo "U oneside" >expect && + test_cmp expect actual +' + +test_expect_success 'Report new path with conflict' ' + git diff --cached --name-status HEAD^ >actual && + echo "U oneside" >expect && + test_cmp expect actual +' + +cat >expect <<EOF +# On branch side +# Unmerged paths: +# (use "git reset HEAD <file>..." to unstage) +# (use "git add <file>..." to mark resolution) +# +# deleted by us: foo +# +no changes added to commit (use "git add" and/or "git commit -a") +EOF + +test_expect_success 'M/D conflict does not segfault' ' + mkdir mdconflict && + ( + cd mdconflict && + git init && + test_commit initial foo "" && + test_commit modify foo foo && + git checkout -b side HEAD^ && + git rm foo && + git commit -m delete && + test_must_fail git merge master && + test_must_fail git status > ../actual + ) && + test_cmp expect actual +' + +test_done diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index e637c7d4db..e85ff02c3e 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -419,7 +419,8 @@ test_expect_success 'resetting an unmodified path is a no-op' ' ' cat > expect << EOF -file2: locally modified +Unstaged changes after reset: +M file2 EOF test_expect_success '--mixed refreshes the index' ' diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh index 61498293b9..6cc16c39fe 100755 --- a/t/t7401-submodule-summary.sh +++ b/t/t7401-submodule-summary.sh @@ -56,6 +56,15 @@ test_expect_success 'modified submodule(forward)' " EOF " +test_expect_success 'modified submodule(forward), --files' " + git submodule summary --files >actual && + diff actual - <<-EOF +* sm1 $head1...$head2 (1): + > Add foo3 + +EOF +" + commit_file sm1 && cd sm1 && git reset --hard HEAD~2 >/dev/null && @@ -114,6 +123,15 @@ test_expect_success 'typechanged submodule(submodule->blob), --cached' " EOF " +test_expect_success 'typechanged submodule(submodule->blob), --files' " + git submodule summary --files >actual && + diff actual - <<-EOF +* sm1 $head5(blob)->$head4(submodule) (3): + > Add foo5 + +EOF +" + rm -rf sm1 && git checkout-index sm1 test_expect_success 'typechanged submodule(submodule->blob)' " @@ -205,4 +223,8 @@ test_expect_success '--for-status' " EOF " +test_expect_success 'fail when using --files together with --cached' " + test_must_fail git submodule summary --files --cached +" + test_done diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh new file mode 100755 index 0000000000..2a527750ce --- /dev/null +++ b/t/t7407-submodule-foreach.sh @@ -0,0 +1,237 @@ +#!/bin/sh +# +# Copyright (c) 2009 Johan Herland +# + +test_description='Test "git submodule foreach" + +This test verifies that "git submodule foreach" correctly visits all submodules +that are currently checked out. +' + +. ./test-lib.sh + + +test_expect_success 'setup a submodule tree' ' + echo file > file && + git add file && + test_tick && + git commit -m upstream + git clone . super && + git clone super submodule && + ( + cd super && + git submodule add ../submodule sub1 && + git submodule add ../submodule sub2 && + git submodule add ../submodule sub3 && + git config -f .gitmodules --rename-section \ + submodule.sub1 submodule.foo1 && + git config -f .gitmodules --rename-section \ + submodule.sub2 submodule.foo2 && + git config -f .gitmodules --rename-section \ + submodule.sub3 submodule.foo3 && + git add .gitmodules + test_tick && + git commit -m "submodules" && + git submodule init sub1 && + git submodule init sub2 && + git submodule init sub3 + ) && + ( + cd submodule && + echo different > file && + git add file && + test_tick && + git commit -m "different" + ) && + ( + cd super && + ( + cd sub3 && + git pull + ) && + git add sub3 && + test_tick && + git commit -m "update sub3" + ) +' + +sub1sha1=$(cd super/sub1 && git rev-parse HEAD) +sub3sha1=$(cd super/sub3 && git rev-parse HEAD) + +cat > expect <<EOF +Entering 'sub1' +foo1-sub1-$sub1sha1 +Entering 'sub3' +foo3-sub3-$sub3sha1 +EOF + +test_expect_success 'test basic "submodule foreach" usage' ' + git clone super clone && + ( + cd clone && + git submodule update --init -- sub1 sub3 && + git submodule foreach "echo \$name-\$path-\$sha1" > ../actual + ) && + test_cmp expect actual +' + +test_expect_success 'setup nested submodules' ' + git clone submodule nested1 && + git clone submodule nested2 && + git clone submodule nested3 && + ( + cd nested3 && + git submodule add ../submodule submodule && + test_tick && + git commit -m "submodule" && + git submodule init submodule + ) && + ( + cd nested2 && + git submodule add ../nested3 nested3 && + test_tick && + git commit -m "nested3" && + git submodule init nested3 + ) && + ( + cd nested1 && + git submodule add ../nested2 nested2 && + test_tick && + git commit -m "nested2" && + git submodule init nested2 + ) && + ( + cd super && + git submodule add ../nested1 nested1 && + test_tick && + git commit -m "nested1" && + git submodule init nested1 + ) +' + +test_expect_success 'use "submodule foreach" to checkout 2nd level submodule' ' + git clone super clone2 && + ( + cd clone2 && + test ! -d sub1/.git && + test ! -d sub2/.git && + test ! -d sub3/.git && + test ! -d nested1/.git && + git submodule update --init && + test -d sub1/.git && + test -d sub2/.git && + test -d sub3/.git && + test -d nested1/.git && + test ! -d nested1/nested2/.git && + git submodule foreach "git submodule update --init" && + test -d nested1/nested2/.git && + test ! -d nested1/nested2/nested3/.git + ) +' + +test_expect_success 'use "foreach --recursive" to checkout all submodules' ' + ( + cd clone2 && + git submodule foreach --recursive "git submodule update --init" && + test -d nested1/nested2/nested3/.git && + test -d nested1/nested2/nested3/submodule/.git + ) +' + +cat > expect <<EOF +Entering 'nested1' +Entering 'nested1/nested2' +Entering 'nested1/nested2/nested3' +Entering 'nested1/nested2/nested3/submodule' +Entering 'sub1' +Entering 'sub2' +Entering 'sub3' +EOF + +test_expect_success 'test messages from "foreach --recursive"' ' + ( + cd clone2 && + git submodule foreach --recursive "true" > ../actual + ) && + test_cmp expect actual +' + +cat > expect <<EOF +nested1-nested1 +nested2-nested2 +nested3-nested3 +submodule-submodule +foo1-sub1 +foo2-sub2 +foo3-sub3 +EOF + +test_expect_success 'test "foreach --quiet --recursive"' ' + ( + cd clone2 && + git submodule foreach -q --recursive "echo \$name-\$path" > ../actual + ) && + test_cmp expect actual +' + +test_expect_success 'use "update --recursive" to checkout all submodules' ' + git clone super clone3 && + ( + cd clone3 && + test ! -d sub1/.git && + test ! -d sub2/.git && + test ! -d sub3/.git && + test ! -d nested1/.git && + git submodule update --init --recursive && + test -d sub1/.git && + test -d sub2/.git && + test -d sub3/.git && + test -d nested1/.git && + test -d nested1/nested2/.git && + test -d nested1/nested2/nested3/.git && + test -d nested1/nested2/nested3/submodule/.git + ) +' + +nested1sha1=$(cd clone3/nested1 && git rev-parse HEAD) +nested2sha1=$(cd clone3/nested1/nested2 && git rev-parse HEAD) +nested3sha1=$(cd clone3/nested1/nested2/nested3 && git rev-parse HEAD) +submodulesha1=$(cd clone3/nested1/nested2/nested3/submodule && git rev-parse HEAD) +sub1sha1=$(cd clone3/sub1 && git rev-parse HEAD) +sub2sha1=$(cd clone3/sub2 && git rev-parse HEAD) +sub3sha1=$(cd clone3/sub3 && git rev-parse HEAD) +sub1sha1_short=$(cd clone3/sub1 && git rev-parse --short HEAD) +sub2sha1_short=$(cd clone3/sub2 && git rev-parse --short HEAD) + +cat > expect <<EOF + $nested1sha1 nested1 (heads/master) + $nested2sha1 nested1/nested2 (heads/master) + $nested3sha1 nested1/nested2/nested3 (heads/master) + $submodulesha1 nested1/nested2/nested3/submodule (heads/master) + $sub1sha1 sub1 ($sub1sha1_short) + $sub2sha1 sub2 ($sub2sha1_short) + $sub3sha1 sub3 (heads/master) +EOF + +test_expect_success 'test "status --recursive"' ' + ( + cd clone3 && + git submodule status --recursive > ../actual + ) && + test_cmp expect actual +' + +test_expect_success 'use "git clone --recursive" to checkout all submodules' ' + git clone --recursive super clone4 && + test -d clone4/.git && + test -d clone4/sub1/.git && + test -d clone4/sub2/.git && + test -d clone4/sub3/.git && + test -d clone4/nested1/.git && + test -d clone4/nested1/nested2/.git && + test -d clone4/nested1/nested2/nested3/.git && + test -d clone4/nested1/nested2/nested3/submodule/.git +' + +test_done diff --git a/t/t7406-submodule-reference.sh b/t/t7408-submodule-reference.sh index cc16d3f05d..cc16d3f05d 100755 --- a/t/t7406-submodule-reference.sh +++ b/t/t7408-submodule-reference.sh diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh index 9da4178c94..929499e996 100755 --- a/t/t9101-git-svn-props.sh +++ b/t/t9101-git-svn-props.sh @@ -142,7 +142,9 @@ test_expect_success 'test show-ignore' " touch deeply/nested/directory/.keep && svn_cmd add deeply && svn_cmd up && - svn_cmd propset -R svn:ignore 'no-such-file*' . + svn_cmd propset -R svn:ignore ' +no-such-file* +' . svn_cmd commit -m 'propset svn:ignore' cd .. && git svn show-ignore > show-ignore.got && @@ -171,6 +173,7 @@ test_expect_success 'test create-ignore' " " cat >prop.expect <<\EOF + no-such-file* EOF diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh index 78610b61e6..bbfd7f4793 100755 --- a/t/t9104-git-svn-follow-parent.sh +++ b/t/t9104-git-svn-follow-parent.sh @@ -172,11 +172,11 @@ test_expect_success "follow-parent is atomic" ' git update-ref refs/remotes/flunk@18 refs/remotes/stunk~2 && git update-ref -d refs/remotes/stunk && git config --unset svn-remote.svn.fetch stunk && - mkdir -p "$GIT_DIR"/svn/flunk@18 && - rev_map=$(cd "$GIT_DIR"/svn/stunk && ls .rev_map*) && - dd if="$GIT_DIR"/svn/stunk/$rev_map \ - of="$GIT_DIR"/svn/flunk@18/$rev_map bs=24 count=1 && - rm -rf "$GIT_DIR"/svn/stunk && + mkdir -p "$GIT_DIR"/svn/refs/remotes/flunk@18 && + rev_map=$(cd "$GIT_DIR"/svn/refs/remotes/stunk && ls .rev_map*) && + dd if="$GIT_DIR"/svn/refs/remotes/stunk/$rev_map \ + of="$GIT_DIR"/svn/refs/remotes/flunk@18/$rev_map bs=24 count=1 && + rm -rf "$GIT_DIR"/svn/refs/remotes/stunk && git svn init --minimize-url -i flunk "$svnrepo"/flunk && git svn fetch -i flunk && git svn init --minimize-url -i stunk "$svnrepo"/stunk && diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh index 3a9e07768d..901b8e09fb 100755 --- a/t/t9107-git-svn-migrate.sh +++ b/t/t9107-git-svn-migrate.sh @@ -16,9 +16,7 @@ test_expect_success 'setup old-looking metadata' ' cd .. && git svn init "$svnrepo" && git svn fetch && - mv "$GIT_DIR"/svn/* "$GIT_DIR"/ && - mv "$GIT_DIR"/svn/.metadata "$GIT_DIR"/ && - rmdir "$GIT_DIR"/svn && + rm -rf "$GIT_DIR"/svn && git update-ref refs/heads/git-svn-HEAD refs/${remotes_git_svn} && git update-ref refs/heads/svn-HEAD refs/${remotes_git_svn} && git update-ref -d refs/${remotes_git_svn} refs/${remotes_git_svn} @@ -56,7 +54,15 @@ test_expect_success 'initialize a multi-repository repo' ' git config --add svn-remote.svn.fetch "branches/b:refs/remotes/b" && for i in tags/0.1 tags/0.2 tags/0.3; do git config --add svn-remote.svn.fetch \ - $i:refs/remotes/$i || exit 1; done + $i:refs/remotes/$i || exit 1; done && + git config --get-all svn-remote.svn.fetch > fetch.out && + grep "^trunk:refs/remotes/trunk$" fetch.out && + grep "^branches/a:refs/remotes/a$" fetch.out && + grep "^branches/b:refs/remotes/b$" fetch.out && + grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out && + grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out && + grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out && + grep "^:refs/${remotes_git_svn}" fetch.out ' # refs should all be different, but the trees should all be the same: @@ -79,36 +85,36 @@ test_expect_success 'migrate --minimize on old inited layout' ' rm -rf "$GIT_DIR"/svn && for i in `cat fetch.out`; do path=`expr $i : "\([^:]*\):.*$"` - ref=`expr $i : "[^:]*:refs/remotes/\(.*\)$"` + ref=`expr $i : "[^:]*:\(refs/remotes/.*\)$"` if test -z "$ref"; then continue; fi if test -n "$path"; then path="/$path"; fi ( mkdir -p "$GIT_DIR"/svn/$ref/info/ && echo "$svnrepo"$path > "$GIT_DIR"/svn/$ref/info/url ) || exit 1; done && git svn migrate --minimize && - test -z "`git config -l |grep -v "^svn-remote\.git-svn\."`" && + test -z "`git config -l | grep "^svn-remote\.git-svn\."`" && git config --get-all svn-remote.svn.fetch > fetch.out && grep "^trunk:refs/remotes/trunk$" fetch.out && grep "^branches/a:refs/remotes/a$" fetch.out && grep "^branches/b:refs/remotes/b$" fetch.out && grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out && grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out && - grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out + grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out && grep "^:refs/${remotes_git_svn}" fetch.out ' test_expect_success ".rev_db auto-converted to .rev_map.UUID" ' git svn fetch -i trunk && - test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" && - expect="$(ls "$GIT_DIR"/svn/trunk/.rev_map.*)" && + test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" && + expect="$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_map.*)" && test -n "$expect" && rev_db="$(echo $expect | sed -e "s,_map,_db,")" && convert_to_rev_db "$expect" "$rev_db" && rm -f "$expect" && test -f "$rev_db" && git svn fetch -i trunk && - test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" && - test ! -e "$GIT_DIR"/svn/trunk/.rev_db && + test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" && + test ! -e "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db && test -f "$expect" ' diff --git a/t/t9120-git-svn-clone-with-percent-escapes.sh b/t/t9120-git-svn-clone-with-percent-escapes.sh index f159ab689b..9d9ebd533c 100755 --- a/t/t9120-git-svn-clone-with-percent-escapes.sh +++ b/t/t9120-git-svn-clone-with-percent-escapes.sh @@ -10,6 +10,10 @@ test_expect_success 'setup svnrepo' ' mkdir project project/trunk project/branches project/tags && echo foo > project/trunk/foo && svn_cmd import -m "$test_description" project "$svnrepo/pr ject" && + svn_cmd cp -m "branch" "$svnrepo/pr ject/trunk" \ + "$svnrepo/pr ject/branches/b" && + svn_cmd cp -m "tag" "$svnrepo/pr ject/trunk" \ + "$svnrepo/pr ject/tags/v1" && rm -rf project && start_httpd ' @@ -21,6 +25,54 @@ test_expect_success 'test clone with percent escapes' ' cd .. ' +# SVN works either way, so should we... + +test_expect_success 'svn checkout with percent escapes' ' + svn_cmd checkout "$svnrepo/pr%20ject" svn.percent && + svn_cmd checkout "$svnrepo/pr%20ject/trunk" svn.percent.trunk +' + +test_expect_success 'svn checkout with space' ' + svn_cmd checkout "$svnrepo/pr ject" svn.space && + svn_cmd checkout "$svnrepo/pr ject/trunk" svn.space.trunk +' + +test_expect_success 'test clone trunk with percent escapes and minimize-url' ' + git svn clone --minimize-url "$svnrepo/pr%20ject/trunk" minimize && + ( + cd minimize && + git rev-parse refs/${remotes_git_svn} + ) +' + +test_expect_success 'test clone trunk with percent escapes' ' + git svn clone "$svnrepo/pr%20ject/trunk" trunk && + ( + cd trunk && + git rev-parse refs/${remotes_git_svn} + ) +' + +test_expect_success 'test clone --stdlayout with percent escapes' ' + git svn clone --stdlayout "$svnrepo/pr%20ject" percent && + ( + cd percent && + git rev-parse refs/remotes/trunk^0 && + git rev-parse refs/remotes/b^0 && + git rev-parse refs/remotes/tags/v1^0 + ) +' + +test_expect_success 'test clone -s with unescaped space' ' + git svn clone -s "$svnrepo/pr ject" space && + ( + cd space && + git rev-parse refs/remotes/trunk^0 && + git rev-parse refs/remotes/b^0 && + git rev-parse refs/remotes/tags/v1^0 + ) +' + stop_httpd test_done diff --git a/t/t9135-git-svn-moved-branch-empty-file.sh b/t/t9135-git-svn-moved-branch-empty-file.sh index 03705fa4ce..5280e5f1e4 100755 --- a/t/t9135-git-svn-moved-branch-empty-file.sh +++ b/t/t9135-git-svn-moved-branch-empty-file.sh @@ -10,7 +10,12 @@ test_expect_success 'load svn dumpfile' ' test_expect_success 'clone using git svn' 'git svn clone -s "$svnrepo" x' test_expect_success 'test that b1 exists and is empty' ' - (cd x && test -f b1 && ! test -s b1) + ( + cd x && + git reset --hard branch-c && + test -f b1 && + ! test -s b1 + ) ' test_done diff --git a/t/t9143-git-svn-gc.sh b/t/t9143-git-svn-gc.sh index f2ba2d1da3..99f69c6a0b 100755 --- a/t/t9143-git-svn-gc.sh +++ b/t/t9143-git-svn-gc.sh @@ -28,26 +28,26 @@ test_expect_success 'Setup repo' 'git svn init "$svnrepo"' test_expect_success 'Fetch repo' 'git svn fetch' test_expect_success 'make backup copy of unhandled.log' ' - cp .git/svn/git-svn/unhandled.log tmp + cp .git/svn/refs/remotes/git-svn/unhandled.log tmp ' -test_expect_success 'create leftover index' '> .git/svn/git-svn/index' +test_expect_success 'create leftover index' '> .git/svn/refs/remotes/git-svn/index' test_expect_success 'git svn gc runs' 'git svn gc' -test_expect_success 'git svn index removed' '! test -f .git/svn/git-svn/index' +test_expect_success 'git svn index removed' '! test -f .git/svn/refs/remotes/git-svn/index' if perl -MCompress::Zlib -e 0 2>/dev/null then test_expect_success 'git svn gc produces a valid gzip file' ' - gunzip .git/svn/git-svn/unhandled.log.gz + gunzip .git/svn/refs/remotes/git-svn/unhandled.log.gz ' else say "Perl Compress::Zlib unavailable, skipping gunzip test" fi test_expect_success 'git svn gc does not change unhandled.log files' ' - test_cmp .git/svn/git-svn/unhandled.log tmp/unhandled.log + test_cmp .git/svn/refs/remotes/git-svn/unhandled.log tmp/unhandled.log ' test_done diff --git a/t/t9144-git-svn-old-rev_map.sh b/t/t9144-git-svn-old-rev_map.sh new file mode 100755 index 0000000000..7600a35cd4 --- /dev/null +++ b/t/t9144-git-svn-old-rev_map.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# +# Copyright (c) 2009 Eric Wong + +test_description='git svn old rev_map preservd' +. ./lib-git-svn.sh + +test_expect_success 'setup test repository with old layout' ' + mkdir i && + (cd i && > a) && + svn_cmd import -m- i "$svnrepo" && + git svn init "$svnrepo" && + git svn fetch && + test -d .git/svn/refs/remotes/git-svn/ && + ! test -e .git/svn/git-svn/ && + mv .git/svn/refs/remotes/git-svn .git/svn/ && + rm -r .git/svn/refs +' + +test_expect_success 'old layout continues to work' ' + svn_cmd import -m- i "$svnrepo/b" && + git svn rebase && + echo a >> b/a && + git add b/a && + git commit -m- -a && + git svn dcommit && + ! test -d .git/svn/refs/ && + test -e .git/svn/git-svn/ +' + +test_done diff --git a/t/t9145-git-svn-master-branch.sh b/t/t9145-git-svn-master-branch.sh new file mode 100755 index 0000000000..16852d26ae --- /dev/null +++ b/t/t9145-git-svn-master-branch.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Copyright (c) 2009 Eric Wong +# +test_description='git svn initial master branch is "trunk" if possible' +. ./lib-git-svn.sh + +test_expect_success 'setup test repository' ' + mkdir i && + > i/a && + svn_cmd import -m trunk i "$svnrepo/trunk" && + svn_cmd import -m b/a i "$svnrepo/branches/a" && + svn_cmd import -m b/b i "$svnrepo/branches/b" +' + +test_expect_success 'git svn clone --stdlayout sets up trunk as master' ' + git svn clone -s "$svnrepo" g && + ( + cd g && + test x`git rev-parse --verify refs/remotes/trunk^0` = \ + x`git rev-parse --verify refs/heads/master^0` + ) +' + +test_done diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index 627518108a..2fc7fdb124 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -9,73 +9,8 @@ This test runs gitweb (git web interface) as CGI script from commandline, and checks that it would not write any errors or warnings to log.' -gitweb_init () { - safe_pwd="$(perl -MPOSIX=getcwd -e 'print quotemeta(getcwd)')" - cat >gitweb_config.perl <<EOF -#!/usr/bin/perl - -# gitweb configuration for tests - -our \$version = "current"; -our \$GIT = "git"; -our \$projectroot = "$safe_pwd"; -our \$project_maxdepth = 8; -our \$home_link_str = "projects"; -our \$site_name = "[localhost]"; -our \$site_header = ""; -our \$site_footer = ""; -our \$home_text = "indextext.html"; -our @stylesheets = ("file:///$TEST_DIRECTORY/../gitweb/gitweb.css"); -our \$logo = "file:///$TEST_DIRECTORY/../gitweb/git-logo.png"; -our \$favicon = "file:///$TEST_DIRECTORY/../gitweb/git-favicon.png"; -our \$projects_list = ""; -our \$export_ok = ""; -our \$strict_export = ""; -EOF - - cat >.git/description <<EOF -$0 test repository -EOF -} - -gitweb_run () { - GATEWAY_INTERFACE="CGI/1.1" - HTTP_ACCEPT="*/*" - REQUEST_METHOD="GET" - SCRIPT_NAME="$TEST_DIRECTORY/../gitweb/gitweb.perl" - QUERY_STRING=""$1"" - PATH_INFO=""$2"" - export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD \ - SCRIPT_NAME QUERY_STRING PATH_INFO - - GITWEB_CONFIG=$(pwd)/gitweb_config.perl - export GITWEB_CONFIG - - # some of git commands write to STDERR on error, but this is not - # written to web server logs, so we are not interested in that: - # we are interested only in properly formatted errors/warnings - rm -f gitweb.log && - perl -- "$SCRIPT_NAME" \ - >/dev/null 2>gitweb.log && - if grep "^[[]" gitweb.log >/dev/null 2>&1; then false; else true; fi - - # gitweb.log is left for debugging -} - -. ./test-lib.sh - -if ! test_have_prereq PERL; then - say 'skipping gitweb tests, perl not available' - test_done -fi - -perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || { - say 'skipping gitweb tests, perl version is too old' - test_done -} - -gitweb_init +. ./gitweb-lib.sh # ---------------------------------------------------------------------- # no commits (empty, just initialized repository) diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh new file mode 100644 index 0000000000..d0ff21d426 --- /dev/null +++ b/t/t9501-gitweb-standalone-http-status.sh @@ -0,0 +1,78 @@ +#!/bin/sh +# +# Copyright (c) 2009 Mark Rada +# + +test_description='gitweb as standalone script (http status tests). + +This test runs gitweb (git web interface) as a CGI script from the +commandline, and checks that it returns the expected HTTP status +code and message.' + + +. ./gitweb-lib.sh + +# ---------------------------------------------------------------------- +# snapshot settings + +test_commit \ + 'SnapshotTests' \ + 'i can has snapshot?' + +cat >>gitweb_config.perl <<\EOF +$feature{'snapshot'}{'override'} = 0; +EOF + +test_expect_success \ + 'snapshots: tgz only default format enabled' \ + 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" && + grep "Status: 200 OK" gitweb.output && + gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tbz2" && + grep "403 - Unsupported snapshot format" gitweb.output && + gitweb_run "p=.git;a=snapshot;h=HEAD;sf=txz" && + grep "403 - Snapshot format not allowed" gitweb.output && + gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" && + grep "403 - Unsupported snapshot format" gitweb.output' +test_debug 'cat gitweb.output' + + +cat >>gitweb_config.perl <<\EOF +$feature{'snapshot'}{'default'} = ['tgz','tbz2','txz','zip']; +EOF + +test_expect_success \ + 'snapshots: all enabled in default, use default disabled value' \ + 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" && + grep "Status: 200 OK" gitweb.output && + gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tbz2" && + grep "Status: 200 OK" gitweb.output && + gitweb_run "p=.git;a=snapshot;h=HEAD;sf=txz" && + grep "403 - Snapshot format not allowed" gitweb.output && + gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" && + grep "Status: 200 OK" gitweb.output' +test_debug 'cat gitweb.output' + + +cat >>gitweb_config.perl <<\EOF +$known_snapshot_formats{'zip'}{'disabled'} = 1; +EOF + +test_expect_success \ + 'snapshots: zip explicitly disabled' \ + 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" && + grep "403 - Snapshot format not allowed" gitweb.output' +test_debug 'cat gitweb.output' + + +cat >>gitweb_config.perl <<\EOF +$known_snapshot_formats{'tgz'}{'disabled'} = 0; +EOF + +test_expect_success \ + 'snapshots: tgz explicitly enabled' \ + 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" && + grep "Status: 200 OK" gitweb.output' +test_debug 'cat gitweb.output' + + +test_done diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh index 4322a0c1ed..363345faef 100755 --- a/t/t9600-cvsimport.sh +++ b/t/t9600-cvsimport.sh @@ -1,7 +1,7 @@ #!/bin/sh test_description='git cvsimport basic tests' -. ./test-lib.sh +. ./lib-cvs.sh if ! test_have_prereq PERL; then say 'skipping git cvsimport tests, perl not available' @@ -10,37 +10,13 @@ fi CVSROOT=$(pwd)/cvsroot export CVSROOT -unset CVS_SERVER -# for clean cvsps cache -HOME=$(pwd) -export HOME - -if ! type cvs >/dev/null 2>&1 -then - say 'skipping cvsimport tests, cvs not found' - test_done -fi - -cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'` -case "$cvsps_version" in -2.1 | 2.2*) - ;; -'') - say 'skipping cvsimport tests, cvsps not found' - test_done - ;; -*) - say 'skipping cvsimport tests, unsupported cvsps version' - test_done - ;; -esac -test_expect_success 'setup cvsroot' 'cvs init' +test_expect_success 'setup cvsroot' '$CVS init' test_expect_success 'setup a cvs module' ' mkdir "$CVSROOT/module" && - cvs co -d module-cvs module && + $CVS co -d module-cvs module && cd module-cvs && cat <<EOF >o_fortuna && O Fortuna @@ -59,13 +35,13 @@ egestatem, potestatem dissolvit ut glaciem. EOF - cvs add o_fortuna && + $CVS add o_fortuna && cat <<EOF >message && add "O Fortuna" lyrics These public domain lyrics make an excellent sample text. EOF - cvs commit -F message && + $CVS commit -F message && cd .. ' @@ -103,7 +79,7 @@ translate to English My Latin is terrible. EOF - cvs commit -F message && + $CVS commit -F message && cd .. ' @@ -121,8 +97,8 @@ test_expect_success 'update cvs module' ' cd module-cvs && echo 1 >tick && - cvs add tick && - cvs commit -m 1 + $CVS add tick && + $CVS commit -m 1 cd .. ' @@ -140,7 +116,7 @@ test_expect_success 'cvsimport.module config works' ' test_expect_success 'import from a CVS working tree' ' - cvs co -d import-from-wt module && + $CVS co -d import-from-wt module && cd import-from-wt && git cvsimport -a -z0 && echo 1 >expect && @@ -150,4 +126,6 @@ test_expect_success 'import from a CVS working tree' ' ' +test_expect_success 'test entire HEAD' 'test_cmp_branch_tree master' + test_done diff --git a/t/t9601-cvsimport-vendor-branch.sh b/t/t9601-cvsimport-vendor-branch.sh new file mode 100755 index 0000000000..3afaf56526 --- /dev/null +++ b/t/t9601-cvsimport-vendor-branch.sh @@ -0,0 +1,86 @@ +#!/bin/sh + +# Description of the files in the repository: +# +# imported-once.txt: +# +# Imported once. 1.1 and 1.1.1.1 should be identical. +# +# imported-twice.txt: +# +# Imported twice. HEAD should reflect the contents of the +# second import (i.e., have the same contents as 1.1.1.2). +# +# imported-modified.txt: +# +# Imported, then modified on HEAD. HEAD should reflect the +# modification. +# +# imported-modified-imported.txt: +# +# Imported, then modified on HEAD, then imported again. +# +# added-imported.txt,v: +# +# Added with 'cvs add' to create 1.1, then imported with +# completely different contents to create 1.1.1.1, therefore the +# vendor branch was never the default branch. +# +# imported-anonymously.txt: +# +# Like imported-twice.txt, but with a vendor branch whose branch +# tag has been removed. + +test_description='git cvsimport handling of vendor branches' +. ./lib-cvs.sh + +CVSROOT="$TEST_DIRECTORY"/t9601/cvsroot +export CVSROOT + +test_expect_success 'import a module with a vendor branch' ' + + git cvsimport -C module-git module + +' + +test_expect_success 'check HEAD out of cvs repository' 'test_cvs_co master' + +test_expect_success 'check master out of git repository' 'test_git_co master' + +test_expect_success 'check a file that was imported once' ' + + test_cmp_branch_file master imported-once.txt + +' + +test_expect_failure 'check a file that was imported twice' ' + + test_cmp_branch_file master imported-twice.txt + +' + +test_expect_success 'check a file that was imported then modified on HEAD' ' + + test_cmp_branch_file master imported-modified.txt + +' + +test_expect_success 'check a file that was imported, modified, then imported again' ' + + test_cmp_branch_file master imported-modified-imported.txt + +' + +test_expect_success 'check a file that was added to HEAD then imported' ' + + test_cmp_branch_file master added-imported.txt + +' + +test_expect_success 'a vendor branch whose tag has been removed' ' + + test_cmp_branch_file master imported-anonymously.txt + +' + +test_done diff --git a/t/t9601/cvsroot/.gitattributes b/t/t9601/cvsroot/.gitattributes new file mode 100644 index 0000000000..562b12e16e --- /dev/null +++ b/t/t9601/cvsroot/.gitattributes @@ -0,0 +1 @@ +* -whitespace diff --git a/t/t9601/cvsroot/CVSROOT/.gitignore b/t/t9601/cvsroot/CVSROOT/.gitignore new file mode 100644 index 0000000000..3bb9b34173 --- /dev/null +++ b/t/t9601/cvsroot/CVSROOT/.gitignore @@ -0,0 +1,2 @@ +history +val-tags diff --git a/t/t9601/cvsroot/module/added-imported.txt,v b/t/t9601/cvsroot/module/added-imported.txt,v new file mode 100644 index 0000000000..5f83072ea4 --- /dev/null +++ b/t/t9601/cvsroot/module/added-imported.txt,v @@ -0,0 +1,44 @@ +head 1.1; +access; +symbols + vtag-4:1.1.1.1 + vbranchA:1.1.1; +locks; strict; +comment @# @; + + +1.1 +date 2004.02.09.15.43.15; author kfogel; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2004.02.09.15.43.16; author kfogel; state Exp; +branches; +next ; + + +desc +@@ + + +1.1 +log +@Add a file to the working copy. +@ +text +@Adding this file, before importing it with different contents. +@ + + +1.1.1.1 +log +@Import (vbranchA, vtag-4). +@ +text +@d1 1 +a1 1 +This is vtag-4 (on vbranchA) of added-then-imported.txt. +@ + diff --git a/t/t9601/cvsroot/module/imported-anonymously.txt,v b/t/t9601/cvsroot/module/imported-anonymously.txt,v new file mode 100644 index 0000000000..55e1b0ca5d --- /dev/null +++ b/t/t9601/cvsroot/module/imported-anonymously.txt,v @@ -0,0 +1,42 @@ +head 1.1; +branch 1.1.1; +access; +symbols + vtag-1:1.1.1.1; +locks; strict; +comment @# @; + + +1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches; +next ; + + +desc +@@ + + +1.1 +log +@Initial revision +@ +text +@This is vtag-1 (on vbranchA) of imported-anonymously.txt. +@ + + +1.1.1.1 +log +@Import (vbranchA, vtag-1). +@ +text +@@ + + diff --git a/t/t9601/cvsroot/module/imported-modified-imported.txt,v b/t/t9601/cvsroot/module/imported-modified-imported.txt,v new file mode 100644 index 0000000000..e5830aeb37 --- /dev/null +++ b/t/t9601/cvsroot/module/imported-modified-imported.txt,v @@ -0,0 +1,76 @@ +head 1.2; +access; +symbols + vtag-2:1.1.1.2 + vtag-1:1.1.1.1 + vbranchA:1.1.1; +locks; strict; +comment @# @; + + +1.2 +date 2004.02.09.15.43.14; author kfogel; state Exp; +branches; +next 1.1; + +1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches; +next 1.1.1.2; + +1.1.1.2 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches; +next ; + + +desc +@@ + + +1.2 +log +@First regular commit, to imported-modified-imported.txt, on HEAD. +@ +text +@This is a modification of imported-modified-imported.txt on HEAD. +It should supersede the version from the vendor branch. +@ + + +1.1 +log +@Initial revision +@ +text +@d1 2 +a2 1 +This is vtag-1 (on vbranchA) of imported-modified-imported.txt. +@ + + +1.1.1.1 +log +@Import (vbranchA, vtag-1). +@ +text +@@ + + +1.1.1.2 +log +@Import (vbranchA, vtag-2). +@ +text +@d1 1 +a1 1 +This is vtag-2 (on vbranchA) of imported-modified-imported.txt. +@ + + diff --git a/t/t9601/cvsroot/module/imported-modified.txt,v b/t/t9601/cvsroot/module/imported-modified.txt,v new file mode 100644 index 0000000000..bbcfe447b9 --- /dev/null +++ b/t/t9601/cvsroot/module/imported-modified.txt,v @@ -0,0 +1,59 @@ +head 1.2; +access; +symbols + vtag-1:1.1.1.1 + vbranchA:1.1.1; +locks; strict; +comment @# @; + + +1.2 +date 2004.02.09.15.43.14; author kfogel; state Exp; +branches; +next 1.1; + +1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches; +next ; + + +desc +@@ + + +1.2 +log +@Commit on HEAD. +@ +text +@This is a modification of imported-modified.txt on HEAD. +It should supersede the version from the vendor branch. +@ + + +1.1 +log +@Initial revision +@ +text +@d1 2 +a2 1 +This is vtag-1 (on vbranchA) of imported-modified.txt. +@ + + +1.1.1.1 +log +@Import (vbranchA, vtag-1). +@ +text +@@ + + diff --git a/t/t9601/cvsroot/module/imported-once.txt,v b/t/t9601/cvsroot/module/imported-once.txt,v new file mode 100644 index 0000000000..c5dd82b12d --- /dev/null +++ b/t/t9601/cvsroot/module/imported-once.txt,v @@ -0,0 +1,43 @@ +head 1.1; +branch 1.1.1; +access; +symbols + vtag-1:1.1.1.1 + vbranchA:1.1.1; +locks; strict; +comment @# @; + + +1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches; +next ; + + +desc +@@ + + +1.1 +log +@Initial revision +@ +text +@This is vtag-1 (on vbranchA) of imported-once.txt. +@ + + +1.1.1.1 +log +@Import (vbranchA, vtag-1). +@ +text +@@ + + diff --git a/t/t9601/cvsroot/module/imported-twice.txt,v b/t/t9601/cvsroot/module/imported-twice.txt,v new file mode 100644 index 0000000000..d1f3f1b344 --- /dev/null +++ b/t/t9601/cvsroot/module/imported-twice.txt,v @@ -0,0 +1,60 @@ +head 1.1; +branch 1.1.1; +access; +symbols + vtag-2:1.1.1.2 + vtag-1:1.1.1.1 + vbranchA:1.1.1; +locks; strict; +comment @# @; + + +1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches; +next 1.1.1.2; + +1.1.1.2 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches; +next ; + + +desc +@@ + + +1.1 +log +@Initial revision +@ +text +@This is vtag-1 (on vbranchA) of imported-twice.txt. +@ + + +1.1.1.1 +log +@Import (vbranchA, vtag-1). +@ +text +@@ + + +1.1.1.2 +log +@Import (vbranchA, vtag-2). +@ +text +@d1 1 +a1 1 +This is vtag-2 (on vbranchA) of imported-twice.txt. +@ + + diff --git a/t/t9602-cvsimport-branches-tags.sh b/t/t9602-cvsimport-branches-tags.sh new file mode 100755 index 0000000000..67878b2d0c --- /dev/null +++ b/t/t9602-cvsimport-branches-tags.sh @@ -0,0 +1,79 @@ +#!/bin/sh + +# A description of the repository used for this test can be found in +# t9602/README. + +test_description='git cvsimport handling of branches and tags' +. ./lib-cvs.sh + +CVSROOT="$TEST_DIRECTORY"/t9602/cvsroot +export CVSROOT + +test_expect_success 'import module' ' + + git cvsimport -C module-git module + +' + +test_expect_success 'test branch master' ' + + test_cmp_branch_tree master + +' + +test_expect_success 'test branch vendorbranch' ' + + test_cmp_branch_tree vendorbranch + +' + +test_expect_failure 'test branch B_FROM_INITIALS' ' + + test_cmp_branch_tree B_FROM_INITIALS + +' + +test_expect_failure 'test branch B_FROM_INITIALS_BUT_ONE' ' + + test_cmp_branch_tree B_FROM_INITIALS_BUT_ONE + +' + +test_expect_failure 'test branch B_MIXED' ' + + test_cmp_branch_tree B_MIXED + +' + +test_expect_success 'test branch B_SPLIT' ' + + test_cmp_branch_tree B_SPLIT + +' + +test_expect_failure 'test tag vendortag' ' + + test_cmp_branch_tree vendortag + +' + +test_expect_success 'test tag T_ALL_INITIAL_FILES' ' + + test_cmp_branch_tree T_ALL_INITIAL_FILES + +' + +test_expect_failure 'test tag T_ALL_INITIAL_FILES_BUT_ONE' ' + + test_cmp_branch_tree T_ALL_INITIAL_FILES_BUT_ONE + +' + +test_expect_failure 'test tag T_MIXED' ' + + test_cmp_branch_tree T_MIXED + +' + + +test_done diff --git a/t/t9602/README b/t/t9602/README new file mode 100644 index 0000000000..c231e0f26f --- /dev/null +++ b/t/t9602/README @@ -0,0 +1,62 @@ +This repository is for testing the ability to group revisions +correctly along tags and branches. Here is its history: + + 1. The initial import (revision 1.1 of everybody) created a + directory structure with a file named `default' in each dir: + + ./ + default + sub1/default + subsubA/default + subsubB/default + sub2/default + subsubA/default + sub3/default + + 2. Then tagged everyone with T_ALL_INITIAL_FILES. + + 3. Then tagged everyone except sub1/subsubB/default with + T_ALL_INITIAL_FILES_BUT_ONE. + + 4. Then created branch B_FROM_INITIALS on everyone. + + 5. Then created branch B_FROM_INITIALS_BUT_ONE on everyone except + /sub1/subsubB/default. + + 6. Then committed modifications to two files: sub3/default, and + sub1/subsubA/default. + + 7. Then committed a modification to all 7 files. + + 8. Then backdated sub3/default to revision 1.2, and + sub2/subsubA/default to revision 1.1, and tagged with T_MIXED. + + 9. Same as 8, but tagged with -b to create branch B_MIXED. + + 10. Switched the working copy to B_MIXED, and added + sub2/branch_B_MIXED_only. (That's why the RCS file is in + sub2/Attic/ -- it never existed on trunk.) + + 11. In one commit, modified default, sub1/default, and + sub2/subsubA/default, on branch B_MIXED. + + 12. Did "cvs up -A" on sub2/default, then in one commit, made a + change to sub2/default and sub2/branch_B_MIXED_only. So this + commit should be spread between the branch and the trunk. + + 13. Do "cvs up -A" to get everyone back to trunk, then make a new + branch B_SPLIT on everyone except sub1/subsubB/default,v. + + 14. Switch to branch B_SPLIT (see sub1/subsubB/default disappear) + and commit a change that affects everyone except sub3/default. + + 15. An hour or so later, "cvs up -A" to get sub1/subsubB/default + back, then commit a change on that file, on trunk. (It's + important that this change happened after the previous commits + on B_SPLIT.) + + 16. Branch sub1/subsubB/default to B_SPLIT, then "cvs up -r B_SPLIT" + to switch the whole working copy to the branch. + + 17. Commit a change on B_SPLIT, to sub1/subsubB/default and + sub3/default. diff --git a/t/t9602/cvsroot/.gitattributes b/t/t9602/cvsroot/.gitattributes new file mode 100644 index 0000000000..562b12e16e --- /dev/null +++ b/t/t9602/cvsroot/.gitattributes @@ -0,0 +1 @@ +* -whitespace diff --git a/t/t9602/cvsroot/CVSROOT/.gitignore b/t/t9602/cvsroot/CVSROOT/.gitignore new file mode 100644 index 0000000000..3bb9b34173 --- /dev/null +++ b/t/t9602/cvsroot/CVSROOT/.gitignore @@ -0,0 +1,2 @@ +history +val-tags diff --git a/t/t9602/cvsroot/module/default,v b/t/t9602/cvsroot/module/default,v new file mode 100644 index 0000000000..3b68382a3b --- /dev/null +++ b/t/t9602/cvsroot/module/default,v @@ -0,0 +1,102 @@ +head 1.2; +access; +symbols + B_SPLIT:1.2.0.4 + B_MIXED:1.2.0.2 + T_MIXED:1.2 + B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4 + B_FROM_INITIALS:1.1.1.1.0.2 + T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1 + T_ALL_INITIAL_FILES:1.1.1.1 + vendortag:1.1.1.1 + vendorbranch:1.1.1; +locks; strict; +comment @# @; + + +1.2 +date 2003.05.23.00.17.53; author jrandom; state Exp; +branches + 1.2.2.1 + 1.2.4.1; +next 1.1; + +1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches; +next ; + +1.2.2.1 +date 2003.05.23.00.31.36; author jrandom; state Exp; +branches; +next ; + +1.2.4.1 +date 2003.06.03.03.20.31; author jrandom; state Exp; +branches; +next ; + + +desc +@@ + + +1.2 +log +@Second commit to proj, affecting all 7 files. +@ +text +@This is the file `default' in the top level of the project. + +Every directory in the `proj' project has a file named `default'. + +This line was added in the second commit (affecting all 7 files). +@ + + +1.2.4.1 +log +@First change on branch B_SPLIT. + +This change excludes sub3/default, because it was not part of this +commit, and sub1/subsubB/default, which is not even on the branch yet. +@ +text +@a5 2 + +First change on branch B_SPLIT. +@ + + +1.2.2.1 +log +@Modify three files, on branch B_MIXED. +@ +text +@a5 2 + +This line was added on branch B_MIXED only (affecting 3 files). +@ + + +1.1 +log +@Initial revision +@ +text +@d4 2 +@ + + +1.1.1.1 +log +@Initial import. +@ +text +@@ diff --git a/t/t9602/cvsroot/module/sub1/default,v b/t/t9602/cvsroot/module/sub1/default,v new file mode 100644 index 0000000000..b7fdccdfdf --- /dev/null +++ b/t/t9602/cvsroot/module/sub1/default,v @@ -0,0 +1,102 @@ +head 1.2; +access; +symbols + B_SPLIT:1.2.0.4 + B_MIXED:1.2.0.2 + T_MIXED:1.2 + B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4 + B_FROM_INITIALS:1.1.1.1.0.2 + T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1 + T_ALL_INITIAL_FILES:1.1.1.1 + vendortag:1.1.1.1 + vendorbranch:1.1.1; +locks; strict; +comment @# @; + + +1.2 +date 2003.05.23.00.17.53; author jrandom; state Exp; +branches + 1.2.2.1 + 1.2.4.1; +next 1.1; + +1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches; +next ; + +1.2.2.1 +date 2003.05.23.00.31.36; author jrandom; state Exp; +branches; +next ; + +1.2.4.1 +date 2003.06.03.03.20.31; author jrandom; state Exp; +branches; +next ; + + +desc +@@ + + +1.2 +log +@Second commit to proj, affecting all 7 files. +@ +text +@This is sub1/default. + +Every directory in the `proj' project has a file named `default'. + +This line was added in the second commit (affecting all 7 files). +@ + + +1.2.4.1 +log +@First change on branch B_SPLIT. + +This change excludes sub3/default, because it was not part of this +commit, and sub1/subsubB/default, which is not even on the branch yet. +@ +text +@a5 2 + +First change on branch B_SPLIT. +@ + + +1.2.2.1 +log +@Modify three files, on branch B_MIXED. +@ +text +@a5 2 + +This line was added on branch B_MIXED only (affecting 3 files). +@ + + +1.1 +log +@Initial revision +@ +text +@d4 2 +@ + + +1.1.1.1 +log +@Initial import. +@ +text +@@ diff --git a/t/t9602/cvsroot/module/sub1/subsubA/default,v b/t/t9602/cvsroot/module/sub1/subsubA/default,v new file mode 100644 index 0000000000..472b7b2bd9 --- /dev/null +++ b/t/t9602/cvsroot/module/sub1/subsubA/default,v @@ -0,0 +1,101 @@ +head 1.3; +access; +symbols + B_SPLIT:1.3.0.4 + B_MIXED:1.3.0.2 + T_MIXED:1.3 + B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4 + B_FROM_INITIALS:1.1.1.1.0.2 + T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1 + T_ALL_INITIAL_FILES:1.1.1.1 + vendortag:1.1.1.1 + vendorbranch:1.1.1; +locks; strict; +comment @# @; + + +1.3 +date 2003.05.23.00.17.53; author jrandom; state Exp; +branches + 1.3.4.1; +next 1.2; + +1.2 +date 2003.05.23.00.15.26; author jrandom; state Exp; +branches; +next 1.1; + +1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches; +next ; + +1.3.4.1 +date 2003.06.03.03.20.31; author jrandom; state Exp; +branches; +next ; + + +desc +@@ + + +1.3 +log +@Second commit to proj, affecting all 7 files. +@ +text +@This is sub1/subsubA/default. + +Every directory in the `proj' project has a file named `default'. + +This line was added by the first commit (affecting two files). + +This line was added in the second commit (affecting all 7 files). +@ + + +1.3.4.1 +log +@First change on branch B_SPLIT. + +This change excludes sub3/default, because it was not part of this +commit, and sub1/subsubB/default, which is not even on the branch yet. +@ +text +@a7 2 + +First change on branch B_SPLIT. +@ + + +1.2 +log +@First commit to proj, affecting two files. +@ +text +@d6 2 +@ + + +1.1 +log +@Initial revision +@ +text +@d4 2 +@ + + +1.1.1.1 +log +@Initial import. +@ +text +@@ diff --git a/t/t9602/cvsroot/module/sub1/subsubB/default,v b/t/t9602/cvsroot/module/sub1/subsubB/default,v new file mode 100644 index 0000000000..fe6efa4554 --- /dev/null +++ b/t/t9602/cvsroot/module/sub1/subsubB/default,v @@ -0,0 +1,107 @@ +head 1.3; +access; +symbols + B_SPLIT:1.3.0.2 + B_MIXED:1.2.0.2 + T_MIXED:1.2 + B_FROM_INITIALS:1.1.1.1.0.2 + T_ALL_INITIAL_FILES:1.1.1.1 + vendortag:1.1.1.1 + vendorbranch:1.1.1; +locks; strict; +comment @# @; + + +1.3 +date 2003.06.03.04.29.14; author jrandom; state Exp; +branches + 1.3.2.1; +next 1.2; + +1.2 +date 2003.05.23.00.17.53; author jrandom; state Exp; +branches; +next 1.1; + +1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches; +next ; + +1.3.2.1 +date 2003.06.03.04.33.13; author jrandom; state Exp; +branches; +next ; + + +desc +@@ + + +1.3 +log +@A trunk change to sub1/subsubB/default. This was committed about an +hour after an earlier change that affected most files on branch +B_SPLIT. This file is not on that branch yet, but after this commit, +we'll branch to B_SPLIT, albeit rooted in a revision that didn't exist +at the time the rest of B_SPLIT was created. +@ +text +@This is sub1/subsubB/default. + +Every directory in the `proj' project has a file named `default'. + +This line was added in the second commit (affecting all 7 files). + +This bit was committed on trunk about an hour after an earlier change +to everyone else on branch B_SPLIT. Afterwards, we'll finally branch +this file to B_SPLIT, but rooted in a revision that didn't exist at +the time the rest of B_SPLIT was created. +@ + + +1.3.2.1 +log +@This change affects sub3/default and sub1/subsubB/default, on branch +B_SPLIT. Note that the latter file did not even exist on this branch +until after some other files had had revisions committed on B_SPLIT. +@ +text +@a10 4 + +This change affects sub3/default and sub1/subsubB/default, on branch +B_SPLIT. Note that the latter file did not even exist on this branch +until after some other files had had revisions committed on B_SPLIT. +@ + + +1.2 +log +@Second commit to proj, affecting all 7 files. +@ +text +@d6 5 +@ + + +1.1 +log +@Initial revision +@ +text +@d4 2 +@ + + +1.1.1.1 +log +@Initial import. +@ +text +@@ diff --git a/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v b/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v new file mode 100644 index 0000000000..34c9789f2f --- /dev/null +++ b/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v @@ -0,0 +1,59 @@ +head 1.1; +access; +symbols + B_MIXED:1.1.0.2; +locks; strict; +comment @# @; + + +1.1 +date 2003.05.23.00.25.26; author jrandom; state dead; +branches + 1.1.2.1; +next ; + +1.1.2.1 +date 2003.05.23.00.25.26; author jrandom; state Exp; +branches; +next 1.1.2.2; + +1.1.2.2 +date 2003.05.23.00.48.51; author jrandom; state Exp; +branches; +next ; + + +desc +@@ + + +1.1 +log +@file branch_B_MIXED_only was initially added on branch B_MIXED. +@ +text +@@ + + +1.1.2.1 +log +@Add a file on branch B_MIXED. +@ +text +@a0 1 +This file was added on branch B_MIXED. It never existed on trunk. +@ + + +1.1.2.2 +log +@A single commit affecting one file on branch B_MIXED and one on trunk. +@ +text +@a1 3 + +The same commit added these two lines here on branch B_MIXED, and two +similar lines to ./default on trunk. +@ + + diff --git a/t/t9602/cvsroot/module/sub2/default,v b/t/t9602/cvsroot/module/sub2/default,v new file mode 100644 index 0000000000..018f7f8ece --- /dev/null +++ b/t/t9602/cvsroot/module/sub2/default,v @@ -0,0 +1,102 @@ +head 1.3; +access; +symbols + B_SPLIT:1.3.0.2 + B_MIXED:1.2.0.2 + T_MIXED:1.2 + B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4 + B_FROM_INITIALS:1.1.1.1.0.2 + T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1 + T_ALL_INITIAL_FILES:1.1.1.1 + vendortag:1.1.1.1 + vendorbranch:1.1.1; +locks; strict; +comment @# @; + + +1.3 +date 2003.05.23.00.48.51; author jrandom; state Exp; +branches + 1.3.2.1; +next 1.2; + +1.2 +date 2003.05.23.00.17.53; author jrandom; state Exp; +branches; +next 1.1; + +1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches; +next ; + +1.3.2.1 +date 2003.06.03.03.20.31; author jrandom; state Exp; +branches; +next ; + + +desc +@@ + + +1.3 +log +@A single commit affecting one file on branch B_MIXED and one on trunk. +@ +text +@This is sub2/default. + +Every directory in the `proj' project has a file named `default'. + +This line was added in the second commit (affecting all 7 files). + +The same commit added these two lines here on trunk, and two similar +lines to ./branch_B_MIXED_only on branch B_MIXED. +@ + + +1.3.2.1 +log +@First change on branch B_SPLIT. + +This change excludes sub3/default, because it was not part of this +commit, and sub1/subsubB/default, which is not even on the branch yet. +@ +text +@a8 2 + +First change on branch B_SPLIT. +@ + + +1.2 +log +@Second commit to proj, affecting all 7 files. +@ +text +@d6 3 +@ + + +1.1 +log +@Initial revision +@ +text +@d4 2 +@ + + +1.1.1.1 +log +@Initial import. +@ +text +@@ diff --git a/t/t9602/cvsroot/module/sub2/subsubA/default,v b/t/t9602/cvsroot/module/sub2/subsubA/default,v new file mode 100644 index 0000000000..d13242cb09 --- /dev/null +++ b/t/t9602/cvsroot/module/sub2/subsubA/default,v @@ -0,0 +1,102 @@ +head 1.2; +access; +symbols + B_SPLIT:1.2.0.2 + B_MIXED:1.1.0.2 + T_MIXED:1.1 + B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4 + B_FROM_INITIALS:1.1.1.1.0.2 + T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1 + T_ALL_INITIAL_FILES:1.1.1.1 + vendortag:1.1.1.1 + vendorbranch:1.1.1; +locks; strict; +comment @# @; + + +1.2 +date 2003.05.23.00.17.53; author jrandom; state Exp; +branches + 1.2.2.1; +next 1.1; + +1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches + 1.1.1.1 + 1.1.2.1; +next ; + +1.1.1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches; +next ; + +1.1.2.1 +date 2003.05.23.00.31.36; author jrandom; state Exp; +branches; +next ; + +1.2.2.1 +date 2003.06.03.03.20.31; author jrandom; state Exp; +branches; +next ; + + +desc +@@ + + +1.2 +log +@Second commit to proj, affecting all 7 files. +@ +text +@This is sub2/subsub2/default. + +Every directory in the `proj' project has a file named `default'. + +This line was added in the second commit (affecting all 7 files). +@ + + +1.2.2.1 +log +@First change on branch B_SPLIT. + +This change excludes sub3/default, because it was not part of this +commit, and sub1/subsubB/default, which is not even on the branch yet. +@ +text +@a5 2 + +First change on branch B_SPLIT. +@ + + +1.1 +log +@Initial revision +@ +text +@d4 2 +@ + + +1.1.2.1 +log +@Modify three files, on branch B_MIXED. +@ +text +@a3 2 + +This line was added on branch B_MIXED only (affecting 3 files). +@ + + +1.1.1.1 +log +@Initial import. +@ +text +@@ diff --git a/t/t9602/cvsroot/module/sub3/default,v b/t/t9602/cvsroot/module/sub3/default,v new file mode 100644 index 0000000000..88e4567434 --- /dev/null +++ b/t/t9602/cvsroot/module/sub3/default,v @@ -0,0 +1,102 @@ +head 1.3; +access; +symbols + B_SPLIT:1.3.0.2 + B_MIXED:1.2.0.2 + T_MIXED:1.2 + B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4 + B_FROM_INITIALS:1.1.1.1.0.2 + T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1 + T_ALL_INITIAL_FILES:1.1.1.1 + vendortag:1.1.1.1 + vendorbranch:1.1.1; +locks; strict; +comment @# @; + + +1.3 +date 2003.05.23.00.17.53; author jrandom; state Exp; +branches + 1.3.2.1; +next 1.2; + +1.2 +date 2003.05.23.00.15.26; author jrandom; state Exp; +branches; +next 1.1; + +1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches; +next ; + +1.3.2.1 +date 2003.06.03.04.33.13; author jrandom; state Exp; +branches; +next ; + + +desc +@@ + + +1.3 +log +@Second commit to proj, affecting all 7 files. +@ +text +@This is sub3/default. + +Every directory in the `proj' project has a file named `default'. + +This line was added by the first commit (affecting two files). + +This line was added in the second commit (affecting all 7 files). +@ + + +1.3.2.1 +log +@This change affects sub3/default and sub1/subsubB/default, on branch +B_SPLIT. Note that the latter file did not even exist on this branch +until after some other files had had revisions committed on B_SPLIT. +@ +text +@a7 4 + +This change affects sub3/default and sub1/subsubB/default, on branch +B_SPLIT. Note that the latter file did not even exist on this branch +until after some other files had had revisions committed on B_SPLIT. +@ + + +1.2 +log +@First commit to proj, affecting two files. +@ +text +@d6 2 +@ + + +1.1 +log +@Initial revision +@ +text +@d4 2 +@ + + +1.1.1.1 +log +@Initial import. +@ +text +@@ diff --git a/t/t9603-cvsimport-patchsets.sh b/t/t9603-cvsimport-patchsets.sh new file mode 100755 index 0000000000..958bdce4dd --- /dev/null +++ b/t/t9603-cvsimport-patchsets.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +# Structure of the test cvs repository +# +# Message File:Content Commit Time +# Rev 1 a: 1.1 2009-02-21 19:11:43 +0100 +# Rev 2 a: 1.2 b: 1.1 2009-02-21 19:11:14 +0100 +# Rev 3 b: 1.2 2009-02-21 19:11:43 +0100 +# +# As you can see the commit of Rev 3 has the same time as +# Rev 1 this leads to a broken import because of a cvsps +# bug. + +test_description='git cvsimport testing for correct patchset estimation' +. ./lib-cvs.sh + +CVSROOT="$TEST_DIRECTORY"/t9603/cvsroot +export CVSROOT + +test_expect_failure 'import with criss cross times on revisions' ' + + git cvsimport -p"-x" -C module-git module && + cd module-git && + git log --pretty=format:%s > ../actual-master && + git log A~2..A --pretty="format:%s %ad" -- > ../actual-A && + echo "" >> ../actual-master && + echo "" >> ../actual-A && + cd .. && + echo "Rev 4 +Rev 3 +Rev 2 +Rev 1" > expect-master && + test_cmp actual-master expect-master && + + echo "Rev 5 Branch A Wed Mar 11 19:09:10 2009 +0000 +Rev 4 Branch A Wed Mar 11 19:03:52 2009 +0000" > expect-A && + test_cmp actual-A expect-A +' + +test_done diff --git a/t/t9603/cvsroot/.gitattributes b/t/t9603/cvsroot/.gitattributes new file mode 100644 index 0000000000..562b12e16e --- /dev/null +++ b/t/t9603/cvsroot/.gitattributes @@ -0,0 +1 @@ +* -whitespace diff --git a/t/t9603/cvsroot/CVSROOT/.gitignore b/t/t9603/cvsroot/CVSROOT/.gitignore new file mode 100644 index 0000000000..3bb9b34173 --- /dev/null +++ b/t/t9603/cvsroot/CVSROOT/.gitignore @@ -0,0 +1,2 @@ +history +val-tags diff --git a/t/t9603/cvsroot/module/a,v b/t/t9603/cvsroot/module/a,v new file mode 100644 index 0000000000..ba8fd5af23 --- /dev/null +++ b/t/t9603/cvsroot/module/a,v @@ -0,0 +1,74 @@ +head 1.2; +access; +symbols + A:1.2.0.2; +locks; strict; +comment @# @; + + +1.2 +date 2009.02.21.18.11.14; author tester; state Exp; +branches + 1.2.2.1; +next 1.1; + +1.1 +date 2009.02.21.18.11.43; author tester; state Exp; +branches; +next ; + +1.2.2.1 +date 2009.03.11.19.03.52; author tester; state Exp; +branches; +next 1.2.2.2; + +1.2.2.2 +date 2009.03.11.19.09.10; author tester; state Exp; +branches; +next ; + + +desc +@@ + + +1.2 +log +@Rev 2 +@ +text +@1.2 +@ + + +1.2.2.1 +log +@Rev 4 Branch A +@ +text +@d1 1 +a1 1 +1.2.2.1 +@ + + +1.2.2.2 +log +@Rev 5 Branch A +@ +text +@d1 1 +a1 1 +1.2.2.2 +@ + + +1.1 +log +@Rev 1 +@ +text +@d1 1 +a1 1 +1.1 +@ diff --git a/t/t9603/cvsroot/module/b,v b/t/t9603/cvsroot/module/b,v new file mode 100644 index 0000000000..d26885518a --- /dev/null +++ b/t/t9603/cvsroot/module/b,v @@ -0,0 +1,90 @@ +head 1.3; +access; +symbols + A:1.2.0.2; +locks; strict; +comment @# @; + + +1.3 +date 2009.03.11.19.05.08; author tester; state Exp; +branches; +next 1.2; + +1.2 +date 2009.02.21.18.11.43; author tester; state Exp; +branches + 1.2.2.1; +next 1.1; + +1.1 +date 2009.02.21.18.11.14; author tester; state Exp; +branches; +next ; + +1.2.2.1 +date 2009.03.11.19.03.52; author tester; state Exp; +branches; +next 1.2.2.2; + +1.2.2.2 +date 2009.03.11.19.09.10; author tester; state Exp; +branches; +next ; + + +desc +@@ + + +1.3 +log +@Rev 4 +@ +text +@1.3 +@ + + +1.2 +log +@Rev 3 +@ +text +@d1 1 +a1 1 +1.2 +@ + + +1.2.2.1 +log +@Rev 4 Branch A +@ +text +@d1 1 +a1 1 +1.2.2.1 +@ + + +1.2.2.2 +log +@Rev 5 Branch A +@ +text +@d1 1 +a1 1 +1.2 +@ + + +1.1 +log +@Rev 2 +@ +text +@d1 1 +a1 1 +1.1 +@ diff --git a/t/test-lib.sh b/t/test-lib.sh index 5fdc5d94a2..f2ca536472 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -114,6 +114,9 @@ do valgrind=t; verbose=t; shift ;; --tee) shift ;; # was handled already + --root=*) + root=$(expr "z$1" : 'z[^=]*=\(.*\)') + shift ;; *) echo "error: unknown test option '$1'" >&2; exit 1 ;; esac @@ -645,7 +648,12 @@ fi # Test repository test="trash directory.$(basename "$0" .sh)" -test ! -z "$debug" || remove_trash="$TEST_DIRECTORY/$test" +test -n "$root" && test="$root/$test" +case "$test" in +/*) TRASH_DIRECTORY="$test" ;; + *) TRASH_DIRECTORY="$TEST_DIRECTORY/$test" ;; +esac +test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY rm -fr "$test" || { GIT_EXIT_OK=t echo >&5 "FATAL: Cannot prepare test area" @@ -677,6 +685,21 @@ do esac done +# Provide an implementation of the 'yes' utility +yes () { + if test $# = 0 + then + y=y + else + y="$*" + fi + + while echo "$y" + do + : + done +} + # Fix some commands on Windows case $(uname -s) in *MINGW*) diff --git a/test-date.c b/test-date.c index 62e8f2387a..a9e705f79a 100644 --- a/test-date.c +++ b/test-date.c @@ -1,20 +1,67 @@ #include "cache.h" -int main(int argc, char **argv) +static const char *usage_msg = "\n" +" test-date show [time_t]...\n" +" test-date parse [date]...\n" +" test-date approxidate [date]...\n"; + +static void show_dates(char **argv, struct timeval *now) { - int i; + char buf[128]; + + for (; *argv; argv++) { + time_t t = atoi(*argv); + show_date_relative(t, 0, now, buf, sizeof(buf)); + printf("%s -> %s\n", *argv, buf); + } +} - for (i = 1; i < argc; i++) { +static void parse_dates(char **argv, struct timeval *now) +{ + for (; *argv; argv++) { char result[100]; time_t t; - memcpy(result, "bad", 4); - parse_date(argv[i], result, sizeof(result)); + result[0] = 0; + parse_date(*argv, result, sizeof(result)); t = strtoul(result, NULL, 0); - printf("%s -> %s -> %s", argv[i], result, ctime(&t)); + printf("%s -> %s\n", *argv, + t ? show_date(t, 0, DATE_ISO8601) : "bad"); + } +} - t = approxidate(argv[i]); - printf("%s -> %s\n", argv[i], ctime(&t)); +static void parse_approxidate(char **argv, struct timeval *now) +{ + for (; *argv; argv++) { + time_t t; + t = approxidate_relative(*argv, now); + printf("%s -> %s\n", *argv, show_date(t, 0, DATE_ISO8601)); + } +} + +int main(int argc, char **argv) +{ + struct timeval now; + const char *x; + + x = getenv("TEST_DATE_NOW"); + if (x) { + now.tv_sec = atoi(x); + now.tv_usec = 0; } + else + gettimeofday(&now, NULL); + + argv++; + if (!*argv) + usage(usage_msg); + if (!strcmp(*argv, "show")) + show_dates(argv+1, &now); + else if (!strcmp(*argv, "parse")) + parse_dates(argv+1, &now); + else if (!strcmp(*argv, "approxidate")) + parse_approxidate(argv+1, &now); + else + usage(usage_msg); return 0; } diff --git a/transport.c b/transport.c index f231b355f2..d6c35d91ce 100644 --- a/transport.c +++ b/transport.c @@ -396,7 +396,6 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons { const char **argv; int argc; - int err; if (flags & TRANSPORT_PUSH_MIRROR) return error("http transport does not support mirror mode"); @@ -416,20 +415,7 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons while (refspec_nr--) argv[argc++] = *refspec++; argv[argc] = NULL; - err = run_command_v_opt(argv, RUN_GIT_CMD); - switch (err) { - case -ERR_RUN_COMMAND_FORK: - error("unable to fork for %s", argv[0]); - case -ERR_RUN_COMMAND_EXEC: - error("unable to exec %s", argv[0]); - break; - case -ERR_RUN_COMMAND_WAITPID: - case -ERR_RUN_COMMAND_WAITPID_WRONG_PID: - case -ERR_RUN_COMMAND_WAITPID_SIGNAL: - case -ERR_RUN_COMMAND_WAITPID_NOEXIT: - error("%s died with strange error", argv[0]); - } - return !!err; + return !!run_command_v_opt(argv, RUN_GIT_CMD); } static struct ref *get_refs_via_curl(struct transport *transport, int for_push) @@ -681,6 +667,21 @@ static int fetch_refs_via_pack(struct transport *transport, return (refs ? 0 : -1); } +static int push_had_errors(struct ref *ref) +{ + for (; ref; ref = ref->next) { + switch (ref->status) { + case REF_STATUS_NONE: + case REF_STATUS_UPTODATE: + case REF_STATUS_OK: + break; + default: + return 1; + } + } + return 0; +} + static int refs_pushed(struct ref *ref) { for (; ref; ref = ref->next) { @@ -895,6 +896,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re args.force_update = !!(flags & TRANSPORT_PUSH_FORCE); args.use_thin_pack = data->thin; args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE); + args.quiet = !!(flags & TRANSPORT_PUSH_QUIET); args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN); ret = send_pack(&args, data->fd, data->conn, remote_refs, @@ -1013,6 +1015,7 @@ int transport_push(struct transport *transport, struct ref *local_refs = get_local_heads(); int match_flags = MATCH_REFS_NONE; int verbose = flags & TRANSPORT_PUSH_VERBOSE; + int quiet = flags & TRANSPORT_PUSH_QUIET; int porcelain = flags & TRANSPORT_PUSH_PORCELAIN; int ret; @@ -1028,7 +1031,10 @@ int transport_push(struct transport *transport, ret = transport->push_refs(transport, remote_refs, flags); - print_push_status(transport->url, remote_refs, verbose | porcelain, porcelain, nonfastforward); + if (!quiet || push_had_errors(remote_refs)) + print_push_status(transport->url, remote_refs, + verbose | porcelain, porcelain, + nonfastforward); if (!(flags & TRANSPORT_PUSH_DRY_RUN)) { struct ref *ref; @@ -1036,7 +1042,7 @@ int transport_push(struct transport *transport, update_tracking_ref(transport->remote, ref, verbose); } - if (!ret && !refs_pushed(remote_refs)) + if (!quiet && !ret && !refs_pushed(remote_refs)) fprintf(stderr, "Everything up-to-date\n"); return ret; } @@ -1053,11 +1059,12 @@ const struct ref *transport_get_remote_refs(struct transport *transport) int transport_fetch_refs(struct transport *transport, const struct ref *refs) { int rc; - int nr_heads = 0, nr_alloc = 0; + int nr_heads = 0, nr_alloc = 0, nr_refs = 0; const struct ref **heads = NULL; const struct ref *rm; for (rm = refs; rm; rm = rm->next) { + nr_refs++; if (rm->peer_ref && !hashcmp(rm->peer_ref->old_sha1, rm->old_sha1)) continue; @@ -1065,6 +1072,19 @@ int transport_fetch_refs(struct transport *transport, const struct ref *refs) heads[nr_heads++] = rm; } + if (!nr_heads) { + /* + * When deepening of a shallow repository is requested, + * then local and remote refs are likely to still be equal. + * Just feed them all to the fetch method in that case. + * This condition shouldn't be met in a non-deepening fetch + * (see builtin-fetch.c:quickfetch()). + */ + heads = xmalloc(nr_refs * sizeof(*heads)); + for (rm = refs; rm; rm = rm->next) + heads[nr_heads++] = rm; + } + rc = transport->fetch(transport, nr_heads, heads); free(heads); return rc; diff --git a/transport.h b/transport.h index 639f13dcfe..171a01c7a3 100644 --- a/transport.h +++ b/transport.h @@ -36,6 +36,7 @@ struct transport { #define TRANSPORT_PUSH_MIRROR 8 #define TRANSPORT_PUSH_VERBOSE 16 #define TRANSPORT_PUSH_PORCELAIN 32 +#define TRANSPORT_PUSH_QUIET 64 /* Returns a transport suitable for the url */ struct transport *transport_get(struct remote *, const char *); diff --git a/unpack-trees.h b/unpack-trees.h index 1e0e2325f1..d19df44f40 100644 --- a/unpack-trees.h +++ b/unpack-trees.h @@ -17,18 +17,18 @@ struct unpack_trees_error_msgs { }; struct unpack_trees_options { - unsigned int reset:1, - merge:1, - update:1, - index_only:1, - nontrivial_merge:1, - trivial_merges_only:1, - verbose_update:1, - aggressive:1, - skip_unmerged:1, - initial_checkout:1, - diff_index_cached:1, - gently:1; + unsigned int reset, + merge, + update, + index_only, + nontrivial_merge, + trivial_merges_only, + verbose_update, + aggressive, + skip_unmerged, + initial_checkout, + diff_index_cached, + gently; const char *prefix; int pos; struct dir_struct *dir; diff --git a/update-server-info.c b/update-server-info.c deleted file mode 100644 index 7b38fd867b..0000000000 --- a/update-server-info.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "cache.h" -#include "exec_cmd.h" - -static const char update_server_info_usage[] = -"git update-server-info [--force]"; - -int main(int ac, char **av) -{ - int i; - int force = 0; - for (i = 1; i < ac; i++) { - if (av[i][0] == '-') { - if (!strcmp("--force", av[i]) || - !strcmp("-f", av[i])) - force = 1; - else - usage(update_server_info_usage); - } - } - if (i != ac) - usage(update_server_info_usage); - - git_extract_argv0_path(av[0]); - - setup_git_directory(); - - return !!update_server_info(force); -} diff --git a/upload-pack.c b/upload-pack.c index 841ebb534a..dacbc7614b 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -29,6 +29,7 @@ static unsigned long oldest_have; static int multi_ack, nr_our_refs; static int use_thin_pack, use_ofs_delta, use_include_tag; static int no_progress, daemon_mode; +static int shallow_nr; static struct object_array have_obj; static struct object_array want_obj; static unsigned int timeout; @@ -107,8 +108,6 @@ static int do_rev_list(int fd, void *create_full_pack) struct rev_info revs; pack_pipe = fdopen(fd, "w"); - if (create_full_pack) - use_thin_pack = 0; /* no point doing it */ init_revisions(&revs, NULL); revs.tag_objects = 1; revs.tree_objects = 1; @@ -155,13 +154,21 @@ static void create_pack_file(void) const char *argv[10]; int arg = 0; - rev_list.proc = do_rev_list; - /* .data is just a boolean: any non-NULL value will do */ - rev_list.data = create_full_pack ? &rev_list : NULL; - if (start_async(&rev_list)) - die("git upload-pack: unable to fork git-rev-list"); + if (shallow_nr) { + rev_list.proc = do_rev_list; + rev_list.data = 0; + if (start_async(&rev_list)) + die("git upload-pack: unable to fork git-rev-list"); + argv[arg++] = "pack-objects"; + } else { + argv[arg++] = "pack-objects"; + argv[arg++] = "--revs"; + if (create_full_pack) + argv[arg++] = "--all"; + else if (use_thin_pack) + argv[arg++] = "--thin"; + } - argv[arg++] = "pack-objects"; argv[arg++] = "--stdout"; if (!no_progress) argv[arg++] = "--progress"; @@ -172,7 +179,7 @@ static void create_pack_file(void) argv[arg++] = NULL; memset(&pack_objects, 0, sizeof(pack_objects)); - pack_objects.in = rev_list.out; /* start_command closes it */ + pack_objects.in = shallow_nr ? rev_list.out : -1; pack_objects.out = -1; pack_objects.err = -1; pack_objects.git_cmd = 1; @@ -181,6 +188,24 @@ static void create_pack_file(void) if (start_command(&pack_objects)) die("git upload-pack: unable to fork git-pack-objects"); + /* pass on revisions we (don't) want */ + if (!shallow_nr) { + FILE *pipe_fd = fdopen(pack_objects.in, "w"); + if (!create_full_pack) { + int i; + for (i = 0; i < want_obj.nr; i++) + fprintf(pipe_fd, "%s\n", sha1_to_hex(want_obj.objects[i].item->sha1)); + fprintf(pipe_fd, "--not\n"); + for (i = 0; i < have_obj.nr; i++) + fprintf(pipe_fd, "%s\n", sha1_to_hex(have_obj.objects[i].item->sha1)); + } + + fprintf(pipe_fd, "\n"); + fflush(pipe_fd); + fclose(pipe_fd); + } + + /* We read from pack_objects.err to capture stderr output for * progress bar, and pack_objects.out to capture the pack data. */ @@ -276,7 +301,7 @@ static void create_pack_file(void) error("git upload-pack: git-pack-objects died with error."); goto fail; } - if (finish_async(&rev_list)) + if (shallow_nr && finish_async(&rev_list)) goto fail; /* error was already reported */ /* flush the data */ @@ -402,7 +427,7 @@ static int get_common_commits(void) save_commit_buffer = 0; - for(;;) { + for (;;) { int len = packet_read_line(0, line, sizeof(line)); reset_timeout(); @@ -451,6 +476,7 @@ static void receive_needs(void) static char line[1000]; int len, depth = 0; + shallow_nr = 0; if (debug_fd) write_in_full(debug_fd, "#S\n", 3); for (;;) { @@ -538,6 +564,7 @@ static void receive_needs(void) packet_write(1, "shallow %s", sha1_to_hex(object->sha1)); register_shallow(object->sha1); + shallow_nr++; } result = result->next; } @@ -571,6 +598,8 @@ static void receive_needs(void) for (i = 0; i < shallows.nr; i++) register_shallow(shallows.objects[i].item->sha1); } + + shallow_nr += shallows.nr; free(shallows.objects); } @@ -622,6 +651,7 @@ int main(int argc, char **argv) int strict = 0; git_extract_argv0_path(argv[0]); + read_replace_refs = 0; for (i = 1; i < argc; i++) { char *arg = argv[i]; @@ -21,9 +21,8 @@ static struct git_var git_vars[] = { static void list_vars(void) { struct git_var *ptr; - for(ptr = git_vars; ptr->read; ptr++) { + for (ptr = git_vars; ptr->read; ptr++) printf("%s=%s\n", ptr->name, ptr->read(IDENT_WARN_ON_NO_NAME)); - } } static const char *read_var(const char *var) @@ -31,7 +30,7 @@ static const char *read_var(const char *var) struct git_var *ptr; const char *val; val = NULL; - for(ptr = git_vars; ptr->read; ptr++) { + for (ptr = git_vars; ptr->read; ptr++) { if (strcmp(var, ptr->name) == 0) { val = ptr->read(IDENT_ERROR_ON_NO_NAME); break; diff --git a/wt-status.c b/wt-status.c index 47735d8129..85f3fcb8a2 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1,6 +1,5 @@ #include "cache.h" #include "wt-status.h" -#include "color.h" #include "object.h" #include "dir.h" #include "commit.h" @@ -11,38 +10,18 @@ #include "run-command.h" #include "remote.h" -int wt_status_relative_paths = 1; -int wt_status_use_color = -1; -static int wt_status_submodule_summary; -static char wt_status_colors[][COLOR_MAXLEN] = { +static char default_wt_status_colors[][COLOR_MAXLEN] = { GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */ GIT_COLOR_GREEN, /* WT_STATUS_UPDATED */ GIT_COLOR_RED, /* WT_STATUS_CHANGED */ GIT_COLOR_RED, /* WT_STATUS_UNTRACKED */ GIT_COLOR_RED, /* WT_STATUS_NOBRANCH */ + GIT_COLOR_RED, /* WT_STATUS_UNMERGED */ }; -enum untracked_status_type show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; - -static int parse_status_slot(const char *var, int offset) -{ - if (!strcasecmp(var+offset, "header")) - return WT_STATUS_HEADER; - if (!strcasecmp(var+offset, "updated") - || !strcasecmp(var+offset, "added")) - return WT_STATUS_UPDATED; - if (!strcasecmp(var+offset, "changed")) - return WT_STATUS_CHANGED; - if (!strcasecmp(var+offset, "untracked")) - return WT_STATUS_UNTRACKED; - if (!strcasecmp(var+offset, "nobranch")) - return WT_STATUS_NOBRANCH; - die("bad config variable '%s'", var); -} - -static const char *color(int slot) +static const char *color(int slot, struct wt_status *s) { - return wt_status_use_color > 0 ? wt_status_colors[slot] : ""; + return s->use_color > 0 ? s->color_palette[slot] : ""; } void wt_status_prepare(struct wt_status *s) @@ -51,16 +30,35 @@ void wt_status_prepare(struct wt_status *s) const char *head; memset(s, 0, sizeof(*s)); + memcpy(s->color_palette, default_wt_status_colors, + sizeof(default_wt_status_colors)); + s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; + s->use_color = -1; + s->relative_paths = 1; head = resolve_ref("HEAD", sha1, 0, NULL); s->branch = head ? xstrdup(head) : NULL; s->reference = "HEAD"; s->fp = stdout; s->index_file = get_index_file(); + s->change.strdup_strings = 1; + s->untracked.strdup_strings = 1; +} + +static void wt_status_print_unmerged_header(struct wt_status *s) +{ + const char *c = color(WT_STATUS_HEADER, s); + color_fprintf_ln(s->fp, c, "# Unmerged paths:"); + if (!s->is_initial) + color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference); + else + color_fprintf_ln(s->fp, c, "# (use \"git rm --cached <file>...\" to unstage)"); + color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to mark resolution)"); + color_fprintf_ln(s->fp, c, "#"); } static void wt_status_print_cached_header(struct wt_status *s) { - const char *c = color(WT_STATUS_HEADER); + const char *c = color(WT_STATUS_HEADER, s); color_fprintf_ln(s->fp, c, "# Changes to be committed:"); if (!s->is_initial) { color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference); @@ -73,7 +71,7 @@ static void wt_status_print_cached_header(struct wt_status *s) static void wt_status_print_dirty_header(struct wt_status *s, int has_deleted) { - const char *c = color(WT_STATUS_HEADER); + const char *c = color(WT_STATUS_HEADER, s); color_fprintf_ln(s->fp, c, "# Changed but not updated:"); if (!has_deleted) color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to update what will be committed)"); @@ -85,7 +83,7 @@ static void wt_status_print_dirty_header(struct wt_status *s, static void wt_status_print_untracked_header(struct wt_status *s) { - const char *c = color(WT_STATUS_HEADER); + const char *c = color(WT_STATUS_HEADER, s); color_fprintf_ln(s->fp, c, "# Untracked files:"); color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to include in what will be committed)"); color_fprintf_ln(s->fp, c, "#"); @@ -93,23 +91,63 @@ static void wt_status_print_untracked_header(struct wt_status *s) static void wt_status_print_trailer(struct wt_status *s) { - color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#"); + color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#"); } #define quote_path quote_path_relative -static void wt_status_print_filepair(struct wt_status *s, - int t, struct diff_filepair *p) +static void wt_status_print_unmerged_data(struct wt_status *s, + struct string_list_item *it) { - const char *c = color(t); + const char *c = color(WT_STATUS_UNMERGED, s); + struct wt_status_change_data *d = it->util; + struct strbuf onebuf = STRBUF_INIT; + const char *one, *how = "bug"; + + one = quote_path(it->string, -1, &onebuf, s->prefix); + color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t"); + switch (d->stagemask) { + case 1: how = "both deleted:"; break; + case 2: how = "added by us:"; break; + case 3: how = "deleted by them:"; break; + case 4: how = "added by them:"; break; + case 5: how = "deleted by us:"; break; + case 6: how = "both added:"; break; + case 7: how = "both modified:"; break; + } + color_fprintf(s->fp, c, "%-20s%s\n", how, one); + strbuf_release(&onebuf); +} + +static void wt_status_print_change_data(struct wt_status *s, + int change_type, + struct string_list_item *it) +{ + struct wt_status_change_data *d = it->util; + const char *c = color(change_type, s); + int status = status; + char *one_name; + char *two_name; const char *one, *two; struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT; - one = quote_path(p->one->path, -1, &onebuf, s->prefix); - two = quote_path(p->two->path, -1, &twobuf, s->prefix); + one_name = two_name = it->string; + switch (change_type) { + case WT_STATUS_UPDATED: + status = d->index_status; + if (d->head_path) + one_name = d->head_path; + break; + case WT_STATUS_CHANGED: + status = d->worktree_status; + break; + } - color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t"); - switch (p->status) { + one = quote_path(one_name, -1, &onebuf, s->prefix); + two = quote_path(two_name, -1, &twobuf, s->prefix); + + color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t"); + switch (status) { case DIFF_STATUS_ADDED: color_fprintf(s->fp, c, "new file: %s", one); break; @@ -135,64 +173,114 @@ static void wt_status_print_filepair(struct wt_status *s, color_fprintf(s->fp, c, "unmerged: %s", one); break; default: - die("bug: unhandled diff status %c", p->status); + die("bug: unhandled diff status %c", status); } fprintf(s->fp, "\n"); strbuf_release(&onebuf); strbuf_release(&twobuf); } -static void wt_status_print_updated_cb(struct diff_queue_struct *q, - struct diff_options *options, - void *data) +static void wt_status_collect_changed_cb(struct diff_queue_struct *q, + struct diff_options *options, + void *data) { struct wt_status *s = data; - int shown_header = 0; int i; + + if (!q->nr) + return; + s->workdir_dirty = 1; for (i = 0; i < q->nr; i++) { - if (q->queue[i]->status == 'U') - continue; - if (!shown_header) { - wt_status_print_cached_header(s); - s->commitable = 1; - shown_header = 1; + struct diff_filepair *p; + struct string_list_item *it; + struct wt_status_change_data *d; + + p = q->queue[i]; + it = string_list_insert(p->one->path, &s->change); + d = it->util; + if (!d) { + d = xcalloc(1, sizeof(*d)); + it->util = d; } - wt_status_print_filepair(s, WT_STATUS_UPDATED, q->queue[i]); + if (!d->worktree_status) + d->worktree_status = p->status; } - if (shown_header) - wt_status_print_trailer(s); } -static void wt_status_print_changed_cb(struct diff_queue_struct *q, - struct diff_options *options, - void *data) +static int unmerged_mask(const char *path) +{ + int pos, mask; + struct cache_entry *ce; + + pos = cache_name_pos(path, strlen(path)); + if (0 <= pos) + return 0; + + mask = 0; + pos = -pos-1; + while (pos < active_nr) { + ce = active_cache[pos++]; + if (strcmp(ce->name, path) || !ce_stage(ce)) + break; + mask |= (1 << (ce_stage(ce) - 1)); + } + return mask; +} + +static void wt_status_collect_updated_cb(struct diff_queue_struct *q, + struct diff_options *options, + void *data) { struct wt_status *s = data; int i; - if (q->nr) { - int has_deleted = 0; - s->workdir_dirty = 1; - for (i = 0; i < q->nr; i++) - if (q->queue[i]->status == DIFF_STATUS_DELETED) { - has_deleted = 1; - break; - } - wt_status_print_dirty_header(s, has_deleted); + + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p; + struct string_list_item *it; + struct wt_status_change_data *d; + + p = q->queue[i]; + it = string_list_insert(p->two->path, &s->change); + d = it->util; + if (!d) { + d = xcalloc(1, sizeof(*d)); + it->util = d; + } + if (!d->index_status) + d->index_status = p->status; + switch (p->status) { + case DIFF_STATUS_COPIED: + case DIFF_STATUS_RENAMED: + d->head_path = xstrdup(p->one->path); + break; + case DIFF_STATUS_UNMERGED: + d->stagemask = unmerged_mask(p->two->path); + break; + } } - for (i = 0; i < q->nr; i++) - wt_status_print_filepair(s, WT_STATUS_CHANGED, q->queue[i]); - if (q->nr) - wt_status_print_trailer(s); } -static void wt_status_print_updated(struct wt_status *s) +static void wt_status_collect_changes_worktree(struct wt_status *s) { struct rev_info rev; + + init_revisions(&rev, NULL); + setup_revisions(0, NULL, &rev, NULL); + rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; + rev.diffopt.format_callback = wt_status_collect_changed_cb; + rev.diffopt.format_callback_data = s; + run_diff_files(&rev, 0); +} + +static void wt_status_collect_changes_index(struct wt_status *s) +{ + struct rev_info rev; + init_revisions(&rev, NULL); setup_revisions(0, NULL, &rev, s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference); rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; - rev.diffopt.format_callback = wt_status_print_updated_cb; + rev.diffopt.format_callback = wt_status_collect_updated_cb; rev.diffopt.format_callback_data = s; rev.diffopt.detect_rename = 1; rev.diffopt.rename_limit = 200; @@ -200,15 +288,155 @@ static void wt_status_print_updated(struct wt_status *s) run_diff_index(&rev, 1); } +static void wt_status_collect_changes_initial(struct wt_status *s) +{ + int i; + + for (i = 0; i < active_nr; i++) { + struct string_list_item *it; + struct wt_status_change_data *d; + struct cache_entry *ce = active_cache[i]; + + it = string_list_insert(ce->name, &s->change); + d = it->util; + if (!d) { + d = xcalloc(1, sizeof(*d)); + it->util = d; + } + if (ce_stage(ce)) { + d->index_status = DIFF_STATUS_UNMERGED; + d->stagemask |= (1 << (ce_stage(ce) - 1)); + } + else + d->index_status = DIFF_STATUS_ADDED; + } +} + +static void wt_status_collect_untracked(struct wt_status *s) +{ + int i; + struct dir_struct dir; + + if (!s->show_untracked_files) + return; + memset(&dir, 0, sizeof(dir)); + if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES) + dir.flags |= + DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES; + setup_standard_excludes(&dir); + + fill_directory(&dir, NULL); + for (i = 0; i < dir.nr; i++) { + struct dir_entry *ent = dir.entries[i]; + if (!cache_name_is_other(ent->name, ent->len)) + continue; + s->workdir_untracked = 1; + string_list_insert(ent->name, &s->untracked); + } +} + +void wt_status_collect(struct wt_status *s) +{ + wt_status_collect_changes_worktree(s); + + if (s->is_initial) + wt_status_collect_changes_initial(s); + else + wt_status_collect_changes_index(s); + wt_status_collect_untracked(s); +} + +static void wt_status_print_unmerged(struct wt_status *s) +{ + int shown_header = 0; + int i; + + for (i = 0; i < s->change.nr; i++) { + struct wt_status_change_data *d; + struct string_list_item *it; + it = &(s->change.items[i]); + d = it->util; + if (!d->stagemask) + continue; + if (!shown_header) { + wt_status_print_unmerged_header(s); + shown_header = 1; + } + wt_status_print_unmerged_data(s, it); + } + if (shown_header) + wt_status_print_trailer(s); + +} + +static void wt_status_print_updated(struct wt_status *s) +{ + int shown_header = 0; + int i; + + for (i = 0; i < s->change.nr; i++) { + struct wt_status_change_data *d; + struct string_list_item *it; + it = &(s->change.items[i]); + d = it->util; + if (!d->index_status || + d->index_status == DIFF_STATUS_UNMERGED) + continue; + if (!shown_header) { + wt_status_print_cached_header(s); + s->commitable = 1; + shown_header = 1; + } + wt_status_print_change_data(s, WT_STATUS_UPDATED, it); + } + if (shown_header) + wt_status_print_trailer(s); +} + +/* + * -1 : has delete + * 0 : no change + * 1 : some change but no delete + */ +static int wt_status_check_worktree_changes(struct wt_status *s) +{ + int i; + int changes = 0; + + for (i = 0; i < s->change.nr; i++) { + struct wt_status_change_data *d; + d = s->change.items[i].util; + if (!d->worktree_status || + d->worktree_status == DIFF_STATUS_UNMERGED) + continue; + changes = 1; + if (d->worktree_status == DIFF_STATUS_DELETED) + return -1; + } + return changes; +} + static void wt_status_print_changed(struct wt_status *s) { - struct rev_info rev; - init_revisions(&rev, ""); - setup_revisions(0, NULL, &rev, NULL); - rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; - rev.diffopt.format_callback = wt_status_print_changed_cb; - rev.diffopt.format_callback_data = s; - run_diff_files(&rev, 0); + int i; + int worktree_changes = wt_status_check_worktree_changes(s); + + if (!worktree_changes) + return; + + wt_status_print_dirty_header(s, worktree_changes < 0); + + for (i = 0; i < s->change.nr; i++) { + struct wt_status_change_data *d; + struct string_list_item *it; + it = &(s->change.items[i]); + d = it->util; + if (!d->worktree_status || + d->worktree_status == DIFF_STATUS_UNMERGED) + continue; + wt_status_print_change_data(s, WT_STATUS_CHANGED, it); + } + wt_status_print_trailer(s); } static void wt_status_print_submodule_summary(struct wt_status *s) @@ -228,7 +456,7 @@ static void wt_status_print_submodule_summary(struct wt_status *s) NULL }; - sprintf(summary_limit, "%d", wt_status_submodule_summary); + sprintf(summary_limit, "%d", s->submodule_summary); snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file); memset(&sm_summary, 0, sizeof(sm_summary)); @@ -243,32 +471,20 @@ static void wt_status_print_submodule_summary(struct wt_status *s) static void wt_status_print_untracked(struct wt_status *s) { - struct dir_struct dir; int i; - int shown_header = 0; struct strbuf buf = STRBUF_INIT; - memset(&dir, 0, sizeof(dir)); - - if (!s->untracked) - dir.flags |= - DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES; - setup_standard_excludes(&dir); + if (!s->untracked.nr) + return; - fill_directory(&dir, NULL); - for(i = 0; i < dir.nr; i++) { - struct dir_entry *ent = dir.entries[i]; - if (!cache_name_is_other(ent->name, ent->len)) - continue; - if (!shown_header) { - s->workdir_untracked = 1; - wt_status_print_untracked_header(s); - shown_header = 1; - } - color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t"); - color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%s", - quote_path(ent->name, ent->len, - &buf, s->prefix)); + wt_status_print_untracked_header(s); + for (i = 0; i < s->untracked.nr; i++) { + struct string_list_item *it; + it = &(s->untracked.items[i]); + color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t"); + color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED, s), "%s", + quote_path(it->string, strlen(it->string), + &buf, s->prefix)); } strbuf_release(&buf); } @@ -310,15 +526,15 @@ static void wt_status_print_tracking(struct wt_status *s) return; for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1) - color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), + color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "# %.*s", (int)(ep - cp), cp); - color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#"); + color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#"); } void wt_status_print(struct wt_status *s) { unsigned char sha1[20]; - const char *branch_color = color(WT_STATUS_HEADER); + const char *branch_color = color(WT_STATUS_HEADER, s); s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0; if (s->branch) { @@ -328,26 +544,29 @@ void wt_status_print(struct wt_status *s) branch_name += 11; else if (!strcmp(branch_name, "HEAD")) { branch_name = ""; - branch_color = color(WT_STATUS_NOBRANCH); + branch_color = color(WT_STATUS_NOBRANCH, s); on_what = "Not currently on any branch."; } - color_fprintf(s->fp, color(WT_STATUS_HEADER), "# "); + color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "# "); color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name); if (!s->is_initial) wt_status_print_tracking(s); } + wt_status_collect(s); + if (s->is_initial) { - color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#"); - color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "# Initial commit"); - color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#"); + color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#"); + color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "# Initial commit"); + color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#"); } wt_status_print_updated(s); + wt_status_print_unmerged(s); wt_status_print_changed(s); - if (wt_status_submodule_summary) + if (s->submodule_summary) wt_status_print_submodule_summary(s); - if (show_untracked_files) + if (s->show_untracked_files) wt_status_print_untracked(s); else if (s->commitable) fprintf(s->fp, "# Untracked files not listed (use -u option to show untracked files)\n"); @@ -361,53 +580,13 @@ void wt_status_print(struct wt_status *s) ; /* 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) + else if (s->untracked.nr) printf("nothing added to commit but untracked files present (use \"git add\" to track)\n"); else if (s->is_initial) printf("nothing to commit (create/copy files and use \"git add\" to track)\n"); - else if (!show_untracked_files) + else if (!s->show_untracked_files) printf("nothing to commit (use -u to show untracked files)\n"); else printf("nothing to commit (working directory clean)\n"); } } - -int git_status_config(const char *k, const char *v, void *cb) -{ - if (!strcmp(k, "status.submodulesummary")) { - int is_bool; - wt_status_submodule_summary = git_config_bool_or_int(k, v, &is_bool); - if (is_bool && wt_status_submodule_summary) - wt_status_submodule_summary = -1; - return 0; - } - if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) { - wt_status_use_color = git_config_colorbool(k, v, -1); - return 0; - } - if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) { - int slot = parse_status_slot(k, 13); - if (!v) - return config_error_nonbool(k); - color_parse(v, k, wt_status_colors[slot]); - return 0; - } - if (!strcmp(k, "status.relativepaths")) { - wt_status_relative_paths = git_config_bool(k, v); - return 0; - } - if (!strcmp(k, "status.showuntrackedfiles")) { - if (!v) - return config_error_nonbool(k); - else if (!strcmp(v, "no")) - show_untracked_files = SHOW_NO_UNTRACKED_FILES; - else if (!strcmp(v, "normal")) - show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; - else if (!strcmp(v, "all")) - show_untracked_files = SHOW_ALL_UNTRACKED_FILES; - else - return error("Invalid untracked files mode '%s'", v); - return 0; - } - return git_diff_ui_config(k, v, cb); -} diff --git a/wt-status.h b/wt-status.h index 78add09bd6..a0e75177be 100644 --- a/wt-status.h +++ b/wt-status.h @@ -2,13 +2,16 @@ #define STATUS_H #include <stdio.h> +#include "string-list.h" +#include "color.h" enum color_wt_status { - WT_STATUS_HEADER, + WT_STATUS_HEADER = 0, WT_STATUS_UPDATED, WT_STATUS_CHANGED, WT_STATUS_UNTRACKED, WT_STATUS_NOBRANCH, + WT_STATUS_UNMERGED, }; enum untracked_status_type { @@ -16,7 +19,13 @@ enum untracked_status_type { SHOW_NORMAL_UNTRACKED_FILES, SHOW_ALL_UNTRACKED_FILES }; -extern enum untracked_status_type show_untracked_files; + +struct wt_status_change_data { + int worktree_status; + int index_status; + int stagemask; + char *head_path; +}; struct wt_status { int is_initial; @@ -24,8 +33,13 @@ struct wt_status { const char *reference; int verbose; int amend; - int untracked; int nowarn; + int use_color; + int relative_paths; + int submodule_summary; + enum untracked_status_type show_untracked_files; + char color_palette[WT_STATUS_UNMERGED+1][COLOR_MAXLEN]; + /* These are computed during processing of the individual sections */ int commitable; int workdir_dirty; @@ -33,12 +47,12 @@ struct wt_status { const char *index_file; FILE *fp; const char *prefix; + struct string_list change; + struct string_list untracked; }; -int git_status_config(const char *var, const char *value, void *cb); -extern int wt_status_use_color; -extern int wt_status_relative_paths; void wt_status_prepare(struct wt_status *s); void wt_status_print(struct wt_status *s); +void wt_status_collect(struct wt_status *s); #endif /* STATUS_H */ diff --git a/xdiff/xutils.c b/xdiff/xutils.c index 04ad468702..bc12f29895 100644 --- a/xdiff/xutils.c +++ b/xdiff/xutils.c @@ -190,48 +190,66 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags) { int i1, i2; + if (!(flags & XDF_WHITESPACE_FLAGS)) + return s1 == s2 && !memcmp(l1, l2, s1); + + i1 = 0; + i2 = 0; + + /* + * -w matches everything that matches with -b, and -b in turn + * matches everything that matches with --ignore-space-at-eol. + * + * Each flavor of ignoring needs different logic to skip whitespaces + * while we have both sides to compare. + */ if (flags & XDF_IGNORE_WHITESPACE) { - for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) { - if (isspace(l1[i1])) - while (isspace(l1[i1]) && i1 < s1) - i1++; - if (isspace(l2[i2])) - while (isspace(l2[i2]) && i2 < s2) - i2++; - if (i1 < s1 && i2 < s2 && l1[i1++] != l2[i2++]) + goto skip_ws; + while (i1 < s1 && i2 < s2) { + if (l1[i1++] != l2[i2++]) return 0; + skip_ws: + while (i1 < s1 && isspace(l1[i1])) + i1++; + while (i2 < s2 && isspace(l2[i2])) + i2++; } - return (i1 >= s1 && i2 >= s2); } else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) { - for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) { - if (isspace(l1[i1])) { - if (!isspace(l2[i2])) - return 0; - while (isspace(l1[i1]) && i1 < s1) - i1++; - while (isspace(l2[i2]) && i2 < s2) - i2++; - } else if (l1[i1++] != l2[i2++]) - return 0; - } - return (i1 >= s1 && i2 >= s2); - } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) { - for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) { - if (l1[i1] != l2[i2]) { + while (i1 < s1 && i2 < s2) { + if (isspace(l1[i1]) && isspace(l2[i2])) { + /* Skip matching spaces and try again */ while (i1 < s1 && isspace(l1[i1])) i1++; while (i2 < s2 && isspace(l2[i2])) i2++; - if (i1 < s1 || i2 < s2) - return 0; - return 1; + continue; } + if (l1[i1++] != l2[i2++]) + return 0; + } + } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) { + while (i1 < s1 && i2 < s2 && l1[i1++] == l2[i2++]) + ; /* keep going */ + } + + /* + * After running out of one side, the remaining side must have + * nothing but whitespace for the lines to match. Note that + * ignore-whitespace-at-eol case may break out of the loop + * while there still are characters remaining on both lines. + */ + if (i1 < s1) { + while (i1 < s1 && isspace(l1[i1])) i1++; + if (s1 != i1) + return 0; + } + if (i2 < s2) { + while (i2 < s2 && isspace(l2[i2])) i2++; - } - return i1 >= s1 && i2 >= s2; - } else - return s1 == s2 && !memcmp(l1, l2, s1); + return (s2 == i2); + } + return 1; } static unsigned long xdl_hash_record_with_whitespace(char const **data, @@ -242,18 +260,20 @@ static unsigned long xdl_hash_record_with_whitespace(char const **data, for (; ptr < top && *ptr != '\n'; ptr++) { if (isspace(*ptr)) { const char *ptr2 = ptr; + int at_eol; while (ptr + 1 < top && isspace(ptr[1]) && ptr[1] != '\n') ptr++; + at_eol = (top <= ptr + 1 || ptr[1] == '\n'); if (flags & XDF_IGNORE_WHITESPACE) ; /* already handled */ else if (flags & XDF_IGNORE_WHITESPACE_CHANGE - && ptr[1] != '\n') { + && !at_eol) { ha += (ha << 5); ha ^= (unsigned long) ' '; } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL - && ptr[1] != '\n') { + && !at_eol) { while (ptr2 != ptr + 1) { ha += (ha << 5); ha ^= (unsigned long) *ptr2; |