Code

Merge branch 'lw/perlish'
authorJunio C Hamano <gitster@pobox.com>
Wed, 25 Jun 2008 20:18:46 +0000 (13:18 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 25 Jun 2008 20:18:46 +0000 (13:18 -0700)
* lw/perlish:
  Git.pm: add test suite
  t/test-lib.sh: add test_external and test_external_without_stderr

108 files changed:
Documentation/RelNotes-1.5.6.txt
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/git-commit.txt
Documentation/git-fast-export.txt
Documentation/git-push.txt
Documentation/git-svn.txt
Documentation/git-update-ref.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/githooks.txt
Documentation/technical/api-builtin.txt
Documentation/technical/api-parse-options.txt
Documentation/technical/api-path-list.txt
Documentation/technical/api-run-command.txt
GIT-VERSION-GEN
Makefile
archive-tar.c
archive-zip.c
archive.c
archive.h
builtin-clone.c
builtin-commit.c
builtin-fast-export.c
builtin-fsck.c
builtin-merge-recursive.c
builtin-pack-objects.c
builtin-rerere.c
builtin-update-ref.c
builtin-verify-pack.c
cache.h
combine-diff.c
config.c
contrib/completion/git-completion.bash
contrib/thunderbird-patch-inline/README [new file with mode: 0644]
contrib/thunderbird-patch-inline/appp.sh [new file with mode: 0755]
date.c
diff.c
environment.c
git-am.sh
git-compat-util.h
git-merge.sh
git-svn.perl
gitweb/gitweb.perl
http-push.c
http-walker.c
pack-check.c
pack-revindex.c
pack-revindex.h
pack.h
parse-options.c
path.c
read-cache.c
remote.c
setup.c
sha1_file.c
t/.gitattributes
t/README
t/t0040-parse-options.sh
t/t1400-update-ref.sh
t/t1502-rev-parse-parseopt.sh
t/t3800-mktag.sh
t/t3903-stash.sh
t/t4014-format-patch.sh
t/t4015-diff-whitespace.sh
t/t4016-diff-quote.sh
t/t4109-apply-multifrag.sh
t/t4109/patch1.patch [new file with mode: 0644]
t/t4109/patch2.patch [new file with mode: 0644]
t/t4109/patch3.patch [new file with mode: 0644]
t/t4109/patch4.patch [new file with mode: 0644]
t/t4119-apply-config.sh
t/t4150-am.sh
t/t5000-tar-tree.sh
t/t5302-pack-index.sh
t/t5303-pack-corruption-resilience.sh [new file with mode: 0755]
t/t5510-fetch.sh
t/t5540-http-push.sh
t/t5601-clone.sh
t/t7502-commit.sh
t/t7502-status.sh
t/t9123-git-svn-rebuild-with-rewriteroot.sh [new file with mode: 0755]
t/t9301-fast-export.sh
t/test-lib.sh
templates/hooks--applypatch-msg [deleted file]
templates/hooks--applypatch-msg.sample [new file with mode: 0755]
templates/hooks--commit-msg [deleted file]
templates/hooks--commit-msg.sample [new file with mode: 0755]
templates/hooks--post-commit [deleted file]
templates/hooks--post-commit.sample [new file with mode: 0755]
templates/hooks--post-receive [deleted file]
templates/hooks--post-receive.sample [new file with mode: 0755]
templates/hooks--post-update [deleted file]
templates/hooks--post-update.sample [new file with mode: 0755]
templates/hooks--pre-applypatch [deleted file]
templates/hooks--pre-applypatch.sample [new file with mode: 0755]
templates/hooks--pre-commit [deleted file]
templates/hooks--pre-commit.sample [new file with mode: 0755]
templates/hooks--pre-rebase [deleted file]
templates/hooks--pre-rebase.sample [new file with mode: 0755]
templates/hooks--prepare-commit-msg [deleted file]
templates/hooks--prepare-commit-msg.sample [new file with mode: 0755]
templates/hooks--update [deleted file]
templates/hooks--update.sample [new file with mode: 0755]
test-parse-options.c
wrapper.c [new file with mode: 0644]
wt-status.c
wt-status.h

index b22bfa89a8f1f26e6cd469e84b7715fffae58080..e143d8d61be1bb2fac024b5d5f270b33f4f898d4 100644 (file)
@@ -18,6 +18,8 @@ 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
@@ -110,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-rc3
-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 ;-)
index 0e155c936c25255706bc6d47651c2b336c628417..b1164753e13dbe015d073346bc0b7c992532efe8 100644 (file)
@@ -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
 ----
index 5331b450ea051334d53ce3f1e727e33def2ea2cf..1e09a57c8c5aeb5d49367a4c96f32ebafe07d410 100644 (file)
@@ -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
index 7e8b4ff72c0192f01523118b9a8caeddbcbda09c..d0fe192fb315e62d0daa2d468bd5709601abbe5d 100644 (file)
@@ -8,7 +8,7 @@ git-commit - Record changes to the repository
 SYNOPSIS
 --------
 [verse]
-'git-commit' [-a | --interactive] [-s] [-v] [-u] [--amend]
+'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>...]
@@ -162,13 +162,22 @@ 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::
index 332346cc5dfd7fffa3e80ad6ca6b413bbbfdb0c2..277a547a026f69faa0d923ca8a9f191608de7294 100644 (file)
@@ -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
 --------
index 89e0049bce1367f308c58d3306266f93a6cd7b75..f3d5d883a7e4e42c47670eeeef41799b1cb0c228 100644 (file)
@@ -67,7 +67,8 @@ nor in any Push line of the corresponding remotes file---see below).
 
 --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
index f4cbd2f212e544f0a17c761a4c870a925db5802b..97bed54fbde18a1e7c5516382a54b341fc81668e 100644 (file)
@@ -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
 --------------
index 7f7e3d197bafbbb2efe610096b0cad6901488be5..bae2c8b7eced2042af0437b1e92cc73636c7cfa5 100644 (file)
@@ -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
 -----------
index ee96774009c79881ef79be8bbae65bb191ff7d3c..7414238fe52819689615e57d10f53dd2b29a3cbe 100644 (file)
@@ -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
index 471754eb12afa5862aa9c5e88ed3bfd2ee5cac7f..6e67990f64372855dd665da4747a7f7c235ac856 100644 (file)
@@ -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`
 ^^^^^^^^^^^^^^
 
index 4f06ae0ed4bd625bf5a33fca215c90fb1b5810c9..262a4f1626864c9dd4b180a2659336900412421c 100644 (file)
@@ -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.
 
index 52cdb4c5201b7971ba908c35722e4914f408fbec..7ede1e64e5d40ec8f742e900d7273d6f961605e2 100644 (file)
@@ -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
index b7cda94f54962b185da33fcddf3e033c1e18c5ae..539863b1f920f8f34ad9272907cbacbd35a7fcbd 100644 (file)
@@ -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.
index d0776831710eaffa6e4b88edfd62e5fbb25fff31..9dbedd0a67ce6c1cecd157b0d89f9b1333e180e3 100644 (file)
@@ -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.
index c364a22c8f98f6fbb71d4059f7458ed0fa78ed43..3e1342acf405adb322054a19123cd98fc12b69a9 100644 (file)
@@ -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();
index 3cabc92e7a1f8ce30eb6e5151a4c81c82468bcee..f221447478aea8628c0ab6d098338f7e8630201e 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.5.5.GIT
+DEF_VER=v1.5.6.GIT
 
 LF='
 '
index b003e3e60a1aecae27497d941d211849bedec02b..314339d7e0071ac3e7f761e9c70d1df0c12e7b8c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -467,6 +467,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
@@ -1290,6 +1291,9 @@ endif
 install-doc:
        $(MAKE) -C Documentation install
 
+install-html:
+       $(MAKE) -C Documentation install-html
+
 install-info:
        $(MAKE) -C Documentation install-info
 
index d7598f907d9f7fb40c24c8cef815f8fc33a8b19b..99db58f1cf21ee30f150c54acd6ffefbb14b6f57 100644 (file)
@@ -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;
index 18c0f8710c6150e3b0211d311a70bf4b34608370..5742762ac30c92e796255b74d2ed1ea087f3fba0 100644 (file)
@@ -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) {
index 7a32c19d3ca8043f3ca22dadfdbc60dbbb747d59..6502b76ef10a182e9d9f0e60ad926c55a31a737a 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -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);
+}
index 5791e657e9a0c22081f4f42b9d8ca5b3c536baf2..ddf004acdfa2125a73df11e923eda902d3ca8277 100644 (file)
--- a/archive.h
+++ b/archive.h
@@ -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 */
index 71909520710ab046fe04723f5a25ac9a53374025..b2dfe1ab5c6df0da0af0004bdf5accd6d37a90d8 100644 (file)
@@ -400,6 +400,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 +413,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);
index 90200ed643bcf21f28a66396f0d15db920a5d4c8..e3ad38b3bd78bc2d19c5e5d1ebfbe0388b22582e 100644 (file)
@@ -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)
index d0a462ff8b3760a4a21d9a72339a02891a5b4aef..45786ef1b740c1e21c1cffeeea89cb0250f446c5 100644 (file)
@@ -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,7 +199,7 @@ 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)
@@ -354,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()
        };
 
@@ -378,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))
@@ -400,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;
 }
index 78a6e1ff7101f7bb616365e5d4c9d9cf7f8963ae..b0f9648f862248d1b985f31406b8f3de26b47aff 100644 (file)
@@ -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;
index 4aa28a1babbb275df8110fd80311f34a12fea750..43bf6aa45eeacbb67490c809070f40023ce4e434 100644 (file)
@@ -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 */
@@ -583,7 +574,7 @@ 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);
index 447d492dbbfb578b8903293fa1d2a988eecfefac..28207d9b3aee9f3e886384871a6632fbaf7eee4f 100644 (file)
@@ -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]);
 
index 5c811423cc1234d933cff013f9923b45f19dc6fa..85222d9bc591e0b603bf6f33c32d6bb1bad479ee 100644 (file)
@@ -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);
 }
index 93c127196d272d99d8af24a5fbc7ac89efdbb3d4..d90d11d2e35c38baab32f05f58125aaf1baee6cc 100644 (file)
@@ -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);
 }
index 4c515a0570ec2b05e5dca8156d885a70b922251e..222c39e7edc85c8356db7821416574f60316422d 100644 (file)
@@ -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;
 }
diff --git a/cache.h b/cache.h
index 23f3b923350669001fffdc8c670825bdaefa7f3c..c8954ef15f28e83e4029481b1cb3cd793f7f3d58 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -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 *);
@@ -641,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;
@@ -694,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);
@@ -711,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);
@@ -737,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 *);
index 588c58bc55dfe78998d40a4dcce2a7b6ea6f7f5a..9f80a1c5e3a461afd11966625589684d61187911 100644 (file)
@@ -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++) {
index c2f2bbb000cd003002c1bd346e6be6c9d3ef5c2d..04d97e3d0b3a202868d3a8645710cc387f14443f 100644 (file)
--- a/config.c
+++ b/config.c
@@ -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;
index 2141b6b6ba4cfed2c2ea3beca1e1149d489b75fa..ebf7cde5c023c86ee7ed0751730e09d1245930da 100755 (executable)
@@ -761,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 (file)
index 0000000..39f96aa
--- /dev/null
@@ -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 (executable)
index 0000000..cc518f3
--- /dev/null
@@ -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}"
diff --git a/date.c b/date.c
index a74ed86422763e7d7e5dccf73530e52551a6929a..1a4eb87b01d5dab0c4a0c455bbef3fda132415ee 100644 (file)
--- a/date.c
+++ b/date.c
@@ -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 {
diff --git a/diff.c b/diff.c
index 526249008662d4041e5623914c1a420f3443c4d4..44e8790690ebdfa45ff7d6c89ede85e8f4f9e857 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -928,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");
index 73feb2d03a917d1fbdc8d9397b42fd90e5916bca..187248bf5d027c8d860ccbf839bc6296d0a42436 100644 (file)
@@ -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)
index b48096ec2037e3f14d4bdf89a0afe4623aff090e..2c517ede590c5c3274b03d1c980f9a847cc7d643 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -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
index c04e8baa87f263b426de17556f63351ae254ae95..6f94a8197f479f4e3fd93f8f80bdd125efd8a363 100644 (file)
@@ -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)
 {
index 5fc5f5201f8ea8e155a25795b93bfea86fc12d12..8026ccff4a459a75148740382646f7fe10b00255 100755 (executable)
@@ -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)
index a54979dc51f0fc392b357da9071b6ea19aac4798..4c9c59bc3ffb9ed2f8e808cf6849562669adfd18 100755 (executable)
@@ -2577,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>) {
@@ -2596,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;
                }
 
index 4de964792fe429211a3e540e05c8cf640660e2ca..87887ab358298565bc14e3c5663f38b7250218fb 100755 (executable)
@@ -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/ /&nbsp;/g;
-               $age_class  = age_class($age);
-               $author     = esc_html ($author);
-               $author     =~ s/ /&nbsp;/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;
index 665712a85de3fb4bba3a879753b60ece64a4243a..2cd068a6f1ef0199cc27a1564678c91b66237a9b 100644 (file)
@@ -783,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;
index 99f397e32b673e289c2f9d298b1287a97cb7a49e..51c18f2685aa1bdb7e4ed471d0ec5d31c6684fda 100644 (file)
@@ -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);
 
index f4898732dd8cd8ad2d8599cb13e0d9d96dda753b..f596bf2db5e0a0065e6856b8caa3ded8a134f74d 100644 (file)
@@ -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;
 }
index a8aa2cd6caefe7d37febdf5c3426cec043492b19..cd300bdff5b524a4d509ba5276e9ef21f443013d 100644 (file)
@@ -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)
index c3527a75655470b95ab4ba0900e9c1ad6a15a35f..36a514a6cf600e7e77794e50998a9d160e30c8e9 100644 (file)
@@ -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
diff --git a/pack.h b/pack.h
index b31b37608d7f1901c74a20552770c306e633670c..76e6aa2aad06545e7c44fc2c1e117ea668356ccf 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -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);
 
index acf3fe3a1a82cd99e7bd6e0ff9d6d3d7e344a155..b8bde2b04a14936787392babf5e0ec0a9e3ef1a7 100644 (file)
@@ -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, " ...");
                        }
diff --git a/path.c b/path.c
index 7a35a26a1697480f0f43244132d8267deb301651..6e3df1849965be6a88fac89c007f9b5fe960f825 100644 (file)
--- a/path.c
+++ b/path.c
@@ -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];
index 8e5fbb619295fe3e4d950e8926d8034d6f825e41..f83de8c4158e08bab3e0445c4d15caf2d4d105aa 100644 (file)
@@ -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;
 }
 
index 91e3b112bb5ffa151a8ede9391d3bd88ea4d3500..ff2c8021678fee5ef12060d35c6eb1960e8792c7 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -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,
diff --git a/setup.c b/setup.c
index d630e374e7c03b934de14adceb93c9c327796e6d..3b111ea7cf5f68ecca085f5a56421ea190e1771a 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -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;
 }
 
index 37bcc5474efac82ab2c822947bae8a738d342bca..e79b2c1145e800e88c0f6e79338d2befa2443494 100644 (file)
@@ -116,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];
@@ -792,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)) {
@@ -814,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))
@@ -831,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;
 }
 
@@ -988,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];
@@ -1306,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");
@@ -1412,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));
@@ -1606,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);
@@ -1648,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;
@@ -1674,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;
@@ -1794,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) {
                        /*
@@ -1878,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;
 }
 
 /*
@@ -2124,8 +2184,9 @@ static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename)
        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))
+               if (mkdir(buffer, 0777) || adjust_shared_perm(buffer))
                        return -1;
 
                /* Try again */
@@ -2145,20 +2206,6 @@ 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));
-       }
-
        fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
        if (fd < 0) {
                if (errno == EPERM)
index ab6edbf19e80a5e07c85ff7cde05061fba349647..1b97c5465b7d9e4404e11668b44c5c507a302d4a 100644 (file)
@@ -1,2 +1 @@
-t[0-9][0-9][0-9][0-9]-*.sh -whitespace
 t[0-9][0-9][0-9][0-9]/* -whitespace
index 70841a4645b2469d8263553a7ba41d4f953774f7..8f12d48fe8b4ffe4a4b37dcd16ce58e50837433f 100644 (file)
--- a/t/README
+++ b/t/README
@@ -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
 ------------
index 9965cfa1dca7948f24a835d5beae4608e6947c0a..6309aed4511738b2f68e16974a5a5ceadf2617e5 100755 (executable)
@@ -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
index b8b7ab410354b01584092802365f4b53cd991b4a..f387d46f1a17ac9f9d3319c4da7e30a6c6326c67 100755 (executable)
@@ -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
index d24a47d1149061565822391542c044a7db193ce5..997002d4c40dd8e66e3be5a701e3d99bab1c57c4 100755 (executable)
@@ -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
index df1fd6f86f11b40667dfbd8132fef8da45d03d75..c851db8ca9373a890ede7c230710690d5b4b4165 100755 (executable)
@@ -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
 
index 2d3ee3b78c66e4e964fffccaa1ae8252929c1732..54d99ed0c3864e464740833f16833ce6458f731f 100755 (executable)
@@ -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' '
index 3583e68e92148a5145b82f93ec8a52fd4bb7f200..7fe853c20d1111e40371a3796d82bb8485f5ebcf 100755 (executable)
@@ -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
index ca0302f41b332df053baa72362099ba691f2ff97..b7cc6b28e61e73af3206a9613620a635a467ea0e 100755 (executable)
@@ -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
index 0950250c9be7f7833b7542f5491a4dad8f5ef82e..f07035ab7ec72557be7a0cba9ea286bcbaa79da9 100755 (executable)
@@ -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' '
index bd40a218cd81fdcb4417cb693cfbf047bc0e64c7..ff5fdf35f99e4c50d1bd27eeacd816c37cc5bdfa 100755 (executable)
@@ -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 (file)
index 0000000..1d411fc
--- /dev/null
@@ -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 (file)
index 0000000..8c6b06d
--- /dev/null
@@ -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 (file)
index 0000000..d696c55
--- /dev/null
@@ -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 (file)
index 0000000..4b08590
--- /dev/null
@@ -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;
+ }
index b540f7295a1bb48bf044d297201b07aca9fb5005..3c73a783a7e908070308fb1f972f6b5d152e12a4 100755 (executable)
@@ -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 &&
index 722ae96cd5d320e3a8d5771e81d76165b3166b3e..bc982607d09bbdeb0d486eb282f5756a48ae5bcb 100755 (executable)
@@ -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
 
index 9b0baac8db4b342206d37ab5eaff0b7a09d33967..3f1e25d921c2e90176d8487ae0519af2cbd1e17b 100755 (executable)
@@ -44,6 +44,11 @@ test_expect_success \
       echo text >file_with_long_path) &&
      (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 &&
@@ -53,6 +58,10 @@ test_expect_success \
      git update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \
      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'
index 09fd91767297a8c59edb08944740bbcf08f8a7c4..ecec5916346ce76936a807ebc769c3981fc0bf8c 100755 (executable)
@@ -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 (executable)
index 0000000..b0f5693
--- /dev/null
@@ -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
index 6946557c677ccc190309cd5961b046c420174f7d..df7750f7d1ede6e888ec3071397115022e067eb3 100755 (executable)
@@ -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
index 7372439164d521df9c29d98b4507612013aba86b..f15dd03e4dbf9febed662f3a285ea1bb35ce396c 100755 (executable)
@@ -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
index 593d1a38772843f1afd67814dcf8920bea3366bc..b642fb260b37a7be2bcd37ea22ba2097e26f07a2 100755 (executable)
@@ -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
index ed871a6b4d7963f520b994f4d378b02194d69f9e..c25eff9e468acd628cf0a5e7a0968c801396e9f8 100755 (executable)
@@ -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"
 '
 
index 80a438d4d988baa54a25e5725665904c6c45f431..38a48b57c70a888838cfa114be843e1d4aea00d8 100755 (executable)
@@ -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/t9123-git-svn-rebuild-with-rewriteroot.sh b/t/t9123-git-svn-rebuild-with-rewriteroot.sh
new file mode 100755 (executable)
index 0000000..c18878f
--- /dev/null
@@ -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
+
index f09bfb1117caa05981d65e45fe2a9cc4c7ca19f3..f1bc5ceef0453fcdf711afd6225a5f0cd5dc69e5 100755 (executable)
@@ -77,6 +77,29 @@ test_expect_success 'iso-8859-1' '
                 git fast-import &&
                 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
index 4be466edb79fb7b509888ef5adac5f09c076bb33..f4f35ed4337a5cf2091b14f770988a7dd70f4361 100644 (file)
@@ -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)
diff --git a/templates/hooks--applypatch-msg b/templates/hooks--applypatch-msg
deleted file mode 100644 (file)
index 02de1ef..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh
-#
-# An example hook script to check the commit log message taken by
-# applypatch from an e-mail message.
-#
-# The hook should exit with non-zero 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.
-
-. git-sh-setup
-test -x "$GIT_DIR/hooks/commit-msg" &&
-       exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
-:
diff --git a/templates/hooks--applypatch-msg.sample b/templates/hooks--applypatch-msg.sample
new file mode 100755 (executable)
index 0000000..8b2a2fe
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# An example hook script to check the commit log message taken by
+# applypatch from an e-mail message.
+#
+# The hook should exit with non-zero 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, rename this file to "applypatch-msg".
+
+. git-sh-setup
+test -x "$GIT_DIR/hooks/commit-msg" &&
+       exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
+:
diff --git a/templates/hooks--commit-msg b/templates/hooks--commit-msg
deleted file mode 100644 (file)
index 4ef86eb..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/sh
-#
-# An example hook script to check the commit log message.
-# Called by git-commit with one argument, the name of the file
-# that has the commit message.  The hook should exit with non-zero
-# 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.
-
-# 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
-# hook is more suited to it.
-#
-# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
-# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
-
-# This example catches duplicate Signed-off-by lines.
-
-test "" = "$(grep '^Signed-off-by: ' "$1" |
-        sort | uniq -c | sed -e '/^[   ]*1[    ]/d')" || {
-       echo >&2 Duplicate Signed-off-by lines.
-       exit 1
-}
diff --git a/templates/hooks--commit-msg.sample b/templates/hooks--commit-msg.sample
new file mode 100755 (executable)
index 0000000..6ef1d29
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# An example hook script to check the commit log message.
+# Called by git-commit with one argument, the name of the file
+# that has the commit message.  The hook should exit with non-zero
+# 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, 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
+# hook is more suited to it.
+#
+# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
+
+# This example catches duplicate Signed-off-by lines.
+
+test "" = "$(grep '^Signed-off-by: ' "$1" |
+        sort | uniq -c | sed -e '/^[   ]*1[    ]/d')" || {
+       echo >&2 Duplicate Signed-off-by lines.
+       exit 1
+}
diff --git a/templates/hooks--post-commit b/templates/hooks--post-commit
deleted file mode 100644 (file)
index 8be6f34..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/sh
-#
-# An example hook script that is called after a successful
-# commit is made.
-#
-# To enable this hook, make this file executable.
-
-: Nothing
diff --git a/templates/hooks--post-commit.sample b/templates/hooks--post-commit.sample
new file mode 100755 (executable)
index 0000000..2266821
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# An example hook script that is called after a successful
+# commit is made.
+#
+# 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 (file)
index b70c8fd..0000000
+++ /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 (executable)
index 0000000..18d2e0f
--- /dev/null
@@ -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
deleted file mode 100644 (file)
index bcba893..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/sh
-#
-# 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".
-
-exec git-update-server-info
diff --git a/templates/hooks--post-update.sample b/templates/hooks--post-update.sample
new file mode 100755 (executable)
index 0000000..5323b56
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# An example hook script to prepare a packed repository for use over
+# dumb transports.
+#
+# 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
deleted file mode 100644 (file)
index eeccc93..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/sh
-#
-# An example hook script to verify what is about to be committed
-# by applypatch from an e-mail message.
-#
-# 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.
-
-. git-sh-setup
-test -x "$GIT_DIR/hooks/pre-commit" &&
-       exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
-:
diff --git a/templates/hooks--pre-applypatch.sample b/templates/hooks--pre-applypatch.sample
new file mode 100755 (executable)
index 0000000..b1f187c
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed
+# by applypatch from an e-mail message.
+#
+# The hook should exit with non-zero status after issuing an
+# appropriate message if it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-applypatch".
+
+. git-sh-setup
+test -x "$GIT_DIR/hooks/pre-commit" &&
+       exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
+:
diff --git a/templates/hooks--pre-commit b/templates/hooks--pre-commit
deleted file mode 100644 (file)
index b25dce6..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/bin/sh
-#
-# An example hook script to verify what is about to be committed.
-# Called by git-commit with no arguments.  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.
-
-# This is slightly modified from Andrew Morton's Perfect Patch.
-# Lines you introduce should not have trailing whitespace.
-# Also check for an indentation that has SP before a TAB.
-
-if git-rev-parse --verify HEAD 2>/dev/null
-then
-       git-diff-index -p -M --cached HEAD --
-else
-       # NEEDSWORK: we should produce a diff with an empty tree here
-       # if we want to do the same verification for the initial import.
-       :
-fi |
-perl -e '
-    my $found_bad = 0;
-    my $filename;
-    my $reported_filename = "";
-    my $lineno;
-    sub bad_line {
-       my ($why, $line) = @_;
-       if (!$found_bad) {
-           print STDERR "*\n";
-           print STDERR "* You have some suspicious patch lines:\n";
-           print STDERR "*\n";
-           $found_bad = 1;
-       }
-       if ($reported_filename ne $filename) {
-           print STDERR "* In $filename\n";
-           $reported_filename = $filename;
-       }
-       print STDERR "* $why (line $lineno)\n";
-       print STDERR "$filename:$lineno:$line\n";
-    }
-    while (<>) {
-       if (m|^diff --git a/(.*) b/\1$|) {
-           $filename = $1;
-           next;
-       }
-       if (/^@@ -\S+ \+(\d+)/) {
-           $lineno = $1 - 1;
-           next;
-       }
-       if (/^ /) {
-           $lineno++;
-           next;
-       }
-       if (s/^\+//) {
-           $lineno++;
-           chomp;
-           if (/\s$/) {
-               bad_line("trailing whitespace", $_);
-           }
-           if (/^\s* \t/) {
-               bad_line("indent SP followed by a TAB", $_);
-           }
-           if (/^([<>])\1{6} |^={7}$/) {
-               bad_line("unresolved merge conflict", $_);
-           }
-       }
-    }
-    exit($found_bad);
-'
diff --git a/templates/hooks--pre-commit.sample b/templates/hooks--pre-commit.sample
new file mode 100755 (executable)
index 0000000..71c10f2
--- /dev/null
@@ -0,0 +1,70 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed.
+# Called by git-commit with no arguments.  The hook should
+# exit with non-zero status after issuing an appropriate message if
+# it wants to stop the commit.
+#
+# 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.
+# Also check for an indentation that has SP before a TAB.
+
+if git-rev-parse --verify HEAD 2>/dev/null
+then
+       git-diff-index -p -M --cached HEAD --
+else
+       # NEEDSWORK: we should produce a diff with an empty tree here
+       # if we want to do the same verification for the initial import.
+       :
+fi |
+perl -e '
+    my $found_bad = 0;
+    my $filename;
+    my $reported_filename = "";
+    my $lineno;
+    sub bad_line {
+       my ($why, $line) = @_;
+       if (!$found_bad) {
+           print STDERR "*\n";
+           print STDERR "* You have some suspicious patch lines:\n";
+           print STDERR "*\n";
+           $found_bad = 1;
+       }
+       if ($reported_filename ne $filename) {
+           print STDERR "* In $filename\n";
+           $reported_filename = $filename;
+       }
+       print STDERR "* $why (line $lineno)\n";
+       print STDERR "$filename:$lineno:$line\n";
+    }
+    while (<>) {
+       if (m|^diff --git a/(.*) b/\1$|) {
+           $filename = $1;
+           next;
+       }
+       if (/^@@ -\S+ \+(\d+)/) {
+           $lineno = $1 - 1;
+           next;
+       }
+       if (/^ /) {
+           $lineno++;
+           next;
+       }
+       if (s/^\+//) {
+           $lineno++;
+           chomp;
+           if (/\s$/) {
+               bad_line("trailing whitespace", $_);
+           }
+           if (/^\s* \t/) {
+               bad_line("indent SP followed by a TAB", $_);
+           }
+           if (/^([<>])\1{6} |^={7}$/) {
+               bad_line("unresolved merge conflict", $_);
+           }
+       }
+    }
+    exit($found_bad);
+'
diff --git a/templates/hooks--pre-rebase b/templates/hooks--pre-rebase
deleted file mode 100644 (file)
index 981c454..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2006 Junio C Hamano
-#
-
-publish=next
-basebranch="$1"
-if test "$#" = 2
-then
-       topic="refs/heads/$2"
-else
-       topic=`git symbolic-ref HEAD`
-fi
-
-case "$basebranch,$topic" in
-master,refs/heads/??/*)
-       ;;
-*)
-       exit 0 ;# we do not interrupt others.
-       ;;
-esac
-
-# Now we are dealing with a topic branch being rebased
-# on top of master.  Is it OK to rebase it?
-
-# Is topic fully merged to master?
-not_in_master=`git-rev-list --pretty=oneline ^master "$topic"`
-if test -z "$not_in_master"
-then
-       echo >&2 "$topic is fully merged to master; better remove it."
-       exit 1 ;# we could allow it, but there is no point.
-fi
-
-# Is topic ever merged to next?  If so you should not be rebasing it.
-only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort`
-only_next_2=`git-rev-list ^master           ${publish} | sort`
-if test "$only_next_1" = "$only_next_2"
-then
-       not_in_topic=`git-rev-list "^$topic" master`
-       if test -z "$not_in_topic"
-       then
-               echo >&2 "$topic is already up-to-date with master"
-               exit 1 ;# we could allow it, but there is no point.
-       else
-               exit 0
-       fi
-else
-       not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"`
-       perl -e '
-               my $topic = $ARGV[0];
-               my $msg = "* $topic has commits already merged to public branch:\n";
-               my (%not_in_next) = map {
-                       /^([0-9a-f]+) /;
-                       ($1 => 1);
-               } split(/\n/, $ARGV[1]);
-               for my $elem (map {
-                               /^([0-9a-f]+) (.*)$/;
-                               [$1 => $2];
-                       } split(/\n/, $ARGV[2])) {
-                       if (!exists $not_in_next{$elem->[0]}) {
-                               if ($msg) {
-                                       print STDERR $msg;
-                                       undef $msg;
-                               }
-                               print STDERR " $elem->[1]\n";
-                       }
-               }
-       ' "$topic" "$not_in_next" "$not_in_master"
-       exit 1
-fi
-
-exit 0
-
-################################################################
-
-This sample hook safeguards topic branches that have been
-published from being rewound.
-
-The workflow assumed here is:
-
- * Once a topic branch forks from "master", "master" is never
-   merged into it again (either directly or indirectly).
-
- * Once a topic branch is fully cooked and merged into "master",
-   it is deleted.  If you need to build on top of it to correct
-   earlier mistakes, a new topic branch is created by forking at
-   the tip of the "master".  This is not strictly necessary, but
-   it makes it easier to keep your history simple.
-
- * Whenever you need to test or publish your changes to topic
-   branches, merge them into "next" branch.
-
-The script, being an example, hardcodes the publish branch name
-to be "next", but it is trivial to make it configurable via
-$GIT_DIR/config mechanism.
-
-With this workflow, you would want to know:
-
-(1) ... if a topic branch has ever been merged to "next".  Young
-    topic branches can have stupid mistakes you would rather
-    clean up before publishing, and things that have not been
-    merged into other branches can be easily rebased without
-    affecting other people.  But once it is published, you would
-    not want to rewind it.
-
-(2) ... if a topic branch has been fully merged to "master".
-    Then you can delete it.  More importantly, you should not
-    build on top of it -- other people may already want to
-    change things related to the topic as patches against your
-    "master", so if you need further changes, it is better to
-    fork the topic (perhaps with the same name) afresh from the
-    tip of "master".
-
-Let's look at this example:
-
-                  o---o---o---o---o---o---o---o---o---o "next"
-                 /       /           /           /
-                /   a---a---b A     /           /
-               /   /               /           /
-              /   /   c---c---c---c B         /
-             /   /   /             \         /
-            /   /   /   b---b C     \       /
-           /   /   /   /             \     /
-    ---o---o---o---o---o---o---o---o---o---o---o "master"
-
-
-A, B and C are topic branches.
-
- * A has one fix since it was merged up to "next".
-
- * B has finished.  It has been fully merged up to "master" and "next",
-   and is ready to be deleted.
-
- * C has not merged to "next" at all.
-
-We would want to allow C to be rebased, refuse A, and encourage
-B to be deleted.
-
-To compute (1):
-
-       git-rev-list ^master ^topic next
-       git-rev-list ^master        next
-
-       if these match, topic has not merged in next at all.
-
-To compute (2):
-
-       git-rev-list master..topic
-
-       if this is empty, it is fully merged to "master".
diff --git a/templates/hooks--pre-rebase.sample b/templates/hooks--pre-rebase.sample
new file mode 100755 (executable)
index 0000000..be1b06e
--- /dev/null
@@ -0,0 +1,169 @@
+#!/bin/sh
+#
+# 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"
+if test "$#" = 2
+then
+       topic="refs/heads/$2"
+else
+       topic=`git symbolic-ref HEAD` ||
+       exit 0 ;# we do not interrupt rebasing detached HEAD
+fi
+
+case "$topic" in
+refs/heads/??/*)
+       ;;
+*)
+       exit 0 ;# we do not interrupt others.
+       ;;
+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"
+then
+       echo >&2 "$topic is fully merged to master; better remove it."
+       exit 1 ;# we could allow it, but there is no point.
+fi
+
+# Is topic ever merged to next?  If so you should not be rebasing it.
+only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort`
+only_next_2=`git-rev-list ^master           ${publish} | sort`
+if test "$only_next_1" = "$only_next_2"
+then
+       not_in_topic=`git-rev-list "^$topic" master`
+       if test -z "$not_in_topic"
+       then
+               echo >&2 "$topic is already up-to-date with master"
+               exit 1 ;# we could allow it, but there is no point.
+       else
+               exit 0
+       fi
+else
+       not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"`
+       perl -e '
+               my $topic = $ARGV[0];
+               my $msg = "* $topic has commits already merged to public branch:\n";
+               my (%not_in_next) = map {
+                       /^([0-9a-f]+) /;
+                       ($1 => 1);
+               } split(/\n/, $ARGV[1]);
+               for my $elem (map {
+                               /^([0-9a-f]+) (.*)$/;
+                               [$1 => $2];
+                       } split(/\n/, $ARGV[2])) {
+                       if (!exists $not_in_next{$elem->[0]}) {
+                               if ($msg) {
+                                       print STDERR $msg;
+                                       undef $msg;
+                               }
+                               print STDERR " $elem->[1]\n";
+                       }
+               }
+       ' "$topic" "$not_in_next" "$not_in_master"
+       exit 1
+fi
+
+exit 0
+
+################################################################
+
+This sample hook safeguards topic branches that have been
+published from being rewound.
+
+The workflow assumed here is:
+
+ * Once a topic branch forks from "master", "master" is never
+   merged into it again (either directly or indirectly).
+
+ * Once a topic branch is fully cooked and merged into "master",
+   it is deleted.  If you need to build on top of it to correct
+   earlier mistakes, a new topic branch is created by forking at
+   the tip of the "master".  This is not strictly necessary, but
+   it makes it easier to keep your history simple.
+
+ * Whenever you need to test or publish your changes to topic
+   branches, merge them into "next" branch.
+
+The script, being an example, hardcodes the publish branch name
+to be "next", but it is trivial to make it configurable via
+$GIT_DIR/config mechanism.
+
+With this workflow, you would want to know:
+
+(1) ... if a topic branch has ever been merged to "next".  Young
+    topic branches can have stupid mistakes you would rather
+    clean up before publishing, and things that have not been
+    merged into other branches can be easily rebased without
+    affecting other people.  But once it is published, you would
+    not want to rewind it.
+
+(2) ... if a topic branch has been fully merged to "master".
+    Then you can delete it.  More importantly, you should not
+    build on top of it -- other people may already want to
+    change things related to the topic as patches against your
+    "master", so if you need further changes, it is better to
+    fork the topic (perhaps with the same name) afresh from the
+    tip of "master".
+
+Let's look at this example:
+
+                  o---o---o---o---o---o---o---o---o---o "next"
+                 /       /           /           /
+                /   a---a---b A     /           /
+               /   /               /           /
+              /   /   c---c---c---c B         /
+             /   /   /             \         /
+            /   /   /   b---b C     \       /
+           /   /   /   /             \     /
+    ---o---o---o---o---o---o---o---o---o---o---o "master"
+
+
+A, B and C are topic branches.
+
+ * A has one fix since it was merged up to "next".
+
+ * B has finished.  It has been fully merged up to "master" and "next",
+   and is ready to be deleted.
+
+ * C has not merged to "next" at all.
+
+We would want to allow C to be rebased, refuse A, and encourage
+B to be deleted.
+
+To compute (1):
+
+       git-rev-list ^master ^topic next
+       git-rev-list ^master        next
+
+       if these match, topic has not merged in next at all.
+
+To compute (2):
+
+       git-rev-list master..topic
+
+       if this is empty, it is fully merged to "master".
diff --git a/templates/hooks--prepare-commit-msg b/templates/hooks--prepare-commit-msg
deleted file mode 100644 (file)
index d3c1da3..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/sh
-#
-# An example hook script to prepare the commit log message.
-# Called by git-commit with the name of the file that has the
-# commit message, followed by the description of the commit
-# message's source.  The hook's purpose is to edit the commit
-# message file.  If the hook fails with a non-zero status,
-# the commit is aborted.
-#
-# To enable this hook, make this file executable.
-
-# This hook includes three examples.  The first comments out the
-# "Conflicts:" part of a merge commit.
-#
-# The second includes the output of "git diff --name-status -r"
-# into the message, just before the "git status" output.  It is
-# commented because it doesn't cope with --amend or with squashed
-# commits.
-#
-# The third example adds a Signed-off-by line to the message, that can
-# still be edited.  This is rarely a good idea.
-
-case "$2,$3" in
-  merge,)
-    perl -i -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
-
-# ,|template,)
-#   perl -i -pe '
-#      print "\n" . `git diff --cached --name-status -r`
-#       if /^#/ && $first++ == 0' "$1" ;;
-
-  *) ;;
-esac
-
-# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
-# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
diff --git a/templates/hooks--prepare-commit-msg.sample b/templates/hooks--prepare-commit-msg.sample
new file mode 100755 (executable)
index 0000000..aa42acf
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# An example hook script to prepare the commit log message.
+# Called by git-commit with the name of the file that has the
+# commit message, followed by the description of the commit
+# message's source.  The hook's purpose is to edit the commit
+# message file.  If the hook fails with a non-zero status,
+# the commit is aborted.
+#
+# 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.
+#
+# The second includes the output of "git diff --name-status -r"
+# into the message, just before the "git status" output.  It is
+# commented because it doesn't cope with --amend or with squashed
+# commits.
+#
+# The third example adds a Signed-off-by line to the message, that can
+# still be edited.  This is rarely a good idea.
+
+case "$2,$3" in
+  merge,)
+    perl -i -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
+
+# ,|template,)
+#   perl -i -pe '
+#      print "\n" . `git diff --cached --name-status -r`
+#       if /^#/ && $first++ == 0' "$1" ;;
+
+  *) ;;
+esac
+
+# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
diff --git a/templates/hooks--update b/templates/hooks--update
deleted file mode 100644 (file)
index 4b69268..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-#!/bin/sh
-#
-# 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".
-#
-# Config
-# ------
-# hooks.allowunannotated
-#   This boolean sets whether unannotated tags will be allowed into the
-#   repository.  By default they won't be.
-# hooks.allowdeletetag
-#   This boolean sets whether deleting tags will be allowed in the
-#   repository.  By default they won't be.
-# hooks.allowdeletebranch
-#   This boolean sets whether deleting branches will be allowed in the
-#   repository.  By default they won't be.
-#
-
-# --- Command line
-refname="$1"
-oldrev="$2"
-newrev="$3"
-
-# --- Safety check
-if [ -z "$GIT_DIR" ]; then
-       echo "Don't run this script from the command line." >&2
-       echo " (if you want, you could supply GIT_DIR then run" >&2
-       echo "  $0 <ref> <oldrev> <newrev>)" >&2
-       exit 1
-fi
-
-if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
-       echo "Usage: $0 <ref> <oldrev> <newrev>" >&2
-       exit 1
-fi
-
-# --- Config
-allowunannotated=$(git config --bool hooks.allowunannotated)
-allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
-allowdeletetag=$(git config --bool hooks.allowdeletetag)
-
-# check for no description
-projectdesc=$(sed -e '1q' "$GIT_DIR/description")
-if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb." ]; then
-       echo "*** Project description file hasn't been set" >&2
-       exit 1
-fi
-
-# --- Check types
-# if $newrev is 0000...0000, it's a commit to delete a ref.
-if [ "$newrev" = "0000000000000000000000000000000000000000" ]; then
-       newrev_type=delete
-else
-       newrev_type=$(git-cat-file -t $newrev)
-fi
-
-case "$refname","$newrev_type" in
-       refs/tags/*,commit)
-               # un-annotated tag
-               short_refname=${refname##refs/tags/}
-               if [ "$allowunannotated" != "true" ]; then
-                       echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
-                       echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
-                       exit 1
-               fi
-               ;;
-       refs/tags/*,delete)
-               # delete tag
-               if [ "$allowdeletetag" != "true" ]; then
-                       echo "*** Deleting a tag is not allowed in this repository" >&2
-                       exit 1
-               fi
-               ;;
-       refs/tags/*,tag)
-               # annotated tag
-               ;;
-       refs/heads/*,commit)
-               # branch
-               ;;
-       refs/heads/*,delete)
-               # delete branch
-               if [ "$allowdeletebranch" != "true" ]; then
-                       echo "*** Deleting a branch is not allowed in this repository" >&2
-                       exit 1
-               fi
-               ;;
-       refs/remotes/*,commit)
-               # tracking branch
-               ;;
-       refs/remotes/*,delete)
-               # delete tracking branch
-               if [ "$allowdeletebranch" != "true" ]; then
-                       echo "*** Deleting a tracking branch is not allowed in this repository" >&2
-                       exit 1
-               fi
-               ;;
-       *)
-               # Anything else (is there anything else?)
-               echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
-               exit 1
-               ;;
-esac
-
-# --- Finished
-exit 0
diff --git a/templates/hooks--update.sample b/templates/hooks--update.sample
new file mode 100755 (executable)
index 0000000..93c6055
--- /dev/null
@@ -0,0 +1,107 @@
+#!/bin/sh
+#
+# 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, rename this file to "update".
+#
+# Config
+# ------
+# hooks.allowunannotated
+#   This boolean sets whether unannotated tags will be allowed into the
+#   repository.  By default they won't be.
+# hooks.allowdeletetag
+#   This boolean sets whether deleting tags will be allowed in the
+#   repository.  By default they won't be.
+# hooks.allowdeletebranch
+#   This boolean sets whether deleting branches will be allowed in the
+#   repository.  By default they won't be.
+#
+
+# --- Command line
+refname="$1"
+oldrev="$2"
+newrev="$3"
+
+# --- Safety check
+if [ -z "$GIT_DIR" ]; then
+       echo "Don't run this script from the command line." >&2
+       echo " (if you want, you could supply GIT_DIR then run" >&2
+       echo "  $0 <ref> <oldrev> <newrev>)" >&2
+       exit 1
+fi
+
+if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
+       echo "Usage: $0 <ref> <oldrev> <newrev>" >&2
+       exit 1
+fi
+
+# --- Config
+allowunannotated=$(git config --bool hooks.allowunannotated)
+allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
+allowdeletetag=$(git config --bool hooks.allowdeletetag)
+
+# check for no description
+projectdesc=$(sed -e '1q' "$GIT_DIR/description")
+if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb." ]; then
+       echo "*** Project description file hasn't been set" >&2
+       exit 1
+fi
+
+# --- Check types
+# if $newrev is 0000...0000, it's a commit to delete a ref.
+if [ "$newrev" = "0000000000000000000000000000000000000000" ]; then
+       newrev_type=delete
+else
+       newrev_type=$(git-cat-file -t $newrev)
+fi
+
+case "$refname","$newrev_type" in
+       refs/tags/*,commit)
+               # un-annotated tag
+               short_refname=${refname##refs/tags/}
+               if [ "$allowunannotated" != "true" ]; then
+                       echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
+                       echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
+                       exit 1
+               fi
+               ;;
+       refs/tags/*,delete)
+               # delete tag
+               if [ "$allowdeletetag" != "true" ]; then
+                       echo "*** Deleting a tag is not allowed in this repository" >&2
+                       exit 1
+               fi
+               ;;
+       refs/tags/*,tag)
+               # annotated tag
+               ;;
+       refs/heads/*,commit)
+               # branch
+               ;;
+       refs/heads/*,delete)
+               # delete branch
+               if [ "$allowdeletebranch" != "true" ]; then
+                       echo "*** Deleting a branch is not allowed in this repository" >&2
+                       exit 1
+               fi
+               ;;
+       refs/remotes/*,commit)
+               # tracking branch
+               ;;
+       refs/remotes/*,delete)
+               # delete tracking branch
+               if [ "$allowdeletebranch" != "true" ]; then
+                       echo "*** Deleting a tracking branch is not allowed in this repository" >&2
+                       exit 1
+               fi
+               ;;
+       *)
+               # Anything else (is there anything else?)
+               echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
+               exit 1
+               ;;
+esac
+
+# --- Finished
+exit 0
index 73360d75126af2f231b3cfdad26a47e18fff0a39..2a79e729a4018ddb2da9ff633f4bf3b102fa8f88 100644 (file)
@@ -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 (file)
index 0000000..4e04f76
--- /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;
+}
index 5b4d74c1f3f4c24aa23cf564a16592d188449da1..28c9e637e3dd42a2c2364dd7b9f099f4671861cb 100644 (file)
@@ -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);
 }
index 597c7ea988634409f920c86008f5ba70910722f9..78add09bd67c727babb61cd1eaa773bcd0c6e55e 100644 (file)
@@ -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;