Merge branch 'js/rebase-detached'
authorJunio C Hamano <gitster@pobox.com>
Wed, 14 Nov 2007 22:04:06 +0000 (14:04 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 14 Nov 2007 22:04:06 +0000 (14:04 -0800)
* js/rebase-detached:
  rebase: fix "rebase --continue" breakage
  rebase: operate on a detached HEAD

55 files changed:
Documentation/Makefile
Documentation/asciidoc.conf
Documentation/cmd-list.perl
Documentation/core-tutorial.txt
Documentation/git-add.txt
Documentation/git-branch.txt
Documentation/git-commit.txt
Documentation/git-cvsexportcommit.txt
Documentation/git-get-tar-commit-id.txt
Documentation/git-local-fetch.txt [deleted file]
Documentation/git-push.txt
Documentation/git-remote.txt
Documentation/git-ssh-fetch.txt [deleted file]
Documentation/git-ssh-upload.txt [deleted file]
Documentation/gitattributes.txt
Documentation/howto/recover-corrupted-blob-object.txt [new file with mode: 0644]
Documentation/user-manual.txt
Makefile
builtin-blame.c
builtin-fetch--tool.c
builtin-for-each-ref.c
builtin-push.c
builtin-reset.c
builtin-revert.c
fast-import.c
git-clean.sh
git-compat-util.h
git-cvsexportcommit.perl
git-cvsimport.perl
git-svn.perl
gitweb/gitweb.perl
hash-object.c
index-pack.c
list-objects.c
parse-options.c
pretty.c
setup.c
strbuf.c
strbuf.h
t/t0040-parse-options.sh
t/t3404-rebase-interactive.sh
t/t5530-upload-pack-error.sh [new file with mode: 0755]
t/t6300-for-each-ref.sh
t/t7102-reset.sh
t/t7300-clean.sh
t/t7501-commit.sh
t/t9106-git-svn-dcommit-clobber-series.sh
t/t9118-git-svn-funky-branch-names.sh [new file with mode: 0755]
t/test-lib.sh
test-parse-options.c
trace.c
transport.c
transport.h
upload-pack.c
usage.c

index 39ec0ed..d886641 100644 (file)
@@ -37,9 +37,6 @@ man7dir=$(mandir)/man7
 
 ASCIIDOC=asciidoc
 ASCIIDOC_EXTRA =
-ifdef ASCIIDOC8
-ASCIIDOC_EXTRA += -a asciidoc7compatible
-endif
 INSTALL?=install
 RM ?= rm -f
 DOC_REF = origin/man
@@ -52,6 +49,13 @@ DOCBOOK2X_TEXI=docbook2x-texi
 -include ../config.mak.autogen
 -include ../config.mak
 
+ifdef ASCIIDOC8
+ASCIIDOC_EXTRA += -a asciidoc7compatible
+endif
+ifdef DOCBOOK_XSL_172
+ASCIIDOC_EXTRA += -a docbook-xsl-172
+endif
+
 #
 # Please note that there is a minor bug in asciidoc.
 # The version after 6.0.3 _will_ include the patch found here:
index af5b155..99d8874 100644 (file)
@@ -23,7 +23,9 @@ ifdef::backend-docbook[]
 endif::backend-docbook[]
 
 ifdef::backend-docbook[]
+ifndef::docbook-xsl-172[]
 # "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this.
+# v1.72 breaks with this because it replaces dots not in roff requests.
 [listingblock]
 <example><title>{title}</title>
 <literallayout>
@@ -36,6 +38,7 @@ ifdef::doctype-manpage[]
 endif::doctype-manpage[]
 </literallayout>
 {title#}</example>
+endif::docbook-xsl-172[]
 endif::backend-docbook[]
 
 ifdef::doctype-manpage[]
index 8d21d42..57a790d 100755 (executable)
@@ -3,7 +3,8 @@
 use File::Compare qw(compare);
 
 sub format_one {
-       my ($out, $name) = @_;
+       my ($out, $nameattr) = @_;
+       my ($name, $attr) = @$nameattr;
        my ($state, $description);
        $state = 0;
        open I, '<', "$name.txt" or die "No such file $name.txt";
@@ -26,8 +27,11 @@ sub format_one {
                die "No description found in $name.txt";
        }
        if (my ($verify_name, $text) = ($description =~ /^($name) - (.*)/)) {
-               print $out "gitlink:$name\[1\]::\n";
-               print $out "\t$text.\n\n";
+               print $out "gitlink:$name\[1\]::\n\t";
+               if ($attr) {
+                       print $out "($attr) ";
+               }
+               print $out "$text.\n\n";
        }
        else {
                die "Description does not match $name: $description";
@@ -39,8 +43,8 @@ while (<DATA>) {
        next if /^#/;
 
        chomp;
-       my ($name, $cat) = /^(\S+)\s+(.*)$/;
-       push @{$cmds{$cat}}, $name;
+       my ($name, $cat, $attr) = /^(\S+)\s+(.*?)(?:\s+(.*))?$/;
+       push @{$cmds{$cat}}, [$name, $attr];
 }
 
 for my $cat (qw(ancillaryinterrogators
@@ -124,9 +128,8 @@ git-index-pack                          plumbingmanipulators
 git-init                                mainporcelain
 git-instaweb                            ancillaryinterrogators
 gitk                                    mainporcelain
-git-local-fetch                         synchingrepositories
 git-log                                 mainporcelain
-git-lost-found                          ancillarymanipulators
+git-lost-found                          ancillarymanipulators  deprecated
 git-ls-files                            plumbinginterrogators
 git-ls-remote                           plumbinginterrogators
 git-ls-tree                             plumbinginterrogators
@@ -178,8 +181,6 @@ git-show-branch                         ancillaryinterrogators
 git-show-index                          plumbinginterrogators
 git-show-ref                            plumbinginterrogators
 git-sh-setup                            purehelpers
-git-ssh-fetch                           synchingrepositories
-git-ssh-upload                          synchingrepositories
 git-stash                               mainporcelain
 git-status                              mainporcelain
 git-stripspace                          purehelpers
@@ -187,7 +188,7 @@ git-submodule                           mainporcelain
 git-svn                                 foreignscminterface
 git-symbolic-ref                        plumbingmanipulators
 git-tag                                 mainporcelain
-git-tar-tree                            plumbinginterrogators
+git-tar-tree                            plumbinginterrogators  deprecated
 git-unpack-file                         plumbinginterrogators
 git-unpack-objects                      plumbingmanipulators
 git-update-index                        plumbingmanipulators
index 99817c5..bd6cd41 100644 (file)
@@ -931,12 +931,13 @@ Another useful tool, especially if you do not always work in X-Window
 environment, is `git show-branch`.
 
 ------------------------------------------------
-$ git show-branch --topo-order master mybranch
+$ git-show-branch --topo-order --more=1 master mybranch
 * [master] Merge work in mybranch
  ! [mybranch] Some work.
 --
 -  [master] Merge work in mybranch
 *+ [mybranch] Some work.
+*  [master^] Some fun.
 ------------------------------------------------
 
 The first two lines indicate that it is showing the two branches
@@ -954,10 +955,22 @@ because `mybranch` has not been merged to incorporate these
 commits from the master branch.  The string inside brackets
 before the commit log message is a short name you can use to
 name the commit.  In the above example, 'master' and 'mybranch'
-are branch heads.  'master~1' is the first parent of 'master'
+are branch heads.  'master^' is the first parent of 'master'
 branch head.  Please see 'git-rev-parse' documentation if you
 see more complex cases.
 
+[NOTE]
+Without the '--more=1' option, 'git-show-branch' would not output the
+'[master^]' commit, as '[mybranch]' commit is a common ancestor of
+both 'master' and 'mybranch' tips.  Please see 'git-show-branch'
+documentation for details.
+
+[NOTE]
+If there were more commits on the 'master' branch after the merge, the
+merge commit itself would not be shown by 'git-show-branch' by
+default.  You would need to provide '--sparse' option to make the
+merge commit visible in this case.
+
 Now, let's pretend you are the one who did all the work in
 `mybranch`, and the fruit of your hard work has finally been merged
 to the `master` branch. Let's go back to `mybranch`, and run
@@ -1077,11 +1090,6 @@ server like git Native transport does.  Any stock HTTP server
 that does not even support directory index would suffice.  But
 you must prepare your repository with `git-update-server-info`
 to help dumb transport downloaders.
-+
-There are (confusingly enough) `git-ssh-fetch` and `git-ssh-upload`
-programs, which are 'commit walkers'; they outlived their
-usefulness when git Native and SSH transports were introduced,
-and are not used by `git pull` or `git push` scripts.
 
 Once you fetch from the remote repository, you `merge` that
 with your current branch.
@@ -1144,7 +1152,7 @@ back to the earlier repository with "hello" and "example" file,
 and bring ourselves back to the pre-merge state:
 
 ------------
-$ git show-branch --more=3 master mybranch
+$ git show-branch --more=2 master mybranch
 ! [master] Merge work in mybranch
  * [mybranch] Merge work in mybranch
 --
@@ -1207,7 +1215,7 @@ $ git-read-tree -m -u $mb HEAD mybranch
 This is the same `git-read-tree` command we have already seen,
 but it takes three trees, unlike previous examples.  This reads
 the contents of each tree into different 'stage' in the index
-file (the first tree goes to stage 1, the second stage 2,
+file (the first tree goes to stage 1, the second to stage 2,
 etc.).  After reading three trees into three stages, the paths
 that are the same in all three stages are 'collapsed' into stage
 0.  Also paths that are the same in two of three stages are
index 963e1ab..63829d9 100644 (file)
@@ -224,6 +224,7 @@ See Also
 --------
 gitlink:git-status[1]
 gitlink:git-rm[1]
+gitlink:git-reset[1]
 gitlink:git-mv[1]
 gitlink:git-commit[1]
 gitlink:git-update-index[1]
index 5e81aa4..5ce905d 100644 (file)
@@ -105,7 +105,7 @@ OPTIONS
        '--track' were given.
 
 --no-track::
-       When -b is given and a branch is created off a remote branch,
+       When a branch is created off a remote branch,
        set up configuration so that git-pull will not retrieve data
        from the remote branch, ignoring the branch.autosetupmerge
        configuration variable.
index e54fb12..d4bfd49 100644 (file)
@@ -154,10 +154,13 @@ EXAMPLES
 --------
 When recording your own work, the contents of modified files in
 your working tree are temporarily stored to a staging area
-called the "index" with gitlink:git-add[1].  Removal
-of a file is staged with gitlink:git-rm[1].  After building the
-state to be committed incrementally with these commands, `git
-commit` (without any pathname parameter) is used to record what
+called the "index" with gitlink:git-add[1].  A file can be
+reverted back, only in the index but not in the working tree,
+to that of the last commit with `git-reset HEAD -- <file>`,
+which effectively reverts `git-add` and prevents the changes to
+this file from participating in the next commit.  After building
+the state to be committed incrementally with these commands,
+`git commit` (without any pathname parameter) is used to record what
 has been staged so far.  This is the most basic form of the
 command.  An example:
 
index c3922f9..3f9d229 100644 (file)
@@ -8,7 +8,7 @@ git-cvsexportcommit - Export a single commit to a CVS checkout
 
 SYNOPSIS
 --------
-'git-cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
+'git-cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-w cvsworkdir] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
 
 
 DESCRIPTION
@@ -16,8 +16,9 @@ DESCRIPTION
 Exports a commit from GIT to a CVS checkout, making it easier
 to merge patches from a git repository into a CVS repository.
 
-Execute it from the root of the CVS working copy. GIT_DIR must be defined.
-See examples below.
+Specify the name of a CVS checkout using the -w switch or execute it
+from the root of the CVS working copy. In the latter case GIT_DIR must
+be defined. See examples below.
 
 It does its best to do the safe thing, it will check that the files are
 unchanged and up to date in the CVS checkout, and it will not autocommit
@@ -61,6 +62,11 @@ OPTIONS
 -u::
        Update affected files from CVS repository before attempting export.
 
+-w::
+       Specify the location of the CVS checkout to use for the export. This
+       option does not require GIT_DIR to be set before execution if the
+       current directory is within a git repository.
+
 -v::
        Verbose.
 
@@ -76,6 +82,12 @@ $ git-cvsexportcommit -v <commit-sha1>
 $ cvs commit -F .msg <files>
 ------------
 
+Merge one patch into CVS (-c and -w options). The working directory is within the Git Repo::
++
+------------
+       $ git-cvsexportcommit -v -c -w ~/project_cvs_checkout <commit-sha1>
+------------
+
 Merge pending patches into CVS automatically -- only if you really know what you are doing::
 +
 ------------
@@ -86,11 +98,11 @@ $ git-cherry cvshead myhead | sed -n 's/^+ //p' | xargs -l1 git-cvsexportcommit
 
 Author
 ------
-Written by Martin Langhoff <martin@catalyst.net.nz>
+Written by Martin Langhoff <martin@catalyst.net.nz> and others.
 
 Documentation
 --------------
-Documentation by Martin Langhoff <martin@catalyst.net.nz>
+Documentation by Martin Langhoff <martin@catalyst.net.nz> and others.
 
 GIT
 ---
index 9b5f86f..76316bb 100644 (file)
@@ -14,12 +14,12 @@ SYNOPSIS
 DESCRIPTION
 -----------
 Acts as a filter, extracting the commit ID stored in archives created by
-git-tar-tree.  It reads only the first 1024 bytes of input, thus its
+gitlink:git-archive[1].  It reads only the first 1024 bytes of input, thus its
 runtime is not influenced by the size of <tarfile> very much.
 
 If no commit ID is found, git-get-tar-commit-id quietly exists with a
 return code of 1.  This can happen if <tarfile> had not been created
-using git-tar-tree or if the first parameter of git-tar-tree had been
+using git-archive or if the <treeish> parameter of git-archive had been
 a tree ID instead of a commit ID or tag.
 
 
diff --git a/Documentation/git-local-fetch.txt b/Documentation/git-local-fetch.txt
deleted file mode 100644 (file)
index e830dee..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-git-local-fetch(1)
-==================
-
-NAME
-----
-git-local-fetch - Duplicate another git repository on a local system
-
-
-SYNOPSIS
---------
-[verse]
-'git-local-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [-l] [-s] [-n]
-                  commit-id path
-
-DESCRIPTION
------------
-THIS COMMAND IS DEPRECATED.
-
-Duplicates another git repository on a local system.
-
-OPTIONS
--------
--c::
-       Get the commit objects.
--t::
-       Get trees associated with the commit objects.
--a::
-       Get all the objects.
--v::
-       Report what is downloaded.
--s::
-       Instead of regular file-to-file copying use symbolic links to the objects
-       in the remote repository.
--l::
-       Before attempting symlinks (if -s is specified) or file-to-file copying the
-       remote objects, try to hardlink the remote objects into the local
-       repository.
--n::
-       Never attempt to file-to-file copy remote objects.  Only useful with
-       -s or -l command-line options.
-
--w <filename>::
-        Writes the commit-id into the filename under $GIT_DIR/refs/<filename> on
-        the local end after the transfer is complete.
-
---stdin::
-       Instead of a commit id on the command line (which is not expected in this
-       case), 'git-local-fetch' expects lines on stdin in the format
-
-               <commit-id>['\t'<filename-as-in--w>]
-
---recover::
-       Verify that everything reachable from target is fetched.  Used after
-       an earlier fetch is interrupted.
-
-Author
-------
-Written by Junio C Hamano <junkio@cox.net>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
-GIT
----
-Part of the gitlink:git[7] suite
index e5dd4c1..4a68aab 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git-push' [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>]
-           [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]
+           [--repo=all] [-f | --force] [-v | --verbose] [<repository> <refspec>...]
 
 DESCRIPTION
 -----------
@@ -95,7 +95,7 @@ the remote repository.
        transfer spends extra cycles to minimize the number of
        objects to be sent and meant to be used on slower connection.
 
--v::
+-v, \--verbose::
        Run verbosely.
 
 include::urls-remotes.txt[]
index 027ba11..0da8704 100644 (file)
@@ -79,7 +79,7 @@ caution.
 Fetch updates for a named set of remotes in the repository as defined by
 remotes.<group>.  If a named group is not specified on the command line,
 the configuration parameter remotes.default will get used; if
-remotes.default is not defined, all remotes which do not the
+remotes.default is not defined, all remotes which do not have the
 configuration parameter remote.<name>.skipDefaultUpdate set to true will
 be updated.  (See gitlink:git-config[1]).
 
diff --git a/Documentation/git-ssh-fetch.txt b/Documentation/git-ssh-fetch.txt
deleted file mode 100644 (file)
index 8d3e2ff..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-git-ssh-fetch(1)
-================
-
-NAME
-----
-git-ssh-fetch - Fetch from a remote repository over ssh connection
-
-
-
-SYNOPSIS
---------
-'git-ssh-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] commit-id url
-
-DESCRIPTION
------------
-THIS COMMAND IS DEPRECATED.
-
-Pulls from a remote repository over ssh connection, invoking
-git-ssh-upload on the other end. It functions identically to
-git-ssh-upload, aside from which end you run it on.
-
-
-OPTIONS
--------
-commit-id::
-        Either the hash or the filename under [URL]/refs/ to
-        pull.
-
--c::
-       Get the commit objects.
--t::
-       Get trees associated with the commit objects.
--a::
-       Get all the objects.
--v::
-       Report what is downloaded.
--w::
-        Writes the commit-id into the filename under $GIT_DIR/refs/ on
-        the local end after the transfer is complete.
-
-
-Author
-------
-Written by Daniel Barkalow <barkalow@iabervon.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
-GIT
----
-Part of the gitlink:git[7] suite
diff --git a/Documentation/git-ssh-upload.txt b/Documentation/git-ssh-upload.txt
deleted file mode 100644 (file)
index 5e2ca8d..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-git-ssh-upload(1)
-=================
-
-NAME
-----
-git-ssh-upload - Push to a remote repository over ssh connection
-
-
-SYNOPSIS
---------
-'git-ssh-upload' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] commit-id url
-
-DESCRIPTION
------------
-THIS COMMAND IS DEPRECATED.
-
-Pushes from a remote repository over ssh connection, invoking
-git-ssh-fetch on the other end. It functions identically to
-git-ssh-fetch, aside from which end you run it on.
-
-OPTIONS
--------
-commit-id::
-        Id of commit to push.
-
--c::
-        Get the commit objects.
--t::
-        Get tree associated with the requested commit object.
--a::
-        Get all the objects.
--v::
-        Report what is uploaded.
--w::
-        Writes the commit-id into the filename under [URL]/refs/ on
-        the remote end after the transfer is complete.
-
-Author
-------
-Written by Daniel Barkalow <barkalow@iabervon.org>
-
-Documentation
---------------
-Documentation by Daniel Barkalow
-
-GIT
----
-Part of the gitlink:git[7] suite
index 20cf8ff..19bd25f 100644 (file)
@@ -148,22 +148,23 @@ with `$Id$` upon check-in.
 `filter`
 ^^^^^^^^
 
-A `filter` attribute can be set to a string value.  This names
+A `filter` attribute can be set to a string value that names a
 filter driver specified in the configuration.
 
-A filter driver consists of `clean` command and `smudge`
+A filter driver consists of a `clean` command and a `smudge`
 command, either of which can be left unspecified.  Upon
-checkout, when `smudge` command is specified, the command is fed
-the blob object from its standard input, and its standard output
-is used to update the worktree file.  Similarly, `clean` command
-is used to convert the contents of worktree file upon checkin.
+checkout, when the `smudge` command is specified, the command is
+fed the blob object from its standard input, and its standard
+output is used to update the worktree file.  Similarly, the
+`clean` command is used to convert the contents of worktree file
+upon checkin.
 
-Missing filter driver definition in the config is not an error
+A missing filter driver definition in the config is not an error
 but makes the filter a no-op passthru.
 
 The content filtering is done to massage the content into a
 shape that is more convenient for the platform, filesystem, and
-the user to use.  The keyword here is "more convenient" and not
+the user to use.  The key phrase here is "more convenient" and not
 "turning something unusable into usable".  In other words, the
 intent is that if someone unsets the filter driver definition,
 or does not have the appropriate filter program, the project
diff --git a/Documentation/howto/recover-corrupted-blob-object.txt b/Documentation/howto/recover-corrupted-blob-object.txt
new file mode 100644 (file)
index 0000000..323b513
--- /dev/null
@@ -0,0 +1,134 @@
+Date: Fri, 9 Nov 2007 08:28:38 -0800 (PST)
+From: Linus Torvalds <torvalds@linux-foundation.org>
+Subject: corrupt object on git-gc
+Abstract: Some tricks to reconstruct blob objects in order to fix
+ a corrupted repository.
+
+On Fri, 9 Nov 2007, Yossi Leybovich wrote:
+>
+> Did not help still the repository look for this object?
+> Any one know how can I track this object and understand which file is it
+
+So exactly *because* the SHA1 hash is cryptographically secure, the hash
+itself doesn't actually tell you anything, in order to fix a corrupt
+object you basically have to find the "original source" for it.
+
+The easiest way to do that is almost always to have backups, and find the
+same object somewhere else. Backups really are a good idea, and git makes
+it pretty easy (if nothing else, just clone the repository somewhere else,
+and make sure that you do *not* use a hard-linked clone, and preferably
+not the same disk/machine).
+
+But since you don't seem to have backups right now, the good news is that
+especially with a single blob being corrupt, these things *are* somewhat
+debuggable.
+
+First off, move the corrupt object away, and *save* it. The most common
+cause of corruption so far has been memory corruption, but even so, there
+are people who would be interested in seeing the corruption - but it's
+basically impossible to judge the corruption until we can also see the
+original object, so right now the corrupt object is useless, but it's very
+interesting for the future, in the hope that you can re-create a
+non-corrupt version.
+
+So:
+
+> ib]$ mv .git/objects/4b/9458b3786228369c63936db65827de3cc06200 ../
+
+This is the right thing to do, although it's usually best to save it under
+it's full SHA1 name (you just dropped the "4b" from the result ;).
+
+Let's see what that tells us:
+
+> ib]$ git-fsck --full
+> broken link from    tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
+>              to    blob 4b9458b3786228369c63936db65827de3cc06200
+> missing blob 4b9458b3786228369c63936db65827de3cc06200
+
+Ok, I removed the "dangling commit" messages, because they are just
+messages about the fact that you probably have rebased etc, so they're not
+at all interesting. But what remains is still very useful. In particular,
+we now know which tree points to it!
+
+Now you can do
+
+       git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
+
+which will show something like
+
+       100644 blob 8d14531846b95bfa3564b58ccfb7913a034323b8    .gitignore
+       100644 blob ebf9bf84da0aab5ed944264a5db2a65fe3a3e883    .mailmap
+       100644 blob ca442d313d86dc67e0a2e5d584b465bd382cbf5c    COPYING
+       100644 blob ee909f2cc49e54f0799a4739d24c4cb9151ae453    CREDITS
+       040000 tree 0f5f709c17ad89e72bdbbef6ea221c69807009f6    Documentation
+       100644 blob 1570d248ad9237e4fa6e4d079336b9da62d9ba32    Kbuild
+       100644 blob 1c7c229a092665b11cd46a25dbd40feeb31661d9    MAINTAINERS
+       ...
+
+and you should now have a line that looks like
+
+       10064 blob 4b9458b3786228369c63936db65827de3cc06200     my-magic-file
+
+in the output. This already tells you a *lot* it tells you what file the
+corrupt blob came from!
+
+Now, it doesn't tell you quite enough, though: it doesn't tell what
+*version* of the file didn't get correctly written! You might be really
+lucky, and it may be the version that you already have checked out in your
+working tree, in which case fixing this problem is really simple, just do
+
+       git hash-object -w my-magic-file
+
+again, and if it outputs the missing SHA1 (4b945..) you're now all done!
+
+But that's the really lucky case, so let's assume that it was some older
+version that was broken. How do you tell which version it was?
+
+The easiest way to do it is to do
+
+       git log --raw --all --full-history -- subdirectory/my-magic-file
+
+and that will show you the whole log for that file (please realize that
+the tree you had may not be the top-level tree, so you need to figure out
+which subdirectory it was in on your own), and because you're asking for
+raw output, you'll now get something like
+
+       commit abc
+       Author:
+       Date:
+         ..
+       :100644 100644 4b9458b... newsha... M  somedirectory/my-magic-file
+
+
+       commit xyz
+       Author:
+       Date:
+
+         ..
+       :100644 100644 oldsha... 4b9458b... M   somedirectory/my-magic-file
+
+and this actually tells you what the *previous* and *subsequent* versions
+of that file were! So now you can look at those ("oldsha" and "newsha"
+respectively), and hopefully you have done commits often, and can
+re-create the missing my-magic-file version by looking at those older and
+newer versions!
+
+If you can do that, you can now recreate the missing object with
+
+       git hash-object -w <recreated-file>
+
+and your repository is good again!
+
+(Btw, you could have ignored the fsck, and started with doing a
+
+       git log --raw --all
+
+and just looked for the sha of the missing object (4b9458b..) in that
+whole thing. It's up to you - git does *have* a lot of information, it is
+just missing one particular blob version.
+
+Trying to recreate trees and especially commits is *much* harder. So you
+were lucky that it's a blob. It's quite possible that you can recreate the
+thing.
+
+                       Linus
index d99adc6..c7cfbbc 100644 (file)
@@ -475,7 +475,7 @@ Bisecting: 3537 revisions left to test after this
 If you run "git branch" at this point, you'll see that git has
 temporarily moved you to a new branch named "bisect".  This branch
 points to a commit (with commit id 65934...) that is reachable from
-v2.6.19 but not from v2.6.18.  Compile and test it, and see whether
+"master" but not from v2.6.18.  Compile and test it, and see whether
 it crashes.  Assume it does crash.  Then:
 
 -------------------------------------------------
@@ -1367,7 +1367,7 @@ If you make a commit that you later wish you hadn't, there are two
 fundamentally different ways to fix the problem:
 
        1. You can create a new commit that undoes whatever was done
-       by the previous commit.  This is the correct thing if your
+       by the old commit.  This is the correct thing if your
        mistake has already been made public.
 
        2. You can go back and modify the old commit.  You should
@@ -1568,7 +1568,7 @@ $ git log master@{1}
 -------------------------------------------------
 
 This lists the commits reachable from the previous version of the head.
-This syntax can be used to with any git command that accepts a commit,
+This syntax can be used with any git command that accepts a commit,
 not just with git log.  Some other examples:
 
 -------------------------------------------------
index 621270f..e830bc7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -115,6 +115,8 @@ all::
 #
 # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
 #
+# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72.
+#
 # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
 # MakeMaker (e.g. using ActiveState under Cygwin).
 #
@@ -998,6 +1000,8 @@ test-date$X: date.o ctype.o
 
 test-delta$X: diff-delta.o patch-delta.o
 
+test-parse-options$X: parse-options.o
+
 .PRECIOUS: $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
 
 test-%$X: test-%.o $(GITLIBS)
@@ -1126,12 +1130,13 @@ endif
 ### Check documentation
 #
 check-docs::
-       @for v in $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk; \
+       @(for v in $(ALL_PROGRAMS) $(BUILT_INS) git gitk; \
        do \
                case "$$v" in \
                git-merge-octopus | git-merge-ours | git-merge-recursive | \
-               git-merge-resolve | git-merge-stupid | \
+               git-merge-resolve | git-merge-stupid | git-merge-subtree | \
                git-add--interactive | git-fsck-objects | git-init-db | \
+               git-rebase--interactive | \
                git-repo-config | git-fetch--tool ) continue ;; \
                esac ; \
                test -f "Documentation/$$v.txt" || \
@@ -1142,7 +1147,30 @@ check-docs::
                git) ;; \
                *) echo "no link: $$v";; \
                esac ; \
-       done | sort
+       done; \
+       ( \
+               sed -e '1,/^__DATA__/d' \
+                   -e 's/[     ].*//' \
+                   -e 's/^/listed /' Documentation/cmd-list.perl; \
+               ls -1 Documentation/git*txt | \
+               sed -e 's|Documentation/|documented |' \
+                   -e 's/\.txt//'; \
+       ) | while read how cmd; \
+       do \
+               case "$$how,$$cmd" in \
+               *,git-citool | \
+               *,git-gui | \
+               documented,gitattributes | \
+               documented,gitignore | \
+               documented,gitmodules | \
+               documented,git-tools | \
+               sentinel,not,matching,is,ok ) continue ;; \
+               esac; \
+               case " $(ALL_PROGRAMS) $(BUILT_INS) git gitk " in \
+               *" $$cmd "*)    ;; \
+               *) echo "removed but $$how: $$cmd" ;; \
+               esac; \
+       done ) | sort
 
 ### Make sure built-ins do not have dups and listed in git.c
 #
index 55a3c0b..ba80bf8 100644 (file)
@@ -2295,6 +2295,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                        else if (i != argc - 1)
                                usage(blame_usage); /* garbage at end */
 
+                       setup_work_tree();
                        if (!has_path_in_work_tree(path))
                                die("cannot stat path %s: %s",
                                    path, strerror(errno));
index 6a78517..ed60847 100644 (file)
@@ -435,9 +435,7 @@ static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_resu
                                cp++;
                        if (!*cp)
                                break;
-                       np = strchr(cp, '\n');
-                       if (!np)
-                               np = cp + strlen(cp);
+                       np = strchrnul(cp, '\n');
                        if (pass) {
                                lrr_list[i].line = cp;
                                lrr_list[i].name = cp + 41;
@@ -461,9 +459,7 @@ static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_resu
                        rref++;
                if (!*rref)
                        break;
-               next = strchr(rref, '\n');
-               if (!next)
-                       next = rref + strlen(rref);
+               next = strchrnul(rref, '\n');
                rreflen = next - rref;
 
                for (i = 0; i < lrr_count; i++) {
index da8c794..bfde2e2 100644 (file)
@@ -304,7 +304,7 @@ static const char *find_wholine(const char *who, int wholen, const char *buf, un
                if (!eol)
                        return "";
                eol++;
-               if (eol[1] == '\n')
+               if (*eol == '\n')
                        return ""; /* end of header */
                buf = eol;
        }
@@ -847,7 +847,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                OPT_GROUP(""),
                OPT_INTEGER( 0 , "count", &maxcount, "show only <n> matched refs"),
                OPT_STRING(  0 , "format", &format, "format", "format to use for the output"),
-               OPT_CALLBACK(0 , "sort", &sort_tail, "key",
+               OPT_CALLBACK(0 , "sort", sort_tail, "key",
                            "field name to sort on", &opt_parse_sort),
                OPT_END(),
        };
index 2c56195..6d1da07 100644 (file)
@@ -115,6 +115,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                flags |= TRANSPORT_PUSH_FORCE;
        if (dry_run)
                flags |= TRANSPORT_PUSH_DRY_RUN;
+       if (verbose)
+               flags |= TRANSPORT_PUSH_VERBOSE;
        if (tags)
                add_refspec("refs/tags/*");
        if (all)
index 9626d4c..4c61025 100644 (file)
@@ -46,26 +46,14 @@ static inline int is_merge(void)
 
 static int unmerged_files(void)
 {
-       char b;
-       ssize_t len;
-       struct child_process cmd;
-       const char *argv_ls_files[] = {"ls-files", "--unmerged", NULL};
-
-       memset(&cmd, 0, sizeof(cmd));
-       cmd.argv = argv_ls_files;
-       cmd.git_cmd = 1;
-       cmd.out = -1;
-
-       if (start_command(&cmd))
-               die("Could not run sub-command: git ls-files");
-
-       len = xread(cmd.out, &b, 1);
-       if (len < 0)
-               die("Could not read output from git ls-files: %s",
-                                               strerror(errno));
-       finish_command(&cmd);
-
-       return len;
+       int i;
+       read_cache();
+       for (i = 0; i < active_nr; i++) {
+               struct cache_entry *ce = active_cache[i];
+               if (ce_stage(ce))
+                       return 1;
+       }
+       return 0;
 }
 
 static int reset_index_file(const unsigned char *sha1, int is_hard_reset)
@@ -107,26 +95,34 @@ static void print_new_head_line(struct commit *commit)
                printf("\n");
 }
 
-static int update_index_refresh(void)
+static int update_index_refresh(int fd, struct lock_file *index_lock)
 {
-       const char *argv_update_index[] = {"update-index", "--refresh", NULL};
-       return run_command_v_opt(argv_update_index, RUN_GIT_CMD);
-}
+       int result;
 
-struct update_cb_data {
-       int index_fd;
-       struct lock_file *lock;
-       int exit_code;
-};
+       if (!index_lock) {
+               index_lock = xcalloc(1, sizeof(struct lock_file));
+               fd = hold_locked_index(index_lock, 1);
+       }
+
+       if (read_cache() < 0)
+               return error("Could not read index");
+       result = refresh_cache(0) ? 1 : 0;
+       if (write_cache(fd, active_cache, active_nr) ||
+                       close(fd) ||
+                       commit_locked_index(index_lock))
+               return error ("Could not refresh index");
+       return result;
+}
 
 static void update_index_from_diff(struct diff_queue_struct *q,
                struct diff_options *opt, void *data)
 {
        int i;
-       struct update_cb_data *cb = data;
+       int *discard_flag = data;
 
        /* do_diff_cache() mangled the index */
        discard_cache();
+       *discard_flag = 1;
        read_cache();
 
        for (i = 0; i < q->nr; i++) {
@@ -140,34 +136,33 @@ static void update_index_from_diff(struct diff_queue_struct *q,
                } else
                        remove_file_from_cache(one->path);
        }
-
-       cb->exit_code = write_cache(cb->index_fd, active_cache, active_nr) ||
-               close(cb->index_fd) ||
-               commit_locked_index(cb->lock);
 }
 
 static int read_from_tree(const char *prefix, const char **argv,
                unsigned char *tree_sha1)
 {
+       struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+       int index_fd, index_was_discarded = 0;
        struct diff_options opt;
-       struct update_cb_data cb;
 
        memset(&opt, 0, sizeof(opt));
        diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt);
        opt.output_format = DIFF_FORMAT_CALLBACK;
        opt.format_callback = update_index_from_diff;
-       opt.format_callback_data = &cb;
+       opt.format_callback_data = &index_was_discarded;
 
-       cb.lock = xcalloc(1, sizeof(struct lock_file));
-       cb.index_fd = hold_locked_index(cb.lock, 1);
-       cb.exit_code = 0;
+       index_fd = hold_locked_index(lock, 1);
+       index_was_discarded = 0;
        read_cache();
        if (do_diff_cache(tree_sha1, &opt))
                return 1;
        diffcore_std(&opt);
        diff_flush(&opt);
 
-       return cb.exit_code;
+       if (!index_was_discarded)
+               /* The index is still clobbered from do_diff_cache() */
+               discard_cache();
+       return update_index_refresh(index_fd, lock);
 }
 
 static void prepend_reflog_action(const char *action, char *buf, size_t size)
@@ -243,9 +238,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                else if (reset_type != NONE)
                        die("Cannot do %s reset with paths.",
                                        reset_type_names[reset_type]);
-               if (read_from_tree(prefix, argv + i, sha1))
-                       return 1;
-               return update_index_refresh() ? 1 : 0;
+               return read_from_tree(prefix, argv + i, sha1);
        }
        if (reset_type == NONE)
                reset_type = MIXED; /* by default */
@@ -282,7 +275,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        case SOFT: /* Nothing else to do. */
                break;
        case MIXED: /* Report what has not been updated. */
-               update_index_refresh();
+               update_index_refresh(0, NULL);
                break;
        }
 
index 62ab1fa..365b330 100644 (file)
@@ -246,7 +246,9 @@ static int revert_or_cherry_pick(int argc, const char **argv)
        if (no_commit) {
                /*
                 * We do not intend to commit immediately.  We just want to
-                * merge the differences in.
+                * merge the differences in, so let's compute the tree
+                * that represents the "current" state for merge-recursive
+                * to work on.
                 */
                if (write_tree(head, 0, NULL))
                        die ("Your index file is unmerged.");
@@ -256,7 +258,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                if (get_sha1("HEAD", head))
                        die ("You do not have a valid HEAD");
                wt_status_prepare(&s);
-               if (s.commitable || s.workdir_dirty)
+               if (s.commitable)
                        die ("Dirty index: cannot %s", me);
                discard_cache();
        }
index f93d7d6..98c2bd5 100644 (file)
@@ -153,13 +153,16 @@ Format of STDIN stream:
 
 #define PACK_ID_BITS 16
 #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
+#define DEPTH_BITS 13
+#define MAX_DEPTH ((1<<DEPTH_BITS)-1)
 
 struct object_entry
 {
        struct object_entry *next;
        uint32_t offset;
-       unsigned type : TYPE_BITS;
-       unsigned pack_id : PACK_ID_BITS;
+       uint32_t type : TYPE_BITS,
+               pack_id : PACK_ID_BITS,
+               depth : DEPTH_BITS;
        unsigned char sha1[20];
 };
 
@@ -1083,7 +1086,7 @@ static int store_object(
                unsigned pos = sizeof(hdr) - 1;
 
                delta_count_by_type[type]++;
-               last->depth++;
+               e->depth = last->depth + 1;
 
                hdrlen = encode_header(OBJ_OFS_DELTA, deltalen, hdr);
                write_or_die(pack_data->pack_fd, hdr, hdrlen);
@@ -1095,8 +1098,7 @@ static int store_object(
                write_or_die(pack_data->pack_fd, hdr + pos, sizeof(hdr) - pos);
                pack_size += sizeof(hdr) - pos;
        } else {
-               if (last)
-                       last->depth = 0;
+               e->depth = 0;
                hdrlen = encode_header(type, dat->len, hdr);
                write_or_die(pack_data->pack_fd, hdr, hdrlen);
                pack_size += hdrlen;
@@ -1114,6 +1116,7 @@ static int store_object(
                        strbuf_swap(&last->data, dat);
                }
                last->offset = e->offset;
+               last->depth = e->depth;
        }
        return 0;
 }
@@ -1160,7 +1163,7 @@ static void load_tree(struct tree_entry *root)
        if (myoe && myoe->pack_id != MAX_PACK_ID) {
                if (myoe->type != OBJ_TREE)
                        die("Not a tree: %s", sha1_to_hex(sha1));
-               t->delta_depth = 0;
+               t->delta_depth = myoe->depth;
                buf = gfi_unpack_entry(myoe, &size);
        } else {
                enum object_type type;
@@ -2289,8 +2292,11 @@ int main(int argc, const char **argv)
                }
                else if (!prefixcmp(a, "--max-pack-size="))
                        max_packsize = strtoumax(a + 16, NULL, 0) * 1024 * 1024;
-               else if (!prefixcmp(a, "--depth="))
+               else if (!prefixcmp(a, "--depth=")) {
                        max_depth = strtoul(a + 8, NULL, 0);
+                       if (max_depth > MAX_DEPTH)
+                               die("--depth cannot exceed %u", MAX_DEPTH);
+               }
                else if (!prefixcmp(a, "--active-branches="))
                        max_active_branches = strtoul(a + 18, NULL, 0);
                else if (!prefixcmp(a, "--import-marks="))
index f4965b8..ad68595 100755 (executable)
@@ -25,10 +25,7 @@ rmrf="rm -rf --"
 rm_refuse="echo Not removing"
 echo1="echo"
 
-# requireForce used to default to false but now it defaults to true.
-# IOW, lack of explicit "clean.requireForce = false" is taken as
-# "clean.requireForce = true".
-disabled=$(git config --bool clean.requireForce || echo true)
+disabled=$(git config --bool clean.requireForce)
 
 while test $# != 0
 do
@@ -37,10 +34,10 @@ do
                cleandir=1
                ;;
        -f)
-               disabled=
+               disabled=false
                ;;
        -n)
-               disabled=
+               disabled=false
                rmf="echo Would remove"
                rmrf="echo Would remove"
                rm_refuse="echo Would not remove"
@@ -68,10 +65,17 @@ do
        shift
 done
 
-if [ "$disabled" = true ]; then
-       echo "clean.requireForce set and -n or -f not given; refusing to clean"
-       exit 1
-fi
+# requireForce used to default to false but now it defaults to true.
+# IOW, lack of explicit "clean.requireForce = false" is taken as
+# "clean.requireForce = true".
+case "$disabled" in
+"")
+       die "clean.requireForce not set and -n or -f not given; refusing to clean"
+       ;;
+"true")
+       die "clean.requireForce set and -n or -f not given; refusing to clean"
+       ;;
+esac
 
 case "$ignored,$ignoredonly" in
        1,1) usage;;
@@ -79,15 +83,22 @@ esac
 
 if [ -z "$ignored" ]; then
        excl="--exclude-per-directory=.gitignore"
+       excl_info= excludes_file=
        if [ -f "$GIT_DIR/info/exclude" ]; then
                excl_info="--exclude-from=$GIT_DIR/info/exclude"
        fi
+       if cfg_excl=$(git config core.excludesfile) && test -f "$cfg_excl"
+       then
+               excludes_file="--exclude-from=$cfg_excl"
+       fi
        if [ "$ignoredonly" ]; then
                excl="$excl --ignored"
        fi
 fi
 
-git ls-files --others --directory $excl ${excl_info:+"$excl_info"} -- "$@" |
+git ls-files --others --directory \
+       $excl ${excl_info:+"$excl_info"} ${excludes_file:+"$excludes_file"} \
+       -- "$@" |
 while read -r file; do
        if [ -d "$file" -a ! -L "$file" ]; then
                if [ -z "$cleandir" ]; then
index 7b29d1b..ede9408 100644 (file)
@@ -183,6 +183,22 @@ void *gitmemmem(const void *haystack, size_t haystacklen,
                 const void *needle, size_t needlelen);
 #endif
 
+#ifdef __GLIBC_PREREQ
+#if __GLIBC_PREREQ(2, 1)
+#define HAVE_STRCHRNUL
+#endif
+#endif
+
+#ifndef HAVE_STRCHRNUL
+#define strchrnul gitstrchrnul
+static inline char *gitstrchrnul(const char *s, int c)
+{
+       while (*s && *s != c)
+               s++;
+       return (char *)s;
+}
+#endif
+
 extern void release_pack_memory(size_t, int);
 
 static inline char* xstrdup(const char *str)
index 26844af..92e4162 100755 (executable)
@@ -1,28 +1,42 @@
 #!/usr/bin/perl -w
 
-# Known limitations:
-# - does not propagate permissions
-# - error handling has not been extensively tested
-#
-
 use strict;
 use Getopt::Std;
 use File::Temp qw(tempdir);
 use Data::Dumper;
 use File::Basename qw(basename dirname);
 
-unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
-    die "GIT_DIR is not defined or is unreadable";
-}
-
-our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u);
+our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u, $opt_w);
 
-getopts('uhPpvcfam:d:');
+getopts('uhPpvcfam:d:w:');
 
 $opt_h && usage();
 
 die "Need at least one commit identifier!" unless @ARGV;
 
+if ($opt_w) {
+       unless ($ENV{GIT_DIR}) {
+               # Remember where our GIT_DIR is before changing to CVS checkout
+               my $gd =`git-rev-parse --git-dir`;
+               chomp($gd);
+               if ($gd eq '.git') {
+                       my $wd = `pwd`;
+                       chomp($wd);
+                       $gd = $wd."/.git"       ;
+               }
+               $ENV{GIT_DIR} = $gd;
+       }
+
+       if (! -d $opt_w."/CVS" ) {
+               die "$opt_w is not a CVS checkout";
+       }
+       chdir $opt_w or die "Cannot change to CVS checkout at $opt_w";
+}
+unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
+    die "GIT_DIR is not defined or is unreadable";
+}
+
+
 my @cvs;
 if ($opt_d) {
        @cvs = ('cvs', '-d', $opt_d);
@@ -274,6 +288,7 @@ if ($dirtypatch) {
     print "You'll need to apply the patch in .cvsexportcommit.diff manually\n";
     print "using a patch program. After applying the patch and resolving the\n";
     print "problems you may commit using:";
+    print "\n    cd \"$opt_w\"" if $opt_w;
     print "\n    $cmd\n\n";
     exit(1);
 }
@@ -301,7 +316,7 @@ sleep(1);
 
 sub usage {
        print STDERR <<END;
-Usage: GIT_DIR=/path/to/.git ${\basename $0} [-h] [-p] [-v] [-c] [-f] [-m msgprefix] [ parent ] commit
+Usage: GIT_DIR=/path/to/.git ${\basename $0} [-h] [-p] [-v] [-c] [-f] [-u] [-w cvsworkdir] [-m msgprefix] [ parent ] commit
 END
        exit(1);
 }
index e4bc2b5..efa6a0c 100755 (executable)
@@ -223,7 +223,8 @@ sub conn {
                        }
                }
 
-               $user="anonymous" unless defined $user;
+               # if username is not explicit in CVSROOT, then use current user, as cvs would
+               $user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || "anonymous") unless $user;
                my $rr2 = "-";
                unless ($port) {
                        $rr2 = ":pserver:$user\@$serv:$repo";
index dd93e32..e3e00fd 100755 (executable)
@@ -390,6 +390,9 @@ sub cmd_set_tree {
 
 sub cmd_dcommit {
        my $head = shift;
+       git_cmd_try { command_oneline(qw/diff-index --quiet HEAD/) }
+               'Cannot dcommit with a dirty index.  Commit your changes first'
+               . "or stash them with `git stash'.\n";
        $head ||= 'HEAD';
        my @refs;
        my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs);
@@ -3220,6 +3223,25 @@ sub _auth_providers () {
        ]
 }
 
+sub escape_uri_only {
+       my ($uri) = @_;
+       my @tmp;
+       foreach (split m{/}, $uri) {
+               s/([^\w.-])/sprintf("%%%02X",ord($1))/eg;
+               push @tmp, $_;
+       }
+       join('/', @tmp);
+}
+
+sub escape_url {
+       my ($url) = @_;
+       if ($url =~ m#^(https?)://([^/]+)(.*)$#) {
+               my ($scheme, $domain, $uri) = ($1, $2, escape_uri_only($3));
+               $url = "$scheme://$domain$uri";
+       }
+       $url;
+}
+
 sub new {
        my ($class, $url) = @_;
        $url =~ s!/+$!!;
@@ -3252,10 +3274,11 @@ sub new {
                        $Git::SVN::Prompt::_no_auth_cache = 1;
                }
        } # no warnings 'once'
-       my $self = SVN::Ra->new(url => $url, auth => $baton,
+       my $self = SVN::Ra->new(url => escape_url($url), auth => $baton,
                              config => $config,
                              pool => SVN::Pool->new,
                              auth_provider_callbacks => $callbacks);
+       $self->{url} = $url;
        $self->{svn_path} = $url;
        $self->{repos_root} = $self->get_repos_root;
        $self->{svn_path} =~ s#^\Q$self->{repos_root}\E(/|$)##;
@@ -3381,7 +3404,7 @@ sub gs_do_switch {
 
        my $full_url = $self->{url};
        my $old_url = $full_url;
-       $full_url .= "/$path" if length $path;
+       $full_url .= '/' . escape_uri_only($path) if length $path;
        my ($ra, $reparented);
        if ($old_url ne $full_url) {
                if ($old_url !~ m#^svn(\+ssh)?://#) {
index 759dff1..e788ef9 100755 (executable)
@@ -1856,7 +1856,7 @@ sub parse_date {
        $date{'mday-time'} = sprintf "%d %s %02d:%02d",
                             $mday, $months[$mon], $hour ,$min;
        $date{'iso-8601'}  = sprintf "%04d-%02d-%02dT%02d:%02d:%02dZ",
-                            1900+$year, $mon, $mday, $hour ,$min, $sec;
+                            1900+$year, 1+$mon, $mday, $hour ,$min, $sec;
 
        $tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/;
        my $local = $epoch + ((int $1 + ($2/60)) * 3600);
index 18f5017..0a58f3f 100644 (file)
@@ -42,6 +42,8 @@ int main(int argc, char **argv)
        int prefix_length = -1;
        int no_more_flags = 0;
 
+       git_config(git_default_config);
+
        for (i = 1 ; i < argc; i++) {
                if (!no_more_flags && argv[i][0] == '-') {
                        if (!strcmp(argv[i], "-t")) {
index 715a5bb..3c99a1f 100644 (file)
@@ -256,7 +256,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
 
 static void *get_data_from_pack(struct object_entry *obj)
 {
-       unsigned long from = obj[0].idx.offset + obj[0].hdr_size;
+       off_t from = obj[0].idx.offset + obj[0].hdr_size;
        unsigned long len = obj[1].idx.offset - from;
        unsigned long rdy = 0;
        unsigned char *src, *data;
index e5c88c2..4ef58e7 100644 (file)
@@ -170,4 +170,11 @@ void traverse_commit_list(struct rev_info *revs,
        }
        for (i = 0; i < objects.nr; i++)
                show_object(&objects.objects[i]);
+       free(objects.objects);
+       if (revs->pending.nr) {
+               free(revs->pending.objects);
+               revs->pending.nr = 0;
+               revs->pending.alloc = 0;
+               revs->pending.objects = NULL;
+       }
 }
index cc09c98..15b32f7 100644 (file)
@@ -119,8 +119,8 @@ static int parse_long_opt(struct optparse_t *p, const char *arg,
                           const struct option *options)
 {
        const char *arg_end = strchr(arg, '=');
-       const struct option *abbrev_option = NULL;
-       int abbrev_flags = 0;
+       const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
+       int abbrev_flags = 0, ambiguous_flags = 0;
 
        if (!arg_end)
                arg_end = arg + strlen(arg);
@@ -137,16 +137,16 @@ static int parse_long_opt(struct optparse_t *p, const char *arg,
                        /* abbreviated? */
                        if (!strncmp(options->long_name, arg, arg_end - arg)) {
 is_abbreviated:
-                               if (abbrev_option)
-                                       return error("Ambiguous option: %s "
-                                               "(could be --%s%s or --%s%s)",
-                                               arg,
-                                               (flags & OPT_UNSET) ?
-                                                       "no-" : "",
-                                               options->long_name,
-                                               (abbrev_flags & OPT_UNSET) ?
-                                                       "no-" : "",
-                                               abbrev_option->long_name);
+                               if (abbrev_option) {
+                                       /*
+                                        * If this is abbreviated, it is
+                                        * ambiguous. So when there is no
+                                        * exact match later, we need to
+                                        * error out.
+                                        */
+                                       ambiguous_option = abbrev_option;
+                                       ambiguous_flags = abbrev_flags;
+                               }
                                if (!(flags & OPT_UNSET) && *arg_end)
                                        p->opt = arg_end + 1;
                                abbrev_option = options;
@@ -176,6 +176,15 @@ is_abbreviated:
                }
                return get_value(p, options, flags);
        }
+
+       if (ambiguous_option)
+               return error("Ambiguous option: %s "
+                       "(could be --%s%s or --%s%s)",
+                       arg,
+                       (ambiguous_flags & OPT_UNSET) ?  "no-" : "",
+                       ambiguous_option->long_name,
+                       (abbrev_flags & OPT_UNSET) ?  "no-" : "",
+                       abbrev_option->long_name);
        if (abbrev_option)
                return get_value(p, abbrev_option, abbrev_flags);
        return error("unknown option `%s'", arg);
index 490cede..9db75b4 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1,6 +1,5 @@
 #include "cache.h"
 #include "commit.h"
-#include "interpolate.h"
 #include "utf8.h"
 #include "diff.h"
 #include "revision.h"
@@ -283,7 +282,8 @@ static char *logmsg_reencode(const struct commit *commit,
        return out;
 }
 
-static void fill_person(struct interp *table, const char *msg, int len)
+static void format_person_part(struct strbuf *sb, char part,
+                               const char *msg, int len)
 {
        int start, end, tz = 0;
        unsigned long date;
@@ -295,7 +295,10 @@ static void fill_person(struct interp *table, const char *msg, int len)
        start = end + 1;
        while (end > 0 && isspace(msg[end - 1]))
                end--;
-       table[0].value = xmemdupz(msg, end);
+       if (part == 'n') {      /* name */
+               strbuf_add(sb, msg, end);
+               return;
+       }
 
        if (start >= len)
                return;
@@ -307,7 +310,10 @@ static void fill_person(struct interp *table, const char *msg, int len)
        if (end >= len)
                return;
 
-       table[1].value = xmemdupz(msg + start, end - start);
+       if (part == 'e') {      /* email */
+               strbuf_add(sb, msg + start, end - start);
+               return;
+       }
 
        /* parse date */
        for (start = end + 1; start < len && isspace(msg[start]); start++)
@@ -318,7 +324,10 @@ static void fill_person(struct interp *table, const char *msg, int len)
        if (msg + start == ep)
                return;
 
-       table[5].value = xmemdupz(msg + start, ep - (msg + start));
+       if (part == 't') {      /* date, UNIX timestamp */
+               strbuf_add(sb, msg + start, ep - (msg + start));
+               return;
+       }
 
        /* parse tz */
        for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
@@ -329,115 +338,66 @@ static void fill_person(struct interp *table, const char *msg, int len)
                        tz = -tz;
        }
 
-       interp_set_entry(table, 2, show_date(date, tz, DATE_NORMAL));
-       interp_set_entry(table, 3, show_date(date, tz, DATE_RFC2822));
-       interp_set_entry(table, 4, show_date(date, tz, DATE_RELATIVE));
-       interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601));
+       switch (part) {
+       case 'd':       /* date */
+               strbuf_addstr(sb, show_date(date, tz, DATE_NORMAL));
+               return;
+       case 'D':       /* date, RFC2822 style */
+               strbuf_addstr(sb, show_date(date, tz, DATE_RFC2822));
+               return;
+       case 'r':       /* date, relative */
+               strbuf_addstr(sb, show_date(date, tz, DATE_RELATIVE));
+               return;
+       case 'i':       /* date, ISO 8601 */
+               strbuf_addstr(sb, show_date(date, tz, DATE_ISO8601));
+               return;
+       }
 }
 
-void format_commit_message(const struct commit *commit,
-                           const void *format, struct strbuf *sb)
-{
-       struct interp table[] = {
-               { "%H" },       /* commit hash */
-               { "%h" },       /* abbreviated commit hash */
-               { "%T" },       /* tree hash */
-               { "%t" },       /* abbreviated tree hash */
-               { "%P" },       /* parent hashes */
-               { "%p" },       /* abbreviated parent hashes */
-               { "%an" },      /* author name */
-               { "%ae" },      /* author email */
-               { "%ad" },      /* author date */
-               { "%aD" },      /* author date, RFC2822 style */
-               { "%ar" },      /* author date, relative */
-               { "%at" },      /* author date, UNIX timestamp */
-               { "%ai" },      /* author date, ISO 8601 */
-               { "%cn" },      /* committer name */
-               { "%ce" },      /* committer email */
-               { "%cd" },      /* committer date */
-               { "%cD" },      /* committer date, RFC2822 style */
-               { "%cr" },      /* committer date, relative */
-               { "%ct" },      /* committer date, UNIX timestamp */
-               { "%ci" },      /* committer date, ISO 8601 */
-               { "%e" },       /* encoding */
-               { "%s" },       /* subject */
-               { "%b" },       /* body */
-               { "%Cred" },    /* red */
-               { "%Cgreen" },  /* green */
-               { "%Cblue" },   /* blue */
-               { "%Creset" },  /* reset color */
-               { "%n" },       /* newline */
-               { "%m" },       /* left/right/bottom */
-       };
-       enum interp_index {
-               IHASH = 0, IHASH_ABBREV,
-               ITREE, ITREE_ABBREV,
-               IPARENTS, IPARENTS_ABBREV,
-               IAUTHOR_NAME, IAUTHOR_EMAIL,
-               IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE,
-               IAUTHOR_TIMESTAMP, IAUTHOR_ISO8601,
-               ICOMMITTER_NAME, ICOMMITTER_EMAIL,
-               ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822,
-               ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP,
-               ICOMMITTER_ISO8601,
-               IENCODING,
-               ISUBJECT,
-               IBODY,
-               IRED, IGREEN, IBLUE, IRESET_COLOR,
-               INEWLINE,
-               ILEFT_RIGHT,
-       };
-       struct commit_list *p;
-       char parents[1024];
-       unsigned long len;
-       int i;
-       enum { HEADER, SUBJECT, BODY } state;
-       const char *msg = commit->buffer;
+struct chunk {
+       size_t off;
+       size_t len;
+};
 
-       if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))
-               die("invalid interp table!");
+struct format_commit_context {
+       const struct commit *commit;
+
+       /* These offsets are relative to the start of the commit message. */
+       int commit_header_parsed;
+       struct chunk subject;
+       struct chunk author;
+       struct chunk committer;
+       struct chunk encoding;
+       size_t body_off;
+
+       /* The following ones are relative to the result struct strbuf. */
+       struct chunk abbrev_commit_hash;
+       struct chunk abbrev_tree_hash;
+       struct chunk abbrev_parent_hashes;
+};
 
-       /* these are independent of the commit */
-       interp_set_entry(table, IRED, "\033[31m");
-       interp_set_entry(table, IGREEN, "\033[32m");
-       interp_set_entry(table, IBLUE, "\033[34m");
-       interp_set_entry(table, IRESET_COLOR, "\033[m");
-       interp_set_entry(table, INEWLINE, "\n");
+static int add_again(struct strbuf *sb, struct chunk *chunk)
+{
+       if (chunk->len) {
+               strbuf_adddup(sb, chunk->off, chunk->len);
+               return 1;
+       }
 
-       /* these depend on the commit */
-       if (!commit->object.parsed)
-               parse_object(commit->object.sha1);
-       interp_set_entry(table, IHASH, sha1_to_hex(commit->object.sha1));
-       interp_set_entry(table, IHASH_ABBREV,
-                       find_unique_abbrev(commit->object.sha1,
-                               DEFAULT_ABBREV));
-       interp_set_entry(table, ITREE, sha1_to_hex(commit->tree->object.sha1));
-       interp_set_entry(table, ITREE_ABBREV,
-                       find_unique_abbrev(commit->tree->object.sha1,
-                               DEFAULT_ABBREV));
-       interp_set_entry(table, ILEFT_RIGHT,
-                        (commit->object.flags & BOUNDARY)
-                        ? "-"
-                        : (commit->object.flags & SYMMETRIC_LEFT)
-                        ? "<"
-                        : ">");
-
-       parents[1] = 0;
-       for (i = 0, p = commit->parents;
-                       p && i < sizeof(parents) - 1;
-                       p = p->next)
-               i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
-                       sha1_to_hex(p->item->object.sha1));
-       interp_set_entry(table, IPARENTS, parents + 1);
-
-       parents[1] = 0;
-       for (i = 0, p = commit->parents;
-                       p && i < sizeof(parents) - 1;
-                       p = p->next)
-               i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
-                       find_unique_abbrev(p->item->object.sha1,
-                               DEFAULT_ABBREV));
-       interp_set_entry(table, IPARENTS_ABBREV, parents + 1);
+       /*
+        * We haven't seen this chunk before.  Our caller is surely
+        * going to add it the hard way now.  Remember the most likely
+        * start of the to-be-added chunk: the current end of the
+        * struct strbuf.
+        */
+       chunk->off = sb->len;
+       return 0;
+}
+
+static void parse_commit_header(struct format_commit_context *context)
+{
+       const char *msg = context->commit->buffer;
+       int i;
+       enum { HEADER, SUBJECT, BODY } state;
 
        for (i = 0, state = HEADER; msg[i] && state < BODY; i++) {
                int eol;
@@ -445,7 +405,8 @@ void format_commit_message(const struct commit *commit,
                        ; /* do nothing */
 
                if (state == SUBJECT) {
-                       table[ISUBJECT].value = xmemdupz(msg + i, eol - i);
+                       context->subject.off = i;
+                       context->subject.len = eol - i;
                        i = eol;
                }
                if (i == eol) {
@@ -453,29 +414,170 @@ void format_commit_message(const struct commit *commit,
                        /* strip empty lines */
                        while (msg[eol + 1] == '\n')
                                eol++;
-               } else if (!prefixcmp(msg + i, "author "))
-                       fill_person(table + IAUTHOR_NAME,
-                                       msg + i + 7, eol - i - 7);
-               else if (!prefixcmp(msg + i, "committer "))
-                       fill_person(table + ICOMMITTER_NAME,
-                                       msg + i + 10, eol - i - 10);
-               else if (!prefixcmp(msg + i, "encoding "))
-                       table[IENCODING].value =
-                               xmemdupz(msg + i + 9, eol - i - 9);
+               } else if (!prefixcmp(msg + i, "author ")) {
+                       context->author.off = i + 7;
+                       context->author.len = eol - i - 7;
+               } else if (!prefixcmp(msg + i, "committer ")) {
+                       context->committer.off = i + 10;
+                       context->committer.len = eol - i - 10;
+               } else if (!prefixcmp(msg + i, "encoding ")) {
+                       context->encoding.off = i + 9;
+                       context->encoding.len = eol - i - 9;
+               }
                i = eol;
        }
-       if (msg[i])
-               table[IBODY].value = xstrdup(msg + i);
+       context->body_off = i;
+       context->commit_header_parsed = 1;
+}
+
+static void format_commit_item(struct strbuf *sb, const char *placeholder,
+                               void *context)
+{
+       struct format_commit_context *c = context;
+       const struct commit *commit = c->commit;
+       const char *msg = commit->buffer;
+       struct commit_list *p;
+
+       /* these are independent of the commit */
+       switch (placeholder[0]) {
+       case 'C':
+               switch (placeholder[3]) {
+               case 'd':       /* red */
+                       strbuf_addstr(sb, "\033[31m");
+                       return;
+               case 'e':       /* green */
+                       strbuf_addstr(sb, "\033[32m");
+                       return;
+               case 'u':       /* blue */
+                       strbuf_addstr(sb, "\033[34m");
+                       return;
+               case 's':       /* reset color */
+                       strbuf_addstr(sb, "\033[m");
+                       return;
+               }
+       case 'n':               /* newline */
+               strbuf_addch(sb, '\n');
+               return;
+       }
 
-       len = interpolate(sb->buf + sb->len, strbuf_avail(sb),
-                               format, table, ARRAY_SIZE(table));
-       if (len > strbuf_avail(sb)) {
-               strbuf_grow(sb, len);
-               interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1,
-                                       format, table, ARRAY_SIZE(table));
+       /* these depend on the commit */
+       if (!commit->object.parsed)
+               parse_object(commit->object.sha1);
+
+       switch (placeholder[0]) {
+       case 'H':               /* commit hash */
+               strbuf_addstr(sb, sha1_to_hex(commit->object.sha1));
+               return;
+       case 'h':               /* abbreviated commit hash */
+               if (add_again(sb, &c->abbrev_commit_hash))
+                       return;
+               strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,
+                                                    DEFAULT_ABBREV));
+               c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;
+               return;
+       case 'T':               /* tree hash */
+               strbuf_addstr(sb, sha1_to_hex(commit->tree->object.sha1));
+               return;
+       case 't':               /* abbreviated tree hash */
+               if (add_again(sb, &c->abbrev_tree_hash))
+                       return;
+               strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1,
+                                                    DEFAULT_ABBREV));
+               c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off;
+               return;
+       case 'P':               /* parent hashes */
+               for (p = commit->parents; p; p = p->next) {
+                       if (p != commit->parents)
+                               strbuf_addch(sb, ' ');
+                       strbuf_addstr(sb, sha1_to_hex(p->item->object.sha1));
+               }
+               return;
+       case 'p':               /* abbreviated parent hashes */
+               if (add_again(sb, &c->abbrev_parent_hashes))
+                       return;
+               for (p = commit->parents; p; p = p->next) {
+                       if (p != commit->parents)
+                               strbuf_addch(sb, ' ');
+                       strbuf_addstr(sb, find_unique_abbrev(
+                                       p->item->object.sha1, DEFAULT_ABBREV));
+               }
+               c->abbrev_parent_hashes.len = sb->len -
+                                             c->abbrev_parent_hashes.off;
+               return;
+       case 'm':               /* left/right/bottom */
+               strbuf_addch(sb, (commit->object.flags & BOUNDARY)
+                                ? '-'
+                                : (commit->object.flags & SYMMETRIC_LEFT)
+                                ? '<'
+                                : '>');
+               return;
+       }
+
+       /* For the rest we have to parse the commit header. */
+       if (!c->commit_header_parsed)
+               parse_commit_header(c);
+
+       switch (placeholder[0]) {
+       case 's':
+               strbuf_add(sb, msg + c->subject.off, c->subject.len);
+               return;
+       case 'a':
+               format_person_part(sb, placeholder[1],
+                                  msg + c->author.off, c->author.len);
+               return;
+       case 'c':
+               format_person_part(sb, placeholder[1],
+                                  msg + c->committer.off, c->committer.len);
+               return;
+       case 'e':
+               strbuf_add(sb, msg + c->encoding.off, c->encoding.len);
+               return;
+       case 'b':
+               strbuf_addstr(sb, msg + c->body_off);
+               return;
        }
-       strbuf_setlen(sb, sb->len + len);
-       interp_clear_table(table, ARRAY_SIZE(table));
+}
+
+void format_commit_message(const struct commit *commit,
+                           const void *format, struct strbuf *sb)
+{
+       const char *placeholders[] = {
+               "H",            /* commit hash */
+               "h",            /* abbreviated commit hash */
+               "T",            /* tree hash */
+               "t",            /* abbreviated tree hash */
+               "P",            /* parent hashes */
+               "p",            /* abbreviated parent hashes */
+               "an",           /* author name */
+               "ae",           /* author email */
+               "ad",           /* author date */
+               "aD",           /* author date, RFC2822 style */
+               "ar",           /* author date, relative */
+               "at",           /* author date, UNIX timestamp */
+               "ai",           /* author date, ISO 8601 */
+               "cn",           /* committer name */
+               "ce",           /* committer email */
+               "cd",           /* committer date */
+               "cD",           /* committer date, RFC2822 style */
+               "cr",           /* committer date, relative */
+               "ct",           /* committer date, UNIX timestamp */
+               "ci",           /* committer date, ISO 8601 */
+               "e",            /* encoding */
+               "s",            /* subject */
+               "b",            /* body */
+               "Cred",         /* red */
+               "Cgreen",       /* green */
+               "Cblue",        /* blue */
+               "Creset",       /* reset color */
+               "n",            /* newline */
+               "m",            /* left/right/bottom */
+               NULL
+       };
+       struct format_commit_context context;
+
+       memset(&context, 0, sizeof(context));
+       context.commit = commit;
+       strbuf_expand(sb, format, placeholders, format_commit_item, &context);
 }
 
 static void pp_header(enum cmit_fmt fmt,
diff --git a/setup.c b/setup.c
index 1e2c55d..43cd3f9 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -208,12 +208,18 @@ static const char *set_work_tree(const char *dir)
 
 void setup_work_tree(void)
 {
-       const char *work_tree = get_git_work_tree();
-       const char *git_dir = get_git_dir();
+       const char *work_tree, *git_dir;
+       static int initialized = 0;
+
+       if (initialized)
+               return;
+       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));
        if (!work_tree || chdir(work_tree))
                die("This operation must be run in a work tree");
+       initialized = 1;
 }
 
 /*
index f4201e1..b9b194b 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -106,17 +106,25 @@ void strbuf_add(struct strbuf *sb, const void *data, size_t len)
        strbuf_setlen(sb, sb->len + len);
 }
 
+void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len)
+{
+       strbuf_grow(sb, len);
+       memcpy(sb->buf + sb->len, sb->buf + pos, len);
+       strbuf_setlen(sb, sb->len + len);
+}
+
 void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
 {
        int len;
        va_list ap;
 
+       if (!strbuf_avail(sb))
+               strbuf_grow(sb, 64);
        va_start(ap, fmt);
        len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
        va_end(ap);
-       if (len < 0) {
-               len = 0;
-       }
+       if (len < 0)
+               die("your vsnprintf is broken");
        if (len > strbuf_avail(sb)) {
                strbuf_grow(sb, len);
                va_start(ap, fmt);
@@ -129,6 +137,30 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
        strbuf_setlen(sb, sb->len + len);
 }
 
+void strbuf_expand(struct strbuf *sb, const char *format,
+                   const char **placeholders, expand_fn_t fn, void *context)
+{
+       for (;;) {
+               const char *percent, **p;
+
+               percent = strchrnul(format, '%');
+               strbuf_add(sb, format, percent - format);
+               if (!*percent)
+                       break;
+               format = percent + 1;
+
+               for (p = placeholders; *p; p++) {
+                       if (!prefixcmp(format, *p))
+                               break;
+               }
+               if (*p) {
+                       fn(sb, *p, context);
+                       format += strlen(*p);
+               } else
+                       strbuf_addch(sb, '%');
+       }
+}
+
 size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
 {
        size_t res;
index cd7f295..1391912 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -101,6 +101,10 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s) {
 static inline void strbuf_addbuf(struct strbuf *sb, struct strbuf *sb2) {
        strbuf_add(sb, sb2->buf, sb2->len);
 }
+extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len);
+
+typedef void (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
+extern void strbuf_expand(struct strbuf *sb, const char *format, const char **placeholders, expand_fn_t fn, void *context);
 
 __attribute__((format(printf,2,3)))
 extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
index ae49424..462fdf2 100755 (executable)
@@ -18,6 +18,7 @@ string options
     -s, --string <string>
                           get a string
     --string2 <str>       get another string
+    --st <st>             get another string (pervert ordering)
 
 EOF
 
@@ -90,4 +91,16 @@ test_expect_failure 'ambiguously abbreviated option' '
         test $? != 129
 '
 
+cat > expect << EOF
+boolean: 0
+integer: 0
+string: 123
+EOF
+
+test_expect_success 'non ambiguous option (after two options it abbreviates)' '
+       test-parse-options --st 123 > output 2> output.err &&
+       test ! -s output.err &&
+       git diff expect output
+'
+
 test_done
index 1113904..f1039d1 100755 (executable)
@@ -149,7 +149,7 @@ test_expect_success 'stop on conflicting pick' '
        diff -u expect .git/.dotest-merge/patch &&
        diff -u expect2 file1 &&
        test 4 = $(grep -v "^#" < .git/.dotest-merge/done | wc -l) &&
-       test 0 = $(grep -v "^#" < .git/.dotest-merge/todo | wc -l)
+       test 0 = $(grep -v "^#" < .git/.dotest-merge/git-rebase-todo | wc -l)
 '
 
 test_expect_success 'abort' '
diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh
new file mode 100755 (executable)
index 0000000..cc8949e
--- /dev/null
@@ -0,0 +1,75 @@
+#!/bin/sh
+
+test_description='errors in upload-pack'
+
+. ./test-lib.sh
+
+D=`pwd`
+
+corrupt_repo () {
+       object_sha1=$(git rev-parse "$1") &&
+       ob=$(expr "$object_sha1" : "\(..\)") &&
+       ject=$(expr "$object_sha1" : "..\(..*\)") &&
+       rm -f ".git/objects/$ob/$ject"
+}
+
+test_expect_success 'setup and corrupt repository' '
+
+       echo file >file &&
+       git add file &&
+       git rev-parse :file &&
+       git commit -a -m original &&
+       test_tick &&
+       echo changed >file &&
+       git commit -a -m changed &&
+       corrupt_repo HEAD:file
+
+'
+
+test_expect_failure 'fsck fails' '
+
+       git fsck
+'
+
+test_expect_success 'upload-pack fails due to error in pack-objects' '
+
+       ! echo "0032want $(git rev-parse HEAD)
+00000009done
+0000" | git-upload-pack . > /dev/null 2> output.err &&
+       grep "pack-objects died" output.err
+'
+
+test_expect_success 'corrupt repo differently' '
+
+       git hash-object -w file &&
+       corrupt_repo HEAD^^{tree}
+
+'
+
+test_expect_failure 'fsck fails' '
+
+       git fsck
+'
+test_expect_success 'upload-pack fails due to error in rev-list' '
+
+       ! echo "0032want $(git rev-parse HEAD)
+00000009done
+0000" | git-upload-pack . > /dev/null 2> output.err &&
+       grep "waitpid (async) failed" output.err
+'
+
+test_expect_success 'create empty repository' '
+
+       mkdir foo &&
+       cd foo &&
+       git init
+
+'
+
+test_expect_failure 'fetch fails' '
+
+       git fetch .. master
+
+'
+
+test_done
index d0809eb..c722635 100755 (executable)
@@ -148,4 +148,26 @@ test_expect_success 'Check format "rfc2822" date fields output' '
        git diff expected actual
 '
 
+cat >expected <<\EOF
+refs/heads/master
+refs/tags/testtag
+EOF
+
+test_expect_success 'Verify ascending sort' '
+       git-for-each-ref --format="%(refname)" --sort=refname >actual &&
+       git diff expected actual
+'
+
+
+cat >expected <<\EOF
+refs/tags/testtag
+refs/heads/master
+EOF
+
+test_expect_success 'Verify descending sort' '
+       git-for-each-ref --format="%(refname)" --sort=-refname >actual &&
+       git diff expected actual
+'
+
+
 test_done
index cea9afb..e5c9f30 100755 (executable)
@@ -59,6 +59,15 @@ test_expect_success 'giving a non existing revision should fail' '
        check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
 '
 
+test_expect_success 'reset --soft with unmerged index should fail' '
+       touch .git/MERGE_HEAD &&
+       echo "100644 44c5b5884550c17758737edcced463447b91d42b 1 un" |
+               git update-index --index-info &&
+       ! git reset --soft HEAD &&
+       rm .git/MERGE_HEAD &&
+       git rm --cached -- un
+'
+
 test_expect_success \
        'giving paths with options different than --mixed should fail' '
        ! git reset --soft -- first &&
@@ -409,4 +418,14 @@ test_expect_success 'resetting an unmodified path is a no-op' '
        git diff-index --cached --exit-code HEAD
 '
 
+cat > expect << EOF
+file2: needs update
+EOF
+
+test_expect_success '--mixed refreshes the index' '
+       echo 123 >> file2 &&
+       git reset --mixed HEAD > output &&
+       git diff --exit-code expect output
+'
+
 test_done
index 25d3102..f013c17 100755 (executable)
@@ -291,4 +291,15 @@ test_expect_success 'clean.requireForce and -f' '
 
 '
 
+test_expect_success 'core.excludesfile' '
+
+       echo excludes >excludes &&
+       echo included >included &&
+       git config core.excludesfile excludes &&
+       output=$(git clean -n excludes included 2>&1) &&
+       expr "$output" : ".*included" >/dev/null &&
+       ! expr "$output" : ".*excludes" >/dev/null
+
+'
+
 test_done
index b151b51..4dc35bd 100644 (file)
@@ -163,4 +163,73 @@ test_expect_success 'partial commit that involves removal (3)' '
 
 '
 
+author="The Real Author <someguy@his.email.org>"
+test_expect_success 'amend commit to fix author' '
+
+       oldtick=$GIT_AUTHOR_DATE &&
+       test_tick &&
+       git reset --hard &&
+       git cat-file -p HEAD |
+       sed -e "s/author.*/author $author $oldtick/" \
+               -e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" > \
+               expected &&
+       git commit --amend --author="$author" &&
+       git cat-file -p HEAD > current &&
+       diff expected current
+
+'
+
+test_expect_success 'sign off (1)' '
+
+       echo 1 >positive &&
+       git add positive &&
+       git commit -s -m "thank you" &&
+       git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+       (
+               echo thank you
+               echo
+               git var GIT_COMMITTER_IDENT |
+               sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
+       ) >expected &&
+       diff -u expected actual
+
+'
+
+test_expect_success 'sign off (2)' '
+
+       echo 2 >positive &&
+       git add positive &&
+       existing="Signed-off-by: Watch This <watchthis@example.com>" &&
+       git commit -s -m "thank you
+
+$existing" &&
+       git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+       (
+               echo thank you
+               echo
+               echo $existing
+               git var GIT_COMMITTER_IDENT |
+               sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
+       ) >expected &&
+       diff -u expected actual
+
+'
+
+test_expect_success 'multiple -m' '
+
+       >negative &&
+       git add negative &&
+       git commit -m "one" -m "two" -m "three" &&
+       git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+       (
+               echo one
+               echo
+               echo two
+               echo
+               echo three
+       ) >expected &&
+       diff -u expected actual
+
+'
+
 test_done
index 7eff4cd..d59acc8 100755 (executable)
@@ -53,4 +53,10 @@ test_expect_success 'change file but in unrelated area' "
                test x\"\`sed -n -e 61p < file\`\" = x6611
        "
 
+test_expect_failure 'attempt to dcommit with a dirty index' '
+       echo foo >>file &&
+       git add file &&
+       git svn dcommit
+'
+
 test_done
diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh
new file mode 100755 (executable)
index 0000000..640bb06
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+#
+
+test_description='git-svn funky branch names'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svnrepo' "
+       mkdir project project/trunk project/branches project/tags &&
+       echo foo > project/trunk/foo &&
+       svn import -m '$test_description' project \"$svnrepo/pr ject\" &&
+       rm -rf project &&
+       svn cp -m 'fun' \"$svnrepo/pr ject/trunk\" \
+                       \"$svnrepo/pr ject/branches/fun plugin\" &&
+       svn cp -m 'more fun!' \"$svnrepo/pr ject/branches/fun plugin\" \
+                             \"$svnrepo/pr ject/branches/more fun plugin!\" &&
+       start_httpd
+       "
+
+test_expect_success 'test clone with funky branch names' "
+       git svn clone -s \"$svnrepo/pr ject\" project &&
+       cd project &&
+               git rev-parse 'refs/remotes/fun%20plugin' &&
+               git rev-parse 'refs/remotes/more%20fun%20plugin!' &&
+       cd ..
+       "
+
+test_expect_success 'test dcommit to funky branch' "
+       cd project &&
+       git reset --hard 'refs/remotes/more%20fun%20plugin!' &&
+       echo hello >> foo &&
+       git commit -m 'hello' -- foo &&
+       git svn dcommit &&
+       cd ..
+       "
+
+stop_httpd
+
+test_done
index 603a8cd..90b6844 100644 (file)
@@ -66,9 +66,6 @@ esac
        tput sgr0 >/dev/null 2>&1 &&
        color=t
 
-test "${test_description}" != "" ||
-error "Test script did not set test_description."
-
 while test "$#" -ne 0
 do
        case "$1" in
@@ -77,8 +74,7 @@ do
        -i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate)
                immediate=t; shift ;;
        -h|--h|--he|--hel|--help)
-               echo "$test_description"
-               exit 0 ;;
+               help=t; shift ;;
        -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
                verbose=t; shift ;;
        -q|--q|--qu|--qui|--quie|--quiet)
@@ -124,6 +120,15 @@ say () {
        say_color info "$*"
 }
 
+test "${test_description}" != "" ||
+error "Test script did not set test_description."
+
+if test "$help" = "t"
+then
+       echo "$test_description"
+       exit 0
+fi
+
 exec 5>&1
 if test "$verbose" = "t"
 then
index 277cfe4..4d3e2ec 100644 (file)
@@ -18,6 +18,7 @@ int main(int argc, const char **argv)
                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_END(),
        };
        int i;
diff --git a/trace.c b/trace.c
index 69fa05e..0d89dbe 100644 (file)
--- a/trace.c
+++ b/trace.c
@@ -72,7 +72,7 @@ void trace_printf(const char *fmt, ...)
        if (!fd)
                return;
 
-       strbuf_init(&buf, 0);
+       strbuf_init(&buf, 64);
        va_start(ap, fmt);
        len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
        va_end(ap);
@@ -103,7 +103,7 @@ void trace_argv_printf(const char **argv, int count, const char *fmt, ...)
        if (!fd)
                return;
 
-       strbuf_init(&buf, 0);
+       strbuf_init(&buf, 64);
        va_start(ap, fmt);
        len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
        va_end(ap);
index fa5cfbb..e8a2608 100644 (file)
@@ -386,7 +386,7 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons
        int argc;
        int err;
 
-       argv = xmalloc((refspec_nr + 11) * sizeof(char *));
+       argv = xmalloc((refspec_nr + 12) * sizeof(char *));
        argv[0] = "http-push";
        argc = 1;
        if (flags & TRANSPORT_PUSH_ALL)
@@ -395,6 +395,8 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons
                argv[argc++] = "--force";
        if (flags & TRANSPORT_PUSH_DRY_RUN)
                argv[argc++] = "--dry-run";
+       if (flags & TRANSPORT_PUSH_VERBOSE)
+               argv[argc++] = "--verbose";
        argv[argc++] = transport->url;
        while (refspec_nr--)
                argv[argc++] = *refspec++;
@@ -655,7 +657,7 @@ static int git_transport_push(struct transport *transport, int refspec_nr, const
        int argc;
        int err;
 
-       argv = xmalloc((refspec_nr + 11) * sizeof(char *));
+       argv = xmalloc((refspec_nr + 12) * sizeof(char *));
        argv[0] = "send-pack";
        argc = 1;
        if (flags & TRANSPORT_PUSH_ALL)
@@ -664,6 +666,8 @@ static int git_transport_push(struct transport *transport, int refspec_nr, const
                argv[argc++] = "--force";
        if (flags & TRANSPORT_PUSH_DRY_RUN)
                argv[argc++] = "--dry-run";
+       if (flags & TRANSPORT_PUSH_VERBOSE)
+               argv[argc++] = "--verbose";
        if (data->receivepack) {
                char *rp = xmalloc(strlen(data->receivepack) + 16);
                sprintf(rp, "--receive-pack=%s", data->receivepack);
index df12ea7..2f80ab4 100644 (file)
@@ -30,6 +30,7 @@ struct transport {
 #define TRANSPORT_PUSH_ALL 1
 #define TRANSPORT_PUSH_FORCE 2
 #define TRANSPORT_PUSH_DRY_RUN 4
+#define TRANSPORT_PUSH_VERBOSE 8
 
 /* Returns a transport suitable for the url */
 struct transport *transport_get(struct remote *, const char *);
index 6799468..7e04311 100644 (file)
@@ -144,6 +144,7 @@ static void create_pack_file(void)
        char abort_msg[] = "aborting due to possible repository "
                "corruption on the remote side.";
        int buffered = -1;
+       ssize_t sz;
        const char *argv[10];
        int arg = 0;
 
@@ -168,22 +169,15 @@ static void create_pack_file(void)
        pack_objects.git_cmd = 1;
        pack_objects.argv = argv;
 
-       if (start_command(&pack_objects)) {
-               /* daemon sets things up to ignore TERM */
-               kill(rev_list.pid, SIGKILL);
+       if (start_command(&pack_objects))
                die("git-upload-pack: unable to fork git-pack-objects");
-       }
 
        /* We read from pack_objects.err to capture stderr output for
         * progress bar, and pack_objects.out to capture the pack data.
         */
 
        while (1) {
-               const char *who;
                struct pollfd pfd[2];
-               pid_t pid;
-               int status;
-               ssize_t sz;
                int pe, pu, pollsize;
 
                reset_timeout();
@@ -204,123 +198,91 @@ static void create_pack_file(void)
                        pollsize++;
                }
 
-               if (pollsize) {
-                       if (poll(pfd, pollsize, -1) < 0) {
-                               if (errno != EINTR) {
-                                       error("poll failed, resuming: %s",
-                                             strerror(errno));
-                                       sleep(1);
-                               }
-                               continue;
-                       }
-                       if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
-                               /* Data ready; we keep the last byte
-                                * to ourselves in case we detect
-                                * broken rev-list, so that we can
-                                * leave the stream corrupted.  This
-                                * is unfortunate -- unpack-objects
-                                * would happily accept a valid pack
-                                * data with trailing garbage, so
-                                * appending garbage after we pass all
-                                * the pack data is not good enough to
-                                * signal breakage to downstream.
-                                */
-                               char *cp = data;
-                               ssize_t outsz = 0;
-                               if (0 <= buffered) {
-                                       *cp++ = buffered;
-                                       outsz++;
-                               }
-                               sz = xread(pack_objects.out, cp,
-                                         sizeof(data) - outsz);
-                               if (0 < sz)
-                                               ;
-                               else if (sz == 0) {
-                                       close(pack_objects.out);
-                                       pack_objects.out = -1;
-                               }
-                               else
-                                       goto fail;
-                               sz += outsz;
-                               if (1 < sz) {
-                                       buffered = data[sz-1] & 0xFF;
-                                       sz--;
-                               }
-                               else
-                                       buffered = -1;
-                               sz = send_client_data(1, data, sz);
-                               if (sz < 0)
-                                       goto fail;
-                       }
-                       if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
-                               /* Status ready; we ship that in the side-band
-                                * or dump to the standard error.
-                                */
-                               sz = xread(pack_objects.err, progress,
-                                         sizeof(progress));
-                               if (0 < sz)
-                                       send_client_data(2, progress, sz);
-                               else if (sz == 0) {
-                                       close(pack_objects.err);
-                                       pack_objects.err = -1;
-                               }
-                               else
-                                       goto fail;
+               if (!pollsize)
+                       break;
+
+               if (poll(pfd, pollsize, -1) < 0) {
+                       if (errno != EINTR) {
+                               error("poll failed, resuming: %s",
+                                     strerror(errno));
+                               sleep(1);
                        }
+                       continue;
                }
-
-               /* See if the children are still there */
-               if (rev_list.pid || pack_objects.pid) {
-                       pid = waitpid(-1, &status, WNOHANG);
-                       if (!pid)
-                               continue;
-                       who = ((pid == rev_list.pid) ? "git-rev-list" :
-                              (pid == pack_objects.pid) ? "git-pack-objects" :
-                              NULL);
-                       if (!who) {
-                               if (pid < 0) {
-                                       error("git-upload-pack: %s",
-                                             strerror(errno));
-                                       goto fail;
-                               }
-                               error("git-upload-pack: we weren't "
-                                     "waiting for %d", pid);
-                               continue;
+               if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
+                       /* Data ready; we keep the last byte to ourselves
+                        * in case we detect broken rev-list, so that we
+                        * can leave the stream corrupted.  This is
+                        * unfortunate -- unpack-objects would happily
+                        * accept a valid packdata with trailing garbage,
+                        * so appending garbage after we pass all the
+                        * pack data is not good enough to signal
+                        * breakage to downstream.
+                        */
+                       char *cp = data;
+                       ssize_t outsz = 0;
+                       if (0 <= buffered) {
+                               *cp++ = buffered;
+                               outsz++;
+                       }
+                       sz = xread(pack_objects.out, cp,
+                                 sizeof(data) - outsz);
+                       if (0 < sz)
+                                       ;
+                       else if (sz == 0) {
+                               close(pack_objects.out);
+                               pack_objects.out = -1;
                        }
-                       if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) {
-                               error("git-upload-pack: %s died with error.",
-                                     who);
+                       else
                                goto fail;
+                       sz += outsz;
+                       if (1 < sz) {
+                               buffered = data[sz-1] & 0xFF;
+                               sz--;
                        }
-                       if (pid == rev_list.pid)
-                               rev_list.pid = 0;
-                       if (pid == pack_objects.pid)
-                               pack_objects.pid = 0;
-                       if (rev_list.pid || pack_objects.pid)
-                               continue;
-               }
-
-               /* both died happily */
-               if (pollsize)
-                       continue;
-
-               /* flush the data */
-               if (0 <= buffered) {
-                       data[0] = buffered;
-                       sz = send_client_data(1, data, 1);
+                       else
+                               buffered = -1;
+                       sz = send_client_data(1, data, sz);
                        if (sz < 0)
                                goto fail;
-                       fprintf(stderr, "flushed.\n");
                }
-               if (use_sideband)
-                       packet_flush(1);
-               return;
+               if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
+                       /* Status ready; we ship that in the side-band
+                        * or dump to the standard error.
+                        */
+                       sz = xread(pack_objects.err, progress,
+                                 sizeof(progress));
+                       if (0 < sz)
+                               send_client_data(2, progress, sz);
+                       else if (sz == 0) {
+                               close(pack_objects.err);
+                               pack_objects.err = -1;
+                       }
+                       else
+                               goto fail;
+               }
+       }
+
+       if (finish_command(&pack_objects)) {
+               error("git-upload-pack: git-pack-objects died with error.");
+               goto fail;
+       }
+       if (finish_async(&rev_list))
+               goto fail;      /* error was already reported */
+
+       /* flush the data */
+       if (0 <= buffered) {
+               data[0] = buffered;
+               sz = send_client_data(1, data, 1);
+               if (sz < 0)
+                       goto fail;
+               fprintf(stderr, "flushed.\n");
        }
+       if (use_sideband)
+               packet_flush(1);
+       return;
+
  fail:
-       if (pack_objects.pid)
-               kill(pack_objects.pid, SIGKILL);
-       if (rev_list.pid)
-               kill(rev_list.pid, SIGKILL);
        send_client_data(3, abort_msg, sizeof(abort_msg));
        die("git-upload-pack: %s", abort_msg);
 }
diff --git a/usage.c b/usage.c
index f5e652c..a5fc4ec 100644 (file)
--- a/usage.c
+++ b/usage.c
@@ -7,9 +7,9 @@
 
 static void report(const char *prefix, const char *err, va_list params)
 {
-       fputs(prefix, stderr);
-       vfprintf(stderr, err, params);
-       fputs("\n", stderr);
+       char msg[256];
+       vsnprintf(msg, sizeof(msg), err, params);
+       fprintf(stderr, "%s%s\n", prefix, msg);
 }
 
 static NORETURN void usage_builtin(const char *err)