diff options
200 files changed, 3464 insertions, 1610 deletions
diff --git a/Documentation/.gitignore b/Documentation/.gitignore index 2f938f471a..d8edd90406 100644 --- a/Documentation/.gitignore +++ b/Documentation/.gitignore @@ -2,7 +2,9 @@ *.html *.[1-8] *.made +*.texi git.info +gitman.info howto-index.txt doc.dep cmds-*.txt diff --git a/Documentation/RelNotes-1.5.6.txt b/Documentation/RelNotes-1.5.6.txt index a79e4b9393..e143d8d61b 100644 --- a/Documentation/RelNotes-1.5.6.txt +++ b/Documentation/RelNotes-1.5.6.txt @@ -18,11 +18,15 @@ Updates since v1.5.5 * "git init" now autodetects the case sensitivity of the filesystem and sets core.ignorecase accordingly. +* cpio is no longer used; neither "curl" binary (libcurl is still used). + (documentation) * Many freestanding documentation pages have been converted and made - available to "git help" (aka "man git-<command>") as section 7 of the - manual pages. + available to "git help" (aka "man git<something>") as section 7 of + the manual pages. This means bookmarks to some HTML documentation + files may need to be updated (eg "tutorial.html" became + "gittutorial.html"). (performance) @@ -70,7 +74,7 @@ Updates since v1.5.5 * "git init --bare" is a synonym for "git --bare init" now. -* "git gc --auto" honors a new pre-aut-gc hook to temporarily disable it. +* "git gc --auto" honors a new pre-auto-gc hook to temporarily disable it. * "git log --pretty=tformat:<custom format>" gives a LF after each entry, instead of giving a LF between each pair of entries which is how @@ -108,9 +112,4 @@ Fixes since v1.5.5 All of the fixes in v1.5.5 maintenance series are included in this release, unless otherwise noted. - --- -exec >/var/tmp/1 -O=v1.5.6-rc1 -echo O=`git describe refs/heads/master` -git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint +And there are too numerous small fixes to otherwise note here ;-) diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 0e155c936c..b1164753e1 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -419,6 +419,11 @@ settings but I haven't tried, yet. mail.identity.default.compose_html => false mail.identity.id?.compose_html => false +(Lukas Sandström) + +There is a script in contrib/thunderbird-patch-inline which can help +you include patches with Thunderbird in an easy way. To use it, do the +steps above and then use the script as the external editor. Gnus ---- diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt index c11bb7d36c..5428111d73 100644 --- a/Documentation/blame-options.txt +++ b/Documentation/blame-options.txt @@ -41,7 +41,8 @@ of lines before or after the line given by <start>. -S <revs-file>:: Use revs from revs-file instead of calling linkgit:git-rev-list[1]. --p, --porcelain:: +-p:: +--porcelain:: Show in a format designed for machine consumption. --incremental:: @@ -83,5 +84,6 @@ alphanumeric characters that git must detect as moving between files for it to associate those lines with the parent commit. --h, --help:: +-h:: +--help:: Show help message. diff --git a/Documentation/cat-texi.perl b/Documentation/cat-texi.perl index e3d8e9faa8..dbc133cd3c 100755 --- a/Documentation/cat-texi.perl +++ b/Documentation/cat-texi.perl @@ -11,7 +11,7 @@ while (<STDIN>) { if (s/^\@top (.*)/\@node $1,,,Top/) { push @menu, $1; } - s/\(\@pxref{\[URLS\]}\)//; + s/\(\@pxref{\[(URLS|REMOTES)\]}\)//; print TMP; } close TMP; diff --git a/Documentation/config.txt b/Documentation/config.txt index 5331b450ea..1e09a57c8c 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1013,6 +1013,25 @@ status.relativePaths:: relative to the repository root (this was the default for git prior to v1.5.4). +status.showUntrackedFiles:: + By default, linkgit:git-status[1] and linkgit:git-commit[1] show + files which are not currently tracked by Git. Directories which + contain only untracked files, are shown with the directory name + only. Showing untracked files means that Git needs to lstat() all + all the files in the whole repository, which might be slow on some + systems. So, this variable controls how the commands displays + the untracked files. Possible values are: ++ +-- + - 'no' - Show no untracked files + - 'normal' - Shows untracked files and directories + - 'all' - Shows also individual files in untracked directories. +-- ++ +If this variable is not specified, it defaults to 'normal'. +This variable can be overridden with the -u|--untracked-files option +of linkgit:git-status[1] and linkgit:git-commit[1]. + tar.umask:: This variable can be used to restrict the permission bits of tar archive entries. The default is 0002, which turns off the diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index b675911480..85c87180db 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -1,22 +1,26 @@ --q, \--quiet:: +-q:: +--quiet:: Pass --quiet to git-fetch-pack and silence any other internally used programs. --v, \--verbose:: +-v:: +--verbose:: Be verbose. --a, \--append:: +-a:: +--append:: Append ref names and object names of fetched refs to the existing contents of `.git/FETCH_HEAD`. Without this option old data in `.git/FETCH_HEAD` will be overwritten. -\--upload-pack <upload-pack>:: +--upload-pack <upload-pack>:: When given, and the repository to fetch from is handled by 'git-fetch-pack', '--exec=<upload-pack>' is passed to the command to specify non-default path for the command run on the other end. --f, \--force:: +-f:: +--force:: When `git-fetch` is used with `<rbranch>:<lbranch>` refspec, it refuses to update the local branch `<lbranch>` unless the remote branch `<rbranch>` it @@ -24,16 +28,18 @@ overrides that check. ifdef::git-pull[] -\--no-tags:: +--no-tags:: endif::git-pull[] ifndef::git-pull[] --n, \--no-tags:: +-n:: +--no-tags:: endif::git-pull[] By default, tags that point at objects that are downloaded from the remote repository are fetched and stored locally. This option disables this automatic tag following. --t, \--tags:: +-t:: +--tags:: Most of the tags are fetched automatically as branch heads are downloaded, but tags that do not point at objects reachable from the branch heads that are being @@ -41,10 +47,12 @@ endif::git-pull[] flag lets all tags and their associated objects be downloaded. --k, \--keep:: +-k:: +--keep:: Keep downloaded pack. --u, \--update-head-ok:: +-u:: +--update-head-ok:: By default `git-fetch` refuses to update the head which corresponds to the current branch. This flag disables the check. This is purely for the internal use for `git-pull` @@ -52,7 +60,7 @@ endif::git-pull[] implementing your own Porcelain you are not supposed to use it. -\--depth=<depth>:: +--depth=<depth>:: Deepen the history of a 'shallow' repository created by `git clone` with `--depth=<depth>` option (see linkgit:git-clone[1]) by the specified number of commits. diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 88165da38f..b8e3fa6759 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -8,8 +8,9 @@ git-add - Add file contents to the index SYNOPSIS -------- [verse] -'git-add' [-n] [-v] [-f] [--interactive | -i] [--patch | -p] [-u] [--refresh] - [--ignore-errors] [--] <filepattern>... +'git-add' [-n] [-v] [--force | -f] [--interactive | -i] [--patch | -p] + [--update | -u] [--refresh] [--ignore-errors] [--] + <filepattern>... DESCRIPTION ----------- @@ -50,27 +51,33 @@ OPTIONS and `dir/file2`) can be given to add all files in the directory, recursively. --n, \--dry-run:: +-n:: +--dry-run:: Don't actually add the file(s), just show if they exist. --v, \--verbose:: +-v:: +--verbose:: Be verbose. -f:: +--force:: Allow adding otherwise ignored files. --i, \--interactive:: +-i:: +--interactive:: Add modified contents in the working tree interactively to the index. Optional path arguments may be supplied to limit operation to a subset of the working tree. See ``Interactive mode'' for details. --p, \--patch:: +-p:: +--patch:: Similar to Interactive mode but the initial command loop is bypassed and the 'patch' subcommand is invoked using each of the specified filepatterns before exiting. -u:: +--update:: Update only files that git already knows about, staging modified content for commit and marking deleted files for removal. This is similar @@ -79,11 +86,11 @@ OPTIONS command line. If no paths are specified, all tracked files in the current directory and its subdirectories are updated. -\--refresh:: +--refresh:: Don't add the file(s), but only refresh their stat() information in the index. -\--ignore-errors:: +--ignore-errors:: If some files could not be added because of errors indexing them, do not abort the operation, but continue adding the others. The command shall still exit with non-zero status. diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt index 7f6f5b472a..46544a0769 100644 --- a/Documentation/git-am.txt +++ b/Documentation/git-am.txt @@ -28,14 +28,17 @@ OPTIONS supply this argument, reads from the standard input. If you supply directories, they'll be treated as Maildirs. --s, --signoff:: +-s:: +--signoff:: Add `Signed-off-by:` line to the commit message, using the committer identity of yourself. --k, --keep:: +-k:: +--keep:: Pass `-k` flag to `git-mailinfo` (see linkgit:git-mailinfo[1]). --u, --utf8:: +-u:: +--utf8:: Pass `-u` flag to `git-mailinfo` (see linkgit:git-mailinfo[1]). The proposed commit log message taken from the e-mail is re-coded into UTF-8 encoding (configuration variable @@ -49,13 +52,15 @@ default. You could use `--no-utf8` to override this. Pass `-n` flag to `git-mailinfo` (see linkgit:git-mailinfo[1]). --3, --3way:: +-3:: +--3way:: When the patch does not apply cleanly, fall back on 3-way merge, if the patch records the identity of blobs it is supposed to apply to, and we have those blobs available locally. --b, --binary:: +-b:: +--binary:: Pass `--allow-binary-replacement` flag to `git-apply` (see linkgit:git-apply[1]). @@ -64,19 +69,22 @@ default. You could use `--no-utf8` to override this. program that applies the patch. --C<n>, -p<n>:: +-C<n>:: +-p<n>:: These flags are passed to the `git-apply` (see linkgit:git-apply[1]) program that applies the patch. --i, --interactive:: +-i:: +--interactive:: Run interactively. --skip:: Skip the current patch. This is only meaningful when restarting an aborted patch. --r, --resolved:: +-r:: +--resolved:: After a patch failure (e.g. attempting to apply conflicting patch), the user has applied it by hand and the index file stores the result of the application. diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt index 76277bd178..c8347637da 100644 --- a/Documentation/git-apply.txt +++ b/Documentation/git-apply.txt @@ -73,7 +73,8 @@ OPTIONS When a pure mode change is encountered (which has no index information), the information is read from the current index instead. --R, --reverse:: +-R:: +--reverse:: Apply the patch in reverse. --reject:: @@ -124,7 +125,8 @@ discouraged. the result with this option, which would apply the deletion part but not addition part. ---allow-binary-replacement, --binary:: +--allow-binary-replacement:: +--binary:: Historically we did not allow binary patch applied without an explicit permission from the user, and this flag was the way to do so. Currently we always allow binary @@ -169,7 +171,8 @@ behavior: correctly. This option adds support for applying such patches by working around this bug. --v, --verbose:: +-v:: +--verbose:: Report progress to stderr. By default, only a message about the current patch being applied will be printed. This option will cause additional information to be reported. diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt index a3e36dbb0d..9b5f3ae5ed 100644 --- a/Documentation/git-archive.txt +++ b/Documentation/git-archive.txt @@ -36,10 +36,12 @@ OPTIONS Format of the resulting archive: 'tar' or 'zip'. The default is 'tar'. ---list, -l:: +-l:: +--list:: Show all available formats. ---verbose, -v:: +-v:: +--verbose:: Report progress to stderr. --prefix=<prefix>/:: diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index 37c7acbe9f..3ca0d330ad 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -85,10 +85,9 @@ Oh, and then after you want to reset to the original head, do a $ git bisect reset ------------------------------------------------ -to get back to the original branch, instead of being in one of the -bisection branches ("git bisect start" will do that for you too, -actually: it will reset the bisection state, and before it does that -it checks that you're not using some old bisection branch). +to get back to the original branch, instead of being on the bisection +commit ("git bisect start" will do that for you too, actually: it will +reset the bisection state). Bisect visualize ~~~~~~~~~~~~~~~~ diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt index 7900f33d4b..8f4fb46685 100644 --- a/Documentation/git-blame.txt +++ b/Documentation/git-blame.txt @@ -52,12 +52,14 @@ include::blame-options.txt[] a certain threshold for git-blame to consider those lines of code to have been moved. --f, --show-name:: +-f:: +--show-name:: Show filename in the original commit. By default filename is shown if there is any line that came from a file with different name, due to rename detection. --n, --show-number:: +-n:: +--show-number:: Show line number in the original commit (Default: off). -s:: diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index a70fa76f31..0fd58083eb 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -95,7 +95,8 @@ OPTIONS -a:: List both remote-tracking branches and local branches. --v, --verbose:: +-v:: +--verbose:: Show sha1 and commit subject line for each head. --abbrev=<length>:: diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt index 3f77dbd107..f58013ca60 100644 --- a/Documentation/git-cat-file.txt +++ b/Documentation/git-cat-file.txt @@ -8,6 +8,7 @@ git-cat-file - Provide content or type/size information for repository objects SYNOPSIS -------- +[verse] 'git-cat-file' [-t | -s | -e | -p | <type>] <object> 'git-cat-file' [--batch | --batch-check] < <list-of-objects> diff --git a/Documentation/git-checkout-index.txt b/Documentation/git-checkout-index.txt index a8cad9c21f..676203b2eb 100644 --- a/Documentation/git-checkout-index.txt +++ b/Documentation/git-checkout-index.txt @@ -22,21 +22,26 @@ Will copy all files listed from the index to the working directory OPTIONS ------- --u|--index:: +-u:: +--index:: update stat information for the checked out entries in the index file. --q|--quiet:: +-q:: +--quiet:: be quiet if files exist or are not in the index --f|--force:: +-f:: +--force:: forces overwrite of existing files --a|--all:: +-a:: +--all:: checks out all files in the index. Cannot be used together with explicit filenames. --n|--no-create:: +-n:: +--no-create:: Don't checkout new files, only refresh files already checked out. diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index a5bf9d6f89..3ad9760a4d 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -47,7 +47,8 @@ OPTIONS by linkgit:git-check-ref-format[1]. Some of these checks may restrict the characters allowed in a branch name. --t, --track:: +-t:: +--track:: When creating a new branch, set up configuration so that git-pull will automatically retrieve data from the start point, which must be a branch. Use this if you always pull from the same upstream branch diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index 3762272b88..5ac9cfb0ef 100644 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@ -22,7 +22,8 @@ OPTIONS For a more complete list of ways to spell commits, see "SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1]. --e|--edit:: +-e:: +--edit:: With this option, `git-cherry-pick` will let you edit the commit message prior to committing. @@ -44,14 +45,16 @@ OPTIONS described above, and `-r` was to disable it. Now the default is not to do `-x` so this option is a no-op. --m parent-number|--mainline parent-number:: +-m parent-number:: +--mainline parent-number:: Usually you cannot cherry-pick a merge because you do not know which side of the merge should be considered the mainline. This option specifies the parent number (starting from 1) of the mainline and allows cherry-pick to replay the change relative to the specified parent. --n|--no-commit:: +-n:: +--no-commit:: Usually the command automatically creates a commit with a commit log message stating which commit was cherry-picked. This flag applies the change necessary @@ -64,7 +67,8 @@ OPTIONS This is useful when cherry-picking more than one commits' effect to your working tree in a row. --s|--signoff:: +-s:: +--signoff:: Add Signed-off-by line at the end of the commit message. diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt index deebf3b315..37a82ee4b8 100644 --- a/Documentation/git-clean.txt +++ b/Documentation/git-clean.txt @@ -30,9 +30,11 @@ OPTIONS git-clean will refuse to run unless given -f or -n. -n:: +--dry-run:: Don't actually remove anything, just show what would be done. -q:: +--quiet:: Be quiet, only report errors, but not the files that are successfully removed. diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 02d4baab6d..d0fe192fb3 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -8,9 +8,9 @@ git-commit - Record changes to the repository SYNOPSIS -------- [verse] -'git-commit' [-a | --interactive] [-s] [-v] [-u] - [(-c | -C) <commit> | -F <file> | -m <msg> | --amend] - [--allow-empty] [--no-verify] [-e] [--author <author>] +'git-commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend] + [(-c | -C) <commit>] [-F <file> | -m <msg>] + [--allow-empty] [--no-verify] [-e] [--author=<author>] [--cleanup=<mode>] [--] [[-i | -o ]<file>...] DESCRIPTION @@ -52,39 +52,49 @@ that, you can recover from it with linkgit:git-reset[1]. OPTIONS ------- --a|--all:: +-a:: +--all:: Tell the command to automatically stage files that have been modified and deleted, but new files you have not told git about are not affected. --c or -C <commit>:: - Take existing commit object, and reuse the log message +-C <commit>:: +--reuse-message=<commit>:: + Take an existing commit object, and reuse the log message and the authorship information (including the timestamp) - when creating the commit. With '-C', the editor is not - invoked; with '-c' the user can further edit the commit - message. + when creating the commit. + +-c <commit>:: +--reedit-message=<commit>:: + Like '-C', but with '-c' the editor is invoked, so that + the user can further edit the commit message. -F <file>:: +--file=<file>:: Take the commit message from the given file. Use '-' to read the message from the standard input. ---author <author>:: +--author=<author>:: Override the author name used in the commit. Use `A U Thor <author@example.com>` format. --m <msg>|--message=<msg>:: +-m <msg>:: +--message=<msg>:: Use the given <msg> as the commit message. --t <file>|--template=<file>:: +-t <file>:: +--template=<file>:: Use the contents of the given file as the initial version of the commit message. The editor is invoked and you can make subsequent changes. If a message is specified using the `-m` or `-F` options, this option has no effect. This overrides the `commit.template` configuration variable. --s|--signoff:: +-s:: +--signoff:: Add Signed-off-by line at the end of the commit message. +-n:: --no-verify:: This option bypasses the pre-commit and commit-msg hooks. See also linkgit:githooks[5][hooks]. @@ -105,14 +115,14 @@ OPTIONS 'whitespace' removes just leading/trailing whitespace lines and 'strip' removes both whitespace and commentary. --e|--edit:: +-e:: +--edit:: The message taken from file with `-F`, command line with `-m`, and from file with `-C` are usually used as the commit log message unmodified. This option lets you further edit the message taken from these sources. --amend:: - Used to amend the tip of the current branch. Prepare the tree object you would want to replace the latest commit as usual (this includes the usual -i/-o and explicit paths), and the @@ -133,13 +143,15 @@ It is a rough equivalent for: but can be used to amend a merge commit. -- --i|--include:: +-i:: +--include:: Before making a commit out of staged contents so far, stage the contents of paths given on the command line as well. This is usually not what you want unless you are concluding a conflicted merge. --o|--only:: +-o:: +--only:: Make a commit only from the paths specified on the command line, disregarding any contents that have been staged so far. This is the default mode of operation of @@ -150,20 +162,32 @@ but can be used to amend a merge commit. the last commit without committing changes that have already been staged. --u|--untracked-files:: - Show all untracked files, also those in uninteresting - directories, in the "Untracked files:" section of commit - message template. Without this option only its name and - a trailing slash are displayed for each untracked - directory. +-u[<mode>]:: +--untracked-files[=<mode>]:: + Show untracked files (Default: 'all'). ++ +The mode parameter is optional, and is used to specify +the handling of untracked files. The possible options are: ++ +-- + - 'no' - Show no untracked files + - 'normal' - Shows untracked files and directories + - 'all' - Also shows individual files in untracked directories. +-- ++ +See linkgit:git-config[1] for configuration variable +used to change the default for when the option is not +specified. --v|--verbose:: +-v:: +--verbose:: Show unified diff between the HEAD commit and what would be committed at the bottom of the commit message template. Note that this diff output doesn't have its lines prefixed with '#'. --q|--quiet:: +-q:: +--quiet:: Suppress commit summary message. \--:: diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 84b754f8d0..c90421ee7f 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -101,7 +101,8 @@ rather than from all available files. + See also <<FILES>>. --f config-file, --file config-file:: +-f config-file:: +--file config-file:: Use the given config file instead of the one specified by GIT_CONFIG. --remove-section:: @@ -116,7 +117,8 @@ See also <<FILES>>. --unset-all:: Remove all lines matching the key from config file. --l, --list:: +-l:: +--list:: List all variables set in config file. --bool:: @@ -128,7 +130,8 @@ See also <<FILES>>. in the config file will cause the value to be multiplied by 1024, 1048576, or 1073741824 prior to output. --z, --null:: +-z:: +--null:: For all options that output values and/or keys, always end values with the null character (instead of a newline). Use newline instead as a delimiter between diff --git a/Documentation/git-count-objects.txt b/Documentation/git-count-objects.txt index e1219b3167..1ba85a259a 100644 --- a/Documentation/git-count-objects.txt +++ b/Documentation/git-count-objects.txt @@ -18,6 +18,7 @@ them, to help you decide when it is a good time to repack. OPTIONS ------- -v:: +--verbose:: In addition to the number of loose objects and disk space consumed, it reports the number of in-pack objects, number of packs, and number of objects that can be diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt index b7721131dd..3310ae25ff 100644 --- a/Documentation/git-cvsserver.txt +++ b/Documentation/git-cvsserver.txt @@ -41,10 +41,13 @@ Don't allow recursing into subdirectories Don't check for `gitcvs.enabled` in config. You also have to specify a list of allowed directories (see below) if you want to use this option. ---version, -V:: +-V:: +--version:: Print version information and exit ---help, -h, -H:: +-h:: +-H:: +--help:: Print usage information and exit <directory>:: diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt index 3fb71d683e..344f24ea59 100644 --- a/Documentation/git-daemon.txt +++ b/Documentation/git-daemon.txt @@ -103,7 +103,8 @@ OPTIONS Log to syslog instead of stderr. Note that this option does not imply --verbose, thus by default only error conditions will be logged. ---user-path, --user-path=path:: +--user-path:: +--user-path=path:: Allow ~user notation to be used in requests. When specified with no parameter, requests to git://host/~alice/foo is taken as a request to access @@ -127,7 +128,8 @@ OPTIONS Save the process id in 'file'. Ignored when the daemon is run under `--inetd`. ---user=user, --group=group:: +--user=user:: +--group=group:: Change daemon's uid and gid before entering the service loop. When only `--user` is given without `--group`, the primary group ID for the user is used. The values of @@ -138,14 +140,16 @@ Giving these options is an error when used with `--inetd`; use the facility of inet daemon to achieve the same before spawning `git-daemon` if needed. ---enable=service, --disable=service:: +--enable=service:: +--disable=service:: Enable/disable the service site-wide per default. Note that a service disabled site-wide can still be enabled per repository if it is marked overridable and the repository enables the service with an configuration item. ---allow-override=service, --forbid-override=service:: +--allow-override=service:: +--forbid-override=service:: Allow/forbid overriding the site-wide default with per repository configuration. By default, all the services are overridable. diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt index 3f0b7b2f47..9f6f483186 100644 --- a/Documentation/git-describe.txt +++ b/Documentation/git-describe.txt @@ -70,6 +70,9 @@ OPTIONS Only consider tags matching the given pattern (can be used to avoid leaking private tags made from the repository). +--always:: + Show uniquely abbreviated commit object as fallback. + EXAMPLES -------- diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt index ed9bd75b74..8a64869d27 100644 --- a/Documentation/git-diff-files.txt +++ b/Documentation/git-diff-files.txt @@ -30,7 +30,8 @@ The default is to diff against our branch (-2) and the cleanly resolved paths. The option -0 can be given to omit diff output for unmerged entries and just show "Unmerged". --c,--cc:: +-c:: +--cc:: This compares stage 2 (our branch), stage 3 (their branch) and the working tree file and outputs a combined diff, similar to the way 'diff-tree' shows a merge diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt index 332346cc5d..277a547a02 100644 --- a/Documentation/git-fast-export.txt +++ b/Documentation/git-fast-export.txt @@ -36,6 +36,26 @@ when encountering a signed tag. With 'strip', the tags will be made unsigned, with 'verbatim', they will be silently exported and with 'warn', they will be exported, but you will see a warning. +--export-marks=<file>:: + Dumps the internal marks table to <file> when complete. + Marks are written one per line as `:markid SHA-1`. Only marks + for revisions are dumped; marks for blobs are ignored. + Backends can use this file to validate imports after they + have been completed, or to save the marks table across + incremental runs. As <file> is only opened and truncated + at completion, the same path can also be safely given to + \--import-marks. + +--import-marks=<file>:: + Before processing any input, load the marks specified in + <file>. The input file must exist, must be readable, and + must use the same format as produced by \--export-marks. ++ +Any commits that have already been marked will not be exported again. +If the backend uses a similar \--import-marks file, this allows for +incremental bidirectional exporting of the repository by keeping the +marks the same across runs. + EXAMPLES -------- diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt index 6ee3dccc1f..282fcaf17f 100644 --- a/Documentation/git-fetch-pack.txt +++ b/Documentation/git-fetch-pack.txt @@ -28,30 +28,32 @@ have a common ancestor commit. OPTIONS ------- -\--all:: +--all:: Fetch all remote refs. -\--quiet, \-q:: +-q:: +--quiet:: Pass '-q' flag to 'git-unpack-objects'; this makes the cloning process less verbose. -\--keep, \-k:: +-k:: +--keep:: Do not invoke 'git-unpack-objects' on received data, but create a single packfile out of it instead, and store it in the object database. If provided twice then the pack is locked against repacking. -\--thin:: +--thin:: Spend extra cycles to minimize the number of objects to be sent. Use it on slower connection. -\--include-tag:: +--include-tag:: If the remote side supports it, annotated tags objects will be downloaded on the same connection as the other objects if the object the tag references is downloaded. The caller must otherwise determine the tags this option made available. -\--upload-pack=<git-upload-pack>:: +--upload-pack=<git-upload-pack>:: Use this to specify the path to 'git-upload-pack' on the remote side, if is not found on your $PATH. Installations of sshd ignores the user's environment @@ -63,16 +65,16 @@ OPTIONS shells by having a lean .bashrc file (they set most of the things up in .bash_profile). -\--exec=<git-upload-pack>:: +--exec=<git-upload-pack>:: Same as \--upload-pack=<git-upload-pack>. -\--depth=<n>:: +--depth=<n>:: Limit fetching to ancestor-chains not longer than n. -\--no-progress:: +--no-progress:: Do not show the progress. -\-v:: +-v:: Run verbosely. <host>:: diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index 44060687ab..ea77f1fce5 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -161,7 +161,8 @@ to other tags will be rewritten to point to the underlying commit. does this in the '.git-rewrite/' directory but you can override that choice by this parameter. --f|--force:: +-f:: +--force:: `git filter-branch` refuses to start with an existing temporary directory or when there are already refs starting with 'refs/original/', unless forced. diff --git a/Documentation/git-fmt-merge-msg.txt b/Documentation/git-fmt-merge-msg.txt index 328cc542dc..2a7cfb980b 100644 --- a/Documentation/git-fmt-merge-msg.txt +++ b/Documentation/git-fmt-merge-msg.txt @@ -33,11 +33,13 @@ OPTIONS Do not list one-line descriptions from the actual commits being merged. ---summary,--no-summary:: +--summary:: +--no-summary:: Synonyms to --log and --no-log; these are deprecated and will be removed in the future. ---file <file>, -F <file>:: +-F <file>:: +--file <file>:: Take the list of merged objects from <file> instead of stdin. diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 6325ff9a68..b347bfbb14 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -53,7 +53,10 @@ OPTIONS literally, in the latter case matching completely or from the beginning up to a slash. ---shell, --perl, --python, --tcl:: +--shell:: +--perl:: +--python:: +--tcl:: If given, strings that substitute `%(fieldname)` placeholders are quoted as string literals suitable for the specified host language. This is meant to produce diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index cdee642060..4dafa39a92 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -74,14 +74,17 @@ include::diff-options.txt[] -<n>:: Limits the number of patches to prepare. --o|--output-directory <dir>:: +-o <dir>:: +--output-directory <dir>:: Use <dir> to store the resulting files, instead of the current working directory. --n|--numbered:: +-n:: +--numbered:: Name output in '[PATCH n/m]' format. --N|--no-numbered:: +-N:: +--no-numbered:: Name output in '[PATCH]' format. --start-number <n>:: @@ -92,11 +95,13 @@ include::diff-options.txt[] without the default first line of the commit appended. Mutually exclusive with the --stdout option. --k|--keep-subject:: +-k:: +--keep-subject:: Do not strip/add '[PATCH]' from the first line of the commit log message. --s|--signoff:: +-s:: +--signoff:: Add `Signed-off-by:` line to the commit message, using the committer identity of yourself. diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt index 13b9cf770c..1b646b73f0 100644 --- a/Documentation/git-grep.txt +++ b/Documentation/git-grep.txt @@ -33,25 +33,30 @@ OPTIONS Instead of searching in the working tree files, check the blobs registered in the index file. --a | --text:: +-a:: +--text:: Process binary files as if they were text. --i | --ignore-case:: +-i:: +--ignore-case:: Ignore case differences between the patterns and the files. -I:: Don't match the pattern in binary files. --w | --word-regexp:: +-w:: +--word-regexp:: Match the pattern only at word boundary (either begin at the beginning of a line, or preceded by a non-word character; end at the end of a line or followed by a non-word character). --v | --invert-match:: +-v:: +--invert-match:: Select non-matching lines. --h | -H:: +-h:: +-H:: By default, the command shows the filename for each match. `-h` option is used to suppress this output. `-H` is there for completeness and does not do anything @@ -64,24 +69,33 @@ OPTIONS option forces paths to be output relative to the project top directory. --E | --extended-regexp | -G | --basic-regexp:: +-E:: +--extended-regexp:: +-G:: +--basic-regexp:: Use POSIX extended/basic regexp for patterns. Default is to use basic regexp. --F | --fixed-strings:: +-F:: +--fixed-strings:: Use fixed strings for patterns (don't interpret pattern as a regex). -n:: Prefix the line number to matching lines. --l | --files-with-matches | --name-only | -L | --files-without-match:: +-l:: +--files-with-matches:: +--name-only:: +-L:: +--files-without-match:: Instead of showing every matched line, show only the names of files that contain (or do not contain) matches. For better compatibility with git-diff, --name-only is a synonym for --files-with-matches. --c | --count:: +-c:: +--count:: Instead of showing every matched line, show the number of lines that match. @@ -103,7 +117,10 @@ OPTIONS scripts passing user input to grep. Multiple patterns are combined by 'or'. ---and | --or | --not | ( | ):: +--and:: +--or:: +--not:: +( ... ):: Specify how multiple patterns are combined using Boolean expressions. `--or` is the default operator. `--and` has higher precedence than `--or`. `-e` has to be used for all diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt index 8994c6d434..faecd6bb90 100644 --- a/Documentation/git-help.txt +++ b/Documentation/git-help.txt @@ -28,15 +28,18 @@ former is internally converted into the latter. OPTIONS ------- --a|--all:: +-a:: +--all:: Prints all the available commands on the standard output. This option supersedes any other option. --i|--info:: +-i:: +--info:: Display manual page for the command in the 'info' format. The 'info' program will be used for that purpose. --m|--man:: +-m:: +--man:: Display manual page for the command in the 'man' format. This option may be used to override a value set in the 'help.format' configuration variable. @@ -45,7 +48,8 @@ By default the 'man' program will be used to display the manual page, but the 'man.viewer' configuration variable may be used to choose other display programs (see below). --w|--web:: +-w:: +--web:: Display manual page for the command in the 'web' (HTML) format. A web browser will be used for that purpose. + diff --git a/Documentation/git-http-push.txt b/Documentation/git-http-push.txt index 143291d126..d69b20549b 100644 --- a/Documentation/git-http-push.txt +++ b/Documentation/git-http-push.txt @@ -15,8 +15,8 @@ DESCRIPTION Sends missing objects to remote repository, and updates the remote branch. -*NOTE*: This command is temporarily disabled if your cURL -library is older than 7.16, as the combination has been reported +*NOTE*: This command is temporarily disabled if your libcurl +is older than 7.16, as the combination has been reported not to work and sometimes corrupts repository. OPTIONS @@ -40,7 +40,8 @@ OPTIONS Report the list of objects being walked locally and the list of objects successfully sent to the remote repository. --d, -D:: +-d:: +-D:: Remove <ref> from remote repository. The specified branch cannot be the remote HEAD. If -d is specified the following other conditions must also be met: diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt index c48b615da2..792643c809 100644 --- a/Documentation/git-init.txt +++ b/Documentation/git-init.txt @@ -16,7 +16,8 @@ OPTIONS -- --q, \--quiet:: +-q:: +--quiet:: Only print error and warning messages, all other output will be suppressed. diff --git a/Documentation/git-instaweb.txt b/Documentation/git-instaweb.txt index 11e6a81779..7da5b8d9a9 100644 --- a/Documentation/git-instaweb.txt +++ b/Documentation/git-instaweb.txt @@ -20,24 +20,29 @@ repository. OPTIONS ------- --l|--local:: +-l:: +--local:: Only bind the web server to the local IP (127.0.0.1). --d|--httpd:: +-d:: +--httpd:: The HTTP daemon command-line that will be executed. Command-line options may be specified here, and the configuration file will be added at the end of the command-line. Currently lighttpd, apache2 and webrick are supported. (Default: lighttpd) --m|--module-path:: +-m:: +--module-path:: The module path (only needed if httpd is Apache). (Default: /usr/lib/apache2/modules) --p|--port:: +-p:: +--port:: The port number to bind the httpd to. (Default: 1234) --b|--browser:: +-b:: +--browser:: The web browser that should be used to view the gitweb page. This will be passed to the 'git-web--browse' helper script along with the URL of the gitweb instance. See diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index 1b0b212245..560594e25f 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -30,23 +30,29 @@ shown: OPTIONS ------- --c|--cached:: +-c:: +--cached:: Show cached files in the output (default) --d|--deleted:: +-d:: +--deleted:: Show deleted files in the output --m|--modified:: +-m:: +--modified:: Show modified files in the output --o|--others:: +-o:: +--others:: Show other files in the output --i|--ignored:: +-i:: +--ignored:: Show ignored files in the output. Note that this also reverses any exclude list present. --s|--stage:: +-s:: +--stage:: Show stage files in the output --directory:: @@ -56,10 +62,12 @@ OPTIONS --no-empty-directory:: Do not list empty directories. Has no effect without --directory. --u|--unmerged:: +-u:: +--unmerged:: Show unmerged files in the output (forces --stage) --k|--killed:: +-k:: +--killed:: Show files on the filesystem that need to be removed due to file/directory conflicts for checkout-index to succeed. @@ -67,11 +75,13 @@ OPTIONS -z:: \0 line termination on output. --x|--exclude=<pattern>:: +-x <pattern>:: +--exclude=<pattern>:: Skips files matching pattern. Note that pattern is a shell wildcard pattern. --X|--exclude-from=<file>:: +-X <file>:: +--exclude-from=<file>:: exclude patterns are read from <file>; 1 per line. --exclude-per-directory=<file>:: diff --git a/Documentation/git-ls-remote.txt b/Documentation/git-ls-remote.txt index 8ad7a94f95..f92f3ca186 100644 --- a/Documentation/git-ls-remote.txt +++ b/Documentation/git-ls-remote.txt @@ -20,13 +20,17 @@ commit IDs. OPTIONS ------- --h|--heads, -t|--tags:: +-h:: +--heads:: +-t:: +--tags:: Limit to only refs/heads and refs/tags, respectively. These options are _not_ mutually exclusive; when given both, references stored in refs/heads and refs/tags are displayed. --u <exec>, --upload-pack=<exec>:: +-u <exec>:: +--upload-pack=<exec>:: Specify the full path of linkgit:git-upload-pack[1] on the remote host. This allows listing references from repositories accessed via SSH and where the SSH daemon does not use the PATH configured by the diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt index fb485de2c8..339190600a 100644 --- a/Documentation/git-mv.txt +++ b/Documentation/git-mv.txt @@ -34,7 +34,8 @@ OPTIONS condition. An error happens when a source is neither existing nor controlled by GIT, or when it would overwrite an existing file unless '-f' is given. --n, \--dry-run:: +-n:: +--dry-run:: Do nothing; only show what would happen diff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt index 12784d7372..ffac3f8f56 100644 --- a/Documentation/git-name-rev.txt +++ b/Documentation/git-name-rev.txt @@ -41,6 +41,13 @@ OPTIONS of linkgit:git-describe[1] more closely. This option cannot be combined with --stdin. +--no-undefined:: + Die with error code != 0 when a reference is undefined, + instead of printing `undefined`. + +--always:: + Show uniquely abbreviated commit object as fallback. + EXAMPLE ------- diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt index d5a87effa4..f4d8d68e34 100644 --- a/Documentation/git-pack-objects.txt +++ b/Documentation/git-pack-objects.txt @@ -79,7 +79,8 @@ base-name:: reference was included in the resulting packfile. This can be useful to send new tags to native git clients. ---window=[N], --depth=[N]:: +--window=[N]:: +--depth=[N]:: These two options affect how the objects contained in the pack are stored using delta compression. The objects are first internally sorted by type, size and diff --git a/Documentation/git-pack-refs.txt b/Documentation/git-pack-refs.txt index fa48681374..c0718468d5 100644 --- a/Documentation/git-pack-refs.txt +++ b/Documentation/git-pack-refs.txt @@ -42,7 +42,7 @@ unpacked. OPTIONS ------- -\--all:: +--all:: The command by default packs all tags and refs that are already packed, and leaves other refs @@ -51,7 +51,7 @@ developed and packing their tips does not help performance. This option causes branch tips to be packed as well. Useful for a repository with many branches of historical interests. -\--no-prune:: +--no-prune:: The command usually removes loose refs under `$GIT_DIR/refs` hierarchy after packing them. This option tells it not to. diff --git a/Documentation/git-peek-remote.txt b/Documentation/git-peek-remote.txt index 56be1b6118..ffbf93a799 100644 --- a/Documentation/git-peek-remote.txt +++ b/Documentation/git-peek-remote.txt @@ -16,7 +16,7 @@ This command is deprecated; use `git-ls-remote` instead. OPTIONS ------- -\--upload-pack=<git-upload-pack>:: +--upload-pack=<git-upload-pack>:: Use this to specify the path to 'git-upload-pack' on the remote side, if it is not found on your $PATH. Some installations of sshd ignores the user's environment diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt index 7283d07a0e..ec335d6fab 100644 --- a/Documentation/git-prune.txt +++ b/Documentation/git-prune.txt @@ -37,7 +37,7 @@ OPTIONS \--:: Do not interpret any more arguments as options. -\--expire <time>:: +--expire <time>:: Only expire loose objects older than <time>. <head>...:: diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt index 5c522ced98..d0f1595f7e 100644 --- a/Documentation/git-pull.txt +++ b/Documentation/git-pull.txt @@ -30,7 +30,7 @@ include::merge-options.txt[] :git-pull: 1 -\--rebase:: +--rebase:: Instead of a merge, perform a rebase after fetching. If there is a remote ref for the upstream branch, and this branch was rebased since last fetched, the rebase uses that information @@ -38,13 +38,14 @@ include::merge-options.txt[] for branch `<name>`, set configuration `branch.<name>.rebase` to `true`. + -*NOTE:* This is a potentially _dangerous_ mode of operation. +[NOTE] +This is a potentially _dangerous_ mode of operation. It rewrites history, which does not bode well when you published that history already. Do *not* use this option unless you have read linkgit:git-rebase[1] carefully. -\--no-rebase:: - Override earlier \--rebase. +--no-rebase:: + Override earlier --rebase. include::fetch-options.txt[] diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 9d8c379717..f3d5d883a7 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -61,13 +61,14 @@ already exists on the remote side. This is the default operation mode if no explicit refspec is found (that is neither on the command line nor in any Push line of the corresponding remotes file---see below). -\--all:: +--all:: Instead of naming each ref to push, specifies that all refs under `$GIT_DIR/refs/heads/` be pushed. -\--mirror:: +--mirror:: Instead of naming each ref to push, specifies that all - refs under `$GIT_DIR/refs/heads/` and `$GIT_DIR/refs/tags/` + refs under `$GIT_DIR/refs/` (which includes but is not + limited to `refs/heads/`, `refs/remotes/`, and `refs/tags/`) be mirrored to the remote repository. Newly created local refs will be pushed to the remote end, locally updated refs will be force updated on the remote end, and deleted refs @@ -75,39 +76,42 @@ nor in any Push line of the corresponding remotes file---see below). if the configuration option `remote.<remote>.mirror` is set. -\--dry-run:: +--dry-run:: Do everything except actually send the updates. -\--tags:: +--tags:: All refs under `$GIT_DIR/refs/tags` are pushed, in addition to refspecs explicitly listed on the command line. -\--receive-pack=<git-receive-pack>:: +--receive-pack=<git-receive-pack>:: Path to the 'git-receive-pack' program on the remote end. Sometimes useful when pushing to a remote repository over ssh, and you do not have the program in a directory on the default $PATH. -\--exec=<git-receive-pack>:: +--exec=<git-receive-pack>:: Same as \--receive-pack=<git-receive-pack>. --f, \--force:: +-f:: +--force:: Usually, the command refuses to update a remote ref that is not an ancestor of the local ref used to overwrite it. This flag disables the check. This can cause the remote repository to lose commits; use it with care. -\--repo=<repo>:: +--repo=<repo>:: When no repository is specified the command defaults to "origin"; this overrides it. -\--thin, \--no-thin:: +--thin:: +--no-thin:: These options are passed to `git-send-pack`. Thin transfer spends extra cycles to minimize the number of objects to be sent and meant to be used on slower connection. --v, \--verbose:: +-v:: +--verbose:: Run verbosely. include::urls-remotes.txt[] diff --git a/Documentation/git-quiltimport.txt b/Documentation/git-quiltimport.txt index d0bc182c74..0600379394 100644 --- a/Documentation/git-quiltimport.txt +++ b/Documentation/git-quiltimport.txt @@ -29,6 +29,8 @@ preserved as the 1 line subject in the git description. OPTIONS ------- + +-n:: --dry-run:: Walk through the patches in the series and warn if we cannot find all of the necessary information to commit diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt index cbe68352bf..58fb906ef6 100644 --- a/Documentation/git-read-tree.txt +++ b/Documentation/git-read-tree.txt @@ -50,6 +50,9 @@ OPTIONS trees that are not directly related to the current working tree status into a temporary index file. +-v:: + Show the progress of checking files out. + --trivial:: Restrict three-way merge by `git-read-tree` to happen only if there is no file-level merging required, instead diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index cc48beec75..7166414355 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -213,19 +213,22 @@ OPTIONS --skip:: Restart the rebasing process by skipping the current patch. --m, \--merge:: +-m:: +--merge:: Use merging strategies to rebase. When the recursive (default) merge strategy is used, this allows rebase to be aware of renames on the upstream side. --s <strategy>, \--strategy=<strategy>:: +-s <strategy>:: +--strategy=<strategy>:: Use the given merge strategy; can be supplied more than once to specify them in the order they should be tried. If there is no `-s` option, a built-in list of strategies is used instead (`git-merge-recursive` when merging a single head, `git-merge-octopus` otherwise). This implies --merge. --v, \--verbose:: +-v:: +--verbose:: Display a diffstat of what changed upstream since the last rebase. -C<n>:: @@ -238,12 +241,14 @@ OPTIONS This flag is passed to the `git-apply` program (see linkgit:git-apply[1]) that applies the patch. --i, \--interactive:: +-i:: +--interactive:: Make a list of the commits which are about to be rebased. Let the user edit that list before rebasing. This mode can also be used to split commits (see SPLITTING COMMITS below). --p, \--preserve-merges:: +-p:: +--preserve-merges:: Instead of ignoring merges, try to recreate them. This option only works in interactive mode. diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index e97dc09296..345943a264 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -9,11 +9,11 @@ git-remote - manage set of tracked repositories SYNOPSIS -------- [verse] -'git-remote' +'git-remote' [-v | --verbose] 'git-remote' add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url> 'git-remote' rm <name> -'git-remote' show <name> -'git-remote' prune <name> +'git-remote' show [-n] <name> +'git-remote' prune [-n | --dry-run] <name> 'git-remote' update [group] DESCRIPTION @@ -22,6 +22,14 @@ DESCRIPTION Manage the set of repositories ("remotes") whose branches you track. +OPTIONS +------- + +-v:: +--verbose:: + Be a little more verbose and show remote url after name. + + COMMANDS -------- @@ -72,9 +80,8 @@ These stale branches have already been removed from the remote repository referenced by <name>, but are still locally available in "remotes/<name>". + -With `-n` option, the remote heads are not confirmed first with `git -ls-remote <name>`; cached information is used instead. Use with -caution. +With `--dry-run` option, report what branches will be pruned, but do no +actually prune them. 'update':: diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index 793dccaa9d..04d6f1fbc4 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -73,7 +73,8 @@ OPTIONS this repository (or a direct copy of it) over HTTP or FTP. See gitlink:git-update-server-info[1]. ---window=[N], --depth=[N]:: +--window=[N]:: +--depth=[N]:: These two options affect how the objects contained in the pack are stored using delta compression. The objects are first internally sorted by type, size and optionally names and compared against the diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index a0ef1fd550..9e273bc5a6 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -52,7 +52,8 @@ OPTIONS The parameter given must be usable as a single, valid object name. Otherwise barf and abort. --q, --quiet:: +-q:: +--quiet:: Only meaningful in `--verify` mode. Do not output an error message if the first argument is not a valid object name; instead exit with non-zero status silently. @@ -119,16 +120,19 @@ OPTIONS --is-bare-repository:: When the repository is bare print "true", otherwise "false". ---short, --short=number:: +--short:: +--short=number:: Instead of outputting the full SHA1 values of object names try to abbreviate them to a shorter unique name. When no length is specified 7 is used. The minimum length is 4. ---since=datestring, --after=datestring:: +--since=datestring:: +--after=datestring:: Parses the date string, and outputs corresponding --max-age= parameter for git-rev-list command. ---until=datestring, --before=datestring:: +--until=datestring:: +--before=datestring:: Parses the date string, and outputs corresponding --min-age= parameter for git-rev-list command. diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt index 5e6adfcea3..5b49b81386 100644 --- a/Documentation/git-revert.txt +++ b/Documentation/git-revert.txt @@ -22,12 +22,14 @@ OPTIONS For a more complete list of ways to spell commit names, see "SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1]. --e|--edit:: +-e:: +--edit:: With this option, `git-revert` will let you edit the commit message prior to committing the revert. This is the default if you run the command from a terminal. --m parent-number|--mainline parent-number:: +-m parent-number:: +--mainline parent-number:: Usually you cannot revert a merge because you do not know which side of the merge should be considered the mainline. This option specifies the parent number (starting from 1) of @@ -38,7 +40,8 @@ OPTIONS With this option, `git-revert` will not start the commit message editor. --n|--no-commit:: +-n:: +--no-commit:: Usually the command automatically creates a commit with a commit log message stating which commit was reverted. This flag applies the change necessary to revert the @@ -51,7 +54,8 @@ OPTIONS This is useful when reverting more than one commits' effect to your working tree in a row. --s|--signoff:: +-s:: +--signoff:: Add Signed-off-by line at the end of the commit message. diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt index da3fe59135..d88554bedc 100644 --- a/Documentation/git-rm.txt +++ b/Documentation/git-rm.txt @@ -38,7 +38,8 @@ OPTIONS -f:: Override the up-to-date check. --n, \--dry-run:: +-n:: +--dry-run:: Don't actually remove any file(s). Instead, just show if they exist in the index and would otherwise be removed by the command. @@ -52,15 +53,16 @@ OPTIONS the list of files, (useful when filenames might be mistaken for command-line options). -\--cached:: +--cached:: Use this option to unstage and remove paths only from the index. Working tree files, whether modified or not, will be left alone. -\--ignore-unmatch:: +--ignore-unmatch:: Exit with a zero status even if no files matched. --q, \--quiet:: +-q:: +--quiet:: git-rm normally outputs one line (in the form of an "rm" command) for each file removed. This option suppresses that output. diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index a29583796d..251d661afd 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -40,7 +40,8 @@ The --cc option must be repeated for each user you want on the cc list. Output of this command must be single email address per line. Default is the value of 'sendemail.cccmd' configuration value. ---chain-reply-to, --no-chain-reply-to:: +--chain-reply-to:: +--no-chain-reply-to:: If this is set, each email will be sent as a reply to the previous email sent. If disabled with "--no-chain-reply-to", all emails after the first will be sent as replies to the first email sent. When using @@ -65,7 +66,8 @@ The --cc option must be repeated for each user you want on the cc list. Only necessary if --compose is also set. If --compose is not set, this will be prompted for. ---signed-off-by-cc, --no-signed-off-by-cc:: +--signed-off-by-cc:: +--no-signed-off-by-cc:: If this is set, add emails found in Signed-off-by: or Cc: lines to the cc list. Default is the value of 'sendemail.signedoffcc' configuration value; @@ -141,7 +143,8 @@ user is prompted for a password while the input is masked for privacy. Only necessary if --compose is also set. If --compose is not set, this will be prompted for. ---suppress-from, --no-suppress-from:: +--suppress-from:: +--no-suppress-from:: If this is set, do not add the From: address to the cc: list. Default is the value of 'sendemail.suppressfrom' configuration value; if that is unspecified, default to --no-suppress-from. @@ -157,7 +160,8 @@ user is prompted for a password while the input is masked for privacy. if that is unspecified, default to 'self' if --suppress-from is specified, as well as 'sob' if --no-signed-off-cc is specified. ---thread, --no-thread:: +--thread:: +--no-thread:: If this is set, the In-Reply-To header will be set on each email sent. If disabled with "--no-thread", no emails will have the In-Reply-To header set. diff --git a/Documentation/git-send-pack.txt b/Documentation/git-send-pack.txt index 850c351dcd..ba2fdaec08 100644 --- a/Documentation/git-send-pack.txt +++ b/Documentation/git-send-pack.txt @@ -21,33 +21,33 @@ updates it from the current repository, sending named refs. OPTIONS ------- -\--receive-pack=<git-receive-pack>:: +--receive-pack=<git-receive-pack>:: Path to the 'git-receive-pack' program on the remote end. Sometimes useful when pushing to a remote repository over ssh, and you do not have the program in a directory on the default $PATH. -\--exec=<git-receive-pack>:: +--exec=<git-receive-pack>:: Same as \--receive-pack=<git-receive-pack>. -\--all:: +--all:: Instead of explicitly specifying which refs to update, update all heads that locally exist. -\--dry-run:: +--dry-run:: Do everything except actually send the updates. -\--force:: +--force:: Usually, the command refuses to update a remote ref that is not an ancestor of the local ref used to overwrite it. This flag disables the check. What this means is that the remote repository can lose commits; use it with care. -\--verbose:: +--verbose:: Run verbosely. -\--thin:: +--thin:: Spend extra cycles to minimize the number of objects to be sent. Use it on slower connection. diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt index 5079b568e6..daa64d4d81 100644 --- a/Documentation/git-shortlog.txt +++ b/Documentation/git-shortlog.txt @@ -22,17 +22,21 @@ Additionally, "[PATCH]" will be stripped from the commit description. OPTIONS ------- --h, \--help:: +-h:: +--help:: Print a short usage message and exit. --n, \--numbered:: +-n:: +--numbered:: Sort output according to the number of commits per author instead of author alphabetic order. --s, \--summary:: +-s:: +--summary:: Suppress commit description and provide a commit count summary only. --e, \--email:: +-e:: +--email:: Show the email address of each author. -w[<width>[,<indent1>[,<indent2>]]]:: diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt index 32595ad549..de9e8f885f 100644 --- a/Documentation/git-show-branch.txt +++ b/Documentation/git-show-branch.txt @@ -38,10 +38,12 @@ OPTIONS branches under $GIT_DIR/refs/heads/topic, giving `topic/*` would show all of them. --r|--remotes:: +-r:: +--remotes:: Show the remote-tracking branches. --a|--all:: +-a:: +--all:: Show both remote-tracking branches and local branches. --current:: diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt index a85332c363..6b99529b6b 100644 --- a/Documentation/git-show-ref.txt +++ b/Documentation/git-show-ref.txt @@ -29,22 +29,26 @@ in the `.git` directory. OPTIONS ------- --h, --head:: +-h:: +--head:: Show the HEAD reference. ---tags, --heads:: +--tags:: +--heads:: Limit to only "refs/heads" and "refs/tags", respectively. These options are not mutually exclusive; when given both, references stored in "refs/heads" and "refs/tags" are displayed. --d, --dereference:: +-d:: +--dereference:: Dereference tags into object IDs as well. They will be shown with "^{}" appended. --s, --hash:: +-s:: +--hash:: Only show the SHA1 hash, not the reference name. When also using --dereference the dereferenced tag will still be shown after the SHA1. @@ -55,17 +59,20 @@ OPTIONS Aside from returning an error code of 1, it will also print an error message if '--quiet' was not specified. ---abbrev, --abbrev=len:: +--abbrev:: +--abbrev=len:: Abbreviate the object name. When using `--hash`, you do not have to say `--hash --abbrev`; `--hash=len` would do. --q, --quiet:: +-q:: +--quiet:: Do not print any results to stdout. When combined with '--verify' this can be used to silently check if a reference exists. ---exclude-existing, --exclude-existing=pattern:: +--exclude-existing:: +--exclude-existing=pattern:: Make git-show-ref act as a filter that reads refs from stdin of the form "^(?:<anything>\s)?<refname>(?:\^\{\})?$" and performs the diff --git a/Documentation/git-stripspace.txt b/Documentation/git-stripspace.txt index 4883834a14..8421a39f26 100644 --- a/Documentation/git-stripspace.txt +++ b/Documentation/git-stripspace.txt @@ -16,7 +16,8 @@ Remove multiple empty lines, and empty lines at beginning and end. OPTIONS ------- --s|--strip-comments:: +-s:: +--strip-comments:: In addition to empty lines, also strip lines starting with '#'. <stream>:: diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 139206f014..441ae1483b 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -61,10 +61,12 @@ summary:: OPTIONS ------- --q, --quiet:: +-q:: +--quiet:: Only print error messages. --b, --branch:: +-b:: +--branch:: Branch of repository to add as submodule. --cached:: @@ -72,7 +74,8 @@ OPTIONS commands typically use the commit found in the submodule HEAD, but with this option, the commit stored in the index is used instead. --n, --summary-limit:: +-n:: +--summary-limit:: This option is only valid for the summary command. Limit the summary size (number of commits shown in total). Giving 0 will disable the summary; a negative number means unlimited diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index f4cbd2f212..97bed54fbd 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -448,6 +448,8 @@ svn-remote.<name>.rewriteRoot:: the repository with a public http:// or svn:// URL in the metadata so users of it will see the public URL. +-- + Since the noMetadata, rewriteRoot, useSvnsyncProps and useSvmProps options all affect the metadata generated and used by git-svn; they *must* be set in the configuration file before any history is imported @@ -456,7 +458,6 @@ and these settings should never be changed once they are set. Additionally, only one of these four options can be used per-svn-remote section because they affect the 'git-svn-id:' metadata line. --- BASIC EXAMPLES -------------- diff --git a/Documentation/git-symbolic-ref.txt b/Documentation/git-symbolic-ref.txt index e9b996baa8..3d3a059c5e 100644 --- a/Documentation/git-symbolic-ref.txt +++ b/Documentation/git-symbolic-ref.txt @@ -26,7 +26,8 @@ a regular file whose contents is `ref: refs/heads/master`. OPTIONS ------- --q, --quiet:: +-q:: +--quiet:: Do not issue an error message if the <name> is not a symbolic ref but a detached HEAD; instead exit with non-zero status silently. diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index c703365186..bbb0a6ad57 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -76,7 +76,8 @@ OPTIONS --chmod=(+|-)x:: Set the execute permissions on the updated files. ---assume-unchanged, --no-assume-unchanged:: +--assume-unchanged:: +--no-assume-unchanged:: When these flags are specified, the object name recorded for the paths are not updated. Instead, these options sets and unsets the "assume unchanged" bit for the @@ -88,7 +89,8 @@ OPTIONS filesystem that has very slow lstat(2) system call (e.g. cifs). ---again, -g:: +-g:: +--again:: Runs `git-update-index` itself on the paths whose index entries are different from those from the `HEAD` commit. diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt index 7f7e3d197b..bae2c8b7ec 100644 --- a/Documentation/git-update-ref.txt +++ b/Documentation/git-update-ref.txt @@ -7,7 +7,7 @@ git-update-ref - Update the object name stored in a ref safely SYNOPSIS -------- -'git-update-ref' [-m <reason>] (-d <ref> <oldvalue> | [--no-deref] <ref> <newvalue> [<oldvalue>]) +'git-update-ref' [-m <reason>] (-d <ref> [<oldvalue>] | [--no-deref] <ref> <newvalue> [<oldvalue>]) DESCRIPTION ----------- diff --git a/Documentation/git-update-server-info.txt b/Documentation/git-update-server-info.txt index aa1ee67cbb..d21be41d06 100644 --- a/Documentation/git-update-server-info.txt +++ b/Documentation/git-update-server-info.txt @@ -22,7 +22,8 @@ generates such auxiliary files. OPTIONS ------- --f|--force:: +-f:: +--force:: Update the info files from scratch. diff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt index 521da5b200..bac465e13f 100644 --- a/Documentation/git-upload-pack.txt +++ b/Documentation/git-upload-pack.txt @@ -24,10 +24,10 @@ repository. For push operations, see 'git-send-pack'. OPTIONS ------- -\--strict:: +--strict:: Do not try <directory>/.git/ if <directory> is no git directory. -\--timeout=<n>:: +--timeout=<n>:: Interrupt transfer after <n> seconds of inactivity. <directory>:: diff --git a/Documentation/git-web--browse.txt b/Documentation/git-web--browse.txt index f8d5fb11d1..e80a7c1cc4 100644 --- a/Documentation/git-web--browse.txt +++ b/Documentation/git-web--browse.txt @@ -31,14 +31,17 @@ Custom commands may also be specified. OPTIONS ------- --b BROWSER|--browser=BROWSER:: +-b BROWSER:: +--browser=BROWSER:: Use the specified BROWSER. It must be in the list of supported browsers. --t BROWSER|--tool=BROWSER:: +-t BROWSER:: +--tool=BROWSER:: Same as above. --c CONF.VAR|--config=CONF.VAR:: +-c CONF.VAR:: +--config=CONF.VAR:: CONF.VAR is looked up in the git config files. If it's set, then its value specify the browser that should be used. diff --git a/Documentation/git.txt b/Documentation/git.txt index cf67110a91..7414238fe5 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,6 +43,11 @@ unreleased) version of git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: +* link:v1.5.6/git.html[documentation for release 1.5.6] + +* release notes for + link:RelNotes-1.5.6.txt[1.5.6], + * link:v1.5.5/git.html[documentation for release 1.5.5] * release notes for @@ -138,7 +143,8 @@ help ...'. environment variable. If no path is given 'git' will print the current setting and then exit. --p|--paginate:: +-p:: +--paginate:: Pipe all output into 'less' (or if set, $PAGER). --no-pager:: diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 471754eb12..6e67990f64 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -502,6 +502,12 @@ frotz unspecified Creating an archive ~~~~~~~~~~~~~~~~~~~ +`export-ignore` +^^^^^^^^^^^^^^^ + +Files and directories with the attribute `export-ignore` won't be added to +archive files. + `export-subst` ^^^^^^^^^^^^^^ diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 4f06ae0ed4..262a4f1626 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -17,7 +17,8 @@ Hooks are little scripts you can place in `$GIT_DIR/hooks` directory to trigger action at certain points. When `git-init` is run, a handful example hooks are copied in the `hooks` directory of the new repository, but by default they are -all disabled. To enable a hook, make it executable with `chmod +x`. +all disabled. To enable a hook, rename it by removing its `.sample` +suffix. This document describes the currently defined hooks. diff --git a/Documentation/gitk.txt b/Documentation/gitk.txt index 14bcf91f40..f843f39bf2 100644 --- a/Documentation/gitk.txt +++ b/Documentation/gitk.txt @@ -25,7 +25,8 @@ To control which revisions to shown, the command takes options applicable to the linkgit:git-rev-list[1] command. This manual page describes only the most frequently used options. --n <number>, --max-count=<number>:: +-n <number>:: +--max-count=<number>:: Limits the number of commits to show. diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt index 9563a632eb..d465aab64e 100644 --- a/Documentation/gittutorial.txt +++ b/Documentation/gittutorial.txt @@ -114,7 +114,7 @@ newly modified content to the index. Finally, commit your changes with: $ git commit ------------------------------------------------ -This will again prompt your for a message describing the change, and then +This will again prompt you for a message describing the change, and then record a new version of the project. Alternatively, instead of running `git add` beforehand, you can use diff --git a/Documentation/howto/setup-git-server-over-http.txt b/Documentation/howto/setup-git-server-over-http.txt index b7d09c1ec6..4032748608 100644 --- a/Documentation/howto/setup-git-server-over-http.txt +++ b/Documentation/howto/setup-git-server-over-http.txt @@ -186,7 +186,7 @@ Step 3: setup the client ------------------------ Make sure that you have HTTP support, i.e. your git was built with -curl (version more recent than 7.10). The command 'git http-push' with +libcurl (version more recent than 7.10). The command 'git http-push' with no argument should display a usage message. Then, add the following to your $HOME/.netrc (you can do without, but will be diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt index f37a776489..ffbc6e9861 100644 --- a/Documentation/merge-options.txt +++ b/Documentation/merge-options.txt @@ -2,10 +2,12 @@ Show a diffstat at the end of the merge. The diffstat is also controlled by the configuration option merge.stat. --n, \--no-stat:: +-n:: +--no-stat:: Do not show diffstat at the end of the merge. ---summary, \--no-summary:: +--summary:: +--no-summary:: Synonyms to --stat and --no-stat; these are deprecated and will be removed in the future. @@ -49,7 +51,8 @@ a fast-forward, only update the branch pointer. This is the default behavior of git-merge. --s <strategy>, \--strategy=<strategy>:: +-s <strategy>:: +--strategy=<strategy>:: Use the given merge strategy; can be supplied more than once to specify them in the order they should be tried. If there is no `-s` option, a built-in list of strategies diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index e8bea3e18e..ec37555794 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -124,3 +124,25 @@ The placeholders are: - '%m': left, right or boundary mark - '%n': newline - '%x00': print a byte from a hex code + +* 'tformat:' ++ +The 'tformat:' format works exactly like 'format:', except that it +provides "terminator" semantics instead of "separator" semantics. In +other words, each commit has the message terminator character (usually a +newline) appended, rather than a separator placed between entries. +This means that the final entry of a single-line format will be properly +terminated with a new line, just as the "oneline" format does. +For example: ++ +--------------------- +$ git log -2 --pretty=format:%h 4da45bef \ + | perl -pe '$_ .= " -- NO NEWLINE\n" unless /\n/' +4da45be +7134973 -- NO NEWLINE + +$ git log -2 --pretty=tformat:%h 4da45bef \ + | perl -pe '$_ .= " -- NO NEWLINE\n" unless /\n/' +4da45be +7134973 +--------------------- diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 05d5abec25..37dd1d61ea 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -129,7 +129,8 @@ limiting may be applied. -- --n 'number', --max-count='number':: +-n 'number':: +--max-count='number':: Limit the number of commits output. @@ -137,21 +138,25 @@ limiting may be applied. Skip 'number' commits before starting to show the commit output. ---since='date', --after='date':: +--since='date':: +--after='date':: Show commits more recent than a specific date. ---until='date', --before='date':: +--until='date':: +--before='date':: Show commits older than a specific date. ifdef::git-rev-list[] ---max-age='timestamp', --min-age='timestamp':: +--max-age='timestamp':: +--min-age='timestamp':: Limit the commits output to specified time range. endif::git-rev-list[] ---author='pattern', --committer='pattern':: +--author='pattern':: +--committer='pattern':: Limit the commits output to ones with author/committer header lines that match the specified pattern (regular expression). @@ -161,16 +166,19 @@ endif::git-rev-list[] Limit the commits output to ones with log message that matches the specified pattern (regular expression). --i, --regexp-ignore-case:: +-i:: +--regexp-ignore-case:: Match the regexp limiting patterns without regard to letters case. --E, --extended-regexp:: +-E:: +--extended-regexp:: Consider the limiting patterns to be extended regular expressions instead of the default basic regular expressions. --F, --fixed-strings:: +-F:: +--fixed-strings:: Consider the limiting patterns to be fixed strings (don't interpret pattern as a regular expression). @@ -239,7 +247,8 @@ from the other branch (for example, "3rd on b" may be cherry-picked from branch A). With this option, such pairs of commits are excluded from the output. --g, --walk-reflogs:: +-g:: +--walk-reflogs:: Instead of walking the commit ancestry chain, walk reflog entries from the most recent one to older ones. @@ -268,7 +277,8 @@ See also linkgit:git-reflog[1]. Output uninteresting commits at the boundary, which are usually not shown. ---dense, --sparse:: +--dense:: +--sparse:: When optional paths are given, the default behaviour ('--dense') is to only output commits that changes at least one of them, and also ignore diff --git a/Documentation/technical/api-builtin.txt b/Documentation/technical/api-builtin.txt index 52cdb4c520..7ede1e64e5 100644 --- a/Documentation/technical/api-builtin.txt +++ b/Documentation/technical/api-builtin.txt @@ -4,7 +4,7 @@ builtin API Adding a new built-in --------------------- -There are 4 things to do to add a bulit-in command implementation to +There are 4 things to do to add a built-in command implementation to git: . Define the implementation of the built-in command `foo` with @@ -18,8 +18,8 @@ git: defined in `git.c`. The entry should look like: { "foo", cmd_foo, <options> }, - - where options is the bitwise-or of: ++ +where options is the bitwise-or of: `RUN_SETUP`:: @@ -33,6 +33,12 @@ git: If the standard output is connected to a tty, spawn a pager and feed our output to it. +`NEED_WORK_TREE`:: + + Make sure there is a work tree, i.e. the command cannot act + on bare repositories. + This makes only sense when `RUN_SETUP` is also set. + . Add `builtin-foo.o` to `BUILTIN_OBJS` in `Makefile`. Additionally, if `foo` is a new command, there are 3 more things to do: @@ -41,8 +47,7 @@ Additionally, if `foo` is a new command, there are 3 more things to do: . Write documentation in `Documentation/git-foo.txt`. -. Add an entry for `git-foo` to the list at the end of - `Documentation/cmd-list.perl`. +. Add an entry for `git-foo` to `command-list.txt`. How a built-in is called diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt index b7cda94f54..539863b1f9 100644 --- a/Documentation/technical/api-parse-options.txt +++ b/Documentation/technical/api-parse-options.txt @@ -1,6 +1,206 @@ parse-options API ================= -Talk about <parse-options.h> +The parse-options API is used to parse and massage options in git +and to provide a usage help with consistent look. -(Pierre) +Basics +------ + +The argument vector `argv[]` may usually contain mandatory or optional +'non-option arguments', e.g. a filename or a branch, and 'options'. +Options are optional arguments that start with a dash and +that allow to change the behavior of a command. + +* There are basically three types of options: + 'boolean' options, + options with (mandatory) 'arguments' and + options with 'optional arguments' + (i.e. a boolean option that can be adjusted). + +* There are basically two forms of options: + 'Short options' consist of one dash (`-`) and one alphanumeric + character. + 'Long options' begin with two dashes (`\--`) and some + alphanumeric characters. + +* Options are case-sensitive. + Please define 'lower-case long options' only. + +The parse-options API allows: + +* 'sticked' and 'separate form' of options with arguments. + `-oArg` is sticked, `-o Arg` is separate form. + `\--option=Arg` is sticked, `\--option Arg` is separate form. + +* Long options may be 'abbreviated', as long as the abbreviation + is unambiguous. + +* Short options may be bundled, e.g. `-a -b` can be specified as `-ab`. + +* Boolean long options can be 'negated' (or 'unset') by prepending + `no-`, e.g. `\--no-abbrev` instead of `\--abbrev`. + +* Options and non-option arguments can clearly be separated using the `\--` + option, e.g. `-a -b \--option \-- \--this-is-a-file` indicates that + `\--this-is-a-file` must not be processed as an option. + +Steps to parse options +---------------------- + +. `#include "parse-options.h"` + +. define a NULL-terminated + `static const char * const builtin_foo_usage[]` array + containing alternative usage strings + +. define `builtin_foo_options` array as described below + in section 'Data Structure'. + +. in `cmd_foo(int argc, const char **argv, const char *prefix)` + call + + argc = parse_options(argc, argv, builtin_foo_options, builtin_foo_usage, flags); ++ +`parse_options()` will filter out the processed options of `argv[]` and leave the +non-option arguments in `argv[]`. +`argc` is updated appropriately because of the assignment. ++ +Flags are the bitwise-or of: + +`PARSE_OPT_KEEP_DASHDASH`:: + Keep the `\--` that usually separates options from + non-option arguments. + +`PARSE_OPT_STOP_AT_NON_OPTION`:: + Usually the whole argument vector is massaged and reordered. + Using this flag, processing is stopped at the first non-option + argument. + +Data Structure +-------------- + +The main data structure is an array of the `option` struct, +say `static struct option builtin_add_options[]`. +There are some macros to easily define options: + +`OPT__ABBREV(&int_var)`:: + Add `\--abbrev[=<n>]`. + +`OPT__DRY_RUN(&int_var)`:: + Add `-n, \--dry-run`. + +`OPT__QUIET(&int_var)`:: + Add `-q, \--quiet`. + +`OPT__VERBOSE(&int_var)`:: + Add `-v, \--verbose`. + +`OPT_GROUP(description)`:: + Start an option group. `description` is a short string that + describes the group or an empty string. + Start the description with an upper-case letter. + +`OPT_BOOLEAN(short, long, &int_var, description)`:: + Introduce a boolean option. + `int_var` is incremented on each use. + +`OPT_BIT(short, long, &int_var, description, mask)`:: + Introduce a boolean option. + If used, `int_var` is bitwise-ored with `mask`. + +`OPT_SET_INT(short, long, &int_var, description, integer)`:: + Introduce a boolean option. + If used, set `int_var` to `integer`. + +`OPT_SET_PTR(short, long, &ptr_var, description, ptr)`:: + Introduce a boolean option. + If used, set `ptr_var` to `ptr`. + +`OPT_STRING(short, long, &str_var, arg_str, description)`:: + Introduce an option with string argument. + The string argument is put into `str_var`. + +`OPT_INTEGER(short, long, &int_var, description)`:: + Introduce an option with integer argument. + The integer is put into `int_var`. + +`OPT_DATE(short, long, &int_var, description)`:: + Introduce an option with date argument, see `approxidate()`. + The timestamp is put into `int_var`. + +`OPT_CALLBACK(short, long, &var, arg_str, description, func_ptr)`:: + Introduce an option with argument. + The argument will be fed into the function given by `func_ptr` + and the result will be put into `var`. + See 'Option Callbacks' below for a more elaborate description. + +`OPT_ARGUMENT(long, description)`:: + Introduce a long-option argument that will be kept in `argv[]`. + + +The last element of the array must be `OPT_END()`. + +If not stated otherwise, interpret the arguments as follows: + +* `short` is a character for the short option + (e.g. `\'e\'` for `-e`, use `0` to omit), + +* `long` is a string for the long option + (e.g. `"example"` for `\--example`, use `NULL` to omit), + +* `int_var` is an integer variable, + +* `str_var` is a string variable (`char *`), + +* `arg_str` is the string that is shown as argument + (e.g. `"branch"` will result in `<branch>`). + If set to `NULL`, three dots (`...`) will be displayed. + +* `description` is a short string to describe the effect of the option. + It shall begin with a lower-case letter and a full stop (`.`) shall be + omitted at the end. + +Option Callbacks +---------------- + +The function must be defined in this form: + + int func(const struct option *opt, const char *arg, int unset) + +The callback mechanism is as follows: + +* Inside `funct`, the only interesting member of the structure + given by `opt` is the void pointer `opt->value`. + `\*opt->value` will be the value that is saved into `var`, if you + use `OPT_CALLBACK()`. + For example, do `*(unsigned long *)opt->value = 42;` to get 42 + into an `unsigned long` variable. + +* Return value `0` indicates success and non-zero return + value will invoke `usage_with_options()` and, thus, die. + +* If the user negates the option, `arg` is `NULL` and `unset` is 1. + +Sophisticated option parsing +---------------------------- + +If you need, for example, option callbacks with optional arguments +or without arguments at all, or if you need other special cases, +that are not handled by the macros above, you need to specify the +members of the `option` structure manually. + +This is not covered in this document, but well documented +in `parse-options.h` itself. + +Examples +-------- + +See `test-parse-options.c` and +`builtin-add.c`, +`builtin-clone.c`, +`builtin-commit.c`, +`builtin-fetch.c`, +`builtin-fsck.c`, +`builtin-rm.c` +for real-world examples. diff --git a/Documentation/technical/api-path-list.txt b/Documentation/technical/api-path-list.txt index d077683171..9dbedd0a67 100644 --- a/Documentation/technical/api-path-list.txt +++ b/Documentation/technical/api-path-list.txt @@ -1,9 +1,126 @@ path-list API ============= -Talk about <path-list.h>, things like +The path_list API offers a data structure and functions to handle sorted +and unsorted string lists. -* it is not just paths but strings in general; -* the calling sequence. +The name is a bit misleading, a path_list may store not only paths but +strings in general. -(Dscho) +The caller: + +. Allocates and clears a `struct path_list` variable. + +. Initializes the members. You might want to set the flag `strdup_paths` + if the strings should be strdup()ed. For example, this is necessary + when you add something like git_path("..."), since that function returns + a static buffer that will change with the next call to git_path(). ++ +If you need something advanced, you can manually malloc() the `items` +member (you need this if you add things later) and you should set the +`nr` and `alloc` members in that case, too. + +. Adds new items to the list, using `path_list_append` or `path_list_insert`. + +. Can check if a string is in the list using `path_list_has_path` or + `unsorted_path_list_has_path` and get it from the list using + `path_list_lookup` for sorted lists. + +. Can sort an unsorted list using `sort_path_list`. + +. Finally it should free the list using `path_list_clear`. + +Example: + +---- +struct path_list list; +int i; + +memset(&list, 0, sizeof(struct path_list)); +path_list_append("foo", &list); +path_list_append("bar", &list); +for (i = 0; i < list.nr; i++) + printf("%s\n", list.items[i].path) +---- + +NOTE: It is more efficient to build an unsorted list and sort it +afterwards, instead of building a sorted list (`O(n log n)` instead of +`O(n^2)`). ++ +However, if you use the list to check if a certain string was added +already, you should not do that (using unsorted_path_list_has_path()), +because the complexity would be quadratic again (but with a worse factor). + +Functions +--------- + +* General ones (works with sorted and unsorted lists as well) + +`print_path_list`:: + + Dump a path_list to stdout, useful mainly for debugging purposes. It + can take an optional header argument and it writes out the + string-pointer pairs of the path_list, each one in its own line. + +`path_list_clear`:: + + Free a path_list. The `path` pointer of the items will be freed in case + the `strdup_paths` member of the path_list is set. The second parameter + controls if the `util` pointer of the items should be freed or not. + +* Functions for sorted lists only + +`path_list_has_path`:: + + Determine if the path_list has a given string or not. + +`path_list_insert`:: + + Insert a new element to the path_list. The returned pointer can be handy + if you want to write something to the `util` pointer of the + path_list_item containing the just added string. ++ +Since this function uses xrealloc() (which die()s if it fails) if the +list needs to grow, it is safe not to check the pointer. I.e. you may +write `path_list_insert(...)->util = ...;`. + +`path_list_lookup`:: + + Look up a given string in the path_list, returning the containing + path_list_item. If the string is not found, NULL is returned. + +* Functions for unsorted lists only + +`path_list_append`:: + + Append a new string to the end of the path_list. + +`sort_path_list`:: + + Make an unsorted list sorted. + +`unsorted_path_list_has_path`:: + + It's like `path_list_has_path()` but for unsorted lists. ++ +This function needs to look through all items, as opposed to its +counterpart for sorted lists, which performs a binary search. + +Data structures +--------------- + +* `struct path_list_item` + +Represents an item of the list. The `path` member is a pointer to the +string, and you may use the `util` member for any purpose, if you want. + +* `struct path_list` + +Represents the list itself. + +. The array of items are available via the `items` member. +. The `nr` member contains the number of items stored in the list. +. The `alloc` member is used to avoid reallocating at every insertion. + You should not tamper with it. +. Setting the `strdup_paths` member to 1 will strdup() the strings + before adding them, see above. diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt index c364a22c8f..3e1342acf4 100644 --- a/Documentation/technical/api-run-command.txt +++ b/Documentation/technical/api-run-command.txt @@ -63,7 +63,7 @@ command to run in a sub-process. The caller: -1. allocates and clears (memset(&chld, '0', sizeof(chld));) a +1. allocates and clears (memset(&chld, 0, sizeof(chld));) a struct child_process variable; 2. initializes the members; 3. calls start_command(); @@ -136,7 +136,7 @@ to produce output that the caller reads. The caller: -1. allocates and clears (memset(&asy, '0', sizeof(asy));) a +1. allocates and clears (memset(&asy, 0, sizeof(asy));) a struct async variable; 2. initializes .proc and .data; 3. calls start_async(); diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index bfde507e0e..64a820bf60 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1254,16 +1254,15 @@ these three "file stages" represents a different version of the file: ------------------------------------------------- $ git show :1:file.txt # the file in a common ancestor of both branches -$ git show :2:file.txt # the version from HEAD, but including any - # nonconflicting changes from MERGE_HEAD -$ git show :3:file.txt # the version from MERGE_HEAD, but including any - # nonconflicting changes from HEAD. +$ git show :2:file.txt # the version from HEAD. +$ git show :3:file.txt # the version from MERGE_HEAD. ------------------------------------------------- -Since the stage 2 and stage 3 versions have already been updated with -nonconflicting changes, the only remaining differences between them are -the important ones; thus linkgit:git-diff[1] can use the information in -the index to show only those conflicts. +When you ask linkgit:git-diff[1] to show the conflicts, it runs a +three-way diff between the conflicted merge results in the work tree with +stages 2 and 3 to show only hunks whose contents come from both sides, +mixed (in other words, when a hunk's merge results come only from stage 2, +that part is not conflicting and is not shown. Same for stage 3). The diff above shows the differences between the working-tree version of file.txt and the stage 2 and stage 3 versions. So instead of preceding diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 3cabc92e7a..f221447478 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.5.GIT +DEF_VER=v1.5.6.GIT LF=' ' @@ -67,10 +67,10 @@ Issues of note: that come with git (git includes the one from Mozilla, and has its own PowerPC and ARM optimized ones too - see the Makefile). - - "libcurl" and "curl" executable. git-http-fetch and - git-fetch use them. If you do not use http - transfer, you are probably OK if you do not have - them. + - libcurl library; git-http-fetch and git-fetch use them. You + might also want the "curl" executable for debugging purposes. + If you do not use http transfer, you are probably OK if you + do not have them. - expat library; git-http-push uses it for remote lock management over DAV. Similar to "curl" above, this is optional. @@ -83,9 +83,6 @@ Issues of note: - "perl" and POSIX-compliant shells are needed to use most of the barebone Porcelainish scripts. - - "cpio" is used by git-clone when doing a local (possibly - hardlinked) clone. - - Some platform specific issues are dealt with Makefile rules, but depending on your specific installation, you may not have all the libraries/tools needed, or you may have @@ -13,7 +13,7 @@ all:: # Define NO_OPENSSL environment variable if you do not have OpenSSL. # This also implies MOZILLA_SHA1. # -# Define NO_CURL if you do not have curl installed. git-http-pull and +# 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:// # transports. # @@ -354,6 +354,7 @@ LIB_H += log-tree.h LIB_H += mailmap.h LIB_H += object.h LIB_H += pack.h +LIB_H += pack-refs.h LIB_H += pack-revindex.h LIB_H += parse-options.h LIB_H += patch-ids.h @@ -429,6 +430,7 @@ LIB_OBJS += merge-file.o LIB_OBJS += name-hash.o LIB_OBJS += object.o LIB_OBJS += pack-check.o +LIB_OBJS += pack-refs.o LIB_OBJS += pack-revindex.o LIB_OBJS += pack-write.o LIB_OBJS += pager.o @@ -467,6 +469,7 @@ LIB_OBJS += unpack-trees.o LIB_OBJS += usage.o LIB_OBJS += utf8.o LIB_OBJS += walker.o +LIB_OBJS += wrapper.o LIB_OBJS += write_or_die.o LIB_OBJS += ws.o LIB_OBJS += wt-status.o @@ -564,6 +567,45 @@ endif ifeq ($(uname_S),GNU/kFreeBSD) NO_STRLCPY = YesPlease endif +ifeq ($(uname_S),UnixWare) + CC = cc + NEEDS_SOCKET = YesPlease + NEEDS_NSL = YesPlease + NEEDS_SSL_WITH_CRYPTO = YesPlease + NEEDS_LIBICONV = YesPlease + SHELL_PATH = /usr/local/bin/bash + NO_IPV6 = YesPlease + NO_HSTRERROR = YesPlease + BASIC_CFLAGS += -Kthread + BASIC_CFLAGS += -I/usr/local/include + BASIC_LDFLAGS += -L/usr/local/lib + INSTALL = ginstall + TAR = gtar + NO_STRCASESTR = YesPlease + NO_MEMMEM = YesPlease +endif +ifeq ($(uname_S),SCO_SV) + ifeq ($(uname_R),3.2) + CFLAGS = -O2 + endif + ifeq ($(uname_R),5) + CC = cc + BASIC_CFLAGS += -Kthread + endif + NEEDS_SOCKET = YesPlease + NEEDS_NSL = YesPlease + NEEDS_SSL_WITH_CRYPTO = YesPlease + NEEDS_LIBICONV = YesPlease + SHELL_PATH = /usr/bin/bash + NO_IPV6 = YesPlease + NO_HSTRERROR = YesPlease + BASIC_CFLAGS += -I/usr/local/include + BASIC_LDFLAGS += -L/usr/local/lib + NO_STRCASESTR = YesPlease + NO_MEMMEM = YesPlease + INSTALL = ginstall + TAR = gtar +endif ifeq ($(uname_S),Darwin) NEEDS_SSL_WITH_CRYPTO = YesPlease NEEDS_LIBICONV = YesPlease @@ -1251,6 +1293,9 @@ endif install-doc: $(MAKE) -C Documentation install +install-html: + $(MAKE) -C Documentation install-html + install-info: $(MAKE) -C Documentation install-info @@ -1373,6 +1418,14 @@ check-docs:: documented,gitmodules | \ documented,gitcli | \ documented,git-tools | \ + documented,gitcore-tutorial | \ + documented,gitcvs-migration | \ + documented,gitdiffcore | \ + documented,gitglossary | \ + documented,githooks | \ + documented,gitrepository-layout | \ + documented,gittutorial | \ + documented,gittutorial-2 | \ sentinel,not,matching,is,ok ) continue ;; \ esac; \ case " $(ALL_PROGRAMS) $(BUILT_INS) git gitk " in \ diff --git a/archive-tar.c b/archive-tar.c index d7598f907d..99db58f1cf 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -247,6 +247,8 @@ static int write_tar_entry(const unsigned char *sha1, strbuf_grow(&path, PATH_MAX); strbuf_add(&path, base, baselen); strbuf_addstr(&path, filename); + if (is_archive_path_ignored(path.buf + base_len)) + return 0; if (S_ISDIR(mode) || S_ISGITLINK(mode)) { strbuf_addch(&path, '/'); buffer = NULL; diff --git a/archive-zip.c b/archive-zip.c index 18c0f8710c..5742762ac3 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -176,6 +176,8 @@ static int write_zip_entry(const unsigned char *sha1, crc = crc32(0, NULL, 0); path = construct_path(base, baselen, filename, S_ISDIR(mode), &pathlen); + if (is_archive_path_ignored(path + base_len)) + return 0; if (verbose) fprintf(stderr, "%s\n", path); if (pathlen > 0xffff) { @@ -82,3 +82,16 @@ void *sha1_file_to_archive(const char *path, const unsigned char *sha1, return buffer; } +int is_archive_path_ignored(const char *path) +{ + static struct git_attr *attr_export_ignore; + struct git_attr_check check[1]; + + if (!attr_export_ignore) + attr_export_ignore = git_attr("export-ignore", 13); + + check[0].attr = attr_export_ignore; + if (git_checkattr(path, ARRAY_SIZE(check), check)) + return 0; + return ATTR_TRUE(check[0].value); +} @@ -44,5 +44,6 @@ extern int write_zip_archive(struct archiver_args *); extern void *parse_extra_zip_args(int argc, const char **argv); extern void *sha1_file_to_archive(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, unsigned long *size, const struct commit *commit); +extern int is_archive_path_ignored(const char *path); #endif /* ARCHIVE_H */ @@ -438,11 +438,13 @@ static void bootstrap_attr_stack(void) elem->prev = attr_stack; attr_stack = elem; - elem = read_attr(GITATTRIBUTES_FILE, 1); - elem->origin = strdup(""); - elem->prev = attr_stack; - attr_stack = elem; - debug_push(elem); + if (!is_bare_repository()) { + elem = read_attr(GITATTRIBUTES_FILE, 1); + elem->origin = strdup(""); + elem->prev = attr_stack; + attr_stack = elem; + debug_push(elem); + } elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE), 1); if (!elem) @@ -501,22 +503,24 @@ static void prepare_attr_stack(const char *path, int dirlen) /* * Read from parent directories and push them down */ - while (1) { - char *cp; - - len = strlen(attr_stack->origin); - if (dirlen <= len) - break; - memcpy(pathbuf, path, dirlen); - memcpy(pathbuf + dirlen, "/", 2); - cp = strchr(pathbuf + len + 1, '/'); - strcpy(cp + 1, GITATTRIBUTES_FILE); - elem = read_attr(pathbuf, 0); - *cp = '\0'; - elem->origin = strdup(pathbuf); - elem->prev = attr_stack; - attr_stack = elem; - debug_push(elem); + if (!is_bare_repository()) { + while (1) { + char *cp; + + len = strlen(attr_stack->origin); + if (dirlen <= len) + break; + memcpy(pathbuf, path, dirlen); + memcpy(pathbuf + dirlen, "/", 2); + cp = strchr(pathbuf + len + 1, '/'); + strcpy(cp + 1, GITATTRIBUTES_FILE); + elem = read_attr(pathbuf, 0); + *cp = '\0'; + elem->origin = strdup(pathbuf); + elem->prev = attr_stack; + attr_stack = elem; + debug_push(elem); + } } /* diff --git a/builtin-add.c b/builtin-add.c index 1da22eec91..9930cf53f5 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -200,8 +200,8 @@ static struct option builtin_add_options[] = { OPT_GROUP(""), OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"), OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"), - OPT_BOOLEAN('f', NULL, &ignored_too, "allow adding otherwise ignored files"), - OPT_BOOLEAN('u', NULL, &take_worktree_changes, "update tracked files"), + OPT_BOOLEAN('f', "force", &ignored_too, "allow adding otherwise ignored files"), + OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"), OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"), OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"), OPT_END(), diff --git a/builtin-cat-file.c b/builtin-cat-file.c index f8b3160668..bd343efae7 100644 --- a/builtin-cat-file.c +++ b/builtin-cat-file.c @@ -150,7 +150,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) static int batch_one_object(const char *obj_name, int print_contents) { unsigned char sha1[20]; - enum object_type type; + enum object_type type = 0; unsigned long size; void *contents = contents; @@ -168,8 +168,11 @@ static int batch_one_object(const char *obj_name, int print_contents) else type = sha1_object_info(sha1, &size); - if (type <= 0) - return 1; + if (type <= 0) { + printf("%s missing\n", obj_name); + fflush(stdout); + return 0; + } printf("%s %s %lu\n", sha1_to_hex(sha1), typename(type), size); fflush(stdout); diff --git a/builtin-clone.c b/builtin-clone.c index 7190952071..f13845fb7f 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -18,6 +18,7 @@ #include "transport.h" #include "strbuf.h" #include "dir.h" +#include "pack-refs.h" /* * Overall FIXMEs: @@ -321,8 +322,11 @@ static struct ref *write_remote_refs(const struct ref *refs, get_fetch_map(refs, tag_refspec, &tail, 0); for (r = local_refs; r; r = r->next) - update_ref(reflog, - r->peer_ref->name, r->old_sha1, NULL, 0, DIE_ON_ERR); + add_extra_ref(r->peer_ref->name, r->old_sha1, 0); + + pack_refs(PACK_REFS_ALL); + clear_extra_refs(); + return local_refs; } @@ -400,6 +404,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (!option_bare) { junk_work_tree = work_tree; + if (safe_create_leading_directories_const(work_tree) < 0) + die("could not create leading directories of '%s'", + work_tree); if (mkdir(work_tree, 0755)) die("could not create work tree dir '%s'.", work_tree); set_git_work_tree(work_tree); @@ -410,6 +417,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) setenv(CONFIG_ENVIRONMENT, xstrdup(mkpath("%s/config", git_dir)), 1); + if (safe_create_leading_directories_const(git_dir) < 0) + die("could not create leading directories of '%s'", git_dir); set_git_dir(make_absolute_path(git_dir)); fprintf(stderr, "Initialize %s\n", git_dir); diff --git a/builtin-commit.c b/builtin-commit.c index 90200ed643..e3ad38b3bd 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -49,7 +49,8 @@ static char *logfile, *force_author, *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, untracked_files, no_verify, allow_empty; +static int quiet, verbose, no_verify, allow_empty; +static char *untracked_files_arg; /* * The default commit message cleanup mode will remove the lines * beginning with # (shell comments) and leading and trailing @@ -102,7 +103,7 @@ static struct option builtin_commit_options[] = { OPT_BOOLEAN('o', "only", &only, "commit only specified files"), OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"), OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"), - OPT_BOOLEAN('u', "untracked-files", &untracked_files, "show all untracked files"), + { 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"), OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"), @@ -347,7 +348,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int s.reference = "HEAD^1"; } s.verbose = verbose; - s.untracked = untracked_files; + s.untracked = (show_untracked_files == SHOW_ALL_UNTRACKED_FILES); s.index_file = index_file; s.fp = fp; s.nowarn = nowarn; @@ -502,7 +503,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix) fp = fopen(git_path(commit_editmsg), "w"); if (fp == NULL) - die("could not open %s", git_path(commit_editmsg)); + die("could not open %s: %s", + git_path(commit_editmsg), strerror(errno)); if (cleanup_mode != CLEANUP_NONE) stripspace(&sb, 0); @@ -795,6 +797,17 @@ static int parse_and_validate_options(int argc, const char *argv[], else die("Invalid cleanup mode %s", cleanup_arg); + if (!untracked_files_arg) + ; /* default already initialized */ + else if (!strcmp(untracked_files_arg, "no")) + show_untracked_files = SHOW_NO_UNTRACKED_FILES; + else if (!strcmp(untracked_files_arg, "normal")) + show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; + else if (!strcmp(untracked_files_arg, "all")) + show_untracked_files = SHOW_ALL_UNTRACKED_FILES; + else + die("Invalid untracked files mode '%s'", untracked_files_arg); + if (all && argc > 0) die("Paths with -a does not make sense."); else if (interactive && argc > 0) diff --git a/builtin-fast-export.c b/builtin-fast-export.c index 1dfc01e8f0..45786ef1b7 100755..100644 --- a/builtin-fast-export.c +++ b/builtin-fast-export.c @@ -56,10 +56,24 @@ static int has_unshown_parent(struct commit *commit) } /* Since intptr_t is C99, we do not use it here */ -static void mark_object(struct object *object) +static inline uint32_t *mark_to_ptr(uint32_t mark) { - last_idnum++; - add_decoration(&idnums, object, ((uint32_t *)NULL) + last_idnum); + return ((uint32_t *)NULL) + mark; +} + +static inline uint32_t ptr_to_mark(void * mark) +{ + return (uint32_t *)mark - (uint32_t *)NULL; +} + +static inline void mark_object(struct object *object, uint32_t mark) +{ + add_decoration(&idnums, object, mark_to_ptr(mark)); +} + +static inline void mark_next_object(struct object *object) +{ + mark_object(object, ++last_idnum); } static int get_object_mark(struct object *object) @@ -67,7 +81,7 @@ static int get_object_mark(struct object *object) void *decoration = lookup_decoration(&idnums, object); if (!decoration) return 0; - return (uint32_t *)decoration - (uint32_t *)NULL; + return ptr_to_mark(decoration); } static void show_progress(void) @@ -100,7 +114,7 @@ static void handle_object(const unsigned char *sha1) if (!buf) die ("Could not read blob %s", sha1_to_hex(sha1)); - mark_object(object); + mark_next_object(object); printf("blob\nmark :%d\ndata %lu\n", last_idnum, size); if (size && fwrite(buf, size, 1, stdout) != 1) @@ -185,9 +199,11 @@ static void handle_commit(struct commit *commit, struct rev_info *rev) for (i = 0; i < diff_queued_diff.nr; i++) handle_object(diff_queued_diff.queue[i]->two->sha1); - mark_object(&commit->object); + mark_next_object(&commit->object); if (!is_encoding_utf8(encoding)) reencoded = reencode_string(message, "UTF-8", encoding); + if (!commit->parents) + printf("reset %s\n", (const char*)commit->util); printf("commit %s\nmark :%d\n%.*s\n%.*s\ndata %u\n%s", (const char *)commit->util, last_idnum, (int)(author_end - author), author, @@ -352,18 +368,85 @@ static void handle_tags_and_duplicates(struct path_list *extra_refs) } } +static void export_marks(char *file) +{ + unsigned int i; + uint32_t mark; + struct object_decoration *deco = idnums.hash; + FILE *f; + + f = fopen(file, "w"); + if (!f) + error("Unable to open marks file %s for writing", file); + + for (i = 0; i < idnums.size; ++i) { + deco++; + if (deco && deco->base && deco->base->type == 1) { + mark = ptr_to_mark(deco->decoration); + fprintf(f, ":%u %s\n", mark, sha1_to_hex(deco->base->sha1)); + } + } + + if (ferror(f) || fclose(f)) + error("Unable to write marks file %s.", file); +} + +static void import_marks(char * input_file) +{ + char line[512]; + FILE *f = fopen(input_file, "r"); + if (!f) + die("cannot read %s: %s", input_file, strerror(errno)); + + while (fgets(line, sizeof(line), f)) { + uint32_t mark; + char *line_end, *mark_end; + unsigned char sha1[20]; + struct object *object; + + line_end = strchr(line, '\n'); + if (line[0] != ':' || !line_end) + die("corrupt mark line: %s", line); + *line_end = 0; + + mark = strtoumax(line + 1, &mark_end, 10); + if (!mark || mark_end == line + 1 + || *mark_end != ' ' || get_sha1(mark_end + 1, sha1)) + die("corrupt mark line: %s", line); + + object = parse_object(sha1); + if (!object) + die ("Could not read blob %s", sha1_to_hex(sha1)); + + if (object->flags & SHOWN) + error("Object %s already has a mark", sha1); + + mark_object(object, mark); + if (last_idnum < mark) + last_idnum = mark; + + object->flags |= SHOWN; + } + fclose(f); +} + int cmd_fast_export(int argc, const char **argv, const char *prefix) { struct rev_info revs; struct object_array commits = { 0, 0, NULL }; struct path_list extra_refs = { NULL, 0, 0, 0 }; struct commit *commit; + char *export_filename = NULL, *import_filename = NULL; struct option options[] = { OPT_INTEGER(0, "progress", &progress, "show progress after <n> objects"), OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, "mode", "select handling of signed tags", parse_opt_signed_tag_mode), + OPT_STRING(0, "export-marks", &export_filename, "FILE", + "Dump marks to this file"), + OPT_STRING(0, "import-marks", &import_filename, "FILE", + "Import marks from this file"), OPT_END() }; @@ -376,6 +459,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) if (argc > 1) usage_with_options (fast_export_usage, options); + if (import_filename) + import_marks(import_filename); + get_tags_and_duplicates(&revs.pending, &extra_refs); if (prepare_revision_walk(&revs)) @@ -398,5 +484,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) handle_tags_and_duplicates(&extra_refs); + if (export_filename) + export_marks(export_filename); + return 0; } diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index de1e8d1365..f4dbcf069e 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -820,5 +820,6 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args, } } + reprepare_packed_git(); return ref_cpy; } diff --git a/builtin-fsck.c b/builtin-fsck.c index 78a6e1ff71..b0f9648f86 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -585,7 +585,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) prepare_packed_git(); for (p = packed_git; p; p = p->next) /* verify gives error messages itself */ - verify_pack(p, 0); + verify_pack(p); for (p = packed_git; p; p = p->next) { uint32_t j, num; diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c index 362c290028..43bf6aa45e 100644 --- a/builtin-merge-recursive.c +++ b/builtin-merge-recursive.c @@ -481,15 +481,6 @@ static char *unique_path(const char *path, const char *branch) return newpath; } -static int mkdir_p(const char *path, unsigned long mode) -{ - /* path points to cache entries, so xstrdup before messing with it */ - char *buf = xstrdup(path); - int result = safe_create_leading_directories(buf); - free(buf); - return result; -} - static void flush_buffer(int fd, const char *buf, unsigned long size) { while (size > 0) { @@ -512,7 +503,7 @@ static int make_room_for_path(const char *path) int status; const char *msg = "failed to create path '%s'%s"; - status = mkdir_p(path, 0777); + status = safe_create_leading_directories_const(path); if (status) { if (status == -3) { /* something else exists */ @@ -555,9 +546,19 @@ static void update_file_flags(const unsigned char *sha, die("cannot read object %s '%s'", sha1_to_hex(sha), path); if (type != OBJ_BLOB) die("blob expected for %s '%s'", sha1_to_hex(sha), path); + if (S_ISREG(mode)) { + struct strbuf strbuf; + strbuf_init(&strbuf, 0); + if (convert_to_working_tree(path, buf, size, &strbuf)) { + free(buf); + size = strbuf.len; + buf = strbuf_detach(&strbuf, NULL); + } + } if (make_room_for_path(path) < 0) { update_wd = 0; + free(buf); goto update_index; } if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) { @@ -573,13 +574,14 @@ static void update_file_flags(const unsigned char *sha, close(fd); } else if (S_ISLNK(mode)) { char *lnk = xmemdupz(buf, size); - mkdir_p(path, 0777); + safe_create_leading_directories_const(path); unlink(path); symlink(lnk, path); free(lnk); } else die("do not know what to do with %06o %s '%s'", mode, sha1_to_hex(sha), path); + free(buf); } update_index: if (update_cache) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 447d492dbb..28207d9b3a 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -209,28 +209,6 @@ static int check_pack_inflate(struct packed_git *p, stream.total_in == len) ? 0 : -1; } -static int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, - off_t offset, off_t len, unsigned int nr) -{ - const uint32_t *index_crc; - uint32_t data_crc = crc32(0, Z_NULL, 0); - - do { - unsigned int avail; - void *data = use_pack(p, w_curs, offset, &avail); - if (avail > len) - avail = len; - data_crc = crc32(data_crc, data, avail); - offset += avail; - len -= avail; - } while (len); - - index_crc = p->index_data; - index_crc += 2 + 256 + p->num_objects * (20/4) + nr; - - return data_crc != ntohl(*index_crc); -} - static void copy_pack_data(struct sha1file *f, struct packed_git *p, struct pack_window **w_curs, @@ -1148,8 +1126,6 @@ static void get_object_details(void) sorted_by_offset[i] = objects + i; qsort(sorted_by_offset, nr_objects, sizeof(*sorted_by_offset), pack_offset_sort); - init_pack_revindex(); - for (i = 0; i < nr_objects; i++) check_object(sorted_by_offset[i]); diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index 1aaa76dd1f..ff90aefa1c 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -1,125 +1,6 @@ -#include "builtin.h" #include "cache.h" -#include "refs.h" -#include "object.h" -#include "tag.h" #include "parse-options.h" - -struct ref_to_prune { - struct ref_to_prune *next; - unsigned char sha1[20]; - char name[FLEX_ARRAY]; -}; - -#define PACK_REFS_PRUNE 0x0001 -#define PACK_REFS_ALL 0x0002 - -struct pack_refs_cb_data { - unsigned int flags; - struct ref_to_prune *ref_to_prune; - FILE *refs_file; -}; - -static int do_not_prune(int flags) -{ - /* If it is already packed or if it is a symref, - * do not prune it. - */ - return (flags & (REF_ISSYMREF|REF_ISPACKED)); -} - -static int handle_one_ref(const char *path, const unsigned char *sha1, - int flags, void *cb_data) -{ - struct pack_refs_cb_data *cb = cb_data; - int is_tag_ref; - - /* Do not pack the symbolic refs */ - if ((flags & REF_ISSYMREF)) - return 0; - is_tag_ref = !prefixcmp(path, "refs/tags/"); - - /* ALWAYS pack refs that were already packed or are tags */ - if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref && !(flags & REF_ISPACKED)) - return 0; - - fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path); - if (is_tag_ref) { - struct object *o = parse_object(sha1); - if (o->type == OBJ_TAG) { - o = deref_tag(o, path, 0); - if (o) - fprintf(cb->refs_file, "^%s\n", - sha1_to_hex(o->sha1)); - } - } - - if ((cb->flags & PACK_REFS_PRUNE) && !do_not_prune(flags)) { - int namelen = strlen(path) + 1; - struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen); - hashcpy(n->sha1, sha1); - strcpy(n->name, path); - n->next = cb->ref_to_prune; - cb->ref_to_prune = n; - } - return 0; -} - -/* make sure nobody touched the ref, and unlink */ -static void prune_ref(struct ref_to_prune *r) -{ - struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1); - - if (lock) { - unlink(git_path("%s", r->name)); - unlock_ref(lock); - } -} - -static void prune_refs(struct ref_to_prune *r) -{ - while (r) { - prune_ref(r); - r = r->next; - } -} - -static struct lock_file packed; - -static int pack_refs(unsigned int flags) -{ - int fd; - struct pack_refs_cb_data cbdata; - - memset(&cbdata, 0, sizeof(cbdata)); - cbdata.flags = flags; - - fd = hold_lock_file_for_update(&packed, git_path("packed-refs"), 1); - cbdata.refs_file = fdopen(fd, "w"); - if (!cbdata.refs_file) - die("unable to create ref-pack file structure (%s)", - strerror(errno)); - - /* perhaps other traits later as well */ - fprintf(cbdata.refs_file, "# pack-refs with: peeled \n"); - - for_each_ref(handle_one_ref, &cbdata); - if (ferror(cbdata.refs_file)) - die("failed to write ref-pack file"); - if (fflush(cbdata.refs_file) || fsync(fd) || fclose(cbdata.refs_file)) - die("failed to write ref-pack file (%s)", strerror(errno)); - /* - * Since the lock file was fdopen()'ed and then fclose()'ed above, - * assign -1 to the lock file descriptor so that commit_lock_file() - * won't try to close() it. - */ - packed.fd = -1; - if (commit_lock_file(&packed) < 0) - die("unable to overwrite old ref-pack file (%s)", strerror(errno)); - if (cbdata.flags & PACK_REFS_PRUNE) - prune_refs(cbdata.ref_to_prune); - return 0; -} +#include "pack-refs.h" static char const * const pack_refs_usage[] = { "git-pack-refs [options]", diff --git a/builtin-reflog.c b/builtin-reflog.c index 897d1dcac6..b151e24ff9 100644 --- a/builtin-reflog.c +++ b/builtin-reflog.c @@ -307,6 +307,8 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, unlink(newlog_path); } else if (cmd->updateref && commit_ref(lock)) { status |= error("Couldn't set %s", lock->ref_name); + } else { + adjust_shared_perm(log_file); } } free(newlog_path); diff --git a/builtin-remote.c b/builtin-remote.c index c49f00f58b..145dd8568c 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -419,52 +419,68 @@ static void show_list(const char *title, struct path_list *list) printf("\n"); } -static int show_or_prune(int argc, const char **argv, int prune) +static int get_remote_ref_states(const char *name, + struct ref_states *states, + int query) { - int dry_run = 0, result = 0; + struct transport *transport; + const struct ref *ref; + + states->remote = remote_get(name); + if (!states->remote) + return error("No such remote: %s", name); + + read_branches(); + + if (query) { + transport = transport_get(NULL, states->remote->url_nr > 0 ? + states->remote->url[0] : NULL); + ref = transport_get_remote_refs(transport); + transport_disconnect(transport); + + get_ref_states(ref, states); + } + + return 0; +} + +static int append_ref_to_tracked_list(const char *refname, + const unsigned char *sha1, int flags, void *cb_data) +{ + struct ref_states *states = cb_data; + struct refspec refspec; + + memset(&refspec, 0, sizeof(refspec)); + refspec.dst = (char *)refname; + if (!remote_find_tracking(states->remote, &refspec)) { + path_list_append(skip_prefix(refspec.src, "refs/heads/"), + &states->tracked); + } + + return 0; +} + +static int show(int argc, const char **argv) +{ + int no_query = 0, result = 0; struct option options[] = { OPT_GROUP("show specific options"), - OPT__DRY_RUN(&dry_run), + OPT_BOOLEAN('n', NULL, &no_query, "do not query remotes"), OPT_END() }; struct ref_states states; argc = parse_options(argc, argv, options, builtin_remote_usage, 0); - if (argc < 1) { - if (!prune) - return show_all(); - usage_with_options(builtin_remote_usage, options); - } + if (argc < 1) + return show_all(); memset(&states, 0, sizeof(states)); for (; argc; argc--, argv++) { - struct transport *transport; - const struct ref *ref; struct strbuf buf; - int i, got_states; - - states.remote = remote_get(*argv); - if (!states.remote) - return error("No such remote: %s", *argv); - transport = transport_get(NULL, states.remote->url_nr > 0 ? - states.remote->url[0] : NULL); - ref = transport_get_remote_refs(transport); - transport_disconnect(transport); - - read_branches(); - got_states = get_ref_states(ref, &states); - if (got_states) - result = error("Error getting local info for '%s'", - states.remote->name); + int i; - if (prune) { - for (i = 0; i < states.stale.nr; i++) { - const char *refname = states.stale.items[i].util; - result |= delete_ref(refname, NULL); - } - goto cleanup_states; - } + get_remote_ref_states(*argv, &states, !no_query); printf("* remote %s\n URL: %s\n", *argv, states.remote->url_nr > 0 ? @@ -486,17 +502,19 @@ static int show_or_prune(int argc, const char **argv, int prune) printf("\n"); } - if (got_states) - continue; - strbuf_init(&buf, 0); - strbuf_addf(&buf, " New remote branch%%s (next fetch will " - "store in remotes/%s)", states.remote->name); - show_list(buf.buf, &states.new); - strbuf_release(&buf); - show_list(" Stale tracking branch%s (use 'git remote prune')", - &states.stale); - show_list(" Tracked remote branch%s", - &states.tracked); + if (!no_query) { + strbuf_init(&buf, 0); + strbuf_addf(&buf, " New remote branch%%s (next fetch " + "will store in remotes/%s)", states.remote->name); + show_list(buf.buf, &states.new); + strbuf_release(&buf); + show_list(" Stale tracking branch%s (use 'git remote " + "prune')", &states.stale); + } + + if (no_query) + for_each_ref(append_ref_to_tracked_list, &states); + show_list(" Tracked remote branch%s", &states.tracked); if (states.remote->push_refspec_nr) { printf(" Local branch%s pushed with 'git push'\n ", @@ -511,7 +529,55 @@ static int show_or_prune(int argc, const char **argv, int prune) } printf("\n"); } -cleanup_states: + + /* NEEDSWORK: free remote */ + path_list_clear(&states.new, 0); + path_list_clear(&states.stale, 0); + path_list_clear(&states.tracked, 0); + } + + return result; +} + +static int prune(int argc, const char **argv) +{ + int dry_run = 0, result = 0; + struct option options[] = { + OPT_GROUP("prune specific options"), + OPT__DRY_RUN(&dry_run), + OPT_END() + }; + struct ref_states states; + + argc = parse_options(argc, argv, options, builtin_remote_usage, 0); + + if (argc < 1) + usage_with_options(builtin_remote_usage, options); + + memset(&states, 0, sizeof(states)); + for (; argc; argc--, argv++) { + int i; + + get_remote_ref_states(*argv, &states, 1); + + if (states.stale.nr) { + printf("Pruning %s\n", *argv); + printf("URL: %s\n", + states.remote->url_nr + ? states.remote->url[0] + : "(no URL)"); + } + + for (i = 0; i < states.stale.nr; i++) { + const char *refname = states.stale.items[i].util; + + if (!dry_run) + result |= delete_ref(refname, NULL); + + printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned", + skip_prefix(refname, "refs/remotes/")); + } + /* NEEDSWORK: free remote */ path_list_clear(&states.new, 0); path_list_clear(&states.stale, 0); @@ -632,9 +698,9 @@ int cmd_remote(int argc, const char **argv, const char *prefix) else if (!strcmp(argv[0], "rm")) result = rm(argc, argv); else if (!strcmp(argv[0], "show")) - result = show_or_prune(argc, argv, 0); + result = show(argc, argv); else if (!strcmp(argv[0], "prune")) - result = show_or_prune(argc, argv, 1); + result = prune(argc, argv); else if (!strcmp(argv[0], "update")) result = update(argc, argv); else { diff --git a/builtin-rerere.c b/builtin-rerere.c index 5c811423cc..85222d9bc5 100644 --- a/builtin-rerere.c +++ b/builtin-rerere.c @@ -43,7 +43,7 @@ static void read_rr(struct path_list *rr) ; /* do nothing */ if (i == sizeof(buf)) die("filename too long"); - path_list_insert(buf, rr)->util = xstrdup(name); + path_list_insert(buf, rr)->util = name; } fclose(in); } diff --git a/builtin-update-ref.c b/builtin-update-ref.c index 93c127196d..d90d11d2e3 100644 --- a/builtin-update-ref.c +++ b/builtin-update-ref.c @@ -4,14 +4,14 @@ #include "parse-options.h" static const char * const git_update_ref_usage[] = { - "git-update-ref [options] -d <refname> <oldval>", + "git-update-ref [options] -d <refname> [<oldval>]", "git-update-ref [options] <refname> <newval> [<oldval>]", NULL }; int cmd_update_ref(int argc, const char **argv, const char *prefix) { - const char *refname, *value, *oldval, *msg=NULL; + const char *refname, *oldval, *msg=NULL; unsigned char sha1[20], oldsha1[20]; int delete = 0, no_deref = 0; struct option options[] = { @@ -27,25 +27,29 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) if (msg && !*msg) die("Refusing to perform update with empty message."); - if (argc < 2 || argc > 3) - usage_with_options(git_update_ref_usage, options); - refname = argv[0]; - value = argv[1]; - oldval = argv[2]; - - if (get_sha1(value, sha1)) - die("%s: not a valid SHA1", value); - if (delete) { - if (oldval) + if (argc < 1 || argc > 2) + usage_with_options(git_update_ref_usage, options); + refname = argv[0]; + oldval = argv[1]; + } else { + const char *value; + if (argc < 2 || argc > 3) usage_with_options(git_update_ref_usage, options); - return delete_ref(refname, sha1); + refname = argv[0]; + value = argv[1]; + oldval = argv[2]; + if (get_sha1(value, sha1)) + die("%s: not a valid SHA1", value); } - hashclr(oldsha1); + hashclr(oldsha1); /* all-zero hash in case oldval is the empty string */ if (oldval && *oldval && get_sha1(oldval, oldsha1)) die("%s: not a valid old SHA1", oldval); - return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL, - no_deref ? REF_NODEREF : 0, DIE_ON_ERR); + if (delete) + return delete_ref(refname, oldval ? oldsha1 : NULL); + else + return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL, + no_deref ? REF_NODEREF : 0, DIE_ON_ERR); } diff --git a/builtin-verify-pack.c b/builtin-verify-pack.c index 4c515a0570..222c39e7ed 100644 --- a/builtin-verify-pack.c +++ b/builtin-verify-pack.c @@ -2,6 +2,58 @@ #include "cache.h" #include "pack.h" + +#define MAX_CHAIN 50 + +static void show_pack_info(struct packed_git *p) +{ + uint32_t nr_objects, i, chain_histogram[MAX_CHAIN+1]; + + nr_objects = p->num_objects; + memset(chain_histogram, 0, sizeof(chain_histogram)); + + for (i = 0; i < nr_objects; i++) { + const unsigned char *sha1; + unsigned char base_sha1[20]; + const char *type; + unsigned long size; + unsigned long store_size; + off_t offset; + unsigned int delta_chain_length; + + sha1 = nth_packed_object_sha1(p, i); + if (!sha1) + die("internal error pack-check nth-packed-object"); + offset = nth_packed_object_offset(p, i); + type = packed_object_info_detail(p, offset, &size, &store_size, + &delta_chain_length, + base_sha1); + printf("%s ", sha1_to_hex(sha1)); + if (!delta_chain_length) + printf("%-6s %lu %lu %"PRIuMAX"\n", + type, size, store_size, (uintmax_t)offset); + 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 (delta_chain_length <= MAX_CHAIN) + chain_histogram[delta_chain_length]++; + else + chain_histogram[0]++; + } + } + + for (i = 0; i <= MAX_CHAIN; i++) { + if (!chain_histogram[i]) + continue; + printf("chain length = %d: %d object%s\n", i, + chain_histogram[i], chain_histogram[i] > 1 ? "s" : ""); + } + if (chain_histogram[0]) + printf("chain length > %d: %d object%s\n", MAX_CHAIN, + chain_histogram[0], chain_histogram[0] > 1 ? "s" : ""); +} + static int verify_one_pack(const char *path, int verbose) { char arg[PATH_MAX]; @@ -41,7 +93,16 @@ 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, verbose); + err = verify_pack(pack); + + if (verbose) { + if (err) + printf("%s: bad\n", pack->pack_name); + else { + show_pack_info(pack); + printf("%s: ok\n", pack->pack_name); + } + } return err; } @@ -311,7 +311,6 @@ extern char *git_work_tree_cfg; extern int is_inside_work_tree(void); extern const char *get_git_dir(void); extern char *get_object_directory(void); -extern char *get_refs_directory(void); extern char *get_index_file(void); extern char *get_graft_file(void); extern int set_git_dir(const char *path); @@ -518,6 +517,7 @@ enum sharedrepo { int git_config_perm(const char *var, const char *value); int adjust_shared_perm(const char *path); int safe_create_leading_directories(char *path); +int safe_create_leading_directories_const(const char *path); char *enter_repo(char *path, int strict); static inline int is_absolute_path(const char *path) { @@ -525,6 +525,7 @@ static inline int is_absolute_path(const char *path) } const char *make_absolute_path(const char *path); const char *make_nonrelative_path(const char *path); +const char *make_relative_path(const char *abs, const char *base); /* Read and unpack a sha1 file into memory, write memory to a sha1 file */ extern int sha1_object_info(const unsigned char *, unsigned long *); @@ -536,9 +537,6 @@ extern int force_object_loose(const unsigned char *sha1, time_t mtime); extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type); -extern int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, - size_t bufsize, size_t *bufposn); -extern int write_sha1_to_fd(int fd, const unsigned char *sha1); extern int move_temp_to_file(const char *tmpfile, const char *filename); extern int has_sha1_pack(const unsigned char *sha1, const char **ignore); @@ -644,6 +642,8 @@ extern struct packed_git { const void *index_data; size_t index_size; uint32_t num_objects; + uint32_t num_bad_objects; + unsigned char *bad_object_sha1; int index_version; time_t mtime; int pack_fd; @@ -697,8 +697,6 @@ extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, ch extern int server_supports(const char *feature); extern struct packed_git *parse_pack_index(unsigned char *sha1); -extern struct packed_git *parse_pack_index_file(const unsigned char *sha1, - const char *idx_path); extern void prepare_packed_git(void); extern void reprepare_packed_git(void); @@ -714,6 +712,7 @@ extern void close_pack_windows(struct packed_git *); extern void unuse_pack(struct pack_window **); extern struct packed_git *add_packed_git(const char *, int, int); extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t); +extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t); extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *); extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *); extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep); @@ -740,7 +739,6 @@ extern int git_config_set_multivar(const char *, const char *, const char *, int extern int git_config_rename_section(const char *, const char *); extern const char *git_etc_gitconfig(void); extern int check_repository_format_version(const char *var, const char *value, void *cb); -extern int git_env_bool(const char *, int); extern int git_config_system(void); extern int git_config_global(void); extern int config_error_nonbool(const char *); diff --git a/combine-diff.c b/combine-diff.c index 588c58bc55..9f80a1c5e3 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -84,6 +84,7 @@ struct sline { /* bit 0 up to (N-1) are on if the parent has this line (i.e. * we did not change it). * bit N is used for "interesting" lines, including context. + * bit (N+1) is used for "do not show deletion before this". */ unsigned long flag; unsigned long *p_lno; @@ -308,6 +309,7 @@ static int give_context(struct sline *sline, unsigned long cnt, int num_parent) { unsigned long all_mask = (1UL<<num_parent) - 1; unsigned long mark = (1UL<<num_parent); + unsigned long no_pre_delete = (2UL<<num_parent); unsigned long i; /* Two groups of interesting lines may have a short gap of @@ -329,7 +331,7 @@ static int give_context(struct sline *sline, unsigned long cnt, int num_parent) /* Paint a few lines before the first interesting line. */ while (j < i) - sline[j++].flag |= mark; + sline[j++].flag |= mark | no_pre_delete; again: /* we know up to i is to be included. where does the @@ -502,6 +504,7 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent, int use_color) { unsigned long mark = (1UL<<num_parent); + unsigned long no_pre_delete = (2UL<<num_parent); int i; unsigned long lno = 0; const char *c_frag = diff_get_color(use_color, DIFF_FRAGINFO); @@ -581,7 +584,7 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent, int j; unsigned long p_mask; sl = &sline[lno++]; - ll = sl->lost_head; + ll = (sl->flag & no_pre_delete) ? NULL : sl->lost_head; while (ll) { fputs(c_old, stdout); for (j = 0; j < num_parent; j++) { @@ -243,7 +243,6 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) unsigned char parent[20]; struct commit_list **pptr; struct commit_graft *graft; - unsigned n_refs = 0; if (item->object.parsed) return 0; @@ -255,8 +254,6 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) return error("bad tree pointer in commit %s", sha1_to_hex(item->object.sha1)); item->tree = lookup_tree(parent); - if (item->tree) - n_refs++; bufptr += 46; /* "tree " + "hex sha1" + "\n" */ pptr = &item->parents; @@ -272,10 +269,8 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) if (graft) continue; new_parent = lookup_commit(parent); - if (new_parent) { + if (new_parent) pptr = &commit_list_insert(new_parent, pptr)->next; - n_refs++; - } } if (graft) { int i; @@ -285,7 +280,6 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) if (!new_parent) continue; pptr = &commit_list_insert(new_parent, pptr)->next; - n_refs++; } } item->date = parse_commit_date(bufptr, tail); @@ -549,7 +549,7 @@ const char *git_etc_gitconfig(void) return system_wide; } -int git_env_bool(const char *k, int def) +static int git_env_bool(const char *k, int def) { const char *v = getenv(k); return v ? git_config_bool(k, v) : def; diff --git a/configure.ac b/configure.ac index 82584e9153..7c2856efc9 100644 --- a/configure.ac +++ b/configure.ac @@ -158,7 +158,7 @@ AC_CHECK_LIB([crypto], [SHA1_Init], AC_SUBST(NEEDS_SSL_WITH_CRYPTO) AC_SUBST(NO_OPENSSL) # -# Define NO_CURL if you do not have curl installed. git-http-pull and +# 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:// # transports. AC_CHECK_LIB([curl], [curl_global_init], diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 16984632d9..ebf7cde5c0 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -500,7 +500,10 @@ _git_add () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp "--interactive --refresh" + __gitcomp " + --interactive --refresh --patch --update --dry-run + --ignore-errors + " return esac COMPREPLY=() @@ -758,6 +761,10 @@ _git_log () --pretty= --name-status --name-only --raw --not --all --left-right --cherry-pick + --graph + --stat --numstat --shortstat + --decorate --diff-filter= + --color-words --walk-reflogs " return ;; diff --git a/contrib/thunderbird-patch-inline/README b/contrib/thunderbird-patch-inline/README new file mode 100644 index 0000000000..39f96aa115 --- /dev/null +++ b/contrib/thunderbird-patch-inline/README @@ -0,0 +1,20 @@ +appp.sh is a script that is supposed to be used together with ExternalEditor +for Mozilla Thundebird. It will let you include patches inline in e-mails +in an easy way. + +Usage: +- Generate the patch with git format-patch. +- Start writing a new e-mail in Thunderbird. +- Press the external editor button (or Ctrl-E) to run appp.sh +- Select the previosly generated patch file. +- Finish editing the e-mail. + +Any text that is entered into the message editor before appp.sh is called +will be moved to the section between the --- and the diffstat. + +All S-O-B:s and Cc:s in the patch will be added to the CC list. + +To set it up, just install External Editor and tell it to use appp.sh as the +editor. + +Zenity is a required dependency. diff --git a/contrib/thunderbird-patch-inline/appp.sh b/contrib/thunderbird-patch-inline/appp.sh new file mode 100755 index 0000000000..cc518f3c89 --- /dev/null +++ b/contrib/thunderbird-patch-inline/appp.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# Copyright 2008 Lukas Sandström <luksan@gmail.com> +# +# AppendPatch - A script to be used together with ExternalEditor +# for Mozilla Thunderbird to properly include pathes inline i e-mails. + +# ExternalEditor can be downloaded at http://globs.org/articles.php?lng=en&pg=2 + +CONFFILE=~/.appprc + +SEP="-=-=-=-=-=-=-=-=-=# Don't remove this line #=-=-=-=-=-=-=-=-=-" +if [ -e "$CONFFILE" ] ; then + LAST_DIR=`grep -m 1 "^LAST_DIR=" "${CONFFILE}"|sed -e 's/^LAST_DIR=//'` + cd "${LAST_DIR}" +else + cd > /dev/null +fi + +PATCH=$(zenity --file-selection) + +if [ "$?" != "0" ] ; then + #zenity --error --text "No patchfile given." + exit 1 +fi + +cd - > /dev/null + +SUBJECT=`sed -n -e '/^Subject: /p' "${PATCH}"` +HEADERS=`sed -e '/^'"${SEP}"'$/,$d' $1` +BODY=`sed -e "1,/${SEP}/d" $1` +CMT_MSG=`sed -e '1,/^$/d' -e '/^---$/,$d' "${PATCH}"` +DIFF=`sed -e '1,/^---$/d' "${PATCH}"` + +CCS=`echo -e "$CMT_MSG\n$HEADERS" | sed -n -e 's/^Cc: \(.*\)$/\1,/gp' \ + -e 's/^Signed-off-by: \(.*\)/\1,/gp'` + +echo "$SUBJECT" > $1 +echo "Cc: $CCS" >> $1 +echo "$HEADERS" | sed -e '/^Subject: /d' -e '/^Cc: /d' >> $1 +echo "$SEP" >> $1 + +echo "$CMT_MSG" >> $1 +echo "---" >> $1 +if [ "x${BODY}x" != "xx" ] ; then + echo >> $1 + echo "$BODY" >> $1 + echo >> $1 +fi +echo "$DIFF" >> $1 + +LAST_DIR=`dirname "${PATCH}"` + +grep -v "^LAST_DIR=" "${CONFFILE}" > "${CONFFILE}_" +echo "LAST_DIR=${LAST_DIR}" >> "${CONFFILE}_" +mv "${CONFFILE}_" "${CONFFILE}" @@ -682,10 +682,8 @@ static void date_am(struct tm *tm, int *num) static void date_never(struct tm *tm, int *num) { - tm->tm_mon = tm->tm_wday = tm->tm_yday - = tm->tm_hour = tm->tm_min = tm->tm_sec = 0; - tm->tm_year = 70; - tm->tm_mday = 1; + time_t n = 0; + localtime_r(&n, tm); } static const struct special { @@ -514,9 +514,15 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix) static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len) { + int has_trailing_newline = (len > 0 && line[len-1] == '\n'); + if (has_trailing_newline) + len--; + fputs(set, file); fwrite(line, len, 1, file); fputs(reset, file); + if (has_trailing_newline) + fputc('\n', file); } static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len) @@ -922,7 +928,8 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) total = add + del; } show_name(options->file, prefix, name, len, reset, set); - fprintf(options->file, "%5d ", added + deleted); + fprintf(options->file, "%5d%s", added + deleted, + added + deleted ? " " : ""); show_graph(options->file, '+', add, add_c, reset); show_graph(options->file, '-', del, del_c, reset); fprintf(options->file, "\n"); diff --git a/environment.c b/environment.c index 73feb2d03a..187248bf5d 100644 --- a/environment.c +++ b/environment.c @@ -129,13 +129,6 @@ char *get_object_directory(void) return git_object_dir; } -char *get_refs_directory(void) -{ - if (!git_refs_dir) - setup_git_env(); - return git_refs_dir; -} - char *get_index_file(void) { if (!git_index_file) @@ -421,7 +421,7 @@ do else action=yes fi - FIRSTLINE=$(head -1 "$dotest/final-commit") + FIRSTLINE=$(sed 1q "$dotest/final-commit") if test $action = skip then diff --git a/git-compat-util.h b/git-compat-util.h index 01c4045e89..6f94a8197f 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -39,7 +39,7 @@ /* Approximation of the length of the decimal representation of this type. */ #define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) -#if !defined(__APPLE__) && !defined(__FreeBSD__) +#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && !defined(_M_UNIX) #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */ #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */ #endif @@ -240,161 +240,18 @@ static inline char *gitstrchrnul(const char *s, int c) extern void release_pack_memory(size_t, int); -static inline char* xstrdup(const char *str) -{ - char *ret = strdup(str); - if (!ret) { - release_pack_memory(strlen(str) + 1, -1); - ret = strdup(str); - if (!ret) - die("Out of memory, strdup failed"); - } - return ret; -} - -static inline void *xmalloc(size_t size) -{ - void *ret = malloc(size); - if (!ret && !size) - ret = malloc(1); - if (!ret) { - release_pack_memory(size, -1); - ret = malloc(size); - if (!ret && !size) - ret = malloc(1); - if (!ret) - die("Out of memory, malloc failed"); - } -#ifdef XMALLOC_POISON - memset(ret, 0xA5, size); -#endif - return ret; -} - -/* - * xmemdupz() allocates (len + 1) bytes of memory, duplicates "len" bytes of - * "data" to the allocated memory, zero terminates the allocated memory, - * and returns a pointer to the allocated memory. If the allocation fails, - * the program dies. - */ -static inline void *xmemdupz(const void *data, size_t len) -{ - char *p = xmalloc(len + 1); - memcpy(p, data, len); - p[len] = '\0'; - return p; -} - -static inline char *xstrndup(const char *str, size_t len) -{ - char *p = memchr(str, '\0', len); - return xmemdupz(str, p ? p - str : len); -} - -static inline void *xrealloc(void *ptr, size_t size) -{ - void *ret = realloc(ptr, size); - if (!ret && !size) - ret = realloc(ptr, 1); - if (!ret) { - release_pack_memory(size, -1); - ret = realloc(ptr, size); - if (!ret && !size) - ret = realloc(ptr, 1); - if (!ret) - die("Out of memory, realloc failed"); - } - return ret; -} - -static inline void *xcalloc(size_t nmemb, size_t size) -{ - void *ret = calloc(nmemb, size); - if (!ret && (!nmemb || !size)) - ret = calloc(1, 1); - if (!ret) { - release_pack_memory(nmemb * size, -1); - ret = calloc(nmemb, size); - if (!ret && (!nmemb || !size)) - ret = calloc(1, 1); - if (!ret) - die("Out of memory, calloc failed"); - } - return ret; -} - -static inline void *xmmap(void *start, size_t length, - int prot, int flags, int fd, off_t offset) -{ - void *ret = mmap(start, length, prot, flags, fd, offset); - if (ret == MAP_FAILED) { - if (!length) - return NULL; - release_pack_memory(length, fd); - ret = mmap(start, length, prot, flags, fd, offset); - if (ret == MAP_FAILED) - die("Out of memory? mmap failed: %s", strerror(errno)); - } - return ret; -} - -/* - * xread() is the same a read(), but it automatically restarts read() - * operations with a recoverable error (EAGAIN and EINTR). xread() - * DOES NOT GUARANTEE that "len" bytes is read even if the data is available. - */ -static inline ssize_t xread(int fd, void *buf, size_t len) -{ - ssize_t nr; - while (1) { - nr = read(fd, buf, len); - if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) - continue; - return nr; - } -} - -/* - * xwrite() is the same a write(), but it automatically restarts write() - * operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT - * GUARANTEE that "len" bytes is written even if the operation is successful. - */ -static inline ssize_t xwrite(int fd, const void *buf, size_t len) -{ - ssize_t nr; - while (1) { - nr = write(fd, buf, len); - if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) - continue; - return nr; - } -} - -static inline int xdup(int fd) -{ - int ret = dup(fd); - if (ret < 0) - die("dup failed: %s", strerror(errno)); - return ret; -} - -static inline FILE *xfdopen(int fd, const char *mode) -{ - FILE *stream = fdopen(fd, mode); - if (stream == NULL) - die("Out of memory? fdopen failed: %s", strerror(errno)); - return stream; -} - -static inline int xmkstemp(char *template) -{ - int fd; - - fd = mkstemp(template); - if (fd < 0) - die("Unable to create temporary file: %s", strerror(errno)); - return fd; -} +extern char *xstrdup(const char *str); +extern void *xmalloc(size_t size); +extern void *xmemdupz(const void *data, size_t len); +extern char *xstrndup(const char *str, size_t len); +extern void *xrealloc(void *ptr, size_t size); +extern void *xcalloc(size_t nmemb, size_t size); +extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); +extern ssize_t xread(int fd, void *buf, size_t len); +extern ssize_t xwrite(int fd, const void *buf, size_t len); +extern int xdup(int fd); +extern FILE *xfdopen(int fd, const char *mode); +extern int xmkstemp(char *template); static inline size_t xsize_t(off_t len) { diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 5a0255052c..cacbfc0259 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -227,6 +227,7 @@ sub conn { $proxyport = $1; } } + $repo ||= '/'; # if username is not explicit in CVSROOT, then use current user, as cvs would $user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || "anonymous") unless $user; diff --git a/git-instaweb.sh b/git-instaweb.sh index 6f91c8f845..af0fde538c 100755 --- a/git-instaweb.sh +++ b/git-instaweb.sh @@ -22,10 +22,10 @@ restart restart the web server . git-sh-setup fqgitdir="$GIT_DIR" -local="`git config --bool --get instaweb.local`" -httpd="`git config --get instaweb.httpd`" -port=`git config --get instaweb.port` -module_path="`git config --get instaweb.modulepath`" +local="$(git config --bool --get instaweb.local)" +httpd="$(git config --get instaweb.httpd)" +port=$(git config --get instaweb.port) +module_path="$(git config --get instaweb.modulepath)" conf="$GIT_DIR/gitweb/httpd.conf" @@ -37,11 +37,21 @@ test -z "$httpd" && httpd='lighttpd -f' # any untaken local port will do... test -z "$port" && port=1234 -start_httpd () { - httpd_only="`echo $httpd | cut -f1 -d' '`" +resolve_full_httpd () { + case "$httpd" in + *apache2*|*lighttpd*) + # ensure that the apache2/lighttpd command ends with "-f" + if ! echo "$httpd" | grep -- '-f *$' >/dev/null 2>&1 + then + httpd="$httpd -f" + fi + ;; + esac + + httpd_only="$(echo $httpd | cut -f1 -d' ')" if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null;; esac then - $httpd "$fqgitdir/gitweb/httpd.conf" + full_httpd=$httpd else # many httpds are installed in /usr/sbin or /usr/local/sbin # these days and those are not in most users $PATHs @@ -51,16 +61,23 @@ start_httpd () { do if test -x "$i/$httpd_only" then - # don't quote $httpd, there can be - # arguments to it (-f) - $i/$httpd "$fqgitdir/gitweb/httpd.conf" + full_httpd=$i/$httpd return fi done - echo "$httpd_only not found. Install $httpd_only or use" \ - "--httpd to specify another http daemon." + + echo >&2 "$httpd_only not found. Install $httpd_only or use" \ + "--httpd to specify another httpd daemon." exit 1 fi +} + +start_httpd () { + # here $httpd should have a meaningful value + 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 @@ -68,7 +85,7 @@ start_httpd () { } stop_httpd () { - test -f "$fqgitdir/pid" && kill `cat "$fqgitdir/pid"` + test -f "$fqgitdir/pid" && kill $(cat "$fqgitdir/pid") } while test $# != 0 @@ -116,7 +133,7 @@ do done mkdir -p "$GIT_DIR/gitweb/tmp" -GIT_EXEC_PATH="`git --exec-path`" +GIT_EXEC_PATH="$(git --exec-path)" GIT_DIR="$fqgitdir" export GIT_EXEC_PATH GIT_DIR @@ -215,7 +232,8 @@ PerlPassEnv GIT_EXEC_DIR EOF else # plain-old CGI - list_mods=`echo "$httpd" | sed "s/-f$/-l/"` + resolve_full_httpd + list_mods=$(echo "$full_httpd" | sed "s/-f$/-l/") $list_mods | grep 'mod_cgi\.c' >/dev/null 2>&1 || \ echo "LoadModule cgi_module $module_path/mod_cgi.so" >> "$conf" cat >> "$conf" <<EOF diff --git a/git-merge.sh b/git-merge.sh index 5fc5f5201f..8026ccff4a 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -13,7 +13,7 @@ n don't show a diffstat at the end of the merge summary (synonym to --stat) log add list of one-line log to merge commit message squash create a single commit instead of doing a merge -commit perform a commit if the merge sucesses (default) +commit perform a commit if the merge succeeds (default) ff allow fast forward (default) s,strategy= merge strategy to use m,message= message to be used for the merge commit (if any) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 0ca986f721..a64d9d57ab 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -530,9 +530,9 @@ do # Rebase $SHORTUPSTREAM..$SHORTHEAD onto $SHORTONTO # # Commands: -# pick = use commit -# edit = use commit, but stop for amending -# squash = use commit, but meld into previous commit +# p, pick = use commit +# e, edit = use commit, but stop for amending +# s, squash = use commit, but meld into previous commit # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. diff --git a/git-send-email.perl b/git-send-email.perl index a598fdc890..0b04ba32f0 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -209,6 +209,7 @@ my %config_settings = ( "bcc" => \@bcclist, "aliasesfile" => \@alias_files, "suppresscc" => \@suppress_cc, + "envelopesender" => \$envelope_sender, ); # Handle Uncouth Termination @@ -441,7 +442,7 @@ if (!@to) { } my $to = $_; - push @to, split /,/, $to; + push @to, split /,\s*/, $to; $prompting++; } diff --git a/git-submodule.sh b/git-submodule.sh index 100737210d..3eb78cc724 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -45,8 +45,8 @@ resolve_relative_url () branch="$(git symbolic-ref HEAD 2>/dev/null)" remote="$(git config branch.${branch#refs/heads/}.remote)" remote="${remote:-origin}" - remoteurl="$(git config remote.$remote.url)" || - die "remote ($remote) does not have a url in .git/config" + remoteurl=$(git config "remote.$remote.url") || + die "remote ($remote) does not have a url defined in .git/config" url="$1" while test -n "$url" do @@ -73,7 +73,7 @@ resolve_relative_url () module_name() { # Do we have "submodule.<something>.path = $1" defined in .gitmodules file? - re=$(printf '%s' "$1" | sed -e 's/[].[^$\\*]/\\&/g') + re=$(printf '%s\n' "$1" | sed -e 's/[].[^$\\*]/\\&/g') name=$( git config -f .gitmodules --get-regexp '^submodule\..*\.path$' | sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' ) test -z "$name" && @@ -178,7 +178,8 @@ cmd_add() case "$repo" in ./*|../*) # dereference source url relative to parent's url - realrepo="$(resolve_relative_url $repo)" ;; + realrepo=$(resolve_relative_url "$repo") || exit + ;; *) # Turn the source into an absolute path if # it is local @@ -246,7 +247,7 @@ cmd_init() # Possibly a url relative to parent case "$url" in ./*|../*) - url="$(resolve_relative_url "$url")" + url=$(resolve_relative_url "$url") || exit ;; esac diff --git a/git-svn.perl b/git-svn.perl index 47b0c37d17..4c9c59bc3f 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1023,6 +1023,7 @@ sub get_commit_entry { my $in_msg = 0; my $author; my $saw_from = 0; + my $msgbuf = ""; while (<$msg_fh>) { if (!$in_msg) { $in_msg = 1 if (/^\s*$/); @@ -1035,14 +1036,15 @@ sub get_commit_entry { if (/^From:/ || /^Signed-off-by:/) { $saw_from = 1; } - print $log_fh $_ or croak $!; + $msgbuf .= $_; } } + $msgbuf =~ s/\s+$//s; if ($Git::SVN::_add_author_from && defined($author) && !$saw_from) { - print $log_fh "\nFrom: $author\n" - or croak $!; + $msgbuf .= "\n\nFrom: $author"; } + print $log_fh $msgbuf or croak $!; command_close_pipe($msg_fh, $ctx); } close $log_fh or croak $!; @@ -2575,8 +2577,8 @@ sub rebuild { my ($log, $ctx) = command_output_pipe(qw/rev-list --pretty=raw --no-color --reverse/, $self->refname, '--'); - my $full_url = $self->full_url; - remove_username($full_url); + my $metadata_url = $self->metadata_url; + remove_username($metadata_url); my $svn_uuid = $self->ra_uuid; my $c; while (<$log>) { @@ -2594,7 +2596,7 @@ sub rebuild { # if we merged or otherwise started elsewhere, this is # how we break out of it if (($uuid ne $svn_uuid) || - ($full_url && $url && ($url ne $full_url))) { + ($metadata_url && $url && ($url ne $metadata_url))) { next; } @@ -286,7 +286,7 @@ static void handle_internal_command(int argc, const char **argv) { "checkout-index", cmd_checkout_index, RUN_SETUP | NEED_WORK_TREE}, { "check-ref-format", cmd_check_ref_format }, - { "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE }, + { "check-attr", cmd_check_attr, RUN_SETUP }, { "cherry", cmd_cherry, RUN_SETUP }, { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE }, { "clone", cmd_clone }, diff --git a/git.spec.in b/git.spec.in index 97a26be29a..3d7f3ef4af 100644 --- a/git.spec.in +++ b/git.spec.in @@ -12,7 +12,7 @@ BuildRequires: zlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel, gettex BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Requires: perl-Git = %{version}-%{release} -Requires: zlib >= 1.2, rsync, curl, less, openssh-clients, expat +Requires: zlib >= 1.2, rsync, less, openssh-clients, expat Provides: git-core = %{version}-%{release} Obsoletes: git-core <= 1.5.4.2 Obsoletes: git-p4 @@ -187,6 +187,9 @@ rm -rf $RPM_BUILD_ROOT # No files for you! %changelog +* Sun Jun 15 2008 Junio C Hamano <gitster@pobox.com> +- Remove curl from Requires list. + * Fri Feb 15 2008 Kristian Høgsberg <krh@redhat.com> - Rename git-core to just git and rename meta package from git to git-all. diff --git a/gitk-git/gitk b/gitk-git/gitk index 22bcd18a46..fddcb45817 100644 --- a/gitk-git/gitk +++ b/gitk-git/gitk @@ -296,7 +296,7 @@ proc start_rev_list {view} { global startmsecs commitidx viewcomplete curview global commfd leftover tclencoding global viewargs viewargscmd viewfiles vfilelimit - global showlocalchanges commitinterest mainheadid + global showlocalchanges commitinterest global viewactive loginstance viewinstances vmergeonly global pending_select mainheadid global vcanopt vflags vrevs vorigargs @@ -358,7 +358,7 @@ proc start_rev_list {view} { set viewinstances($view) [list $i] set commfd($i) $fd set leftover($i) {} - if {$showlocalchanges} { + if {$showlocalchanges && $mainheadid ne {}} { lappend commitinterest($mainheadid) {dodiffindex} } fconfigure $fd -blocking 0 -translation lf -eofchar {} @@ -406,7 +406,7 @@ proc getcommits {} { proc updatecommits {} { global curview vcanopt vorigargs vfilelimit viewinstances - global viewactive viewcomplete loginstance tclencoding mainheadid + global viewactive viewcomplete loginstance tclencoding global startmsecs commfd showneartags showlocalchanges leftover global mainheadid pending_select global isworktree @@ -1467,7 +1467,6 @@ proc chewcommits {} { if {$viewcomplete($curview)} { global commitidx varctok global numcommits startmsecs - global mainheadid nullid if {[info exists pending_select]} { set row [first_real_row] @@ -1604,12 +1603,10 @@ proc readrefs {} { set mainhead {} set mainheadid {} catch { + set mainheadid [exec git rev-parse HEAD] set thehead [exec git symbolic-ref HEAD] if {[string match "refs/heads/*" $thehead]} { set mainhead [string range $thehead 11 end] - if {[info exists headids($mainhead)]} { - set mainheadid $headids($mainhead) - } } } } @@ -4022,6 +4019,7 @@ proc layoutmore {} { proc doshowlocalchanges {} { global curview mainheadid + if {$mainheadid eq {}} return if {[commitinview $mainheadid $curview]} { dodiffindex } else { @@ -4841,7 +4839,8 @@ proc drawcmittext {id row col} { global cmitlisted commitinfo rowidlist parentlist global rowtextx idpos idtags idheads idotherrefs global linehtag linentag linedtag selectedline - global canvxmax boldrows boldnamerows fgcolor nullid nullid2 + global canvxmax boldrows boldnamerows fgcolor + global mainheadid nullid nullid2 circleitem circlecolors # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right set listed $cmitlisted($curview,$id) @@ -4849,8 +4848,10 @@ proc drawcmittext {id row col} { set ofill red } elseif {$id eq $nullid2} { set ofill green + } elseif {$id eq $mainheadid} { + set ofill yellow } else { - set ofill [expr {$listed != 0 ? $listed == 2 ? "gray" : "blue" : "white"}] + set ofill [lindex $circlecolors $listed] } set x [xc $row $col] set y [yc $row] @@ -4874,6 +4875,7 @@ proc drawcmittext {id row col} { [expr {$x - $orad}] [expr {$y + $orad - 1}] \ -fill $ofill -outline $fgcolor -width 1 -tags circle] } + set circleitem($row) $t $canv raise $t $canv bind $t <1> {selcanvline {} %x %y} set rmx [llength [lindex $rowidlist $row]] @@ -7399,12 +7401,18 @@ proc domktag {} { } proc redrawtags {id} { - global canv linehtag idpos currentid curview - global canvxmax iddrawn + global canv linehtag idpos currentid curview cmitlisted + global canvxmax iddrawn circleitem mainheadid circlecolors if {![commitinview $id $curview]} return if {![info exists iddrawn($id)]} return set row [rowofcommit $id] + if {$id eq $mainheadid} { + set ofill yellow + } else { + set ofill [lindex $circlecolors $cmitlisted($curview,$id)] + } + $canv itemconf $circleitem($row) -fill $ofill $canv delete tag.$id set xt [eval drawtags $id $idpos($id)] $canv coords $linehtag($row) $xt [lindex $idpos($id) 2] @@ -7574,8 +7582,8 @@ proc cherrypick {} { if {$mainhead ne {}} { movehead $newhead $mainhead movedhead $newhead $mainhead - set mainheadid $newhead } + set mainheadid $newhead redrawtags $oldhead redrawtags $newhead selbyid $newhead @@ -7675,7 +7683,7 @@ proc headmenu {x y id head} { } proc cobranch {} { - global headmenuid headmenuhead mainhead headids + global headmenuid headmenuhead headids global showlocalchanges mainheadid # check the tree is clean first?? @@ -7711,12 +7719,10 @@ proc readcheckoutstat {fd newhead newheadid} { if {[catch {close $fd} err]} { error_popup $err } - set oldmainhead $mainhead + set oldmainid $mainheadid set mainhead $newhead set mainheadid $newheadid - if {[info exists headids($oldmainhead)]} { - redrawtags $headids($oldmainhead) - } + redrawtags $oldmainid redrawtags $newheadid selbyid $newheadid if {$showlocalchanges} { @@ -9016,12 +9022,14 @@ proc rereadrefs {} { [array names idheads] [array names idotherrefs]]] foreach id $refids { set v [listrefs $id] - if {![info exists ref($id)] || $ref($id) != $v || - ($id eq $oldmainhead && $id ne $mainheadid) || - ($id eq $mainheadid && $id ne $oldmainhead)} { + if {![info exists ref($id)] || $ref($id) != $v} { redrawtags $id } } + if {$oldmainhead ne $mainheadid} { + redrawtags $oldmainhead + redrawtags $mainheadid + } run refill_reflist } @@ -9761,6 +9769,8 @@ set diffcontext 3 set ignorespace 0 set selectbgcolor gray85 +set circlecolors {white blue gray blue blue} + ## For msgcat loading, first locate the installation location. if { [info exists ::env(GITK_MSGSDIR)] } { ## Msgsdir was manually set in the environment. diff --git a/gitweb/README b/gitweb/README index 8f7ea367ba..356ab7b327 100644 --- a/gitweb/README +++ b/gitweb/README @@ -255,12 +255,15 @@ Webserver configuration If you want to have one URL for both gitweb and your http:// repositories, you can configure apache like this: -<VirtualHost www:80> - ServerName git.domain.org +<VirtualHost *:80> + ServerName git.example.org DocumentRoot /pub/git - RewriteEngine on - RewriteRule ^/(.*\.git/(?!/?(info|objects|refs)).*)?$ /cgi-bin/gitweb.cgi%{REQUEST_URI} [L,PT] SetEnv GITWEB_CONFIG /etc/gitweb.conf + RewriteEngine on + # make the front page an internal rewrite to the gitweb script + RewriteRule ^/$ /cgi-bin/gitweb.cgi + # make access for "dumb clients" work + RewriteRule ^/(.*\.git/(?!/?(HEAD|info|objects|refs)).*)?$ /cgi-bin/gitweb.cgi%{REQUEST_URI} [L,PT] </VirtualHost> The above configuration expects your public repositories to live under @@ -276,6 +279,13 @@ override the defaults given at the head of the gitweb.perl (or gitweb.cgi). Look at the comments in that file for information on which variables and what they mean. +If you use the rewrite rules from the example you'll likely also need +something like the following in your gitweb.conf (or gitweb_config.perl) file: + + @stylesheets = ("/some/absolute/path/gitweb.css"); + $my_uri = "/"; + $home_link = "/"; + Originally written by: Kay Sievers <kay.sievers@vrfy.org> diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 198772c210..87887ab358 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -377,7 +377,7 @@ if (-e $GITWEB_CONFIG) { } # version of the core git binary -our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown"; +our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown"; $projects_list ||= $projectroot; @@ -539,7 +539,7 @@ $git_dir = "$projectroot/$project" if $project; # dispatch my %actions = ( - "blame" => \&git_blame2, + "blame" => \&git_blame, "blobdiff" => \&git_blobdiff, "blobdiff_plain" => \&git_blobdiff_plain, "blob" => \&git_blob, @@ -1500,9 +1500,13 @@ sub git_cmd { return $GIT, '--git-dir='.$git_dir; } -# returns path to the core git executable and the --git-dir parameter as string -sub git_cmd_str { - return join(' ', git_cmd()); +# quote the given arguments for passing them to the shell +# quote_command("command", "arg 1", "arg with ' and ! characters") +# => "'command' 'arg 1' 'arg with '\'' and '\!' characters'" +# Try to avoid using this function wherever possible. +sub quote_command { + return join(' ', + map( { my $a = $_; $a =~ s/(['!])/'\\$1'/g; "'$a'" } @_ )); } # get HEAD ref of given project as hash @@ -2158,49 +2162,6 @@ sub parse_commits { return wantarray ? @cos : \@cos; } -# parse ref from ref_file, given by ref_id, with given type -sub parse_ref { - my $ref_file = shift; - my $ref_id = shift; - my $type = shift || git_get_type($ref_id); - my %ref_item; - - $ref_item{'type'} = $type; - $ref_item{'id'} = $ref_id; - $ref_item{'epoch'} = 0; - $ref_item{'age'} = "unknown"; - if ($type eq "tag") { - my %tag = parse_tag($ref_id); - $ref_item{'comment'} = $tag{'comment'}; - if ($tag{'type'} eq "commit") { - my %co = parse_commit($tag{'object'}); - $ref_item{'epoch'} = $co{'committer_epoch'}; - $ref_item{'age'} = $co{'age_string'}; - } elsif (defined($tag{'epoch'})) { - my $age = time - $tag{'epoch'}; - $ref_item{'epoch'} = $tag{'epoch'}; - $ref_item{'age'} = age_string($age); - } - $ref_item{'reftype'} = $tag{'type'}; - $ref_item{'name'} = $tag{'name'}; - $ref_item{'refid'} = $tag{'object'}; - } elsif ($type eq "commit"){ - my %co = parse_commit($ref_id); - $ref_item{'reftype'} = "commit"; - $ref_item{'name'} = $ref_file; - $ref_item{'title'} = $co{'title'}; - $ref_item{'refid'} = $ref_id; - $ref_item{'epoch'} = $co{'committer_epoch'}; - $ref_item{'age'} = $co{'age_string'}; - } else { - $ref_item{'reftype'} = $type; - $ref_item{'name'} = $ref_file; - $ref_item{'refid'} = $ref_id; - } - - return %ref_item; -} - # parse line of git-diff-tree "raw" output sub parse_difftree_raw_line { my $line = shift; @@ -3559,21 +3520,24 @@ sub git_patchset_body { # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . -sub git_project_list_body { - my ($projlist, $order, $from, $to, $extra, $no_header) = @_; - - my ($check_forks) = gitweb_check_feature('forks'); - +# fills project list info (age, description, owner, forks) for each +# project in the list, removing invalid projects from returned list +# NOTE: modifies $projlist, but does not remove entries from it +sub fill_project_list_info { + my ($projlist, $check_forks) = @_; my @projects; + + PROJECT: foreach my $pr (@$projlist) { - my (@aa) = git_get_last_activity($pr->{'path'}); - unless (@aa) { - next; + my (@activity) = git_get_last_activity($pr->{'path'}); + unless (@activity) { + next PROJECT; } - ($pr->{'age'}, $pr->{'age_string'}) = @aa; + ($pr->{'age'}, $pr->{'age_string'}) = @activity; if (!defined $pr->{'descr'}) { my $descr = git_get_project_description($pr->{'path'}) || ""; - $pr->{'descr_long'} = to_utf8($descr); + $descr = to_utf8($descr); + $pr->{'descr_long'} = $descr; $pr->{'descr'} = chop_str($descr, $projects_list_description_width, 5); } if (!defined $pr->{'owner'}) { @@ -3585,14 +3549,52 @@ sub git_project_list_body { ($pname !~ /\/$/) && (-d "$projectroot/$pname")) { $pr->{'forks'} = "-d $projectroot/$pname"; - } - else { + } else { $pr->{'forks'} = 0; } } push @projects, $pr; } + return @projects; +} + +# print 'sort by' <th> element, either sorting by $key if $name eq $order +# (changing $list), or generating 'sort by $name' replay link otherwise +sub print_sort_th { + my ($str_sort, $name, $order, $key, $header, $list) = @_; + $key ||= $name; + $header ||= ucfirst($name); + + if ($order eq $name) { + if ($str_sort) { + @$list = sort {$a->{$key} cmp $b->{$key}} @$list; + } else { + @$list = sort {$a->{$key} <=> $b->{$key}} @$list; + } + print "<th>$header</th>\n"; + } else { + print "<th>" . + $cgi->a({-href => href(-replay=>1, order=>$name), + -class => "header"}, $header) . + "</th>\n"; + } +} + +sub print_sort_th_str { + print_sort_th(1, @_); +} + +sub print_sort_th_num { + print_sort_th(0, @_); +} + +sub git_project_list_body { + my ($projlist, $order, $from, $to, $extra, $no_header) = @_; + + my ($check_forks) = gitweb_check_feature('forks'); + my @projects = fill_project_list_info($projlist, $check_forks); + $order ||= $default_projects_order; $from = 0 unless defined $from; $to = $#projects if (!defined $to || $#projects < $to); @@ -3603,43 +3605,15 @@ sub git_project_list_body { if ($check_forks) { print "<th></th>\n"; } - if ($order eq "project") { - @projects = sort {$a->{'path'} cmp $b->{'path'}} @projects; - print "<th>Project</th>\n"; - } else { - print "<th>" . - $cgi->a({-href => href(project=>undef, order=>'project'), - -class => "header"}, "Project") . - "</th>\n"; - } - if ($order eq "descr") { - @projects = sort {$a->{'descr'} cmp $b->{'descr'}} @projects; - print "<th>Description</th>\n"; - } else { - print "<th>" . - $cgi->a({-href => href(project=>undef, order=>'descr'), - -class => "header"}, "Description") . - "</th>\n"; - } - if ($order eq "owner") { - @projects = sort {$a->{'owner'} cmp $b->{'owner'}} @projects; - print "<th>Owner</th>\n"; - } else { - print "<th>" . - $cgi->a({-href => href(project=>undef, order=>'owner'), - -class => "header"}, "Owner") . - "</th>\n"; - } - if ($order eq "age") { - @projects = sort {$a->{'age'} <=> $b->{'age'}} @projects; - print "<th>Last Change</th>\n"; - } else { - print "<th>" . - $cgi->a({-href => href(project=>undef, order=>'age'), - -class => "header"}, "Last Change") . - "</th>\n"; - } - print "<th></th>\n" . + print_sort_th_str('project', $order, 'path', + 'Project', \@projects); + print_sort_th_str('descr', $order, 'descr_long', + 'Description', \@projects); + print_sort_th_str('owner', $order, 'owner', + 'Owner', \@projects); + print_sort_th_num('age', $order, 'age', + 'Last Change', \@projects); + print "<th></th>\n" . # for links "</tr>\n"; } my $alternate = 1; @@ -4152,7 +4126,7 @@ sub git_tag { git_footer_html(); } -sub git_blame2 { +sub git_blame { my $fd; my $ftype; @@ -4260,103 +4234,6 @@ HTML git_footer_html(); } -sub git_blame { - my $fd; - - my ($have_blame) = gitweb_check_feature('blame'); - if (!$have_blame) { - die_error('403 Permission denied', "Permission denied"); - } - die_error('404 Not Found', "File name not defined") if (!$file_name); - $hash_base ||= git_get_head_hash($project); - die_error(undef, "Couldn't find base commit") unless ($hash_base); - my %co = parse_commit($hash_base) - or die_error(undef, "Reading commit failed"); - if (!defined $hash) { - $hash = git_get_hash_by_path($hash_base, $file_name, "blob") - or die_error(undef, "Error lookup file"); - } - open ($fd, "-|", git_cmd(), "annotate", '-l', '-t', '-r', $file_name, $hash_base) - or die_error(undef, "Open git-annotate failed"); - git_header_html(); - my $formats_nav = - $cgi->a({-href => href(action=>"blob", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, - "blob") . - " | " . - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, - "history") . - " | " . - $cgi->a({-href => href(action=>"blame", file_name=>$file_name)}, - "HEAD"); - git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); - git_print_header_div('commit', esc_html($co{'title'}), $hash_base); - git_print_page_path($file_name, 'blob', $hash_base); - print "<div class=\"page_body\">\n"; - print <<HTML; -<table class="blame"> - <tr> - <th>Commit</th> - <th>Age</th> - <th>Author</th> - <th>Line</th> - <th>Data</th> - </tr> -HTML - my @line_class = (qw(light dark)); - my $line_class_len = scalar (@line_class); - my $line_class_num = $#line_class; - while (my $line = <$fd>) { - my $long_rev; - my $short_rev; - my $author; - my $time; - my $lineno; - my $data; - my $age; - my $age_str; - my $age_class; - - chomp $line; - $line_class_num = ($line_class_num + 1) % $line_class_len; - - if ($line =~ m/^([0-9a-fA-F]{40})\t\(\s*([^\t]+)\t(\d+) [+-]\d\d\d\d\t(\d+)\)(.*)$/) { - $long_rev = $1; - $author = $2; - $time = $3; - $lineno = $4; - $data = $5; - } else { - print qq( <tr><td colspan="5" class="error">Unable to parse: $line</td></tr>\n); - next; - } - $short_rev = substr ($long_rev, 0, 8); - $age = time () - $time; - $age_str = age_string ($age); - $age_str =~ s/ / /g; - $age_class = age_class($age); - $author = esc_html ($author); - $author =~ s/ / /g; - - $data = untabify($data); - $data = esc_html ($data); - - print <<HTML; - <tr class="$line_class[$line_class_num]"> - <td class="sha1"><a href="${\href (action=>"commit", hash=>$long_rev)}" class="text">$short_rev..</a></td> - <td class="$age_class">$age_str</td> - <td>$author</td> - <td class="linenr"><a id="$lineno" href="#$lineno" class="linenr">$lineno</a></td> - <td class="pre">$data</td> - </tr> -HTML - } # while (my $line = <$fd>) - print "</table>\n\n"; - close $fd - or print "Reading blob failed.\n"; - print "</div>"; - git_footer_html(); -} - sub git_tags { my $head = git_get_head_hash($project); git_header_html(); @@ -4633,7 +4510,6 @@ sub git_snapshot { $hash = git_get_head_hash($project); } - my $git_command = git_cmd_str(); my $name = $project; $name =~ s,([^/])/*\.git$,$1,; $name = basename($name); @@ -4641,11 +4517,12 @@ sub git_snapshot { $name =~ s/\047/\047\\\047\047/g; my $cmd; $filename .= "-$hash$known_snapshot_formats{$format}{'suffix'}"; - $cmd = "$git_command archive " . - "--format=$known_snapshot_formats{$format}{'format'} " . - "--prefix=\'$name\'/ $hash"; + $cmd = quote_command( + git_cmd(), 'archive', + "--format=$known_snapshot_formats{$format}{'format'}", + "--prefix=$name/", $hash); if (exists $known_snapshot_formats{$format}{'compressor'}) { - $cmd .= ' | ' . join ' ', @{$known_snapshot_formats{$format}{'compressor'}}; + $cmd .= ' | ' . quote_command(@{$known_snapshot_formats{$format}{'compressor'}}); } print $cgi->header( @@ -4858,8 +4735,8 @@ sub git_object { if ($hash || ($hash_base && !defined $file_name)) { my $object_id = $hash || $hash_base; - my $git_command = git_cmd_str(); - open my $fd, "-|", "$git_command cat-file -t $object_id 2>/dev/null" + open my $fd, "-|", quote_command( + git_cmd(), 'cat-file', '-t', $object_id) . ' 2> /dev/null' or die_error('404 Not Found', "Object does not exist"); $type = <$fd>; chomp $type; diff --git a/http-push.c b/http-push.c index c93e781c3b..2cd068a6f1 100644 --- a/http-push.c +++ b/http-push.c @@ -9,6 +9,7 @@ #include "revision.h" #include "exec_cmd.h" #include "remote.h" +#include "list-objects.h" #include <expat.h> @@ -782,7 +783,7 @@ static void finish_request(struct transfer_request *request) lst = &((*lst)->next); *lst = (*lst)->next; - if (!verify_pack(target, 0)) + if (!verify_pack(target)) install_packed_git(target); else remote->can_update_info_refs = 0; @@ -1878,31 +1879,6 @@ static int ref_newer(const unsigned char *new_sha1, return found; } -static void mark_edge_parents_uninteresting(struct commit *commit) -{ - struct commit_list *parents; - - for (parents = commit->parents; parents; parents = parents->next) { - struct commit *parent = parents->item; - if (!(parent->object.flags & UNINTERESTING)) - continue; - mark_tree_uninteresting(parent->tree); - } -} - -static void mark_edges_uninteresting(struct commit_list *list) -{ - for ( ; list; list = list->next) { - struct commit *commit = list->item; - - if (commit->object.flags & UNINTERESTING) { - mark_tree_uninteresting(commit->tree); - continue; - } - mark_edge_parents_uninteresting(commit); - } -} - static void add_remote_info_ref(struct remote_ls_ctx *ls) { struct strbuf *buf = (struct strbuf *)ls->userData; @@ -2408,6 +2384,7 @@ int main(int argc, char **argv) } init_revisions(&revs, setup_git_directory()); setup_revisions(commit_argc, commit_argv, &revs, NULL); + revs.edge_hint = 0; /* just in case */ free(new_sha1_hex); if (old_sha1_hex) { free(old_sha1_hex); @@ -2418,7 +2395,7 @@ int main(int argc, char **argv) pushing = 0; if (prepare_revision_walk(&revs)) die("revision walk setup failed"); - mark_edges_uninteresting(revs.commits); + mark_edges_uninteresting(revs.commits, &revs, NULL); objects_to_send = get_delta(&revs, ref_lock); finish_all_active_slots(); diff --git a/http-walker.c b/http-walker.c index 99f397e32b..51c18f2685 100644 --- a/http-walker.c +++ b/http-walker.c @@ -795,7 +795,7 @@ static int fetch_pack(struct walker *walker, struct alt_base *repo, unsigned cha lst = &((*lst)->next); *lst = (*lst)->next; - if (verify_pack(target, 0)) + if (verify_pack(target)) return -1; install_packed_git(target); @@ -583,7 +583,7 @@ static char *quote_ref_url(const char *base, const char *ref) int len, baselen, ch; baselen = strlen(base); - len = baselen + 7; /* "/refs/" + NUL */ + len = baselen + 2; /* '/' after base and terminating NUL */ for (cp = ref; (ch = *cp) != 0; cp++, len++) if (needs_quote(ch)) len += 2; /* extra two hex plus replacement % */ diff --git a/pack-check.c b/pack-check.c index f4898732dd..f596bf2db5 100644 --- a/pack-check.c +++ b/pack-check.c @@ -4,8 +4,9 @@ struct idx_entry { - const unsigned char *sha1; off_t offset; + const unsigned char *sha1; + unsigned int nr; }; static int compare_entries(const void *e1, const void *e2) @@ -19,6 +20,28 @@ static int compare_entries(const void *e1, const void *e2) return 0; } +int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, + off_t offset, off_t len, unsigned int nr) +{ + const uint32_t *index_crc; + uint32_t data_crc = crc32(0, Z_NULL, 0); + + do { + unsigned int avail; + void *data = use_pack(p, w_curs, offset, &avail); + if (avail > len) + avail = len; + data_crc = crc32(data_crc, data, avail); + offset += avail; + len -= avail; + } while (len); + + index_crc = p->index_data; + index_crc += 2 + 256 + p->num_objects * (20/4) + nr; + + return data_crc != ntohl(*index_crc); +} + static int verify_packfile(struct packed_git *p, struct pack_window **w_curs) { @@ -61,15 +84,15 @@ static int verify_packfile(struct packed_git *p, * we do not do scan-streaming check on the pack file. */ nr_objects = p->num_objects; - entries = xmalloc(nr_objects * sizeof(*entries)); + entries = xmalloc((nr_objects + 1) * sizeof(*entries)); + entries[nr_objects].offset = pack_sig_ofs; /* first sort entries by pack offset, since unpacking them is more efficient that way */ for (i = 0; i < nr_objects; i++) { entries[i].sha1 = nth_packed_object_sha1(p, i); if (!entries[i].sha1) die("internal error pack-check nth-packed-object"); - entries[i].offset = find_pack_entry_one(entries[i].sha1, p); - if (!entries[i].offset) - die("internal error pack-check find-pack-entry-one"); + entries[i].offset = nth_packed_object_offset(p, i); + entries[i].nr = i; } qsort(entries, nr_objects, sizeof(*entries), compare_entries); @@ -78,6 +101,16 @@ static int verify_packfile(struct packed_git *p, enum object_type type; unsigned long size; + if (p->index_version > 1) { + off_t offset = entries[i].offset; + off_t len = entries[i+1].offset - offset; + unsigned int nr = entries[i].nr; + if (check_pack_crc(p, w_curs, offset, len, nr)) + err = error("index CRC mismatch for object %s " + "from %s at offset %"PRIuMAX"", + sha1_to_hex(entries[i].sha1), + p->pack_name, (uintmax_t)offset); + } data = unpack_entry(p, entries[i].offset, &type, &size); if (!data) { err = error("cannot unpack %s from %s at offset %"PRIuMAX"", @@ -98,63 +131,7 @@ static int verify_packfile(struct packed_git *p, return err; } - -#define MAX_CHAIN 50 - -static void show_pack_info(struct packed_git *p) -{ - uint32_t nr_objects, i, chain_histogram[MAX_CHAIN+1]; - - nr_objects = p->num_objects; - memset(chain_histogram, 0, sizeof(chain_histogram)); - init_pack_revindex(); - - for (i = 0; i < nr_objects; i++) { - const unsigned char *sha1; - unsigned char base_sha1[20]; - const char *type; - unsigned long size; - unsigned long store_size; - off_t offset; - unsigned int delta_chain_length; - - sha1 = nth_packed_object_sha1(p, i); - if (!sha1) - die("internal error pack-check nth-packed-object"); - offset = find_pack_entry_one(sha1, p); - if (!offset) - die("internal error pack-check find-pack-entry-one"); - - type = packed_object_info_detail(p, offset, &size, &store_size, - &delta_chain_length, - base_sha1); - printf("%s ", sha1_to_hex(sha1)); - if (!delta_chain_length) - printf("%-6s %lu %lu %"PRIuMAX"\n", - type, size, store_size, (uintmax_t)offset); - 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 (delta_chain_length <= MAX_CHAIN) - chain_histogram[delta_chain_length]++; - else - chain_histogram[0]++; - } - } - - for (i = 0; i <= MAX_CHAIN; i++) { - if (!chain_histogram[i]) - continue; - printf("chain length = %d: %d object%s\n", i, - chain_histogram[i], chain_histogram[i] > 1 ? "s" : ""); - } - if (chain_histogram[0]) - printf("chain length > %d: %d object%s\n", MAX_CHAIN, - chain_histogram[0], chain_histogram[0] > 1 ? "s" : ""); -} - -int verify_pack(struct packed_git *p, int verbose) +int verify_pack(struct packed_git *p) { off_t index_size; const unsigned char *index_base; @@ -180,14 +157,5 @@ int verify_pack(struct packed_git *p, int verbose) err |= verify_packfile(p, &w_curs); unuse_pack(&w_curs); - if (verbose) { - if (err) - printf("%s: bad\n", p->pack_name); - else { - show_pack_info(p); - printf("%s: ok\n", p->pack_name); - } - } - return err; } diff --git a/pack-refs.c b/pack-refs.c new file mode 100644 index 0000000000..848d311c2b --- /dev/null +++ b/pack-refs.c @@ -0,0 +1,117 @@ +#include "cache.h" +#include "refs.h" +#include "tag.h" +#include "pack-refs.h" + +struct ref_to_prune { + struct ref_to_prune *next; + unsigned char sha1[20]; + char name[FLEX_ARRAY]; +}; + +struct pack_refs_cb_data { + unsigned int flags; + struct ref_to_prune *ref_to_prune; + FILE *refs_file; +}; + +static int do_not_prune(int flags) +{ + /* If it is already packed or if it is a symref, + * do not prune it. + */ + return (flags & (REF_ISSYMREF|REF_ISPACKED)); +} + +static int handle_one_ref(const char *path, const unsigned char *sha1, + int flags, void *cb_data) +{ + struct pack_refs_cb_data *cb = cb_data; + int is_tag_ref; + + /* Do not pack the symbolic refs */ + if ((flags & REF_ISSYMREF)) + return 0; + is_tag_ref = !prefixcmp(path, "refs/tags/"); + + /* ALWAYS pack refs that were already packed or are tags */ + if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref && !(flags & REF_ISPACKED)) + return 0; + + fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path); + if (is_tag_ref) { + struct object *o = parse_object(sha1); + if (o->type == OBJ_TAG) { + o = deref_tag(o, path, 0); + if (o) + fprintf(cb->refs_file, "^%s\n", + sha1_to_hex(o->sha1)); + } + } + + if ((cb->flags & PACK_REFS_PRUNE) && !do_not_prune(flags)) { + int namelen = strlen(path) + 1; + struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen); + hashcpy(n->sha1, sha1); + strcpy(n->name, path); + n->next = cb->ref_to_prune; + cb->ref_to_prune = n; + } + return 0; +} + +/* make sure nobody touched the ref, and unlink */ +static void prune_ref(struct ref_to_prune *r) +{ + struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1); + + if (lock) { + unlink(git_path("%s", r->name)); + unlock_ref(lock); + } +} + +static void prune_refs(struct ref_to_prune *r) +{ + while (r) { + prune_ref(r); + r = r->next; + } +} + +static struct lock_file packed; + +int pack_refs(unsigned int flags) +{ + int fd; + struct pack_refs_cb_data cbdata; + + memset(&cbdata, 0, sizeof(cbdata)); + cbdata.flags = flags; + + fd = hold_lock_file_for_update(&packed, git_path("packed-refs"), 1); + cbdata.refs_file = fdopen(fd, "w"); + if (!cbdata.refs_file) + die("unable to create ref-pack file structure (%s)", + strerror(errno)); + + /* perhaps other traits later as well */ + fprintf(cbdata.refs_file, "# pack-refs with: peeled \n"); + + for_each_ref(handle_one_ref, &cbdata); + if (ferror(cbdata.refs_file)) + die("failed to write ref-pack file"); + if (fflush(cbdata.refs_file) || fsync(fd) || fclose(cbdata.refs_file)) + die("failed to write ref-pack file (%s)", strerror(errno)); + /* + * Since the lock file was fdopen()'ed and then fclose()'ed above, + * assign -1 to the lock file descriptor so that commit_lock_file() + * won't try to close() it. + */ + packed.fd = -1; + if (commit_lock_file(&packed) < 0) + die("unable to overwrite old ref-pack file (%s)", strerror(errno)); + if (cbdata.flags & PACK_REFS_PRUNE) + prune_refs(cbdata.ref_to_prune); + return 0; +} diff --git a/pack-refs.h b/pack-refs.h new file mode 100644 index 0000000000..518acfb370 --- /dev/null +++ b/pack-refs.h @@ -0,0 +1,18 @@ +#ifndef PACK_REFS_H +#define PACK_REFS_H + +/* + * Flags for controlling behaviour of pack_refs() + * PACK_REFS_PRUNE: Prune loose refs after packing + * PACK_REFS_ALL: Pack _all_ refs, not just tags and already packed refs + */ +#define PACK_REFS_PRUNE 0x0001 +#define PACK_REFS_ALL 0x0002 + +/* + * Write a packed-refs file for the current repository. + * flags: Combination of the above PACK_REFS_* flags. + */ +int pack_refs(unsigned int flags); + +#endif /* PACK_REFS_H */ diff --git a/pack-revindex.c b/pack-revindex.c index a8aa2cd6ca..cd300bdff5 100644 --- a/pack-revindex.c +++ b/pack-revindex.c @@ -40,7 +40,7 @@ static int pack_revindex_ix(struct packed_git *p) return -1 - i; } -void init_pack_revindex(void) +static void init_pack_revindex(void) { int num; struct packed_git *p; @@ -118,9 +118,11 @@ struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs) struct pack_revindex *rix; struct revindex_entry *revindex; + if (!pack_revindex_hashsz) + init_pack_revindex(); num = pack_revindex_ix(p); if (num < 0) - die("internal error: pack revindex uninitialized"); + die("internal error: pack revindex fubar"); rix = &pack_revindex[num]; if (!rix->revindex) diff --git a/pack-revindex.h b/pack-revindex.h index c3527a7565..36a514a6cf 100644 --- a/pack-revindex.h +++ b/pack-revindex.h @@ -6,7 +6,6 @@ struct revindex_entry { unsigned int nr; }; -void init_pack_revindex(void); struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs); #endif @@ -56,8 +56,8 @@ struct pack_idx_entry { }; extern char *write_idx_file(char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1); - -extern int verify_pack(struct packed_git *, int); +extern int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr); +extern int verify_pack(struct packed_git *); extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t); extern char *index_pack_lockfile(int fd); diff --git a/parse-options.c b/parse-options.c index acf3fe3a1a..b8bde2b04a 100644 --- a/parse-options.c +++ b/parse-options.c @@ -312,8 +312,12 @@ void usage_with_options_internal(const char * const *usagestr, fprintf(stderr, "usage: %s\n", *usagestr++); while (*usagestr && **usagestr) fprintf(stderr, " or: %s\n", *usagestr++); - while (*usagestr) - fprintf(stderr, " %s\n", *usagestr++); + while (*usagestr) { + fprintf(stderr, "%s%s\n", + **usagestr ? " " : "", + *usagestr); + usagestr++; + } if (opts->type != OPTION_GROUP) fputc('\n', stderr); @@ -344,7 +348,10 @@ void usage_with_options_internal(const char * const *usagestr, break; case OPTION_INTEGER: if (opts->flags & PARSE_OPT_OPTARG) - pos += fprintf(stderr, "[<n>]"); + if (opts->long_name) + pos += fprintf(stderr, "[=<n>]"); + else + pos += fprintf(stderr, "[<n>]"); else pos += fprintf(stderr, " <n>"); break; @@ -355,12 +362,18 @@ void usage_with_options_internal(const char * const *usagestr, case OPTION_STRING: if (opts->argh) { if (opts->flags & PARSE_OPT_OPTARG) - pos += fprintf(stderr, " [<%s>]", opts->argh); + if (opts->long_name) + pos += fprintf(stderr, "[=<%s>]", opts->argh); + else + pos += fprintf(stderr, "[<%s>]", opts->argh); else pos += fprintf(stderr, " <%s>", opts->argh); } else { if (opts->flags & PARSE_OPT_OPTARG) - pos += fprintf(stderr, " [...]"); + if (opts->long_name) + pos += fprintf(stderr, "[=...]"); + else + pos += fprintf(stderr, "[...]"); else pos += fprintf(stderr, " ..."); } @@ -314,7 +314,7 @@ const char *make_nonrelative_path(const char *path) { static char buf[PATH_MAX + 1]; - if (path[0] == '/') { + if (is_absolute_path(path)) { if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) die ("Too long path: %.*s", 60, path); } else { @@ -330,6 +330,23 @@ const char *make_nonrelative_path(const char *path) /* We allow "recursive" symbolic links. Only within reason, though. */ #define MAXDEPTH 5 +const char *make_relative_path(const char *abs, const char *base) +{ + static char buf[PATH_MAX + 1]; + int baselen; + if (!base) + return abs; + baselen = strlen(base); + if (prefixcmp(abs, base)) + return abs; + if (abs[baselen] == '/') + baselen++; + else if (base[baselen - 1] != '/') + return abs; + strcpy(buf, abs + baselen); + return buf; +} + const char *make_absolute_path(const char *path) { static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1]; diff --git a/progress.c b/progress.c index d19f80c0bb..55a8687ad1 100644 --- a/progress.c +++ b/progress.c @@ -241,16 +241,21 @@ void stop_progress_msg(struct progress **p_progress, const char *msg) *p_progress = NULL; if (progress->last_value != -1) { /* Force the last update */ - char buf[strlen(msg) + 5]; + char buf[128], *bufp; + size_t len = strlen(msg) + 5; struct throughput *tp = progress->throughput; + + bufp = (len < sizeof(buf)) ? buf : xmalloc(len + 1); if (tp) { unsigned int rate = !tp->avg_misecs ? 0 : tp->avg_bytes / tp->avg_misecs; throughput_string(tp, tp->curr_total, rate); } progress_update = 1; - sprintf(buf, ", %s.\n", msg); - display(progress, progress->last_value, buf); + sprintf(bufp, ", %s.\n", msg); + display(progress, progress->last_value, bufp); + if (buf != bufp) + free(bufp); } clear_progress_signal(); free(progress->throughput); diff --git a/read-cache.c b/read-cache.c index 8e5fbb6192..f83de8c415 100644 --- a/read-cache.c +++ b/read-cache.c @@ -138,6 +138,16 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st) return 0; } +static int is_empty_blob_sha1(const unsigned char *sha1) +{ + static const unsigned char empty_blob_sha1[20] = { + 0xe6,0x9d,0xe2,0x9b,0xb2,0xd1,0xd6,0x43,0x4b,0x8b, + 0x29,0xae,0x77,0x5a,0xd8,0xc2,0xe4,0x8c,0x53,0x91 + }; + + return !hashcmp(sha1, empty_blob_sha1); +} + static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) { unsigned int changed = 0; @@ -193,6 +203,12 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) if (ce->ce_size != (unsigned int) st->st_size) changed |= DATA_CHANGED; + /* Racily smudged entry? */ + if (!ce->ce_size) { + if (!is_empty_blob_sha1(ce->sha1)) + changed |= DATA_CHANGED; + } + return changed; } @@ -867,8 +867,7 @@ static char *guess_ref(const char *name, struct ref *peer) static int match_explicit(struct ref *src, struct ref *dst, struct ref ***dst_tail, - struct refspec *rs, - int errs) + struct refspec *rs) { struct ref *matched_src, *matched_dst; @@ -876,7 +875,7 @@ static int match_explicit(struct ref *src, struct ref *dst, char *dst_guess; if (rs->pattern || rs->matching) - return errs; + return 0; matched_src = matched_dst = NULL; switch (count_refspec_match(rs->src, src, &matched_src)) { @@ -889,23 +888,16 @@ static int match_explicit(struct ref *src, struct ref *dst, */ matched_src = try_explicit_object_name(rs->src); if (!matched_src) - error("src refspec %s does not match any.", rs->src); + return error("src refspec %s does not match any.", rs->src); break; default: - matched_src = NULL; - error("src refspec %s matches more than one.", rs->src); - break; + return error("src refspec %s matches more than one.", rs->src); } - if (!matched_src) - errs = 1; - if (!dst_value) { unsigned char sha1[20]; int flag; - if (!matched_src) - return errs; dst_value = resolve_ref(matched_src->name, sha1, 1, &flag); if (!dst_value || ((flag & REF_ISSYMREF) && @@ -936,18 +928,16 @@ static int match_explicit(struct ref *src, struct ref *dst, dst_value); break; } - if (errs || !matched_dst) - return 1; - if (matched_dst->peer_ref) { - errs = 1; - error("dst ref %s receives from more than one src.", + if (!matched_dst) + return -1; + if (matched_dst->peer_ref) + return error("dst ref %s receives from more than one src.", matched_dst->name); - } else { matched_dst->peer_ref = matched_src; matched_dst->force = rs->force; } - return errs; + return 0; } static int match_explicit_refs(struct ref *src, struct ref *dst, @@ -956,8 +946,8 @@ static int match_explicit_refs(struct ref *src, struct ref *dst, { int i, errs; for (i = errs = 0; i < rs_nr; i++) - errs |= match_explicit(src, dst, dst_tail, &rs[i], errs); - return -errs; + errs += match_explicit(src, dst, dst_tail, &rs[i]); + return errs; } static const struct refspec *check_pattern_match(const struct refspec *rs, @@ -292,9 +292,10 @@ void setup_work_tree(void) work_tree = get_git_work_tree(); git_dir = get_git_dir(); if (!is_absolute_path(git_dir)) - set_git_dir(make_absolute_path(git_dir)); + git_dir = make_absolute_path(git_dir); if (!work_tree || chdir(work_tree)) die("This operation must be run in a work tree"); + set_git_dir(make_relative_path(git_dir, work_tree)); initialized = 1; } diff --git a/sha1_file.c b/sha1_file.c index adcf37c3f6..e79b2c1145 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -35,8 +35,6 @@ static size_t sz_fmt(size_t s) { return s; } const unsigned char null_sha1[20]; -static unsigned int sha1_file_open_flag = O_NOATIME; - const signed char hexval_table[256] = { -1, -1, -1, -1, -1, -1, -1, -1, /* 00-07 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 08-0f */ @@ -118,7 +116,16 @@ int safe_create_leading_directories(char *path) return 0; } -char * sha1_to_hex(const unsigned char *sha1) +int safe_create_leading_directories_const(const char *path) +{ + /* path points to cache entries, so xstrdup before messing with it */ + char *buf = xstrdup(path); + int result = safe_create_leading_directories(buf); + free(buf); + return result; +} + +char *sha1_to_hex(const unsigned char *sha1) { static int bufno; static char hexbuffer[4][50]; @@ -399,21 +406,21 @@ void prepare_alt_odb(void) read_info_alternates(get_object_directory(), 0); } -static char *find_sha1_file(const unsigned char *sha1, struct stat *st) +static int has_loose_object(const unsigned char *sha1) { char *name = sha1_file_name(sha1); struct alternate_object_database *alt; - if (!stat(name, st)) - return name; + if (!access(name, F_OK)) + return 1; prepare_alt_odb(); for (alt = alt_odb_list; alt; alt = alt->next) { name = alt->name; fill_sha1_path(name, sha1); - if (!stat(alt->base, st)) - return alt->base; + if (!access(alt->base, F_OK)) + return 1; } - return NULL; + return 0; } static unsigned int pack_used_ctr; @@ -794,18 +801,28 @@ unsigned char* use_pack(struct packed_git *p, return win->base + offset; } +static struct packed_git *alloc_packed_git(int extra) +{ + struct packed_git *p = xmalloc(sizeof(*p) + extra); + memset(p, 0, sizeof(*p)); + p->pack_fd = -1; + return p; +} + struct packed_git *add_packed_git(const char *path, int path_len, int local) { struct stat st; - struct packed_git *p = xmalloc(sizeof(*p) + path_len + 2); + struct packed_git *p = alloc_packed_git(path_len + 2); /* * Make sure a corresponding .pack file exists and that * the index looks sane. */ path_len -= strlen(".idx"); - if (path_len < 1) + if (path_len < 1) { + free(p); return NULL; + } memcpy(p->pack_name, path, path_len); strcpy(p->pack_name + path_len, ".pack"); if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) { @@ -816,14 +833,7 @@ struct packed_git *add_packed_git(const char *path, int path_len, int local) /* ok, it looks sane as far as we can check without * actually mapping the pack file. */ - p->index_version = 0; - p->index_data = NULL; - p->index_size = 0; - p->num_objects = 0; p->pack_size = st.st_size; - p->next = NULL; - p->windows = NULL; - p->pack_fd = -1; p->pack_local = local; p->mtime = st.st_mtime; if (path_len < 40 || get_sha1_hex(path + path_len - 40, p->sha1)) @@ -833,27 +843,17 @@ struct packed_git *add_packed_git(const char *path, int path_len, int local) struct packed_git *parse_pack_index(unsigned char *sha1) { - char *path = sha1_pack_index_name(sha1); - return parse_pack_index_file(sha1, path); -} - -struct packed_git *parse_pack_index_file(const unsigned char *sha1, - const char *idx_path) -{ + const char *idx_path = sha1_pack_index_name(sha1); const char *path = sha1_pack_name(sha1); - struct packed_git *p = xmalloc(sizeof(*p) + strlen(path) + 2); + struct packed_git *p = alloc_packed_git(strlen(path) + 1); + strcpy(p->pack_name, path); + hashcpy(p->sha1, sha1); if (check_packed_git_idx(idx_path, p)) { free(p); return NULL; } - strcpy(p->pack_name, path); - p->pack_size = 0; - p->next = NULL; - p->windows = NULL; - p->pack_fd = -1; - hashcpy(p->sha1, sha1); return p; } @@ -990,6 +990,18 @@ void reprepare_packed_git(void) prepare_packed_git(); } +static void mark_bad_packed_object(struct packed_git *p, + const unsigned char *sha1) +{ + unsigned i; + for (i = 0; i < p->num_bad_objects; i++) + if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i)) + return; + p->bad_object_sha1 = xrealloc(p->bad_object_sha1, 20 * (p->num_bad_objects + 1)); + hashcpy(p->bad_object_sha1 + 20 * p->num_bad_objects, sha1); + p->num_bad_objects++; +} + int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type) { unsigned char real_sha1[20]; @@ -997,38 +1009,58 @@ int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long siz return hashcmp(sha1, real_sha1) ? -1 : 0; } +static int git_open_noatime(const char *name) +{ + static int sha1_file_open_flag = O_NOATIME; + int fd = open(name, O_RDONLY | sha1_file_open_flag); + + /* Might the failure be due to O_NOATIME? */ + if (fd < 0 && errno != ENOENT && sha1_file_open_flag) { + fd = open(name, O_RDONLY); + if (fd >= 0) + sha1_file_open_flag = 0; + } + return fd; +} + +static int open_sha1_file(const unsigned char *sha1) +{ + int fd; + char *name = sha1_file_name(sha1); + struct alternate_object_database *alt; + + fd = git_open_noatime(name); + if (fd >= 0) + return fd; + + prepare_alt_odb(); + errno = ENOENT; + for (alt = alt_odb_list; alt; alt = alt->next) { + name = alt->name; + fill_sha1_path(name, sha1); + fd = git_open_noatime(alt->base); + if (fd >= 0) + return fd; + } + return -1; +} + static void *map_sha1_file(const unsigned char *sha1, unsigned long *size) { - struct stat st; void *map; int fd; - char *filename = find_sha1_file(sha1, &st); - if (!filename) { - return NULL; - } + fd = open_sha1_file(sha1); + map = NULL; + if (fd >= 0) { + struct stat st; - fd = open(filename, O_RDONLY | sha1_file_open_flag); - if (fd < 0) { - /* See if it works without O_NOATIME */ - switch (sha1_file_open_flag) { - default: - fd = open(filename, O_RDONLY); - if (fd >= 0) - break; - /* Fallthrough */ - case 0: - return NULL; + if (!fstat(fd, &st)) { + *size = xsize_t(st.st_size); + map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0); } - - /* If it failed once, it will probably fail again. - * Stop using O_NOATIME - */ - sha1_file_open_flag = 0; + close(fd); } - *size = xsize_t(st.st_size); - map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0); - close(fd); return map; } @@ -1288,20 +1320,17 @@ static off_t get_delta_base(struct packed_git *p, while (c & 128) { base_offset += 1; if (!base_offset || MSB(base_offset, 7)) - die("offset value overflow for delta base object"); + return 0; /* overflow */ c = base_info[used++]; base_offset = (base_offset << 7) + (c & 127); } base_offset = delta_obj_offset - base_offset; if (base_offset >= delta_obj_offset) - die("delta base offset out of bound"); + return 0; /* out of bound */ *curpos += used; } else if (type == OBJ_REF_DELTA) { /* The base entry _must_ be in the same pack */ base_offset = find_pack_entry_one(base_info, p); - if (!base_offset) - die("failed to find delta-pack base object %s", - sha1_to_hex(base_info)); *curpos += 20; } else die("I am totally screwed"); @@ -1394,6 +1423,9 @@ const char *packed_object_info_detail(struct packed_git *p, return typename(type); case OBJ_OFS_DELTA: obj_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset); + if (!obj_offset) + die("pack %s contains bad delta base reference of type %s", + p->pack_name, typename(type)); if (*delta_chain_length == 0) { revidx = find_pack_revindex(p, obj_offset); hashcpy(base_sha1, nth_packed_object_sha1(p, revidx->nr)); @@ -1588,17 +1620,41 @@ static void *unpack_delta_entry(struct packed_git *p, off_t base_offset; base_offset = get_delta_base(p, w_curs, &curpos, *type, obj_offset); + if (!base_offset) { + error("failed to validate delta base reference " + "at offset %"PRIuMAX" from %s", + (uintmax_t)curpos, p->pack_name); + return NULL; + } base = cache_or_unpack_entry(p, base_offset, &base_size, type, 0); - if (!base) - die("failed to read delta base object" - " at %"PRIuMAX" from %s", - (uintmax_t)base_offset, p->pack_name); + if (!base) { + /* + * We're probably in deep shit, but let's try to fetch + * the required base anyway from another pack or loose. + * This is costly but should happen only in the presence + * of a corrupted pack, and is better than failing outright. + */ + struct revindex_entry *revidx = find_pack_revindex(p, base_offset); + const unsigned char *base_sha1 = + nth_packed_object_sha1(p, revidx->nr); + error("failed to read delta base object %s" + " at offset %"PRIuMAX" from %s", + sha1_to_hex(base_sha1), (uintmax_t)base_offset, + p->pack_name); + mark_bad_packed_object(p, base_sha1); + base = read_sha1_file(base_sha1, type, &base_size); + if (!base) + return NULL; + } delta_data = unpack_compressed_entry(p, w_curs, curpos, delta_size); - if (!delta_data) - die("failed to unpack compressed delta" - " at %"PRIuMAX" from %s", - (uintmax_t)curpos, p->pack_name); + if (!delta_data) { + error("failed to unpack compressed delta " + "at offset %"PRIuMAX" from %s", + (uintmax_t)curpos, p->pack_name); + free(base); + return NULL; + } result = patch_delta(base, base_size, delta_data, delta_size, sizep); @@ -1630,7 +1686,9 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset, data = unpack_compressed_entry(p, &w_curs, curpos, *sizep); break; default: - die("unknown object type %i in %s", *type, p->pack_name); + data = NULL; + error("unknown object type %i at offset %"PRIuMAX" in %s", + *type, (uintmax_t)obj_offset, p->pack_name); } unuse_pack(&w_curs); return data; @@ -1656,7 +1714,7 @@ const unsigned char *nth_packed_object_sha1(struct packed_git *p, } } -static off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n) +off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n) { const unsigned char *index = p->index_data; index += 4 * 256; @@ -1776,6 +1834,13 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, cons goto next; } + if (p->num_bad_objects) { + unsigned i; + for (i = 0; i < p->num_bad_objects; i++) + if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i)) + goto next; + } + offset = find_pack_entry_one(sha1, p); if (offset) { /* @@ -1860,11 +1925,24 @@ static void *read_packed_sha1(const unsigned char *sha1, enum object_type *type, unsigned long *size) { struct pack_entry e; + void *data; if (!find_pack_entry(sha1, &e, NULL)) return NULL; - else - return cache_or_unpack_entry(e.p, e.offset, size, type, 1); + data = cache_or_unpack_entry(e.p, e.offset, size, type, 1); + if (!data) { + /* + * We're probably in deep shit, but let's try to fetch + * the required object anyway from another pack or loose. + * This should happen only in the presence of a corrupted + * pack, and is better than failing outright. + */ + error("failed to read object %s at offset %"PRIuMAX" from %s", + sha1_to_hex(sha1), (uintmax_t)e.offset, e.p->pack_name); + mark_bad_packed_object(e.p, sha1); + data = read_sha1_file(sha1, type, size); + } + return data; } /* @@ -2019,48 +2097,11 @@ static void write_sha1_file_prepare(const void *buf, unsigned long len, } /* - * Link the tempfile to the final place, possibly creating the - * last directory level as you do so. - * - * Returns the errno on failure, 0 on success. - */ -static int link_temp_to_file(const char *tmpfile, const char *filename) -{ - int ret; - char *dir; - - if (!link(tmpfile, filename)) - return 0; - - /* - * Try to mkdir the last path component if that failed. - * - * Re-try the "link()" regardless of whether the mkdir - * succeeds, since a race might mean that somebody - * else succeeded. - */ - ret = errno; - dir = strrchr(filename, '/'); - if (dir) { - *dir = 0; - if (!mkdir(filename, 0777) && adjust_shared_perm(filename)) { - *dir = '/'; - return -2; - } - *dir = '/'; - if (!link(tmpfile, filename)) - return 0; - ret = errno; - } - return ret; -} - -/* * Move the just written object into its final resting place */ int move_temp_to_file(const char *tmpfile, const char *filename) { - int ret = link_temp_to_file(tmpfile, filename); + int ret = link(tmpfile, filename); /* * Coda hack - coda doesn't like cross-directory links, @@ -2105,6 +2146,56 @@ int hash_sha1_file(const void *buf, unsigned long len, const char *type, return 0; } +/* Finalize a file on disk, and close it. */ +static void close_sha1_file(int fd) +{ + /* For safe-mode, we could fsync_or_die(fd, "sha1 file") here */ + fchmod(fd, 0444); + if (close(fd) != 0) + die("unable to write sha1 file"); +} + +/* Size of directory component, including the ending '/' */ +static inline int directory_size(const char *filename) +{ + const char *s = strrchr(filename, '/'); + if (!s) + return 0; + return s - filename + 1; +} + +/* + * This creates a temporary file in the same directory as the final + * 'filename' + * + * We want to avoid cross-directory filename renames, because those + * can have problems on various filesystems (FAT, NFS, Coda). + */ +static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename) +{ + int fd, dirlen = directory_size(filename); + + if (dirlen + 20 > bufsiz) { + errno = ENAMETOOLONG; + return -1; + } + memcpy(buffer, filename, dirlen); + strcpy(buffer + dirlen, "tmp_obj_XXXXXX"); + fd = mkstemp(buffer); + if (fd < 0 && dirlen) { + /* Make sure the directory exists */ + memcpy(buffer, filename, dirlen); + buffer[dirlen-1] = 0; + if (mkdir(buffer, 0777) || adjust_shared_perm(buffer)) + return -1; + + /* Try again */ + strcpy(buffer + dirlen - 1, "/tmp_obj_XXXXXX"); + fd = mkstemp(buffer); + } + return fd; +} + static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen, void *buf, unsigned long len, time_t mtime) { @@ -2115,23 +2206,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen, static char tmpfile[PATH_MAX]; filename = sha1_file_name(sha1); - fd = open(filename, O_RDONLY); - if (fd >= 0) { - /* - * FIXME!!! We might do collision checking here, but we'd - * need to uncompress the old file and check it. Later. - */ - close(fd); - return 0; - } - - if (errno != ENOENT) { - return error("sha1 file %s: %s\n", filename, strerror(errno)); - } - - snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory()); - - fd = mkstemp(tmpfile); + fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename); if (fd < 0) { if (errno == EPERM) return error("insufficient permission for adding an object to repository database %s\n", get_object_directory()); @@ -2170,9 +2245,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen, if (write_buffer(fd, compressed, size) < 0) die("unable to write sha1 file"); - fchmod(fd, 0444); - if (close(fd)) - die("unable to write sha1 file"); + close_sha1_file(fd); free(compressed); if (mtime) { @@ -2206,14 +2279,13 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha int force_object_loose(const unsigned char *sha1, time_t mtime) { - struct stat st; void *buf; unsigned long len; enum object_type type; char hdr[32]; int hdrlen; - if (find_sha1_file(sha1, &st)) + if (has_loose_object(sha1)) return 0; buf = read_packed_sha1(sha1, &type, &len); if (!buf) @@ -2222,150 +2294,6 @@ int force_object_loose(const unsigned char *sha1, time_t mtime) return write_loose_object(sha1, hdr, hdrlen, buf, len, mtime); } -/* - * We need to unpack and recompress the object for writing - * it out to a different file. - */ -static void *repack_object(const unsigned char *sha1, unsigned long *objsize) -{ - size_t size; - z_stream stream; - unsigned char *unpacked; - unsigned long len; - enum object_type type; - char hdr[32]; - int hdrlen; - void *buf; - - /* need to unpack and recompress it by itself */ - unpacked = read_packed_sha1(sha1, &type, &len); - if (!unpacked) - error("cannot read sha1_file for %s", sha1_to_hex(sha1)); - - hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1; - - /* Set it up */ - memset(&stream, 0, sizeof(stream)); - deflateInit(&stream, zlib_compression_level); - size = deflateBound(&stream, len + hdrlen); - buf = xmalloc(size); - - /* Compress it */ - stream.next_out = buf; - stream.avail_out = size; - - /* First header.. */ - stream.next_in = (void *)hdr; - stream.avail_in = hdrlen; - while (deflate(&stream, 0) == Z_OK) - /* nothing */; - - /* Then the data itself.. */ - stream.next_in = unpacked; - stream.avail_in = len; - while (deflate(&stream, Z_FINISH) == Z_OK) - /* nothing */; - deflateEnd(&stream); - free(unpacked); - - *objsize = stream.total_out; - return buf; -} - -int write_sha1_to_fd(int fd, const unsigned char *sha1) -{ - int retval; - unsigned long objsize; - void *buf = map_sha1_file(sha1, &objsize); - - if (buf) { - retval = write_buffer(fd, buf, objsize); - munmap(buf, objsize); - return retval; - } - - buf = repack_object(sha1, &objsize); - retval = write_buffer(fd, buf, objsize); - free(buf); - return retval; -} - -int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, - size_t bufsize, size_t *bufposn) -{ - char tmpfile[PATH_MAX]; - int local; - z_stream stream; - unsigned char real_sha1[20]; - unsigned char discard[4096]; - int ret; - SHA_CTX c; - - snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory()); - - local = mkstemp(tmpfile); - if (local < 0) { - if (errno == EPERM) - return error("insufficient permission for adding an object to repository database %s\n", get_object_directory()); - else - return error("unable to create temporary sha1 filename %s: %s\n", tmpfile, strerror(errno)); - } - - memset(&stream, 0, sizeof(stream)); - - inflateInit(&stream); - - SHA1_Init(&c); - - do { - ssize_t size; - if (*bufposn) { - stream.avail_in = *bufposn; - stream.next_in = (unsigned char *) buffer; - do { - stream.next_out = discard; - stream.avail_out = sizeof(discard); - ret = inflate(&stream, Z_SYNC_FLUSH); - SHA1_Update(&c, discard, sizeof(discard) - - stream.avail_out); - } while (stream.avail_in && ret == Z_OK); - if (write_buffer(local, buffer, *bufposn - stream.avail_in) < 0) - die("unable to write sha1 file"); - memmove(buffer, buffer + *bufposn - stream.avail_in, - stream.avail_in); - *bufposn = stream.avail_in; - if (ret != Z_OK) - break; - } - size = xread(fd, buffer + *bufposn, bufsize - *bufposn); - if (size <= 0) { - close(local); - unlink(tmpfile); - if (!size) - return error("Connection closed?"); - perror("Reading from connection"); - return -1; - } - *bufposn += size; - } while (1); - inflateEnd(&stream); - - fchmod(local, 0444); - if (close(local) != 0) - die("unable to write sha1 file"); - SHA1_Final(real_sha1, &c); - if (ret != Z_STREAM_END) { - unlink(tmpfile); - return error("File %s corrupted", sha1_to_hex(sha1)); - } - if (hashcmp(sha1, real_sha1)) { - unlink(tmpfile); - return error("File %s has bad hash", sha1_to_hex(sha1)); - } - - return move_temp_to_file(tmpfile, sha1_file_name(sha1)); -} - int has_pack_index(const unsigned char *sha1) { struct stat st; @@ -2390,12 +2318,11 @@ int has_sha1_pack(const unsigned char *sha1, const char **ignore_packed) int has_sha1_file(const unsigned char *sha1) { - struct stat st; struct pack_entry e; if (find_pack_entry(sha1, &e, NULL)) return 1; - return find_sha1_file(sha1, &st) ? 1 : 0; + return has_loose_object(sha1); } int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) diff --git a/t/.gitattributes b/t/.gitattributes index 562b12e16e..1b97c5465b 100644 --- a/t/.gitattributes +++ b/t/.gitattributes @@ -1 +1 @@ -* -whitespace +t[0-9][0-9][0-9][0-9]/* -whitespace @@ -54,6 +54,38 @@ You can pass --verbose (or -v), --debug (or -d), and --immediate This causes the test to immediately exit upon the first failed test. +--long-tests:: + This causes additional long-running tests to be run (where + available), for more exhaustive testing. + + +Skipping Tests +-------------- + +In some environments, certain tests have no way of succeeding +due to platform limitation, such as lack of 'unzip' program, or +filesystem that do not allow arbitrary sequence of non-NUL bytes +as pathnames. + +You should be able to say something like + + $ GIT_SKIP_TESTS=t9200.8 sh ./t9200-git-cvsexport-commit.sh + +and even: + + $ GIT_SKIP_TESTS='t[0-4]??? t91?? t9200.8' make + +to omit such tests. The value of the environment variable is a +SP separated list of patterns that tells which tests to skip, +and either can match the "t[0-9]{4}" part to skip the whole +test, or t[0-9]{4} followed by ".$number" to say which +particular test to skip. + +Note that some tests in the existing test suite rely on previous +test item, so you cannot arbitrarily disable one and expect the +remainder of test to check what the test originally was intended +to check. + Naming Tests ------------ diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh index c56d2fbaba..3d8e06a20f 100755 --- a/t/t0003-attributes.sh +++ b/t/t0003-attributes.sh @@ -54,4 +54,39 @@ test_expect_success 'root subdir attribute test' ' ' +test_expect_success 'setup bare' ' + + git clone --bare . bare.git && + cd bare.git + +' + +test_expect_success 'bare repository: check that .gitattribute is ignored' ' + + ( + echo "f test=f" + echo "a/i test=a/i" + ) >.gitattributes && + attr_check f unspecified && + attr_check a/f unspecified && + attr_check a/c/f unspecified && + attr_check a/i unspecified && + attr_check subdir/a/i unspecified + +' + +test_expect_success 'bare repository: test info/attributes' ' + + ( + echo "f test=f" + echo "a/i test=a/i" + ) >info/attributes && + attr_check f f && + attr_check a/f f && + attr_check a/c/f f && + attr_check a/i a/i && + attr_check subdir/a/i unspecified + +' + test_done diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index 9965cfa1dc..6309aed451 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -11,23 +11,35 @@ cat > expect.err << EOF usage: test-parse-options <options> -b, --boolean get a boolean + -4, --or4 bitwise-or boolean with ...0100 + -i, --integer <n> get a integer -j <n> get a integer, too + --set23 set integer to 23 + -t <time> get timestamp of <time> + -L, --length <str> get length of <str> -string options +String options -s, --string <string> get a string --string2 <str> get another string --st <st> get another string (pervert ordering) -o <str> get another string + --default-string set string to default -magic arguments +Magic arguments --quux means --quux +Standard options + --abbrev[=<n>] use <n> digits to display SHA-1s + -v, --verbose be verbose + -n, --dry-run dry run + -q, --quiet be quiet + EOF test_expect_success 'test help' ' - ! test-parse-options -h > output 2> output.err && + test_must_fail test-parse-options -h > output 2> output.err && test ! -s output && test_cmp expect.err output.err ' @@ -36,21 +48,31 @@ cat > expect << EOF boolean: 2 integer: 1729 string: 123 +abbrev: 7 +verbose: 2 +quiet: no +dry run: yes EOF test_expect_success 'short options' ' - test-parse-options -s123 -b -i 1729 -b > output 2> output.err && + test-parse-options -s123 -b -i 1729 -b -vv -n > output 2> output.err && test_cmp expect output && test ! -s output.err ' + cat > expect << EOF boolean: 2 integer: 1729 string: 321 +abbrev: 10 +verbose: 2 +quiet: no +dry run: no EOF test_expect_success 'long options' ' test-parse-options --boolean --integer 1729 --boolean --string2=321 \ + --verbose --verbose --no-dry-run --abbrev=10 \ > output 2> output.err && test ! -s output.err && test_cmp expect output @@ -60,6 +82,10 @@ cat > expect << EOF boolean: 1 integer: 13 string: 123 +abbrev: 7 +verbose: 0 +quiet: no +dry run: no arg 00: a1 arg 01: b1 arg 02: --boolean @@ -76,6 +102,10 @@ cat > expect << EOF boolean: 0 integer: 2 string: (not set) +abbrev: 7 +verbose: 0 +quiet: no +dry run: no EOF test_expect_success 'unambiguously abbreviated option' ' @@ -99,6 +129,10 @@ cat > expect << EOF boolean: 0 integer: 0 string: 123 +abbrev: 7 +verbose: 0 +quiet: no +dry run: no EOF test_expect_success 'non ambiguous option (after two options it abbreviates)' ' @@ -107,20 +141,24 @@ test_expect_success 'non ambiguous option (after two options it abbreviates)' ' test_cmp expect output ' -cat > expect.err << EOF +cat > typo.err << EOF error: did you mean \`--boolean\` (with two dashes ?) EOF test_expect_success 'detect possible typos' ' - ! test-parse-options -boolean > output 2> output.err && + test_must_fail test-parse-options -boolean > output 2> output.err && test ! -s output && - test_cmp expect.err output.err + test_cmp typo.err output.err ' cat > expect <<EOF boolean: 0 integer: 0 string: (not set) +abbrev: 7 +verbose: 0 +quiet: no +dry run: no arg 00: --quux EOF @@ -130,4 +168,68 @@ test_expect_success 'keep some options as arguments' ' test_cmp expect output ' +cat > expect <<EOF +boolean: 0 +integer: 1 +string: default +abbrev: 7 +verbose: 0 +quiet: yes +dry run: no +arg 00: foo +EOF + +test_expect_success 'OPT_DATE() and OPT_SET_PTR() work' ' + test-parse-options -t "1970-01-01 00:00:01 +0000" --default-string \ + foo -q > output 2> output.err && + test ! -s output.err && + test_cmp expect output +' + +cat > expect <<EOF +Callback: "four", 0 +boolean: 5 +integer: 4 +string: (not set) +abbrev: 7 +verbose: 0 +quiet: no +dry run: no +EOF + +test_expect_success 'OPT_CALLBACK() and OPT_BIT() work' ' + test-parse-options --length=four -b -4 > output 2> output.err && + test ! -s output.err && + test_cmp expect output +' + +cat > expect <<EOF +Callback: "not set", 1 +EOF + +test_expect_success 'OPT_CALLBACK() and callback errors work' ' + test_must_fail test-parse-options --no-length > output 2> output.err && + test_cmp expect output && + test_cmp expect.err output.err +' + +cat > expect <<EOF +boolean: 1 +integer: 23 +string: (not set) +abbrev: 7 +verbose: 0 +quiet: no +dry run: no +EOF + +test_expect_success 'OPT_BIT() and OPT_SET_INT() work' ' + test-parse-options --set23 -bbbbb --no-or4 > output 2> output.err && + test ! -s output.err && + test_cmp expect output +' + +# --or4 +# --no-or4 + test_done diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index cb1fbe5820..d8b7f2ffbc 100755 --- a/t/t1006-cat-file.sh +++ b/t/t1006-cat-file.sh @@ -74,7 +74,7 @@ $content" test -z "$content" || test_expect_success "--batch output of $type is correct" ' expect="$(maybe_remove_timestamp "$batch_output" $no_ts)" - actual="$(maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" no_ts)" + actual="$(maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" $no_ts)" if test "z$expect" = "z$actual" then : happy @@ -174,9 +174,27 @@ do ' done -test_expect_success "--batch-check for a non-existent object" ' - test "deadbeef missing" = \ - "$(echo_without_newline deadbeef | git cat-file --batch-check)" +test_expect_success "--batch-check for a non-existent named object" ' + test "foobar42 missing +foobar84 missing" = \ + "$( ( echo foobar42; echo_without_newline foobar84; ) | git cat-file --batch-check)" +' + +test_expect_success "--batch-check for a non-existent hash" ' + test "0000000000000000000000000000000000000042 missing +0000000000000000000000000000000000000084 missing" = \ + "$( ( echo 0000000000000000000000000000000000000042; + echo_without_newline 0000000000000000000000000000000000000084; ) \ + | git cat-file --batch-check)" +' + +test_expect_success "--batch for an existent and a non-existent hash" ' + test "$tag_sha1 tag $tag_size +$tag_content +0000000000000000000000000000000000000000 missing" = \ + "$( ( echo $tag_sha1; + echo_without_newline 0000000000000000000000000000000000000000; ) \ + | git cat-file --batch)" ' test_expect_success "--batch-check for an emtpy line" ' diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh index 5e4252a320..6c78c8bc9b 100755 --- a/t/t1301-shared-repo.sh +++ b/t/t1301-shared-repo.sh @@ -83,4 +83,19 @@ do done +test_expect_success 'git reflog expire honors core.sharedRepository' ' + git config core.sharedRepository group && + git reflog expire --all && + actual="$(ls -l .git/logs/refs/heads/master)" && + case "$actual" in + -rw-rw-*) + : happy + ;; + *) + echo Ooops, .git/logs/refs/heads/master is not 0662 [$actual] + false + ;; + esac +' + test_done diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index b8b7ab4103..f387d46f1a 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -42,6 +42,14 @@ test_expect_success "delete $m" ' ' rm -f .git/$m +test_expect_success "delete $m without oldvalue verification" " + git update-ref $m $A && + test $A = \$(cat .git/$m) && + git update-ref -d $m && + ! test -f .git/$m +" +rm -f .git/$m + test_expect_success \ "fail to create $n" \ "touch .git/$n_dir diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh index d24a47d114..997002d4c4 100755 --- a/t/t1502-rev-parse-parseopt.sh +++ b/t/t1502-rev-parse-parseopt.sh @@ -5,7 +5,7 @@ test_description='test git rev-parse --parseopt' cat > expect.err <<EOF usage: some-command [options] <args>... - + some-command does foo and bar! -h, --help show the help @@ -13,7 +13,7 @@ usage: some-command [options] <args>... --bar ... some cool option --bar with an argument An option group Header - -C [...] option C with an optional argument + -C[...] option C with an optional argument Extras --extra1 line above used to cause a segfault but no longer does diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh index df1fd6f86f..c851db8ca9 100755 --- a/t/t3800-mktag.sh +++ b/t/t3800-mktag.sh @@ -241,11 +241,11 @@ check_verify_failure 'disallow spaces in tag email' \ ############################################################ # 17. disallow missing tag timestamp -cat >tag.sig <<EOF +tr '_' ' ' >tag.sig <<EOF object $head type commit tag mytag -tagger T A Gger <tagger@example.com> +tagger T A Gger <tagger@example.com>__ EOF diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index 2d3ee3b78c..54d99ed0c3 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -41,7 +41,7 @@ test_expect_success 'apply needs clean working directory' ' echo 4 > other-file && git add other-file && echo 5 > other-file && - test_must_fail git stash apply + test_must_fail git stash apply ' test_expect_success 'apply stashed changes' ' diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 3583e68e92..7fe853c20d 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -98,7 +98,7 @@ test_expect_success 'extra headers' ' sed -e "/^$/q" patch2 > hdrs2 && grep "^To: R. E. Cipient <rcipient@example.com>$" hdrs2 && grep "^Cc: S. E. Cipient <scipient@example.com>$" hdrs2 - + ' test_expect_success 'extra headers without newlines' ' @@ -109,7 +109,7 @@ test_expect_success 'extra headers without newlines' ' sed -e "/^$/q" patch3 > hdrs3 && grep "^To: R. E. Cipient <rcipient@example.com>$" hdrs3 && grep "^Cc: S. E. Cipient <scipient@example.com>$" hdrs3 - + ' test_expect_success 'extra headers with multiple To:s' ' @@ -170,7 +170,7 @@ test_expect_success 'thread cover-letter' ' git checkout side && git format-patch --cover-letter --thread -o patches/ master && FIRST_MID=$(grep "Message-Id:" patches/0000-* | sed "s/^[^<]*\(<[^>]*>\).*$/\1/") && - for i in patches/0001-* patches/0002-* patches/0003-* + for i in patches/0001-* patches/0002-* patches/0003-* do grep "References: $FIRST_MID" $i && grep "In-Reply-To: $FIRST_MID" $i || break diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh index ca0302f41b..b7cc6b28e6 100755 --- a/t/t4015-diff-whitespace.sh +++ b/t/t4015-diff-whitespace.sh @@ -62,16 +62,16 @@ EOF git update-index x -cat << EOF > x +tr '_' ' ' << EOF > x whitespace at beginning whitespace change white space in the middle -whitespace at end +whitespace at end__ unchanged line CR at end EOF -tr 'Q' '\015' << EOF > expect +tr 'Q_' '\015 ' << EOF > expect diff --git a/x b/x index d99af23..8b32fb5 100644 --- a/x @@ -84,7 +84,7 @@ index d99af23..8b32fb5 100644 + whitespace at beginning +whitespace change +white space in the middle -+whitespace at end ++whitespace at end__ unchanged line -CR at endQ +CR at end diff --git a/t/t4016-diff-quote.sh b/t/t4016-diff-quote.sh index 0950250c9b..f07035ab7e 100755 --- a/t/t4016-diff-quote.sh +++ b/t/t4016-diff-quote.sh @@ -53,13 +53,13 @@ test_expect_success 'git diff --summary -M HEAD' ' ' cat >expect <<\EOF - pathname.1 => "Rpathname\twith HT.0" | 0 - pathname.3 => "Rpathname\nwith LF.0" | 0 - "pathname\twith HT.3" => "Rpathname\nwith LF.1" | 0 - pathname.2 => Rpathname with SP.0 | 0 - "pathname\twith HT.2" => Rpathname with SP.1 | 0 - pathname.0 => Rpathname.0 | 0 - "pathname\twith HT.0" => Rpathname.1 | 0 + pathname.1 => "Rpathname\twith HT.0" | 0 + pathname.3 => "Rpathname\nwith LF.0" | 0 + "pathname\twith HT.3" => "Rpathname\nwith LF.1" | 0 + pathname.2 => Rpathname with SP.0 | 0 + "pathname\twith HT.2" => Rpathname with SP.1 | 0 + pathname.0 => Rpathname.0 | 0 + "pathname\twith HT.0" => Rpathname.1 | 0 7 files changed, 0 insertions(+), 0 deletions(-) EOF test_expect_success 'git diff --stat -M HEAD' ' diff --git a/t/t4109-apply-multifrag.sh b/t/t4109-apply-multifrag.sh index bd40a218cd..ff5fdf35f9 100755 --- a/t/t4109-apply-multifrag.sh +++ b/t/t4109-apply-multifrag.sh @@ -9,134 +9,10 @@ test_description='git apply test patches with multiple fragments. ' . ./test-lib.sh -# setup - -cat > patch1.patch <<\EOF -diff --git a/main.c b/main.c -new file mode 100644 ---- /dev/null -+++ b/main.c -@@ -0,0 +1,23 @@ -+#include <stdio.h> -+ -+int func(int num); -+void print_int(int num); -+ -+int main() { -+ int i; -+ -+ for (i = 0; i < 10; i++) { -+ print_int(func(i)); -+ } -+ -+ return 0; -+} -+ -+int func(int num) { -+ return num * num; -+} -+ -+void print_int(int num) { -+ printf("%d", num); -+} -+ -EOF -cat > patch2.patch <<\EOF -diff --git a/main.c b/main.c ---- a/main.c -+++ b/main.c -@@ -1,7 +1,9 @@ -+#include <stdlib.h> - #include <stdio.h> - - int func(int num); - void print_int(int num); -+void print_ln(); - - int main() { - int i; -@@ -10,6 +12,8 @@ - print_int(func(i)); - } - -+ print_ln(); -+ - return 0; - } - -@@ -21,3 +25,7 @@ - printf("%d", num); - } - -+void print_ln() { -+ printf("\n"); -+} -+ -EOF -cat > patch3.patch <<\EOF -diff --git a/main.c b/main.c ---- a/main.c -+++ b/main.c -@@ -1,9 +1,7 @@ --#include <stdlib.h> - #include <stdio.h> - - int func(int num); - void print_int(int num); --void print_ln(); - - int main() { - int i; -@@ -12,8 +10,6 @@ - print_int(func(i)); - } - -- print_ln(); -- - return 0; - } - -@@ -25,7 +21,3 @@ - printf("%d", num); - } - --void print_ln() { -- printf("\n"); --} -- -EOF -cat > patch4.patch <<\EOF -diff --git a/main.c b/main.c ---- a/main.c -+++ b/main.c -@@ -1,13 +1,14 @@ - #include <stdio.h> - - int func(int num); --void print_int(int num); -+int func2(int num); - - int main() { - int i; - - for (i = 0; i < 10; i++) { -- print_int(func(i)); -+ printf("%d", func(i)); -+ printf("%d", func3(i)); - } - - return 0; -@@ -17,7 +18,7 @@ - return num * num; - } - --void print_int(int num) { -- printf("%d", num); -+int func2(int num) { -+ return num * num * num; - } - -EOF +cp ../t4109/patch1.patch . +cp ../t4109/patch2.patch . +cp ../t4109/patch3.patch . +cp ../t4109/patch4.patch . test_expect_success "S = git apply (1)" \ 'git apply patch1.patch patch2.patch' diff --git a/t/t4109/patch1.patch b/t/t4109/patch1.patch new file mode 100644 index 0000000000..1d411fc3cc --- /dev/null +++ b/t/t4109/patch1.patch @@ -0,0 +1,28 @@ +diff --git a/main.c b/main.c +new file mode 100644 +--- /dev/null ++++ b/main.c +@@ -0,0 +1,23 @@ ++#include <stdio.h> ++ ++int func(int num); ++void print_int(int num); ++ ++int main() { ++ int i; ++ ++ for (i = 0; i < 10; i++) { ++ print_int(func(i)); ++ } ++ ++ return 0; ++} ++ ++int func(int num) { ++ return num * num; ++} ++ ++void print_int(int num) { ++ printf("%d", num); ++} ++ diff --git a/t/t4109/patch2.patch b/t/t4109/patch2.patch new file mode 100644 index 0000000000..8c6b06d536 --- /dev/null +++ b/t/t4109/patch2.patch @@ -0,0 +1,30 @@ +diff --git a/main.c b/main.c +--- a/main.c ++++ b/main.c +@@ -1,7 +1,9 @@ ++#include <stdlib.h> + #include <stdio.h> + + int func(int num); + void print_int(int num); ++void print_ln(); + + int main() { + int i; +@@ -10,6 +12,8 @@ + print_int(func(i)); + } + ++ print_ln(); ++ + return 0; + } + +@@ -21,3 +25,7 @@ + printf("%d", num); + } + ++void print_ln() { ++ printf("\n"); ++} ++ diff --git a/t/t4109/patch3.patch b/t/t4109/patch3.patch new file mode 100644 index 0000000000..d696c55a75 --- /dev/null +++ b/t/t4109/patch3.patch @@ -0,0 +1,31 @@ +cat > patch3.patch <<\EOF +diff --git a/main.c b/main.c +--- a/main.c ++++ b/main.c +@@ -1,9 +1,7 @@ +-#include <stdlib.h> + #include <stdio.h> + + int func(int num); + void print_int(int num); +-void print_ln(); + + int main() { + int i; +@@ -12,8 +10,6 @@ + print_int(func(i)); + } + +- print_ln(); +- + return 0; + } + +@@ -25,7 +21,3 @@ + printf("%d", num); + } + +-void print_ln() { +- printf("\n"); +-} +- diff --git a/t/t4109/patch4.patch b/t/t4109/patch4.patch new file mode 100644 index 0000000000..4b085909b1 --- /dev/null +++ b/t/t4109/patch4.patch @@ -0,0 +1,30 @@ +diff --git a/main.c b/main.c +--- a/main.c ++++ b/main.c +@@ -1,13 +1,14 @@ + #include <stdio.h> + + int func(int num); +-void print_int(int num); ++int func2(int num); + + int main() { + int i; + + for (i = 0; i < 10; i++) { +- print_int(func(i)); ++ printf("%d", func(i)); ++ printf("%d", func3(i)); + } + + return 0; +@@ -17,7 +18,7 @@ + return num * num; + } + +-void print_int(int num) { +- printf("%d", num); ++int func2(int num) { ++ return num * num * num; + } + diff --git a/t/t4119-apply-config.sh b/t/t4119-apply-config.sh index b540f7295a..3c73a783a7 100755 --- a/t/t4119-apply-config.sh +++ b/t/t4119-apply-config.sh @@ -19,12 +19,12 @@ test_expect_success setup ' ' # Also handcraft GNU diff output; note this has trailing whitespace. -cat >gpatch.file <<\EOF && +tr '_' ' ' >gpatch.file <<\EOF && --- file1 2007-02-21 01:04:24.000000000 -0800 +++ file1+ 2007-02-21 01:07:44.000000000 -0800 @@ -1 +1 @@ -A -+B ++B_ EOF sed -e 's|file1|sub/&|' gpatch.file >gpatch-sub.file && diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh index 0cfd47cfcf..ceb6a79fe0 100755 --- a/t/t4126-apply-empty.sh +++ b/t/t4126-apply-empty.sh @@ -26,7 +26,6 @@ test_expect_success setup ' test_expect_success 'apply empty' ' git reset --hard && - >empty && rm -f missing && git apply patch0 && test_cmp expect empty @@ -34,7 +33,6 @@ test_expect_success 'apply empty' ' test_expect_success 'apply --index empty' ' git reset --hard && - >empty && rm -f missing && git apply --index patch0 && test_cmp expect empty && @@ -43,7 +41,6 @@ test_expect_success 'apply --index empty' ' test_expect_success 'apply create' ' git reset --hard && - >empty && rm -f missing && git apply patch1 && test_cmp expect missing @@ -51,7 +48,6 @@ test_expect_success 'apply create' ' test_expect_success 'apply --index create' ' git reset --hard && - >empty && rm -f missing && git apply --index patch1 && test_cmp expect missing && diff --git a/t/t4150-am.sh b/t/t4150-am.sh index 722ae96cd5..bc982607d0 100755 --- a/t/t4150-am.sh +++ b/t/t4150-am.sh @@ -110,7 +110,7 @@ test_expect_success 'am applies patch correctly' ' GIT_AUTHOR_NAME="Another Thor" GIT_AUTHOR_EMAIL="a.thor@example.com" -GIT_COMMITTER_NAME="Co M Miter" +GIT_COMMITTER_NAME="Co M Miter" GIT_COMMITTER_EMAIL="c.miter@example.com" export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 9b0baac8db..3f1e25d921 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -45,6 +45,11 @@ test_expect_success \ (cd a && find .) | sort >a.lst' test_expect_success \ + 'add ignored file' \ + 'echo ignore me >a/ignored && + echo ignored export-ignore >.gitattributes' + +test_expect_success \ 'add files to repository' \ 'find a -type f | xargs git update-index --add && find a -type l | xargs git update-index --add && @@ -54,6 +59,10 @@ test_expect_success \ git commit-tree $treeid </dev/null)' test_expect_success \ + 'remove ignored file' \ + 'rm a/ignored' + +test_expect_success \ 'git archive' \ 'git archive HEAD >b.tar' diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh index 09fd917672..ecec591634 100755 --- a/t/t5302-pack-index.sh +++ b/t/t5302-pack-index.sh @@ -162,4 +162,18 @@ test_expect_success \ '[index v2] 5) pack-objects refuses to reuse corrupted data' \ '! git pack-objects test-5 <obj-list' +test_expect_success \ + '[index v2] 6) verify-pack detects CRC mismatch' \ + 'rm -f .git/objects/pack/* && + git-index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" && + git verify-pack ".git/objects/pack/pack-${pack1}.pack" && + chmod +w ".git/objects/pack/pack-${pack1}.idx" && + dd if=/dev/zero of=".git/objects/pack/pack-${pack1}.idx" conv=notrunc \ + bs=1 count=4 seek=$((8 + 256 * 4 + `wc -l <obj-list` * 20 + 0)) && + ( while read obj + do git cat-file -p $obj >/dev/null || exit 1 + done <obj-list ) && + err=$(! git verify-pack ".git/objects/pack/pack-${pack1}.pack" 2>&1) && + echo "$err" | grep "CRC mismatch"' + test_done diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh new file mode 100755 index 0000000000..b0f5693482 --- /dev/null +++ b/t/t5303-pack-corruption-resilience.sh @@ -0,0 +1,194 @@ +#!/bin/sh +# +# Copyright (c) 2008 Nicolas Pitre +# + +test_description='resilience to pack corruptions with redundant objects' +. ./test-lib.sh + +# Note: the test objects are created with knowledge of their pack encoding +# to ensure good code path coverage, and to facilitate direct alteration +# later on. The assumed characteristics are: +# +# 1) blob_2 is a delta with blob_1 for base and blob_3 is a delta with blob2 +# for base, such that blob_3 delta depth is 2; +# +# 2) the bulk of object data is uncompressible so the text part remains +# visible; +# +# 3) object header is always 2 bytes. + +create_test_files() { + test-genrandom "foo" 2000 > file_1 && + test-genrandom "foo" 1800 > file_2 && + test-genrandom "foo" 1800 > file_3 && + echo " base " >> file_1 && + echo " delta1 " >> file_2 && + echo " delta delta2 " >> file_3 && + test-genrandom "bar" 150 >> file_2 && + test-genrandom "baz" 100 >> file_3 +} + +create_new_pack() { + rm -rf .git && + git init && + blob_1=`git hash-object -t blob -w file_1` && + blob_2=`git hash-object -t blob -w file_2` && + blob_3=`git hash-object -t blob -w file_3` && + pack=`printf "$blob_1\n$blob_2\n$blob_3\n" | + git pack-objects $@ .git/objects/pack/pack` && + pack=".git/objects/pack/pack-${pack}" && + git verify-pack -v ${pack}.pack +} + +do_corrupt_object() { + ofs=`git show-index < ${pack}.idx | grep $1 | cut -f1 -d" "` && + ofs=$(($ofs + $2)) && + chmod +w ${pack}.pack && + dd if=/dev/zero of=${pack}.pack count=1 bs=1 conv=notrunc seek=$ofs && + test_must_fail git verify-pack ${pack}.pack +} + +test_expect_success \ + 'initial setup validation' \ + 'create_test_files && + create_new_pack && + git prune-packed && + git cat-file blob $blob_1 > /dev/null && + git cat-file blob $blob_2 > /dev/null && + git cat-file blob $blob_3 > /dev/null' + +test_expect_success \ + 'create corruption in header of first object' \ + 'do_corrupt_object $blob_1 0 && + test_must_fail git cat-file blob $blob_1 > /dev/null && + test_must_fail git cat-file blob $blob_2 > /dev/null && + test_must_fail git cat-file blob $blob_3 > /dev/null' + +test_expect_success \ + '... but having a loose copy allows for full recovery' \ + 'mv ${pack}.idx tmp && + git hash-object -t blob -w file_1 && + mv tmp ${pack}.idx && + git cat-file blob $blob_1 > /dev/null && + git cat-file blob $blob_2 > /dev/null && + git cat-file blob $blob_3 > /dev/null' + +test_expect_success \ + '... and loose copy of first delta allows for partial recovery' \ + 'git prune-packed && + test_must_fail git cat-file blob $blob_2 > /dev/null && + mv ${pack}.idx tmp && + git hash-object -t blob -w file_2 && + mv tmp ${pack}.idx && + test_must_fail git cat-file blob $blob_1 > /dev/null && + git cat-file blob $blob_2 > /dev/null && + git cat-file blob $blob_3 > /dev/null' + +test_expect_success \ + 'create corruption in data of first object' \ + 'create_new_pack && + git prune-packed && + chmod +w ${pack}.pack && + perl -i -pe "s/ base /abcdef/" ${pack}.pack && + test_must_fail git cat-file blob $blob_1 > /dev/null && + test_must_fail git cat-file blob $blob_2 > /dev/null && + test_must_fail git cat-file blob $blob_3 > /dev/null' + +test_expect_success \ + '... but having a loose copy allows for full recovery' \ + 'mv ${pack}.idx tmp && + git hash-object -t blob -w file_1 && + mv tmp ${pack}.idx && + git cat-file blob $blob_1 > /dev/null && + git cat-file blob $blob_2 > /dev/null && + git cat-file blob $blob_3 > /dev/null' + +test_expect_success \ + '... and loose copy of second object allows for partial recovery' \ + 'git prune-packed && + test_must_fail git cat-file blob $blob_2 > /dev/null && + mv ${pack}.idx tmp && + git hash-object -t blob -w file_2 && + mv tmp ${pack}.idx && + test_must_fail git cat-file blob $blob_1 > /dev/null && + git cat-file blob $blob_2 > /dev/null && + git cat-file blob $blob_3 > /dev/null' + +test_expect_success \ + 'create corruption in header of first delta' \ + 'create_new_pack && + git prune-packed && + do_corrupt_object $blob_2 0 && + git cat-file blob $blob_1 > /dev/null && + test_must_fail git cat-file blob $blob_2 > /dev/null && + test_must_fail git cat-file blob $blob_3 > /dev/null' + +test_expect_success \ + '... but having a loose copy allows for full recovery' \ + 'mv ${pack}.idx tmp && + git hash-object -t blob -w file_2 && + mv tmp ${pack}.idx && + git cat-file blob $blob_1 > /dev/null && + git cat-file blob $blob_2 > /dev/null && + git cat-file blob $blob_3 > /dev/null' + +test_expect_success \ + 'create corruption in data of first delta' \ + 'create_new_pack && + git prune-packed && + chmod +w ${pack}.pack && + perl -i -pe "s/ delta1 /abcdefgh/" ${pack}.pack && + git cat-file blob $blob_1 > /dev/null && + test_must_fail git cat-file blob $blob_2 > /dev/null && + test_must_fail git cat-file blob $blob_3 > /dev/null' + +test_expect_success \ + '... but having a loose copy allows for full recovery' \ + 'mv ${pack}.idx tmp && + git hash-object -t blob -w file_2 && + mv tmp ${pack}.idx && + git cat-file blob $blob_1 > /dev/null && + git cat-file blob $blob_2 > /dev/null && + git cat-file blob $blob_3 > /dev/null' + +test_expect_success \ + 'corruption in delta base reference of first delta (OBJ_REF_DELTA)' \ + 'create_new_pack && + git prune-packed && + do_corrupt_object $blob_2 2 && + git cat-file blob $blob_1 > /dev/null && + test_must_fail git cat-file blob $blob_2 > /dev/null && + test_must_fail git cat-file blob $blob_3 > /dev/null' + +test_expect_success \ + '... but having a loose copy allows for full recovery' \ + 'mv ${pack}.idx tmp && + git hash-object -t blob -w file_2 && + mv tmp ${pack}.idx && + git cat-file blob $blob_1 > /dev/null && + git cat-file blob $blob_2 > /dev/null && + git cat-file blob $blob_3 > /dev/null' + +test_expect_success \ + 'corruption in delta base reference of first delta (OBJ_OFS_DELTA)' \ + 'create_new_pack --delta-base-offset && + git prune-packed && + do_corrupt_object $blob_2 2 && + git cat-file blob $blob_1 > /dev/null && + test_must_fail git cat-file blob $blob_2 > /dev/null && + test_must_fail git cat-file blob $blob_3 > /dev/null' + +test_expect_success \ + '... and a redundant pack allows for full recovery too' \ + 'mv ${pack}.idx tmp && + git hash-object -t blob -w file_1 && + git hash-object -t blob -w file_2 && + printf "$blob_1\n$blob_2\n" | git pack-objects .git/objects/pack/pack && + git prune-packed && + mv tmp ${pack}.idx && + git cat-file blob $blob_1 > /dev/null && + git cat-file blob $blob_2 > /dev/null && + git cat-file blob $blob_3 > /dev/null' + +test_done diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index 0d7ed1f99b..1e192a2207 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -138,6 +138,25 @@ test_expect_success 'show' ' test_cmp expect output) ' +cat > test/expect << EOF +* remote origin + URL: $(pwd)/one/.git + Remote branch merged with 'git pull' while on branch master + master + Tracked remote branches + master side + Local branches pushed with 'git push' + master:upstream +refs/tags/lastbackup +EOF + +test_expect_success 'show -n' ' + (mv one one.unreachable && + cd test && + git remote show -n origin > output && + mv ../one.unreachable ../one && + test_cmp expect output) +' + test_expect_success 'prune' ' (cd one && git branch -m side side2) && @@ -148,6 +167,24 @@ test_expect_success 'prune' ' ! git rev-parse refs/remotes/origin/side) ' +cat > test/expect << EOF +Pruning origin +URL: $(pwd)/one/.git + * [would prune] origin/side2 +EOF + +test_expect_success 'prune --dry-run' ' + (cd one && + git branch -m side2 side) && + (cd test && + git remote prune --dry-run origin > output && + git rev-parse refs/remotes/origin/side2 && + ! git rev-parse refs/remotes/origin/side && + (cd ../one && + git branch -m side side2) && + test_cmp expect output) +' + test_expect_success 'add --mirror && prune' ' (mkdir mirror && cd mirror && diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 6946557c67..df7750f7d1 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -37,7 +37,8 @@ test_expect_success "clone and setup child repos" ' echo "Pull: refs/heads/one:refs/heads/one" } >.git/remotes/two && cd .. && - git clone . bundle + git clone . bundle && + git clone . seven ' test_expect_success "fetch test" ' @@ -295,4 +296,11 @@ test_expect_success 'configured fetch updates tracking' ' ) ' +test_expect_success 'pushing nonexistent branch by mistake should not segv' ' + + cd "$D" && + test_must_fail git push seven no:no + +' + test_done diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh index 3def75eeb2..8becbc3f38 100755 --- a/t/t5515-fetch-merge-logic.sh +++ b/t/t5515-fetch-merge-logic.sh @@ -142,9 +142,12 @@ do set x $cmd; shift git symbolic-ref HEAD refs/heads/$1 ; shift rm -f .git/FETCH_HEAD - rm -f .git/refs/heads/* - rm -f .git/refs/remotes/rem/* - rm -f .git/refs/tags/* + git for-each-ref \ + refs/heads refs/remotes/rem refs/tags | + while read val type refname + do + git update-ref -d "$refname" "$val" + done git fetch "$@" >/dev/null cat .git/FETCH_HEAD } >"$actual_f" && diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh index 7372439164..f15dd03e4d 100755 --- a/t/t5540-http-push.sh +++ b/t/t5540-http-push.sh @@ -38,7 +38,7 @@ test_expect_success 'setup remote repository' ' cd - && mv test_repo.git $HTTPD_DOCUMENT_ROOT_PATH ' - + test_expect_success 'clone remote repository' ' cd "$ROOT_PATH" && git clone $HTTPD_URL/test_repo.git test_repo_clone diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index 593d1a3877..b642fb260b 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -30,4 +30,26 @@ test_expect_success 'clone checks out files' ' ' +test_expect_success 'clone respects GIT_WORK_TREE' ' + + GIT_WORK_TREE=worktree git clone src bare && + test -f bare/config && + test -f worktree/file + +' + +test_expect_success 'clone creates intermediate directories' ' + + git clone src long/path/to/dst && + test -f long/path/to/dst/file + +' + +test_expect_success 'clone creates intermediate directories for bare repo' ' + + git clone --bare src long/path/to/bare/dst && + test -f long/path/to/bare/dst/config + +' + test_done diff --git a/t/t6033-merge-crlf.sh b/t/t6033-merge-crlf.sh new file mode 100755 index 0000000000..75d9602de4 --- /dev/null +++ b/t/t6033-merge-crlf.sh @@ -0,0 +1,52 @@ +#!/bin/sh + +append_cr () { + sed -e 's/$/Q/' | tr Q '\015' +} + +remove_cr () { + tr '\015' Q | sed -e 's/Q$//' +} + +test_description='merge conflict in crlf repo + + b---M + / / + initial---a + +' + +. ./test-lib.sh + +test_expect_success setup ' + git config core.autocrlf true && + echo foo | append_cr >file && + git add file && + git commit -m "Initial" && + git tag initial && + git branch side && + echo line from a | append_cr >file && + git commit -m "add line from a" file && + git tag a && + git checkout side && + echo line from b | append_cr >file && + git commit -m "add line from b" file && + git tag b && + git checkout master +' + +test_expect_success 'Check "ours" is CRLF' ' + git reset --hard initial && + git merge side -s ours && + cat file | remove_cr | append_cr >file.temp && + test_cmp file file.temp +' + +test_expect_success 'Check that conflict file is CRLF' ' + git reset --hard a && + test_must_fail git merge side && + cat file | remove_cr | append_cr >file.temp && + test_cmp file file.temp +' + +test_done diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh index ed871a6b4d..c25eff9e46 100755 --- a/t/t7502-commit.sh +++ b/t/t7502-commit.sh @@ -212,7 +212,11 @@ test_expect_success 'do not fire editor in the presence of conflicts' ' # Must fail due to conflict test_must_fail git cherry-pick -n master && echo "editor not started" >.git/result && - test_must_fail GIT_EDITOR="$(pwd)/.git/FAKE_EDITOR" git commit && + ( + GIT_EDITOR="$(pwd)/.git/FAKE_EDITOR" && + export GIT_EDITOR && + test_must_fail git commit + ) && test "$(cat .git/result)" = "editor not started" ' diff --git a/t/t7502-status.sh b/t/t7502-status.sh index 80a438d4d9..38a48b57c7 100755 --- a/t/t7502-status.sh +++ b/t/t7502-status.sh @@ -67,6 +67,104 @@ test_expect_success 'status (2)' ' ' +cat >expect <<EOF +# On branch master +# Changes to be committed: +# (use "git reset HEAD <file>..." to unstage) +# +# new file: dir2/added +# +# Changed but not updated: +# (use "git add <file>..." to update what will be committed) +# +# modified: dir1/modified +# +# Untracked files not listed (use -u option to show untracked files) +EOF +test_expect_success 'status -uno' ' + mkdir dir3 && + : > dir3/untracked1 && + : > dir3/untracked2 && + git status -uno >output && + test_cmp expect output +' + +test_expect_success 'status (status.showUntrackedFiles no)' ' + git config status.showuntrackedfiles no + git status >output && + test_cmp expect output +' + +cat >expect <<EOF +# On branch master +# Changes to be committed: +# (use "git reset HEAD <file>..." to unstage) +# +# new file: dir2/added +# +# Changed but not updated: +# (use "git add <file>..." to update what will be committed) +# +# modified: dir1/modified +# +# Untracked files: +# (use "git add <file>..." to include in what will be committed) +# +# dir1/untracked +# dir2/modified +# dir2/untracked +# dir3/ +# expect +# output +# untracked +EOF +test_expect_success 'status -unormal' ' + git status -unormal >output && + test_cmp expect output +' + +test_expect_success 'status (status.showUntrackedFiles normal)' ' + git config status.showuntrackedfiles normal + git status >output && + test_cmp expect output +' + +cat >expect <<EOF +# On branch master +# Changes to be committed: +# (use "git reset HEAD <file>..." to unstage) +# +# new file: dir2/added +# +# Changed but not updated: +# (use "git add <file>..." to update what will be committed) +# +# modified: dir1/modified +# +# Untracked files: +# (use "git add <file>..." to include in what will be committed) +# +# dir1/untracked +# dir2/modified +# dir2/untracked +# dir3/untracked1 +# dir3/untracked2 +# expect +# output +# untracked +EOF +test_expect_success 'status -uall' ' + git status -uall >output && + test_cmp expect output +' +test_expect_success 'status (status.showUntrackedFiles all)' ' + git config status.showuntrackedfiles all + git status >output && + rm -rf dir3 && + git config --unset status.showuntrackedfiles && + test_cmp expect output +' + cat > expect << \EOF # On branch master # Changes to be committed: diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh index 8c58f0b8d7..1190576a65 100755 --- a/t/t9122-git-svn-author.sh +++ b/t/t9122-git-svn-author.sh @@ -64,7 +64,21 @@ test_expect_success 'interact with it via git-svn' ' # Make sure --add-author-from with --use-log-author affected # the authorship information - grep "^Author: A U Thor " actual.4 + grep "^Author: A U Thor " actual.4 && + + # Make sure there are no commit messages with excess blank lines + test $(grep "^ " actual.2 | wc -l) = 3 && + test $(grep "^ " actual.3 | wc -l) = 5 && + test $(grep "^ " actual.4 | wc -l) = 5 && + + # Make sure there are no svn commit messages with excess blank lines + ( + cd work.svn && + svn up && + + test $(svn log -r2:2 | wc -l) = 5 && + test $(svn log -r4:4 | wc -l) = 7 + ) ' test_done diff --git a/t/t9123-git-svn-rebuild-with-rewriteroot.sh b/t/t9123-git-svn-rebuild-with-rewriteroot.sh new file mode 100755 index 0000000000..c18878fad1 --- /dev/null +++ b/t/t9123-git-svn-rebuild-with-rewriteroot.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# +# Copyright (c) 2008 Jan Krüger +# + +test_description='git-svn respects rewriteRoot during rebuild' + +. ./lib-git-svn.sh + +mkdir import +cd import + touch foo + svn import -m 'import for git-svn' . "$svnrepo" >/dev/null +cd .. +rm -rf import + +test_expect_success 'init, fetch and checkout repository' ' + git svn init --rewrite-root=http://invalid.invalid/ "$svnrepo" && + git svn fetch + git checkout -b mybranch remotes/git-svn + ' + +test_expect_success 'remove rev_map' ' + rm "$GIT_SVN_DIR"/.rev_map.* + ' + +test_expect_success 'rebuild rev_map' ' + git svn rebase >/dev/null + ' + +test_done + diff --git a/t/t9301-fast-export.sh b/t/t9301-fast-export.sh index f09bfb1117..f1bc5ceef0 100755 --- a/t/t9301-fast-export.sh +++ b/t/t9301-fast-export.sh @@ -78,6 +78,29 @@ test_expect_success 'iso-8859-1' ' git cat-file commit i18n | grep "Áéí óú") ' +test_expect_success 'import/export-marks' ' + + git checkout -b marks master && + git fast-export --export-marks=tmp-marks HEAD && + test -s tmp-marks && + test $(wc -l < tmp-marks) -eq 3 && + test $( + git fast-export --import-marks=tmp-marks\ + --export-marks=tmp-marks HEAD | + grep ^commit | + wc -l) \ + -eq 0 && + echo change > file && + git commit -m "last commit" file && + test $( + git fast-export --import-marks=tmp-marks \ + --export-marks=tmp-marks HEAD | + grep ^commit\ | + wc -l) \ + -eq 1 && + test $(wc -l < tmp-marks) -eq 4 + +' cat > signed-tag-import << EOF tag sign-your-name diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh new file mode 100755 index 0000000000..b2fb9ece9c --- /dev/null +++ b/t/t9700-perl-git.sh @@ -0,0 +1,39 @@ +#!/bin/sh +# +# Copyright (c) 2008 Lea Wiemann +# + +test_description='perl interface (Git.pm)' +. ./test-lib.sh + +# set up test repository + +test_expect_success \ + 'set up test repository' \ + 'echo "test file 1" > file1 && + echo "test file 2" > file2 && + mkdir directory1 && + echo "in directory1" >> directory1/file && + mkdir directory2 && + echo "in directory2" >> directory2/file && + git add . && + git commit -m "first commit" && + + echo "changed file 1" > file1 && + git commit -a -m "second commit" && + + git-config --add color.test.slot1 green && + git-config --add test.string value && + git-config --add test.dupstring value1 && + git-config --add test.dupstring value2 && + git-config --add test.booltrue true && + git-config --add test.boolfalse no && + git-config --add test.boolother other && + git-config --add test.int 2k + ' + +test_external_without_stderr \ + 'Perl API' \ + perl ../t9700/test.pl + +test_done diff --git a/t/t9700/test.pl b/t/t9700/test.pl new file mode 100755 index 0000000000..4d2312548a --- /dev/null +++ b/t/t9700/test.pl @@ -0,0 +1,100 @@ +#!/usr/bin/perl +use lib (split(/:/, $ENV{GITPERLLIB})); + +use 5.006002; +use warnings; +use strict; + +use Test::More qw(no_plan); + +use Cwd; +use File::Basename; +use File::Temp; + +BEGIN { use_ok('Git') } + +# set up +our $repo_dir = "trash directory"; +our $abs_repo_dir = Cwd->cwd; +die "this must be run by calling the t/t97* shell script(s)\n" + if basename(Cwd->cwd) ne $repo_dir; +ok(our $r = Git->repository(Directory => "."), "open repository"); + +# config +is($r->config("test.string"), "value", "config scalar: string"); +is_deeply([$r->config("test.dupstring")], ["value1", "value2"], + "config array: string"); +is($r->config("test.nonexistent"), undef, "config scalar: nonexistent"); +is_deeply([$r->config("test.nonexistent")], [], "config array: nonexistent"); +is($r->config_int("test.int"), 2048, "config_int: integer"); +is($r->config_int("test.nonexistent"), undef, "config_int: nonexistent"); +ok($r->config_bool("test.booltrue"), "config_bool: true"); +ok(!$r->config_bool("test.boolfalse"), "config_bool: false"); +our $ansi_green = "\x1b[32m"; +is($r->get_color("color.test.slot1", "red"), $ansi_green, "get_color"); +# Cannot test $r->get_colorbool("color.foo")) because we do not +# control whether our STDOUT is a terminal. + +# Failure cases for config: +# Save and restore STDERR; we will probably extract this into a +# "dies_ok" method and possibly move the STDERR handling to Git.pm. +open our $tmpstderr, ">&", STDERR or die "cannot save STDERR"; close STDERR; +eval { $r->config("test.dupstring") }; +ok($@, "config: duplicate entry in scalar context fails"); +eval { $r->config_bool("test.boolother") }; +ok($@, "config_bool: non-boolean values fail"); +open STDERR, ">&", $tmpstderr or die "cannot restore STDERR"; + +# ident +like($r->ident("aUthor"), qr/^A U Thor <author\@example.com> [0-9]+ \+0000$/, + "ident scalar: author (type)"); +like($r->ident("cOmmitter"), qr/^C O Mitter <committer\@example.com> [0-9]+ \+0000$/, + "ident scalar: committer (type)"); +is($r->ident("invalid"), "invalid", "ident scalar: invalid ident string (no parsing)"); +my ($name, $email, $time_tz) = $r->ident('author'); +is_deeply([$name, $email], ["A U Thor", "author\@example.com"], + "ident array: author"); +like($time_tz, qr/[0-9]+ \+0000/, "ident array: author"); +is_deeply([$r->ident("Name <email> 123 +0000")], ["Name", "email", "123 +0000"], + "ident array: ident string"); +is_deeply([$r->ident("invalid")], [], "ident array: invalid ident string"); + +# ident_person +is($r->ident_person("aUthor"), "A U Thor <author\@example.com>", + "ident_person: author (type)"); +is($r->ident_person("Name <email> 123 +0000"), "Name <email>", + "ident_person: ident string"); +is($r->ident_person("Name", "email", "123 +0000"), "Name <email>", + "ident_person: array"); + +# objects and hashes +ok(our $file1hash = $r->command_oneline('rev-parse', "HEAD:file1"), "(get file hash)"); +our $tmpfile = File::Temp->new; +is($r->cat_blob($file1hash, $tmpfile), 15, "cat_blob: size"); +our $blobcontents; +{ local $/; seek $tmpfile, 0, 0; $blobcontents = <$tmpfile>; } +is($blobcontents, "changed file 1\n", "cat_blob: data"); +seek $tmpfile, 0, 0; +is(Git::hash_object("blob", $tmpfile), $file1hash, "hash_object: roundtrip"); +$tmpfile = File::Temp->new(); +print $tmpfile my $test_text = "test blob, to be inserted\n"; +like(our $newhash = $r->hash_and_insert_object($tmpfile), qr/[0-9a-fA-F]{40}/, + "hash_and_insert_object: returns hash"); +$tmpfile = File::Temp->new; +is($r->cat_blob($newhash, $tmpfile), length $test_text, "cat_blob: roundtrip size"); +{ local $/; seek $tmpfile, 0, 0; $blobcontents = <$tmpfile>; } +is($blobcontents, $test_text, "cat_blob: roundtrip data"); + +# paths +is($r->repo_path, "./.git", "repo_path"); +is($r->wc_path, $abs_repo_dir . "/", "wc_path"); +is($r->wc_subdir, "", "wc_subdir initial"); +$r->wc_chdir("directory1"); +is($r->wc_subdir, "directory1", "wc_subdir after wc_chdir"); +TODO: { + local $TODO = "commands do not work after wc_chdir"; + # Failure output is active even in non-verbose mode and thus + # annoying. Hence we skip these tests as long as they fail. + todo_skip 'config after wc_chdir', 1; + is($r->config("color.string"), "value", "config after wc_chdir"); +} diff --git a/t/test-lib.sh b/t/test-lib.sh index 2ad3f4a126..4010110010 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -80,6 +80,8 @@ do debug=t; shift ;; -i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate) immediate=t; shift ;; + -l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests) + export GIT_TEST_LONG=t; shift ;; -h|--h|--he|--hel|--help) help=t; shift ;; -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) @@ -169,7 +171,7 @@ trap 'die' exit # environment variables to work around this. # # In particular, quoting isn't enough, as the path may contain the same quote -# that we're using. +# that we're using. test_set_editor () { FAKE_EDITOR="$1" export FAKE_EDITOR @@ -304,6 +306,64 @@ test_expect_code () { echo >&3 "" } +# test_external runs external test scripts that provide continuous +# test output about their progress, and succeeds/fails on +# zero/non-zero exit code. It outputs the test output on stdout even +# in non-verbose mode, and announces the external script with "* run +# <n>: ..." before running it. When providing relative paths, keep in +# mind that all scripts run in "trash directory". +# Usage: test_external description command arguments... +# Example: test_external 'Perl API' perl ../path/to/test.pl +test_external () { + test "$#" -eq 3 || + error >&5 "bug in the test script: not 3 parameters to test_external" + descr="$1" + shift + if ! test_skip "$descr" "$@" + then + # Announce the script to reduce confusion about the + # test output that follows. + say_color "" " run $(expr "$test_count" + 1): $descr ($*)" + # Run command; redirect its stderr to &4 as in + # test_run_, but keep its stdout on our stdout even in + # non-verbose mode. + "$@" 2>&4 + if [ "$?" = 0 ] + then + test_ok_ "$descr" + else + test_failure_ "$descr" "$@" + fi + fi +} + +# Like test_external, but in addition tests that the command generated +# no output on stderr. +test_external_without_stderr () { + # The temporary file has no (and must have no) security + # implications. + tmp="$TMPDIR"; if [ -z "$tmp" ]; then tmp=/tmp; fi + stderr="$tmp/git-external-stderr.$$.tmp" + test_external "$@" 4> "$stderr" + [ -f "$stderr" ] || error "Internal error: $stderr disappeared." + descr="no stderr: $1" + shift + say >&3 "expecting no stderr from previous command" + if [ ! -s "$stderr" ]; then + rm "$stderr" + test_ok_ "$descr" + else + if [ "$verbose" = t ]; then + output=`echo; echo Stderr is:; cat "$stderr"` + else + output= + fi + # rm first in case test_failure exits. + rm "$stderr" + test_failure_ "$descr" "$@" "$output" + fi +} + # This is not among top-level (test_expect_success | test_expect_failure) # but is a prefix that can be used in the test script, like: # @@ -398,7 +458,7 @@ test_done () { } # Test the binaries we have just built. The tests are kept in -# t/ subdirectory and are run in trash subdirectory. +# t/ subdirectory and are run in 'trash directory' subdirectory. TEST_DIRECTORY=$(pwd) PATH=$TEST_DIRECTORY/..:$PATH GIT_EXEC_PATH=$(pwd)/.. diff --git a/templates/hooks--applypatch-msg b/templates/hooks--applypatch-msg.sample index 02de1ef84c..8b2a2fe84f 100644..100755 --- a/templates/hooks--applypatch-msg +++ b/templates/hooks--applypatch-msg.sample @@ -7,7 +7,7 @@ # appropriate message if it wants to stop the commit. The hook is # allowed to edit the commit message file. # -# To enable this hook, make this file executable. +# To enable this hook, rename this file to "applypatch-msg". . git-sh-setup test -x "$GIT_DIR/hooks/commit-msg" && diff --git a/templates/hooks--commit-msg b/templates/hooks--commit-msg.sample index 4ef86eb244..6ef1d29d09 100644..100755 --- a/templates/hooks--commit-msg +++ b/templates/hooks--commit-msg.sample @@ -6,7 +6,7 @@ # status after issuing an appropriate message if it wants to stop the # commit. The hook is allowed to edit the commit message file. # -# To enable this hook, make this file executable. +# To enable this hook, rename this file to "commit-msg". # Uncomment the below to add a Signed-off-by line to the message. # Doing this in a hook is a bad idea in general, but the prepare-commit-msg diff --git a/templates/hooks--post-commit b/templates/hooks--post-commit.sample index 8be6f34ad9..22668216a3 100644..100755 --- a/templates/hooks--post-commit +++ b/templates/hooks--post-commit.sample @@ -3,6 +3,6 @@ # An example hook script that is called after a successful # commit is made. # -# To enable this hook, make this file executable. +# To enable this hook, rename this file to "post-commit". : Nothing diff --git a/templates/hooks--post-receive b/templates/hooks--post-receive deleted file mode 100644 index b70c8fd364..0000000000 --- a/templates/hooks--post-receive +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -# -# An example hook script for the post-receive event -# -# This script is run after receive-pack has accepted a pack and the -# repository has been updated. It is passed arguments in through stdin -# in the form -# <oldrev> <newrev> <refname> -# For example: -# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master -# -# see contrib/hooks/ for an sample, or uncomment the next line (on debian) -# - - -#. /usr/share/doc/git-core/contrib/hooks/post-receive-email diff --git a/templates/hooks--post-receive.sample b/templates/hooks--post-receive.sample new file mode 100755 index 0000000000..18d2e0f727 --- /dev/null +++ b/templates/hooks--post-receive.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script for the "post-receive" event. +# +# The "post-receive" script is run after receive-pack has accepted a pack +# and the repository has been updated. It is passed arguments in through +# stdin in the form +# <oldrev> <newrev> <refname> +# For example: +# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master +# +# see contrib/hooks/ for an sample, or uncomment the next line and +# rename the file to "post-receive". + +#. /usr/share/doc/git-core/contrib/hooks/post-receive-email diff --git a/templates/hooks--post-update b/templates/hooks--post-update.sample index bcba8937bb..5323b56b81 100644..100755 --- a/templates/hooks--post-update +++ b/templates/hooks--post-update.sample @@ -3,6 +3,6 @@ # An example hook script to prepare a packed repository for use over # dumb transports. # -# To enable this hook, make this file executable by "chmod +x post-update". +# To enable this hook, rename this file to "post-update". exec git-update-server-info diff --git a/templates/hooks--pre-applypatch b/templates/hooks--pre-applypatch.sample index eeccc934ca..b1f187c2e9 100644..100755 --- a/templates/hooks--pre-applypatch +++ b/templates/hooks--pre-applypatch.sample @@ -6,7 +6,7 @@ # The hook should exit with non-zero status after issuing an # appropriate message if it wants to stop the commit. # -# To enable this hook, make this file executable. +# To enable this hook, rename this file to "pre-applypatch". . git-sh-setup test -x "$GIT_DIR/hooks/pre-commit" && diff --git a/templates/hooks--pre-commit b/templates/hooks--pre-commit.sample index b25dce6bbf..71c10f25f4 100644..100755 --- a/templates/hooks--pre-commit +++ b/templates/hooks--pre-commit.sample @@ -5,7 +5,7 @@ # exit with non-zero status after issuing an appropriate message if # it wants to stop the commit. # -# To enable this hook, make this file executable. +# To enable this hook, rename this file to "pre-commit". # This is slightly modified from Andrew Morton's Perfect Patch. # Lines you introduce should not have trailing whitespace. diff --git a/templates/hooks--pre-rebase b/templates/hooks--pre-rebase.sample index 981c454cda..be1b06e250 100644..100755 --- a/templates/hooks--pre-rebase +++ b/templates/hooks--pre-rebase.sample @@ -1,7 +1,19 @@ #!/bin/sh # -# Copyright (c) 2006 Junio C Hamano +# Copyright (c) 2006, 2008 Junio C Hamano # +# The "pre-rebase" hook is run just before "git-rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. publish=next basebranch="$1" @@ -9,11 +21,12 @@ if test "$#" = 2 then topic="refs/heads/$2" else - topic=`git symbolic-ref HEAD` + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD fi -case "$basebranch,$topic" in -master,refs/heads/??/*) +case "$topic" in +refs/heads/??/*) ;; *) exit 0 ;# we do not interrupt others. @@ -23,6 +36,12 @@ esac # Now we are dealing with a topic branch being rebased # on top of master. Is it OK to rebase it? +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + # Is topic fully merged to master? not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` if test -z "$not_in_master" diff --git a/templates/hooks--prepare-commit-msg b/templates/hooks--prepare-commit-msg.sample index d3c1da34d2..aa42acfd68 100644..100755 --- a/templates/hooks--prepare-commit-msg +++ b/templates/hooks--prepare-commit-msg.sample @@ -7,7 +7,7 @@ # message file. If the hook fails with a non-zero status, # the commit is aborted. # -# To enable this hook, make this file executable. +# To enable this hook, rename this file to "prepare-commit-msg". # This hook includes three examples. The first comments out the # "Conflicts:" part of a merge commit. diff --git a/templates/hooks--update b/templates/hooks--update.sample index 4b69268fd0..93c605594f 100644..100755 --- a/templates/hooks--update +++ b/templates/hooks--update.sample @@ -3,7 +3,7 @@ # An example hook script to blocks unannotated tags from entering. # Called by git-receive-pack with arguments: refname sha1-old sha1-new # -# To enable this hook, make this file executable by "chmod +x update". +# To enable this hook, rename this file to "update". # # Config # ------ diff --git a/test-parse-options.c b/test-parse-options.c index 73360d7512..2a79e729a4 100644 --- a/test-parse-options.c +++ b/test-parse-options.c @@ -2,9 +2,22 @@ #include "parse-options.h" static int boolean = 0; -static int integer = 0; +static unsigned long integer = 0; +static int abbrev = 7; +static int verbose = 0, dry_run = 0, quiet = 0; static char *string = NULL; +int length_callback(const struct option *opt, const char *arg, int unset) +{ + printf("Callback: \"%s\", %d\n", + (arg ? arg : "not set"), unset); + if (unset) + return 1; /* do not support unset */ + + *(unsigned long *)opt->value = strlen(arg); + return 0; +} + int main(int argc, const char **argv) { const char *usage[] = { @@ -13,15 +26,29 @@ int main(int argc, const char **argv) }; struct option options[] = { OPT_BOOLEAN('b', "boolean", &boolean, "get a boolean"), + OPT_BIT('4', "or4", &boolean, + "bitwise-or boolean with ...0100", 4), + OPT_GROUP(""), OPT_INTEGER('i', "integer", &integer, "get a integer"), OPT_INTEGER('j', NULL, &integer, "get a integer, too"), - OPT_GROUP("string options"), + OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23), + OPT_DATE('t', NULL, &integer, "get timestamp of <time>"), + OPT_CALLBACK('L', "length", &integer, "str", + "get length of <str>", length_callback), + OPT_GROUP("String options"), OPT_STRING('s', "string", &string, "string", "get a string"), OPT_STRING(0, "string2", &string, "str", "get another string"), OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"), OPT_STRING('o', NULL, &string, "str", "get another string"), - OPT_GROUP("magic arguments"), + OPT_SET_PTR(0, "default-string", &string, + "set string to default", (unsigned long)"default"), + OPT_GROUP("Magic arguments"), OPT_ARGUMENT("quux", "means --quux"), + OPT_GROUP("Standard options"), + OPT__ABBREV(&abbrev), + OPT__VERBOSE(&verbose), + OPT__DRY_RUN(&dry_run), + OPT__QUIET(&quiet), OPT_END(), }; int i; @@ -29,8 +56,12 @@ int main(int argc, const char **argv) argc = parse_options(argc, argv, options, usage, 0); printf("boolean: %d\n", boolean); - printf("integer: %d\n", integer); + printf("integer: %lu\n", integer); printf("string: %s\n", string ? string : "(not set)"); + printf("abbrev: %d\n", abbrev); + printf("verbose: %d\n", verbose); + printf("quiet: %s\n", quiet ? "yes" : "no"); + printf("dry run: %s\n", dry_run ? "yes" : "no"); for (i = 0; i < argc; i++) printf("arg %02d: %s\n", i, argv[i]); diff --git a/wrapper.c b/wrapper.c new file mode 100644 index 0000000000..4e04f7661b --- /dev/null +++ b/wrapper.c @@ -0,0 +1,160 @@ +/* + * Various trivial helper wrappers around standard functions + */ +#include "cache.h" + +char *xstrdup(const char *str) +{ + char *ret = strdup(str); + if (!ret) { + release_pack_memory(strlen(str) + 1, -1); + ret = strdup(str); + if (!ret) + die("Out of memory, strdup failed"); + } + return ret; +} + +void *xmalloc(size_t size) +{ + void *ret = malloc(size); + if (!ret && !size) + ret = malloc(1); + if (!ret) { + release_pack_memory(size, -1); + ret = malloc(size); + if (!ret && !size) + ret = malloc(1); + if (!ret) + die("Out of memory, malloc failed"); + } +#ifdef XMALLOC_POISON + memset(ret, 0xA5, size); +#endif + return ret; +} + +/* + * xmemdupz() allocates (len + 1) bytes of memory, duplicates "len" bytes of + * "data" to the allocated memory, zero terminates the allocated memory, + * and returns a pointer to the allocated memory. If the allocation fails, + * the program dies. + */ +void *xmemdupz(const void *data, size_t len) +{ + char *p = xmalloc(len + 1); + memcpy(p, data, len); + p[len] = '\0'; + return p; +} + +char *xstrndup(const char *str, size_t len) +{ + char *p = memchr(str, '\0', len); + return xmemdupz(str, p ? p - str : len); +} + +void *xrealloc(void *ptr, size_t size) +{ + void *ret = realloc(ptr, size); + if (!ret && !size) + ret = realloc(ptr, 1); + if (!ret) { + release_pack_memory(size, -1); + ret = realloc(ptr, size); + if (!ret && !size) + ret = realloc(ptr, 1); + if (!ret) + die("Out of memory, realloc failed"); + } + return ret; +} + +void *xcalloc(size_t nmemb, size_t size) +{ + void *ret = calloc(nmemb, size); + if (!ret && (!nmemb || !size)) + ret = calloc(1, 1); + if (!ret) { + release_pack_memory(nmemb * size, -1); + ret = calloc(nmemb, size); + if (!ret && (!nmemb || !size)) + ret = calloc(1, 1); + if (!ret) + die("Out of memory, calloc failed"); + } + return ret; +} + +void *xmmap(void *start, size_t length, + int prot, int flags, int fd, off_t offset) +{ + void *ret = mmap(start, length, prot, flags, fd, offset); + if (ret == MAP_FAILED) { + if (!length) + return NULL; + release_pack_memory(length, fd); + ret = mmap(start, length, prot, flags, fd, offset); + if (ret == MAP_FAILED) + die("Out of memory? mmap failed: %s", strerror(errno)); + } + return ret; +} + +/* + * xread() is the same a read(), but it automatically restarts read() + * operations with a recoverable error (EAGAIN and EINTR). xread() + * DOES NOT GUARANTEE that "len" bytes is read even if the data is available. + */ +ssize_t xread(int fd, void *buf, size_t len) +{ + ssize_t nr; + while (1) { + nr = read(fd, buf, len); + if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) + continue; + return nr; + } +} + +/* + * xwrite() is the same a write(), but it automatically restarts write() + * operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT + * GUARANTEE that "len" bytes is written even if the operation is successful. + */ +ssize_t xwrite(int fd, const void *buf, size_t len) +{ + ssize_t nr; + while (1) { + nr = write(fd, buf, len); + if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) + continue; + return nr; + } +} + +int xdup(int fd) +{ + int ret = dup(fd); + if (ret < 0) + die("dup failed: %s", strerror(errno)); + return ret; +} + +FILE *xfdopen(int fd, const char *mode) +{ + FILE *stream = fdopen(fd, mode); + if (stream == NULL) + die("Out of memory? fdopen failed: %s", strerror(errno)); + return stream; +} + +int xmkstemp(char *template) +{ + int fd; + + fd = mkstemp(template); + if (fd < 0) + die("Unable to create temporary file: %s", strerror(errno)); + return fd; +} diff --git a/wt-status.c b/wt-status.c index 5b4d74c1f3..28c9e637e3 100644 --- a/wt-status.c +++ b/wt-status.c @@ -27,6 +27,7 @@ static const char use_add_rm_msg[] = "use \"git add/rm <file>...\" to update what will be committed"; static const char use_add_to_include_msg[] = "use \"git add <file>...\" to include in what will be committed"; +enum untracked_status_type show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; static int parse_status_slot(const char *var, int offset) { @@ -347,7 +348,10 @@ void wt_status_print(struct wt_status *s) wt_status_print_changed(s); if (wt_status_submodule_summary) wt_status_print_submodule_summary(s); - wt_status_print_untracked(s); + if (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"); if (s->verbose && !s->is_initial) wt_status_print_verbose(s); @@ -362,6 +366,8 @@ void wt_status_print(struct wt_status *s) 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) + printf("nothing to commit (use -u to show untracked files)\n"); else printf("nothing to commit (working directory clean)\n"); } @@ -391,5 +397,18 @@ int git_status_config(const char *k, const char *v, void *cb) wt_status_relative_paths = git_config_bool(k, v); return 0; } + if (!strcmp(k, "status.showuntrackedfiles")) { + if (!v) + return config_error_nonbool(v); + 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_color_default_config(k, v, cb); } diff --git a/wt-status.h b/wt-status.h index 597c7ea988..78add09bd6 100644 --- a/wt-status.h +++ b/wt-status.h @@ -11,6 +11,13 @@ enum color_wt_status { WT_STATUS_NOBRANCH, }; +enum untracked_status_type { + SHOW_NO_UNTRACKED_FILES, + SHOW_NORMAL_UNTRACKED_FILES, + SHOW_ALL_UNTRACKED_FILES +}; +extern enum untracked_status_type show_untracked_files; + struct wt_status { int is_initial; char *branch; |