Merge branch 'jc/branch-contains'
authorJunio C Hamano <gitster@pobox.com>
Sat, 1 Dec 2007 21:58:54 +0000 (13:58 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sat, 1 Dec 2007 21:58:54 +0000 (13:58 -0800)
* jc/branch-contains:
git-branch --contains: doc and test
git-branch --contains=commit
parse-options: Allow to hide options from the default usage.

91 files changed:
Documentation/RelNotes-1.5.3.7.txt [new file with mode: 0644]
Documentation/RelNotes-1.5.4.txt
Documentation/cmd-list.perl
Documentation/git-branch.txt
Documentation/git-peek-remote.txt
Documentation/git-push.txt
Documentation/git-tag.txt
Documentation/git.txt
Documentation/user-manual.txt
Makefile
builtin-clean.c [new file with mode: 0644]
builtin-config.c
builtin-fetch--tool.c
builtin-fetch-pack.c
builtin-fetch.c
builtin-init-db.c
builtin-ls-remote.c [new file with mode: 0644]
builtin-push.c
builtin-revert.c
builtin-send-pack.c [new file with mode: 0644]
builtin-tag.c
builtin.h
cache.h
config.c
configure.ac
connect.c
contrib/completion/git-completion.bash
contrib/examples/git-clean.sh [new file with mode: 0755]
contrib/examples/git-ls-remote.sh [new file with mode: 0755]
contrib/examples/git-merge-ours.sh
diff-lib.c
diffcore-rename.c
dir.c
dir.h
git-am.sh
git-bisect.sh
git-checkout.sh
git-clean.sh [deleted file]
git-compat-util.h
git-cvsimport.perl
git-filter-branch.sh
git-ls-remote.sh [deleted file]
git-rebase--interactive.sh
git-rebase.sh
git-stash.sh
git-svn.perl
git.c
help.c
http-push.c
http-walker.c
lockfile.c
pager.c
peek-remote.c [deleted file]
perl/Git.pm
receive-pack.c
refs.c
remote.c
remote.h
send-pack.c [deleted file]
send-pack.h [new file with mode: 0644]
server-info.c
setup.c
sha1_file.c
t/t3403-rebase-skip.sh
t/t3902-quoted.sh
t/t4000-diff-format.sh
t/t4001-diff-rename.sh
t/t4008-diff-break-rewrite.sh
t/t4100/t-apply-1.patch
t/t4100/t-apply-2.patch
t/t4100/t-apply-5.patch
t/t4100/t-apply-6.patch
t/t4119-apply-config.sh
t/t5300-pack-object.sh
t/t5302-pack-index.sh
t/t5404-tracking-branches.sh [new file with mode: 0755]
t/t5405-send-pack-rewind.sh [new file with mode: 0755]
t/t5406-remote-rejects.sh [new file with mode: 0755]
t/t5512-ls-remote.sh [new file with mode: 0755]
t/t5516-fetch-push.sh
t/t5517-push-mirror.sh [new file with mode: 0755]
t/t6030-bisect-porcelain.sh
t/t7003-filter-branch.sh
t/t7004-tag.sh
t/t7501-commit.sh
t/t9600-cvsimport.sh [new file with mode: 0755]
templates/hooks--pre-commit
trace.c
transport.c
transport.h
unpack-trees.c
diff --git a/Documentation/RelNotes-1.5.3.7.txt b/Documentation/RelNotes-1.5.3.7.txt
new file mode 100644 (file)
index 0000000..2f69061
--- /dev/null
@@ -0,0 +1,45 @@
+GIT v1.5.3.7 Release Notes
+==========================
+
+Fixes since v1.5.3.6
+--------------------
+
+ * git-send-email added 8-bit contents to the payload without
+   marking it as 8-bit in a CTE header.
+
+ * "git-bundle create a.bndl HEAD" dereferenced the symref and
+   did not record the ref as 'HEAD'; this prevented a bundle
+   from being used as a normal source of git-clone.
+
+ * The code to reject nonsense command line of the form
+   "git-commit -a paths..." and "git-commit --interactive
+   paths..." were broken.
+
+ * Adding a signature that is not ASCII-only to an original
+   commit that is ASCII-only would make the result non-ASCII.
+   "git-format-patch -s" did not mark such a message correctly
+   with MIME encoding header.
+
+ * git-add sometimes did not mark the resulting index entry
+   stat-clean.  This affected only cases when adding the
+   contents with the same length as the previously staged
+   contents, and the previous staging made the index entry
+   "racily clean".
+
+ * git-commit did not honor GIT_INDEX_FILE the user had in the
+   environment.
+
+ * When checking out a revision, git-checkout did not report where the
+   updated HEAD is if you happened to have a file called HEAD in the
+   work tree.
+
+ * "git-rev-list --objects" mishandled a tree that points at a
+   submodule.
+
+ * "git cvsimport" was not ready for packed refs that "git gc" can
+   produce and gave incorrect results.
+
+ * Many scripted Porcelains were confused when you happened to have a
+   file called "HEAD" in your work tree.
+
+Also it contains updates to the user manual and documentation.
index a4a2a7f429bc71884b823dffd8db5a1c10b34ecb..192766f868f4669b7cedc5baa4c119a39fc602b5 100644 (file)
@@ -1,20 +1,70 @@
 GIT v1.5.4 Release Notes
 ========================
 
+Deprecation notices
+-------------------
+
+ * Next feature release of git (this change is scheduled for v1.5.5 but
+   it could slip) will by default install dashed form of commands
+   (e.g. "git-commit") outside of users' normal $PATH, and will install
+   only selected commands ("git" itself, and "gitk") in $PATH.  This
+   implies:
+
+   - Using dashed form of git commands (e.g. "git-commit") from the
+     command line has been informally deprecated since early 2006, but
+     now it officially is, and will be removed in the future.  Use
+     dashless form (e.g. "git commit") instead.
+
+   - Using dashed from from your scripts, without first prepending the
+     return value from "git --exec-path" to the scripts' PATH, has been
+     informally deprecated since early 2006, but now it officially is.
+
+   - Use of dashed form with "PATH=$(git --exec-path):$PATH; export
+     PATH" early in your script is not deprecated with this change.
+
+  Users are strongly encouraged to adjust their habits and scripts now
+  to prepare for this.
+
+ * The post-receive hook was introduced in March 2007 to supersede
+   post-update hook, primarily to overcome the command line length
+   limitation of the latter.  Use of post-update hook will be deprecated
+   in future versions of git, perhaps in v1.5.5.
+
+
 Updates since v1.5.3
 --------------------
 
  * Comes with much improved gitk.
 
+ * Comes with git-gui 0.9.0 with i18n.
+
+ * git-lost-found was deprecated in favor of git-fsck's --lost-found
+   option.
+
+ * git-peek-remote is deprecated, as git-ls-remote was written in C and
+   works for all transports.
+
+ * git-cherry-pick made a misguided attempt to repeat the original
+   command line in the generated log message, when told to cherry-pick a
+   commit by naming a tag that points at it.  It does not anymore.
+
  * "progress display" from many commands are a lot nicer to the
    eye.  Transfer commands show throughput data.
 
+ * many commands that pay attention to per-directory .gitignore now do
+   so lazily, which makes the usual case go much faster.
+
  * git-reset is now built-in and its output can be squelched with -q.
 
  * git-send-email can optionally talk over ssmtp and use SMTP-AUTH.
 
  * git-rebase learned --whitespace option.
 
+ * In git-rebase, when you decide not to replay a particular change
+   after the command stopped with a conflict, you can say "git-rebase
+   --skip" without first running "git reset --hard", as the command now
+   runs it for you.
+
  * git-remote knows --mirror mode.
 
  * git-merge can call the "post-merge" hook.
@@ -37,11 +87,20 @@ Updates since v1.5.3
    variable used to mean "do not require", but we now use the safer
    default).
 
+ * git-clean has been rewritten in C.
+
  * git-push has been rewritten in C.
 
  * git-push learned --dry-run option to show what would happen
    if a push is run.
 
+ * git-push does not update a tracking ref on the pushing side when the
+   remote refused to update the corresponding ref.
+
+ * git-push learned --mirror option.  This is to push the local refs
+   one-to-one to the remote, and deletes refs from the remote that do
+   not exist anymore in the repository on the pushing side.
+
  * git-remote learned "rm" subcommand.
 
  * git-rebase --interactive mode can now work on detached HEAD.
@@ -54,9 +113,6 @@ Updates since v1.5.3
 
  * Various Perforce importer updates.
 
- * git-lost-found was deprecated in favor of git-fsck's --lost-found
-   option.
-
  * "git log" learned --early-output option to help interactive
    GUI implementations.
 
@@ -86,14 +142,41 @@ Updates since v1.5.3
    to allow checking out a path outside the current directory
    without cd'ing up.
 
+ * "git send-email --dry-run" shows full headers for easier
+   diagnosis.
+
+ * "git merge-ours" is built-in.
+
+ * "git svn" learned "info" and "show-externals" subcommands.
+
+ * calling "git svn" from a subdirectory failed read settings from the
+   .git/config.
+
+ * "git svn" learned --use-log-author option, which picks up more
+   descriptive name from From: and Signed-off-by: lines in the commit
+   message.
+
+ * "git status" from a subdirectory now shows relative paths
+   which makes copy-and-pasting for git-checkout/git-add/git-rm
+   easier.
+
+ * "git checkout" from and to detached HEAD leaves a bit more
+   information in the reflog.
+
  * Output processing for '--pretty=format:<user format>' has
    been optimized.
 
- * Rename detection diff family, while detecting exact matches,
-   has been greatly optimized.
+ * Rename detection of diff family, while detecting exact matches, has
+   been greatly optimized.
+
+ * Rename detection of diff family tries to make more naturally looking
+   pairing.  Earlier if more than one identical rename sources were
+   found in the preimage, they were picked pretty much at random.
 
  * Example update and post-receive hooks have been improved.
 
+ * A corrupt ref at the remote site can be removed via "git push".
+
  * In addition there are quite a few internal clean-ups. Notably
 
    - many fork/exec have been replaced with run-command API,
@@ -119,6 +202,6 @@ this release, unless otherwise noted.
 
 --
 exec >/var/tmp/1
-O=v1.5.3.6-727-g5d3d1ca
+O=v1.5.3.7-948-gb52e985
 echo O=`git describe refs/heads/master`
 git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
index 57a790df63b884b5ece790422bedacaab0a4ba3b..b709551726989036c7232cb2a5f72b9578b5e4af 100755 (executable)
@@ -151,7 +151,7 @@ sub format_one {
 git-pack-refs                           ancillarymanipulators
 git-parse-remote                        synchelpers
 git-patch-id                            purehelpers
-git-peek-remote                         purehelpers
+git-peek-remote                         purehelpers    deprecated
 git-prune                               ancillarymanipulators
 git-prune-packed                        plumbingmanipulators
 git-pull                                mainporcelain
index 16303ef555aa815e89f7882517644e9b76f8b488..d3f21c797596e9dc633293e0cca4a172940bd4af 100644 (file)
@@ -49,17 +49,22 @@ to happen.
 
 With a `-d` or `-D` option, `<branchname>` will be deleted.  You may
 specify more than one branch for deletion.  If the branch currently
-has a reflog then the reflog will also be deleted. Use -r together with -d
-to delete remote-tracking branches.
+has a reflog then the reflog will also be deleted.
+
+Use -r together with -d to delete remote-tracking branches. Note, that it
+only makes sense to delete remote-tracking branches if they no longer exist
+in remote repository or if gitlink:git-fetch[1] was configured not to fetch
+them again. See also 'prune' subcommand of gitlink:git-remote[1] for way to
+clean up all obsolete remote-tracking branches.
 
 
 OPTIONS
 -------
 -d::
-       Delete a branch. The branch must be fully merged.
+       Delete a branch. The branch must be fully merged in HEAD.
 
 -D::
-       Delete a branch irrespective of its index status.
+       Delete a branch irrespective of its merged status.
 
 -l::
        Create the branch's reflog.  This activates recording of
@@ -157,9 +162,11 @@ $ git branch -d -r origin/todo origin/html origin/man   <1>
 $ git branch -D test                                    <2>
 ------------
 +
-<1> Delete remote-tracking branches "todo", "html", "man"
-<2> Delete "test" branch even if the "master" branch does not have all
-commits from test branch.
+<1> Delete remote-tracking branches "todo", "html", "man". Next 'fetch' or
+'pull' will create them again unless you configure them not to. See
+gitlink:git-fetch[1].
+<2> Delete "test" branch even if the "master" branch (or whichever branch is
+currently checked out) does not have all commits from test branch.
 
 
 Notes
index abc171266a35299159308d0653bb0c659b8bdc77..38a5325af79b8ecdd7893f7cbd5a27da325cb1a5 100644 (file)
@@ -12,8 +12,7 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Lists the references the remote repository has, and optionally
-stores them in the local repository under the same name.
+This command is deprecated; use `git-ls-remote` instead.
 
 OPTIONS
 -------
index 4a68aaba342bd1155beb2ae72fd81c701e6c80eb..b8003c63c7e51dc1e3907645b267805dfb5f38d6 100644 (file)
@@ -63,6 +63,14 @@ the remote repository.
        Instead of naming each ref to push, specifies that all
        refs under `$GIT_DIR/refs/heads/` be pushed.
 
+\--mirror::
+       Instead of naming each ref to push, specifies that all
+       refs under `$GIT_DIR/refs/heads/` and `$GIT_DIR/refs/tags/`
+       be mirrored to the remote repository.  Newly created local
+       refs will be pushed to the remote end, locally updated refs
+       will be force updated on the remote end, and deleted refs
+       will be removed from the remote end.
+
 \--dry-run::
        Do everything except actually send the updates.
 
index 10d3e3fa950e00b6004f968ff2c41477e1d57612..784ec6d4c29879e48c13834819048d4a640dc32d 100644 (file)
@@ -65,7 +65,9 @@ OPTIONS
        Typing "git tag" without arguments, also lists all tags.
 
 -m <msg>::
-       Use the given tag message (instead of prompting)
+       Use the given tag message (instead of prompting).
+       If multiple `-m` options are given, there values are
+       concatenated as separate paragraphs.
 
 -F <file>::
        Take the tag message from the given file.  Use '-' to
index 546020100a533b99126adb1635299e7d025bca4b..9ff4659d8c021465758332270aeaa2c88a593b14 100644 (file)
@@ -46,6 +46,7 @@ Documentation for older releases are available here:
 * link:v1.5.3/git.html[documentation for release 1.5.3]
 
 * release notes for
+  link:RelNotes-1.5.3.7.txt[1.5.3.7],
   link:RelNotes-1.5.3.6.txt[1.5.3.6],
   link:RelNotes-1.5.3.5.txt[1.5.3.5],
   link:RelNotes-1.5.3.4.txt[1.5.3.4],
index 3661879f1ae00c3951c4ecd320b7fab747886902..93a47b439b4504467fc5ba7612b8205367f347c2 100644 (file)
@@ -56,11 +56,12 @@ $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
 The initial clone may be time-consuming for a large project, but you
 will only need to clone once.
 
-The clone command creates a new directory named after the project
-("git" or "linux-2.6" in the examples above).  After you cd into this
+The clone command creates a new directory named after the project ("git"
+or "linux-2.6" in the examples above).  After you cd into this
 directory, you will see that it contains a copy of the project files,
-together with a special top-level directory named ".git", which
-contains all the information about the history of the project.
+called the <<def_working_tree,working tree>>, together with a special
+top-level directory named ".git", which contains all the information
+about the history of the project.
 
 [[how-to-check-out]]
 How to check out a different version of a project
@@ -71,8 +72,13 @@ of files.  It stores the history as a compressed collection of
 interrelated snapshots of the project's contents.  In git each such
 version is called a <<def_commit,commit>>.
 
-A single git repository may contain multiple branches.  It keeps track
-of them by keeping a list of <<def_head,heads>> which reference the
+Those snapshots aren't necessarily all arranged in a single line from
+oldest to newest; instead, work may simultaneously proceed along
+parallel lines of development, called <def_branch,branches>>, which may
+merge and diverge.
+
+A single git repository can track development on multiple branches.  It
+does this by keeping a list of <<def_head,heads>> which reference the
 latest commit on each branch; the gitlink:git-branch[1] command shows
 you the list of branch heads:
 
@@ -1410,8 +1416,8 @@ with the changes to be reverted, then you will be asked to fix
 conflicts manually, just as in the case of <<resolving-a-merge,
 resolving a merge>>.
 
-[[fixing-a-mistake-by-editing-history]]
-Fixing a mistake by editing history
+[[fixing-a-mistake-by-rewriting-history]]
+Fixing a mistake by rewriting history
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 If the problematic commit is the most recent commit, and you have not
@@ -1434,7 +1440,7 @@ Again, you should never do this to a commit that may already have
 been merged into another branch; use gitlink:git-revert[1] instead in
 that case.
 
-It is also possible to edit commits further back in the history, but
+It is also possible to replace commits further back in the history, but
 this is an advanced topic to be left for
 <<cleaning-up-history,another chapter>>.
 
@@ -1554,6 +1560,11 @@ This may be time-consuming.  Unlike most other git operations (including
 git-gc when run without any options), it is not safe to prune while
 other git operations are in progress in the same repository.
 
+If gitlink:git-fsck[1] complains about sha1 mismatches or missing
+objects, you may have a much more serious problem; your best option is
+probably restoring from backups.  See
+<<recovering-from-repository-corruption>> for a detailed discussion.
+
 [[recovering-lost-changes]]
 Recovering lost changes
 ~~~~~~~~~~~~~~~~~~~~~~~
@@ -1923,15 +1934,9 @@ or just
 $ git push ssh://yourserver.com/~you/proj.git master
 -------------------------------------------------
 
-As with git-fetch, git-push will complain if this does not result in
-a <<fast-forwards,fast forward>>.  Normally this is a sign of
-something wrong.  However, if you are sure you know what you're
-doing, you may force git-push to perform the update anyway by
-preceding the branch name by a plus sign:
-
--------------------------------------------------
-$ git push ssh://yourserver.com/~you/proj.git +master
--------------------------------------------------
+As with git-fetch, git-push will complain if this does not result in a
+<<fast-forwards,fast forward>>; see the following section for details on
+handling this case.
 
 Note that the target of a "push" is normally a
 <<def_bare_repository,bare>> repository.  You can also push to a
@@ -1959,6 +1964,52 @@ See the explanations of the remote.<name>.url, branch.<name>.remote,
 and remote.<name>.push options in gitlink:git-config[1] for
 details.
 
+[[forcing-push]]
+What to do when a push fails
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If a push would not result in a <<fast-forwards,fast forward>> of the
+remote branch, then it will fail with an error like:
+
+-------------------------------------------------
+error: remote 'refs/heads/master' is not an ancestor of
+ local  'refs/heads/master'.
+ Maybe you are not up-to-date and need to pull first?
+error: failed to push to 'ssh://yourserver.com/~you/proj.git'
+-------------------------------------------------
+
+This can happen, for example, if you:
+
+       - use `git reset --hard` to remove already-published commits, or
+       - use `git commit --amend` to replace already-published commits
+         (as in <<fixing-a-mistake-by-rewriting-history>>), or
+       - use `git rebase` to rebase any already-published commits (as
+         in <<using-git-rebase>>).
+
+You may force git-push to perform the update anyway by preceding the
+branch name with a plus sign:
+
+-------------------------------------------------
+$ git push ssh://yourserver.com/~you/proj.git +master
+-------------------------------------------------
+
+Normally whenever a branch head in a public repository is modified, it
+is modified to point to a descendent of the commit that it pointed to
+before.  By forcing a push in this situation, you break that convention.
+(See <<problems-with-rewriting-history>>.)
+
+Nevertheless, this is a common practice for people that need a simple
+way to publish a work-in-progress patch series, and it is an acceptable
+compromise as long as you warn other developers that this is how you
+intend to manage the branch.
+
+It's also possible for a push to fail in this way when other people have
+the right to push to the same repository.  In that case, the correct
+solution is to retry the push after first updating your work by either a
+pull or a fetch followed by a rebase; see the
+<<setting-up-a-shared-repository,next section>> and
+link:cvs-migration.html[git for CVS users] for more.
+
 [[setting-up-a-shared-repository]]
 Setting up a shared repository
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2426,11 +2477,11 @@ return mywork to the state it had before you started the rebase:
 $ git rebase --abort
 -------------------------------------------------
 
-[[modifying-one-commit]]
-Modifying a single commit
+[[rewriting-one-commit]]
+Rewriting a single commit
 -------------------------
 
-We saw in <<fixing-a-mistake-by-editing-history>> that you can replace the
+We saw in <<fixing-a-mistake-by-rewriting-history>> that you can replace the
 most recent commit using
 
 -------------------------------------------------
@@ -2440,8 +2491,10 @@ $ git commit --amend
 which will replace the old commit by a new commit incorporating your
 changes, giving you a chance to edit the old commit message first.
 
-You can also use a combination of this and gitlink:git-rebase[1] to edit
-commits further back in your history.  First, tag the problematic commit with
+You can also use a combination of this and gitlink:git-rebase[1] to
+replace a commit further back in your history and recreate the
+intervening changes on top of it.  First, tag the problematic commit
+with
 
 -------------------------------------------------
 $ git tag bad mywork~5
@@ -3172,6 +3225,127 @@ confusing and scary messages, but it won't actually do anything bad. In
 contrast, running "git prune" while somebody is actively changing the
 repository is a *BAD* idea).
 
+[[recovering-from-repository-corruption]]
+Recovering from repository corruption
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+By design, git treats data trusted to it with caution.  However, even in
+the absence of bugs in git itself, it is still possible that hardware or
+operating system errors could corrupt data.
+
+The first defense against such problems is backups.  You can back up a
+git directory using clone, or just using cp, tar, or any other backup
+mechanism.
+
+As a last resort, you can search for the corrupted objects and attempt
+to replace them by hand.  Back up your repository before attempting this
+in case you corrupt things even more in the process.
+
+We'll assume that the problem is a single missing or corrupted blob,
+which is sometimes a solveable problem.  (Recovering missing trees and
+especially commits is *much* harder).
+
+Before starting, verify that there is corruption, and figure out where
+it is with gitlink:git-fsck[1]; this may be time-consuming.
+
+Assume the output looks like this:
+
+------------------------------------------------
+$ git-fsck --full
+broken link from    tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
+              to    blob 4b9458b3786228369c63936db65827de3cc06200
+missing blob 4b9458b3786228369c63936db65827de3cc06200
+------------------------------------------------
+
+(Typically there will be some "dangling object" messages too, but they
+aren't interesting.)
+
+Now you know that blob 4b9458b3 is missing, and that the tree 2d9263c6
+points to it.  If you could find just one copy of that missing blob
+object, possibly in some other repository, you could move it into
+.git/objects/4b/9458b3... and be done.  Suppose you can't.  You can
+still examine the tree that pointed to it with gitlink:git-ls-tree[1],
+which might output something like:
+
+------------------------------------------------
+$ git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
+100644 blob 8d14531846b95bfa3564b58ccfb7913a034323b8   .gitignore
+100644 blob ebf9bf84da0aab5ed944264a5db2a65fe3a3e883   .mailmap
+100644 blob ca442d313d86dc67e0a2e5d584b465bd382cbf5c   COPYING
+...
+100644 blob 4b9458b3786228369c63936db65827de3cc06200   myfile
+...
+------------------------------------------------
+
+So now you know that the missing blob was the data for a file named
+"myfile".  And chances are you can also identify the directory--let's
+say it's in "somedirectory".  If you're lucky the missing copy might be
+the same as the copy you have checked out in your working tree at
+"somedirectory/myfile"; you can test whether that's right with
+gitlink:git-hash-object[1]:
+
+------------------------------------------------
+$ git hash-object -w somedirectory/myfile
+------------------------------------------------
+
+which will create and store a blob object with the contents of
+somedirectory/myfile, and output the sha1 of that object.  if you're
+extremely lucky it might be 4b9458b3786228369c63936db65827de3cc06200, in
+which case you've guessed right, and the corruption is fixed!
+
+Otherwise, you need more information.  How do you tell which version of
+the file has been lost?
+
+The easiest way to do this is with:
+
+------------------------------------------------
+$ git log --raw --all --full-history -- somedirectory/myfile
+------------------------------------------------
+
+Because you're asking for raw output, you'll now get something like
+
+------------------------------------------------
+commit abc
+Author:
+Date:
+...
+:100644 100644 4b9458b... newsha... M somedirectory/myfile
+
+
+commit xyz
+Author:
+Date:
+
+...
+:100644 100644 oldsha... 4b9458b... M somedirectory/myfile
+------------------------------------------------
+
+This tells you that the immediately preceding version of the file was
+"newsha", and that the immediately following version was "oldsha".
+You also know the commit messages that went with the change from oldsha
+to 4b9458b and with the change from 4b9458b to newsha.
+
+If you've been committing small enough changes, you may now have a good
+shot at reconstructing the contents of the in-between state 4b9458b.
+
+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.
+
 [[the-index]]
 The index
 -----------
@@ -3533,7 +3707,7 @@ should use the `--remove` and `--add` flags respectively.
 NOTE! A `--remove` flag does 'not' mean that subsequent filenames will
 necessarily be removed: if the files still exist in your directory
 structure, the index will be updated with their new status, not
-removed. The only thing `--remove` means is that update-cache will be
+removed. The only thing `--remove` means is that update-index will be
 considering a removed file to be a valid thing, and if the file really
 does not exist any more, it will update the index accordingly.
 
@@ -4382,4 +4556,7 @@ Write a chapter on using plumbing and writing scripts.
 
 Alternates, clone -reference, etc.
 
-git unpack-objects -r for recovery
+More on recovery from repository corruption.  See:
+       http://marc.theaimsgroup.com/?l=git&m=117263864820799&w=2
+       http://marc.theaimsgroup.com/?l=git&m=117147855503798&w=2
+       http://marc.theaimsgroup.com/?l=git&m=117147855503798&w=2
index 7a0ee780dc9c8d7129431ebeafb6805b4a4c9dd6..e869b85b952f4945403b7f8fae6a78d5f74aa4ec 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -111,7 +111,7 @@ all::
 # times (my ext3 doesn't).
 #
 # Define USE_STDEV below if you want git to care about the underlying device
-# change being considered an inode change from the update-cache perspective.
+# change being considered an inode change from the update-index perspective.
 #
 # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
 #
@@ -213,8 +213,7 @@ BASIC_LDFLAGS =
 
 SCRIPT_SH = \
        git-bisect.sh git-checkout.sh \
-       git-clean.sh git-clone.sh git-commit.sh \
-       git-ls-remote.sh \
+       git-clone.sh git-commit.sh \
        git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
        git-pull.sh git-rebase.sh git-rebase--interactive.sh \
        git-repack.sh git-request-pull.sh \
@@ -243,7 +242,7 @@ PROGRAMS = \
        git-fast-import$X \
        git-daemon$X \
        git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \
-       git-peek-remote$X git-receive-pack$X \
+       git-receive-pack$X \
        git-send-pack$X git-shell$X \
        git-show-index$X \
        git-unpack-file$X \
@@ -330,6 +329,7 @@ BUILTIN_OBJS = \
        builtin-check-attr.o \
        builtin-checkout-index.o \
        builtin-check-ref-format.o \
+       builtin-clean.o \
        builtin-commit-tree.o \
        builtin-count-objects.o \
        builtin-describe.o \
@@ -349,6 +349,7 @@ BUILTIN_OBJS = \
        builtin-log.o \
        builtin-ls-files.o \
        builtin-ls-tree.o \
+       builtin-ls-remote.o \
        builtin-mailinfo.o \
        builtin-mailsplit.o \
        builtin-merge-base.o \
@@ -362,6 +363,7 @@ BUILTIN_OBJS = \
        builtin-push.o \
        builtin-read-tree.o \
        builtin-reflog.o \
+       builtin-send-pack.o \
        builtin-config.o \
        builtin-rerere.o \
        builtin-reset.o \
@@ -754,7 +756,7 @@ TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
 LIBS = $(GITLIBS) $(EXTLIBS)
 
 BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \
-       -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"' $(COMPAT_CFLAGS)
+       $(COMPAT_CFLAGS)
 LIB_OBJS += $(COMPAT_OBJS)
 
 ALL_CFLAGS += $(BASIC_CFLAGS)
@@ -903,6 +905,9 @@ exec_cmd.o: exec_cmd.c GIT-CFLAGS
 builtin-init-db.o: builtin-init-db.c GIT-CFLAGS
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $<
 
+config.o: config.c GIT-CFLAGS
+       $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"' $<
+
 http.o: http.c GIT-CFLAGS
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $<
 
diff --git a/builtin-clean.c b/builtin-clean.c
new file mode 100644 (file)
index 0000000..56ae4eb
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * "git clean" builtin command
+ *
+ * Copyright (C) 2007 Shawn Bohrer
+ *
+ * Based on git-clean.sh by Pavel Roskin
+ */
+
+#include "builtin.h"
+#include "cache.h"
+#include "dir.h"
+#include "parse-options.h"
+
+static int force = -1; /* unset */
+
+static const char *const builtin_clean_usage[] = {
+       "git-clean [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...",
+       NULL
+};
+
+static int git_clean_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "clean.requireforce"))
+               force = !git_config_bool(var, value);
+       return git_default_config(var, value);
+}
+
+int cmd_clean(int argc, const char **argv, const char *prefix)
+{
+       int j;
+       int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
+       int ignored_only = 0, baselen = 0, config_set = 0;
+       struct strbuf directory;
+       struct dir_struct dir;
+       const char *path, *base;
+       static const char **pathspec;
+       struct option options[] = {
+               OPT__QUIET(&quiet),
+               OPT__DRY_RUN(&show_only),
+               OPT_BOOLEAN('f', NULL, &force, "force"),
+               OPT_BOOLEAN('d', NULL, &remove_directories,
+                               "remove whole directories"),
+               OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
+               OPT_BOOLEAN('X', NULL, &ignored_only,
+                               "remove only ignored files"),
+               OPT_END()
+       };
+
+       git_config(git_clean_config);
+       if (force < 0)
+               force = 0;
+       else
+               config_set = 1;
+
+       argc = parse_options(argc, argv, options, builtin_clean_usage, 0);
+
+       memset(&dir, 0, sizeof(dir));
+       if (ignored_only)
+               dir.show_ignored = 1;
+
+       if (ignored && ignored_only)
+               die("-x and -X cannot be used together");
+
+       if (!show_only && !force)
+               die("clean.requireForce%s set and -n or -f not given; "
+                   "refusing to clean", config_set ? "" : " not");
+
+       dir.show_other_directories = 1;
+
+       if (!ignored)
+               setup_standard_excludes(&dir);
+
+       pathspec = get_pathspec(prefix, argv);
+       read_cache();
+
+       /*
+        * Calculate common prefix for the pathspec, and
+        * use that to optimize the directory walk
+        */
+       baselen = common_prefix(pathspec);
+       path = ".";
+       base = "";
+       if (baselen)
+               path = base = xmemdupz(*pathspec, baselen);
+       read_directory(&dir, path, base, baselen, pathspec);
+       strbuf_init(&directory, 0);
+
+       for (j = 0; j < dir.nr; ++j) {
+               struct dir_entry *ent = dir.entries[j];
+               int len, pos, specs;
+               struct cache_entry *ce;
+               struct stat st;
+               char *seen;
+
+               /*
+                * Remove the '/' at the end that directory
+                * walking adds for directory entries.
+                */
+               len = ent->len;
+               if (len && ent->name[len-1] == '/')
+                       len--;
+               pos = cache_name_pos(ent->name, len);
+               if (0 <= pos)
+                       continue;       /* exact match */
+               pos = -pos - 1;
+               if (pos < active_nr) {
+                       ce = active_cache[pos];
+                       if (ce_namelen(ce) == len &&
+                           !memcmp(ce->name, ent->name, len))
+                               continue; /* Yup, this one exists unmerged */
+               }
+
+               if (!lstat(ent->name, &st) && (S_ISDIR(st.st_mode))) {
+                       int matched_path = 0;
+                       strbuf_addstr(&directory, ent->name);
+                       if (pathspec) {
+                               for (specs =0; pathspec[specs]; ++specs)
+                                       /* nothing */;
+                               seen = xcalloc(specs, 1);
+                               /* Check if directory was explictly passed as
+                                * pathspec.  If so we want to remove it */
+                               if (match_pathspec(pathspec, ent->name, ent->len,
+                                                  baselen, seen))
+                                       matched_path = 1;
+                               free(seen);
+                       }
+                       if (show_only && (remove_directories || matched_path)) {
+                               printf("Would remove %s\n", directory.buf);
+                       } else if (quiet && (remove_directories || matched_path)) {
+                               remove_dir_recursively(&directory, 0);
+                       } else if (remove_directories || matched_path) {
+                               printf("Removing %s\n", directory.buf);
+                               remove_dir_recursively(&directory, 0);
+                       } else if (show_only) {
+                               printf("Would not remove %s\n", directory.buf);
+                       } else {
+                               printf("Not removing %s\n", directory.buf);
+                       }
+                       strbuf_reset(&directory);
+               } else {
+                       if (show_only) {
+                               printf("Would remove %s\n", ent->name);
+                               continue;
+                       } else if (!quiet) {
+                               printf("Removing %s\n", ent->name);
+                       }
+                       unlink(ent->name);
+               }
+       }
+
+       strbuf_release(&directory);
+       return 0;
+}
index e5e243f27cb7ecab11ac0933a361d066f5b35ea9..f672c9cccaeb460a0f0c55ad3e9d2ada95ed1e81 100644 (file)
@@ -81,7 +81,7 @@ static int get_value(const char* key_, const char* regex_)
                        local = repo_config = xstrdup(git_path("config"));
                if (home)
                        global = xstrdup(mkpath("%s/.gitconfig", home));
-               system_wide = ETC_GITCONFIG;
+               system_wide = git_etc_gitconfig();
        }
 
        key = xstrdup(key_);
@@ -191,7 +191,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                        }
                }
                else if (!strcmp(argv[1], "--system"))
-                       setenv(CONFIG_ENVIRONMENT, ETC_GITCONFIG, 1);
+                       setenv(CONFIG_ENVIRONMENT, git_etc_gitconfig(), 1);
                else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) {
                        if (argc < 3)
                                usage(git_config_set_usage);
index ed60847d9fcd0ad8803c432af8d0bc2dd3567768..7460ab7fce2a4e6a7e014f448819140e2204ccb7 100644 (file)
@@ -511,10 +511,14 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
        if (!strcmp("append-fetch-head", argv[1])) {
                int result;
                FILE *fp;
+               char *filename;
 
                if (argc != 8)
                        return error("append-fetch-head takes 6 args");
-               fp = fopen(git_path("FETCH_HEAD"), "a");
+               filename = git_path("FETCH_HEAD");
+               fp = fopen(filename, "a");
+               if (!fp)
+                       return error("cannot open %s: %s\n", filename, strerror(errno));
                result = append_fetch_head(fp, argv[2], argv[3],
                                           argv[4], argv[5],
                                           argv[6], !!argv[7][0],
@@ -525,10 +529,14 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
        if (!strcmp("native-store", argv[1])) {
                int result;
                FILE *fp;
+               char *filename;
 
                if (argc != 5)
                        return error("fetch-native-store takes 3 args");
-               fp = fopen(git_path("FETCH_HEAD"), "a");
+               filename = git_path("FETCH_HEAD");
+               fp = fopen(filename, "a");
+               if (!fp)
+                       return error("cannot open %s: %s\n", filename, strerror(errno));
                result = fetch_native_store(fp, argv[2], argv[3], argv[4],
                                            verbose, force);
                fclose(fp);
index bb1742f1a267042a00f0a81bca32a4eefe87fd5d..807fa93b53ad16552b7d869470f2ab59a325808a 100644 (file)
@@ -462,34 +462,12 @@ static int sideband_demux(int fd, void *data)
 {
        int *xd = data;
 
-       close(xd[1]);
        return recv_sideband("fetch-pack", xd[0], fd, 2);
 }
 
-static void setup_sideband(int fd[2], int xd[2], struct async *demux)
-{
-       if (!use_sideband) {
-               fd[0] = xd[0];
-               fd[1] = xd[1];
-               return;
-       }
-       /* xd[] is talking with upload-pack; subprocess reads from
-        * xd[0], spits out band#2 to stderr, and feeds us band#1
-        * through demux->out.
-        */
-       demux->proc = sideband_demux;
-       demux->data = xd;
-       if (start_async(demux))
-               die("fetch-pack: unable to fork off sideband demultiplexer");
-       close(xd[0]);
-       fd[0] = demux->out;
-       fd[1] = xd[1];
-}
-
 static int get_pack(int xd[2], char **pack_lockfile)
 {
        struct async demux;
-       int fd[2];
        const char *argv[20];
        char keep_arg[256];
        char hdr_arg[256];
@@ -497,7 +475,20 @@ static int get_pack(int xd[2], char **pack_lockfile)
        int do_keep = args.keep_pack;
        struct child_process cmd;
 
-       setup_sideband(fd, xd, &demux);
+       memset(&demux, 0, sizeof(demux));
+       if (use_sideband) {
+               /* xd[] is talking with upload-pack; subprocess reads from
+                * xd[0], spits out band#2 to stderr, and feeds us band#1
+                * through demux->out.
+                */
+               demux.proc = sideband_demux;
+               demux.data = xd;
+               if (start_async(&demux))
+                       die("fetch-pack: unable to fork off sideband"
+                           " demultiplexer");
+       }
+       else
+               demux.out = xd[0];
 
        memset(&cmd, 0, sizeof(cmd));
        cmd.argv = argv;
@@ -506,7 +497,7 @@ static int get_pack(int xd[2], char **pack_lockfile)
        if (!args.keep_pack && unpack_limit) {
                struct pack_header header;
 
-               if (read_pack_header(fd[0], &header))
+               if (read_pack_header(demux.out, &header))
                        die("protocol error: bad pack header");
                snprintf(hdr_arg, sizeof(hdr_arg), "--pack_header=%u,%u",
                         ntohl(header.hdr_version), ntohl(header.hdr_entries));
@@ -542,11 +533,10 @@ static int get_pack(int xd[2], char **pack_lockfile)
                *av++ = hdr_arg;
        *av++ = NULL;
 
-       cmd.in = fd[0];
+       cmd.in = demux.out;
        cmd.git_cmd = 1;
        if (start_command(&cmd))
                die("fetch-pack: unable to fork off %s", argv[0]);
-       close(fd[1]);
        if (do_keep && pack_lockfile)
                *pack_lockfile = index_pack_lockfile(cmd.out);
 
index be9e3ea2bcea0d0ab88a51d396aff5714822b77a..de9947e7ac2cc1236909fab813198b3df93bd39e 100644 (file)
@@ -31,7 +31,7 @@ static void unlock_pack_on_signal(int signo)
 }
 
 static void add_merge_config(struct ref **head,
-                          struct ref *remote_refs,
+                          const struct ref *remote_refs,
                           struct branch *branch,
                           struct ref ***tail)
 {
@@ -79,7 +79,7 @@ static struct ref *get_ref_map(struct transport *transport,
        struct ref *ref_map = NULL;
        struct ref **tail = &ref_map;
 
-       struct ref *remote_refs = transport_get_remote_refs(transport);
+       const struct ref *remote_refs = transport_get_remote_refs(transport);
 
        if (ref_count || tags) {
                for (i = 0; i < ref_count; i++) {
@@ -255,7 +255,7 @@ static int update_local_ref(struct ref *ref,
        }
 }
 
-static void store_updated_refs(const char *url, struct ref *ref_map)
+static int store_updated_refs(const char *url, struct ref *ref_map)
 {
        FILE *fp;
        struct commit *commit;
@@ -263,8 +263,11 @@ static void store_updated_refs(const char *url, struct ref *ref_map)
        char note[1024];
        const char *what, *kind;
        struct ref *rm;
+       char *filename = git_path("FETCH_HEAD");
 
-       fp = fopen(git_path("FETCH_HEAD"), "a");
+       fp = fopen(filename, "a");
+       if (!fp)
+               return error("cannot open %s: %s\n", filename, strerror(errno));
        for (rm = ref_map; rm; rm = rm->next) {
                struct ref *ref = NULL;
 
@@ -335,6 +338,7 @@ static void store_updated_refs(const char *url, struct ref *ref_map)
                }
        }
        fclose(fp);
+       return 0;
 }
 
 /*
@@ -404,7 +408,7 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)
        if (ret)
                ret = transport_fetch_refs(transport, ref_map);
        if (!ret)
-               store_updated_refs(transport->url, ref_map);
+               ret |= store_updated_refs(transport->url, ref_map);
        transport_unlock_pack(transport);
        return ret;
 }
@@ -424,12 +428,12 @@ static struct ref *find_non_local_tags(struct transport *transport,
        struct path_list new_refs = { NULL, 0, 0, 1 };
        char *ref_name;
        int ref_name_len;
-       unsigned char *ref_sha1;
-       struct ref *tag_ref;
+       const unsigned char *ref_sha1;
+       const struct ref *tag_ref;
        struct ref *rm = NULL;
        struct ref *ref_map = NULL;
        struct ref **tail = &ref_map;
-       struct ref *ref;
+       const struct ref *ref;
 
        for_each_ref(add_existing, &existing_refs);
        for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
@@ -487,8 +491,13 @@ static int do_fetch(struct transport *transport,
                die("Don't know how to fetch from %s", transport->url);
 
        /* if not appending, truncate FETCH_HEAD */
-       if (!append)
-               fclose(fopen(git_path("FETCH_HEAD"), "w"));
+       if (!append) {
+               char *filename = git_path("FETCH_HEAD");
+               FILE *fp = fopen(filename, "w");
+               if (!fp)
+                       return error("cannot open %s: %s\n", filename, strerror(errno));
+               fclose(fp);
+       }
 
        ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
 
index 763fa55e76ef5b7e2f244f11199b5925382dac92..e1393b8d1e74c03ff2b45ec93e268daa2e286fd8 100644 (file)
@@ -5,6 +5,7 @@
  */
 #include "cache.h"
 #include "builtin.h"
+#include "exec_cmd.h"
 
 #ifndef DEFAULT_GIT_TEMPLATE_DIR
 #define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
@@ -131,10 +132,19 @@ static void copy_templates(const char *git_dir, int len, const char *template_di
        int template_len;
        DIR *dir;
 
-       if (!template_dir) {
+       if (!template_dir)
                template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
-               if (!template_dir)
-                       template_dir = DEFAULT_GIT_TEMPLATE_DIR;
+       if (!template_dir) {
+               /*
+                * if the hard-coded template is relative, it is
+                * interpreted relative to the exec_dir
+                */
+               template_dir = DEFAULT_GIT_TEMPLATE_DIR;
+               if (!is_absolute_path(template_dir)) {
+                       const char *exec_path = git_exec_path();
+                       template_dir = prefix_path(exec_path, strlen(exec_path),
+                                                  template_dir);
+               }
        }
        strcpy(template_path, template_dir);
        template_len = strlen(template_path);
diff --git a/builtin-ls-remote.c b/builtin-ls-remote.c
new file mode 100644 (file)
index 0000000..56f3f88
--- /dev/null
@@ -0,0 +1,74 @@
+#include "builtin.h"
+#include "cache.h"
+#include "transport.h"
+#include "remote.h"
+
+static const char ls_remote_usage[] =
+"git-ls-remote [--upload-pack=<git-upload-pack>] [<host>:]<directory>";
+
+int cmd_ls_remote(int argc, const char **argv, const char *prefix)
+{
+       int i;
+       const char *dest = NULL;
+       int nongit = 0;
+       unsigned flags = 0;
+       const char *uploadpack = NULL;
+
+       struct remote *remote;
+       struct transport *transport;
+       const struct ref *ref;
+
+       setup_git_directory_gently(&nongit);
+
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+
+               if (*arg == '-') {
+                       if (!prefixcmp(arg, "--upload-pack=")) {
+                               uploadpack = arg + 14;
+                               continue;
+                       }
+                       if (!prefixcmp(arg, "--exec=")) {
+                               uploadpack = arg + 7;
+                               continue;
+                       }
+                       if (!strcmp("--tags", arg)) {
+                               flags |= REF_TAGS;
+                               continue;
+                       }
+                       if (!strcmp("--heads", arg)) {
+                               flags |= REF_HEADS;
+                               continue;
+                       }
+                       if (!strcmp("--refs", arg)) {
+                               flags |= REF_NORMAL;
+                               continue;
+                       }
+                       usage(ls_remote_usage);
+               }
+               dest = arg;
+               break;
+       }
+
+       if (!dest || i != argc - 1)
+               usage(ls_remote_usage);
+
+       remote = nongit ? NULL : remote_get(dest);
+       if (remote && !remote->url_nr)
+               die("remote %s has no configured URL", dest);
+       transport = transport_get(remote, remote ? remote->url[0] : dest);
+       if (uploadpack != NULL)
+               transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack);
+
+       ref = transport_get_remote_refs(transport);
+
+       if (!ref)
+               return 1;
+
+       while (ref) {
+               if (check_ref_type(ref, flags))
+                       printf("%s      %s\n", sha1_to_hex(ref->old_sha1), ref->name);
+               ref = ref->next;
+       }
+       return 0;
+}
index 6d1da07c46704f3de837044652f0a1fd149327ad..41df717f847ecc41bf0a695fa09fbb926b42f720 100644 (file)
@@ -10,7 +10,7 @@
 #include "parse-options.h"
 
 static const char * const push_usage[] = {
-       "git-push [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]",
+       "git-push [--all | --mirror] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]",
        NULL,
 };
 
@@ -91,6 +91,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
 {
        int flags = 0;
        int all = 0;
+       int mirror = 0;
        int dry_run = 0;
        int force = 0;
        int tags = 0;
@@ -100,6 +101,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                OPT__VERBOSE(&verbose),
                OPT_STRING( 0 , "repo", &repo, "repository", "repository"),
                OPT_BOOLEAN( 0 , "all", &all, "push all refs"),
+               OPT_BOOLEAN( 0 , "mirror", &mirror, "mirror all refs"),
                OPT_BOOLEAN( 0 , "tags", &tags, "push tags"),
                OPT_BOOLEAN( 0 , "dry-run", &dry_run, "dry run"),
                OPT_BOOLEAN('f', "force", &force, "force updates"),
@@ -121,13 +123,21 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                add_refspec("refs/tags/*");
        if (all)
                flags |= TRANSPORT_PUSH_ALL;
+       if (mirror)
+               flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
 
        if (argc > 0) {
                repo = argv[0];
                set_refspecs(argv + 1, argc - 1);
        }
-       if ((flags & TRANSPORT_PUSH_ALL) && refspec)
+       if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) && refspec)
                usage_with_options(push_usage, options);
 
+       if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
+                               (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
+               error("--all and --mirror are incompatible");
+               usage_with_options(push_usage, options);
+       }
+
        return do_push(repo, flags);
 }
index 365b330f9e1f2989683611077d260fa49abcb889..a0586f9753189d13d5af1c790ea875a5f383f34f 100644 (file)
@@ -30,7 +30,7 @@ static const char * const cherry_pick_usage[] = {
        NULL
 };
 
-static int edit, no_replay, no_commit, needed_deref, mainline;
+static int edit, no_replay, no_commit, mainline;
 static enum { REVERT, CHERRY_PICK } action;
 static struct commit *commit;
 
@@ -66,7 +66,6 @@ static void parse_args(int argc, const char **argv)
        if (commit->object.type == OBJ_TAG) {
                commit = (struct commit *)
                        deref_tag((struct object *)commit, arg, strlen(arg));
-               needed_deref = 1;
        }
        if (commit->object.type != OBJ_COMMIT)
                die ("'%s' does not point to a commit", arg);
@@ -333,17 +332,6 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                        add_to_msg(")\n");
                }
        }
-       if (needed_deref) {
-               add_to_msg("(original 'git ");
-               add_to_msg(me);
-               add_to_msg("' arguments: ");
-               for (i = 0; i < argc; i++) {
-                       if (i)
-                               add_to_msg(" ");
-                       add_to_msg(argv[i]);
-               }
-               add_to_msg(")\n");
-       }
 
        if (merge_recursive(sha1_to_hex(base->object.sha1),
                                sha1_to_hex(head), "HEAD",
diff --git a/builtin-send-pack.c b/builtin-send-pack.c
new file mode 100644 (file)
index 0000000..25ae1fe
--- /dev/null
@@ -0,0 +1,652 @@
+#include "cache.h"
+#include "commit.h"
+#include "tag.h"
+#include "refs.h"
+#include "pkt-line.h"
+#include "run-command.h"
+#include "remote.h"
+#include "send-pack.h"
+
+static const char send_pack_usage[] =
+"git-send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
+"  --all and explicit <ref> specification are mutually exclusive.";
+
+static struct send_pack_args args = {
+       /* .receivepack = */ "git-receive-pack",
+};
+
+/*
+ * Make a pack stream and spit it out into file descriptor fd
+ */
+static int pack_objects(int fd, struct ref *refs)
+{
+       /*
+        * The child becomes pack-objects --revs; we feed
+        * the revision parameters to it via its stdin and
+        * let its stdout go back to the other end.
+        */
+       const char *argv[] = {
+               "pack-objects",
+               "--all-progress",
+               "--revs",
+               "--stdout",
+               NULL,
+               NULL,
+       };
+       struct child_process po;
+
+       if (args.use_thin_pack)
+               argv[4] = "--thin";
+       memset(&po, 0, sizeof(po));
+       po.argv = argv;
+       po.in = -1;
+       po.out = fd;
+       po.git_cmd = 1;
+       if (start_command(&po))
+               die("git-pack-objects failed (%s)", strerror(errno));
+
+       /*
+        * We feed the pack-objects we just spawned with revision
+        * parameters by writing to the pipe.
+        */
+       while (refs) {
+               char buf[42];
+
+               if (!is_null_sha1(refs->old_sha1) &&
+                   has_sha1_file(refs->old_sha1)) {
+                       memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
+                       buf[0] = '^';
+                       buf[41] = '\n';
+                       if (!write_or_whine(po.in, buf, 42,
+                                               "send-pack: send refs"))
+                               break;
+               }
+               if (!is_null_sha1(refs->new_sha1)) {
+                       memcpy(buf, sha1_to_hex(refs->new_sha1), 40);
+                       buf[40] = '\n';
+                       if (!write_or_whine(po.in, buf, 41,
+                                               "send-pack: send refs"))
+                               break;
+               }
+               refs = refs->next;
+       }
+
+       if (finish_command(&po))
+               return error("pack-objects died with strange error");
+       return 0;
+}
+
+static void unmark_and_free(struct commit_list *list, unsigned int mark)
+{
+       while (list) {
+               struct commit_list *temp = list;
+               temp->item->object.flags &= ~mark;
+               list = temp->next;
+               free(temp);
+       }
+}
+
+static int ref_newer(const unsigned char *new_sha1,
+                    const unsigned char *old_sha1)
+{
+       struct object *o;
+       struct commit *old, *new;
+       struct commit_list *list, *used;
+       int found = 0;
+
+       /* Both new and old must be commit-ish and new is descendant of
+        * old.  Otherwise we require --force.
+        */
+       o = deref_tag(parse_object(old_sha1), NULL, 0);
+       if (!o || o->type != OBJ_COMMIT)
+               return 0;
+       old = (struct commit *) o;
+
+       o = deref_tag(parse_object(new_sha1), NULL, 0);
+       if (!o || o->type != OBJ_COMMIT)
+               return 0;
+       new = (struct commit *) o;
+
+       if (parse_commit(new) < 0)
+               return 0;
+
+       used = list = NULL;
+       commit_list_insert(new, &list);
+       while (list) {
+               new = pop_most_recent_commit(&list, 1);
+               commit_list_insert(new, &used);
+               if (new == old) {
+                       found = 1;
+                       break;
+               }
+       }
+       unmark_and_free(list, 1);
+       unmark_and_free(used, 1);
+       return found;
+}
+
+static struct ref *local_refs, **local_tail;
+static struct ref *remote_refs, **remote_tail;
+
+static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+       struct ref *ref;
+       int len = strlen(refname) + 1;
+       ref = xcalloc(1, sizeof(*ref) + len);
+       hashcpy(ref->new_sha1, sha1);
+       memcpy(ref->name, refname, len);
+       *local_tail = ref;
+       local_tail = &ref->next;
+       return 0;
+}
+
+static void get_local_heads(void)
+{
+       local_tail = &local_refs;
+       for_each_ref(one_local_ref, NULL);
+}
+
+static int receive_status(int in, struct ref *refs)
+{
+       struct ref *hint;
+       char line[1000];
+       int ret = 0;
+       int len = packet_read_line(in, line, sizeof(line));
+       if (len < 10 || memcmp(line, "unpack ", 7))
+               return error("did not receive remote status");
+       if (memcmp(line, "unpack ok\n", 10)) {
+               char *p = line + strlen(line) - 1;
+               if (*p == '\n')
+                       *p = '\0';
+               error("unpack failed: %s", line + 7);
+               ret = -1;
+       }
+       hint = NULL;
+       while (1) {
+               char *refname;
+               char *msg;
+               len = packet_read_line(in, line, sizeof(line));
+               if (!len)
+                       break;
+               if (len < 3 ||
+                   (memcmp(line, "ok ", 3) && memcmp(line, "ng ", 3))) {
+                       fprintf(stderr, "protocol error: %s\n", line);
+                       ret = -1;
+                       break;
+               }
+
+               line[strlen(line)-1] = '\0';
+               refname = line + 3;
+               msg = strchr(refname, ' ');
+               if (msg)
+                       *msg++ = '\0';
+
+               /* first try searching at our hint, falling back to all refs */
+               if (hint)
+                       hint = find_ref_by_name(hint, refname);
+               if (!hint)
+                       hint = find_ref_by_name(refs, refname);
+               if (!hint) {
+                       warning("remote reported status on unknown ref: %s",
+                                       refname);
+                       continue;
+               }
+               if (hint->status != REF_STATUS_EXPECTING_REPORT) {
+                       warning("remote reported status on unexpected ref: %s",
+                                       refname);
+                       continue;
+               }
+
+               if (line[0] == 'o' && line[1] == 'k')
+                       hint->status = REF_STATUS_OK;
+               else {
+                       hint->status = REF_STATUS_REMOTE_REJECT;
+                       ret = -1;
+               }
+               if (msg)
+                       hint->remote_status = xstrdup(msg);
+               /* start our next search from the next ref */
+               hint = hint->next;
+       }
+       return ret;
+}
+
+static void update_tracking_ref(struct remote *remote, struct ref *ref)
+{
+       struct refspec rs;
+
+       if (ref->status != REF_STATUS_OK)
+               return;
+
+       rs.src = ref->name;
+       rs.dst = NULL;
+
+       if (!remote_find_tracking(remote, &rs)) {
+               if (args.verbose)
+                       fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
+               if (ref->deletion) {
+                       if (delete_ref(rs.dst, NULL))
+                               error("Failed to delete");
+               } else
+                       update_ref("update by push", rs.dst,
+                                       ref->new_sha1, NULL, 0, 0);
+               free(rs.dst);
+       }
+}
+
+static const char *prettify_ref(const struct ref *ref)
+{
+       const char *name = ref->name;
+       return name + (
+               !prefixcmp(name, "refs/heads/") ? 11 :
+               !prefixcmp(name, "refs/tags/") ? 10 :
+               !prefixcmp(name, "refs/remotes/") ? 13 :
+               0);
+}
+
+#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
+
+static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg)
+{
+       fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
+       if (from)
+               fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to));
+       else
+               fputs(prettify_ref(to), stderr);
+       if (msg) {
+               fputs(" (", stderr);
+               fputs(msg, stderr);
+               fputc(')', stderr);
+       }
+       fputc('\n', stderr);
+}
+
+static const char *status_abbrev(unsigned char sha1[20])
+{
+       const char *abbrev;
+       abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV);
+       return abbrev ? abbrev : sha1_to_hex(sha1);
+}
+
+static void print_ok_ref_status(struct ref *ref)
+{
+       if (ref->deletion)
+               print_ref_status('-', "[deleted]", ref, NULL, NULL);
+       else if (is_null_sha1(ref->old_sha1))
+               print_ref_status('*',
+                       (!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" :
+                         "[new branch]"),
+                       ref, ref->peer_ref, NULL);
+       else {
+               char quickref[84];
+               char type;
+               const char *msg;
+
+               strcpy(quickref, status_abbrev(ref->old_sha1));
+               if (ref->nonfastforward) {
+                       strcat(quickref, "...");
+                       type = '+';
+                       msg = "forced update";
+               } else {
+                       strcat(quickref, "..");
+                       type = ' ';
+                       msg = NULL;
+               }
+               strcat(quickref, status_abbrev(ref->new_sha1));
+
+               print_ref_status(type, quickref, ref, ref->peer_ref, msg);
+       }
+}
+
+static int print_one_push_status(struct ref *ref, const char *dest, int count)
+{
+       if (!count)
+               fprintf(stderr, "To %s\n", dest);
+
+       switch(ref->status) {
+       case REF_STATUS_NONE:
+               print_ref_status('X', "[no match]", ref, NULL, NULL);
+               break;
+       case REF_STATUS_REJECT_NODELETE:
+               print_ref_status('!', "[rejected]", ref, NULL,
+                               "remote does not support deleting refs");
+               break;
+       case REF_STATUS_UPTODATE:
+               print_ref_status('=', "[up to date]", ref,
+                               ref->peer_ref, NULL);
+               break;
+       case REF_STATUS_REJECT_NONFASTFORWARD:
+               print_ref_status('!', "[rejected]", ref, ref->peer_ref,
+                               "non-fast forward");
+               break;
+       case REF_STATUS_REMOTE_REJECT:
+               print_ref_status('!', "[remote rejected]", ref,
+                               ref->deletion ? NULL : ref->peer_ref,
+                               ref->remote_status);
+               break;
+       case REF_STATUS_EXPECTING_REPORT:
+               print_ref_status('!', "[remote failure]", ref,
+                               ref->deletion ? NULL : ref->peer_ref,
+                               "remote failed to report status");
+               break;
+       case REF_STATUS_OK:
+               print_ok_ref_status(ref);
+               break;
+       }
+
+       return 1;
+}
+
+static void print_push_status(const char *dest, struct ref *refs)
+{
+       struct ref *ref;
+       int n = 0;
+
+       if (args.verbose) {
+               for (ref = refs; ref; ref = ref->next)
+                       if (ref->status == REF_STATUS_UPTODATE)
+                               n += print_one_push_status(ref, dest, n);
+       }
+
+       for (ref = refs; ref; ref = ref->next)
+               if (ref->status == REF_STATUS_OK)
+                       n += print_one_push_status(ref, dest, n);
+
+       for (ref = refs; ref; ref = ref->next) {
+               if (ref->status != REF_STATUS_NONE &&
+                   ref->status != REF_STATUS_UPTODATE &&
+                   ref->status != REF_STATUS_OK)
+                       n += print_one_push_status(ref, dest, n);
+       }
+}
+
+static int refs_pushed(struct ref *ref)
+{
+       for (; ref; ref = ref->next) {
+               switch(ref->status) {
+               case REF_STATUS_NONE:
+               case REF_STATUS_UPTODATE:
+                       break;
+               default:
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec)
+{
+       struct ref *ref;
+       int new_refs;
+       int ask_for_status_report = 0;
+       int allow_deleting_refs = 0;
+       int expect_status_report = 0;
+       int flags = MATCH_REFS_NONE;
+       int ret;
+
+       if (args.send_all)
+               flags |= MATCH_REFS_ALL;
+       if (args.send_mirror)
+               flags |= MATCH_REFS_MIRROR;
+
+       /* No funny business with the matcher */
+       remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL);
+       get_local_heads();
+
+       /* Does the other end support the reporting? */
+       if (server_supports("report-status"))
+               ask_for_status_report = 1;
+       if (server_supports("delete-refs"))
+               allow_deleting_refs = 1;
+
+       /* match them up */
+       if (!remote_tail)
+               remote_tail = &remote_refs;
+       if (match_refs(local_refs, remote_refs, &remote_tail,
+                                              nr_refspec, refspec, flags))
+               return -1;
+
+       if (!remote_refs) {
+               fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
+                       "Perhaps you should specify a branch such as 'master'.\n");
+               return 0;
+       }
+
+       /*
+        * Finally, tell the other end!
+        */
+       new_refs = 0;
+       for (ref = remote_refs; ref; ref = ref->next) {
+               const unsigned char *new_sha1;
+
+               if (!ref->peer_ref) {
+                       if (!args.send_mirror)
+                               continue;
+                       new_sha1 = null_sha1;
+               }
+               else
+                       new_sha1 = ref->peer_ref->new_sha1;
+
+
+               ref->deletion = is_null_sha1(new_sha1);
+               if (ref->deletion && !allow_deleting_refs) {
+                       ref->status = REF_STATUS_REJECT_NODELETE;
+                       continue;
+               }
+               if (!ref->deletion &&
+                   !hashcmp(ref->old_sha1, new_sha1)) {
+                       ref->status = REF_STATUS_UPTODATE;
+                       continue;
+               }
+
+               /* This part determines what can overwrite what.
+                * The rules are:
+                *
+                * (0) you can always use --force or +A:B notation to
+                *     selectively force individual ref pairs.
+                *
+                * (1) if the old thing does not exist, it is OK.
+                *
+                * (2) if you do not have the old thing, you are not allowed
+                *     to overwrite it; you would not know what you are losing
+                *     otherwise.
+                *
+                * (3) if both new and old are commit-ish, and new is a
+                *     descendant of old, it is OK.
+                *
+                * (4) regardless of all of the above, removing :B is
+                *     always allowed.
+                */
+
+               ref->nonfastforward =
+                   !ref->deletion &&
+                   !is_null_sha1(ref->old_sha1) &&
+                   (!has_sha1_file(ref->old_sha1)
+                     || !ref_newer(new_sha1, ref->old_sha1));
+
+               if (ref->nonfastforward && !ref->force && !args.force_update) {
+                       ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
+                       continue;
+               }
+
+               hashcpy(ref->new_sha1, new_sha1);
+               if (!ref->deletion)
+                       new_refs++;
+
+               if (!args.dry_run) {
+                       char *old_hex = sha1_to_hex(ref->old_sha1);
+                       char *new_hex = sha1_to_hex(ref->new_sha1);
+
+                       if (ask_for_status_report) {
+                               packet_write(out, "%s %s %s%c%s",
+                                       old_hex, new_hex, ref->name, 0,
+                                       "report-status");
+                               ask_for_status_report = 0;
+                               expect_status_report = 1;
+                       }
+                       else
+                               packet_write(out, "%s %s %s",
+                                       old_hex, new_hex, ref->name);
+               }
+               ref->status = expect_status_report ?
+                       REF_STATUS_EXPECTING_REPORT :
+                       REF_STATUS_OK;
+       }
+
+       packet_flush(out);
+       if (new_refs && !args.dry_run) {
+               if (pack_objects(out, remote_refs) < 0) {
+                       close(out);
+                       return -1;
+               }
+       }
+       close(out);
+
+       if (expect_status_report)
+               ret = receive_status(in, remote_refs);
+       else
+               ret = 0;
+
+       print_push_status(dest, remote_refs);
+
+       if (!args.dry_run && remote) {
+               for (ref = remote_refs; ref; ref = ref->next)
+                       update_tracking_ref(remote, ref);
+       }
+
+       if (!refs_pushed(remote_refs))
+               fprintf(stderr, "Everything up-to-date\n");
+       if (ret < 0)
+               return ret;
+       for (ref = remote_refs; ref; ref = ref->next) {
+               switch (ref->status) {
+               case REF_STATUS_NONE:
+               case REF_STATUS_UPTODATE:
+               case REF_STATUS_OK:
+                       break;
+               default:
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+static void verify_remote_names(int nr_heads, const char **heads)
+{
+       int i;
+
+       for (i = 0; i < nr_heads; i++) {
+               const char *remote = strchr(heads[i], ':');
+
+               remote = remote ? (remote + 1) : heads[i];
+               switch (check_ref_format(remote)) {
+               case 0: /* ok */
+               case -2: /* ok but a single level -- that is fine for
+                         * a match pattern.
+                         */
+               case -3: /* ok but ends with a pattern-match character */
+                       continue;
+               }
+               die("remote part of refspec is not a valid name in %s",
+                   heads[i]);
+       }
+}
+
+int cmd_send_pack(int argc, const char **argv, const char *prefix)
+{
+       int i, nr_heads = 0;
+       const char **heads = NULL;
+       const char *remote_name = NULL;
+       struct remote *remote = NULL;
+       const char *dest = NULL;
+
+       argv++;
+       for (i = 1; i < argc; i++, argv++) {
+               const char *arg = *argv;
+
+               if (*arg == '-') {
+                       if (!prefixcmp(arg, "--receive-pack=")) {
+                               args.receivepack = arg + 15;
+                               continue;
+                       }
+                       if (!prefixcmp(arg, "--exec=")) {
+                               args.receivepack = arg + 7;
+                               continue;
+                       }
+                       if (!prefixcmp(arg, "--remote=")) {
+                               remote_name = arg + 9;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--all")) {
+                               args.send_all = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--dry-run")) {
+                               args.dry_run = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--mirror")) {
+                               args.send_mirror = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--force")) {
+                               args.force_update = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--verbose")) {
+                               args.verbose = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--thin")) {
+                               args.use_thin_pack = 1;
+                               continue;
+                       }
+                       usage(send_pack_usage);
+               }
+               if (!dest) {
+                       dest = arg;
+                       continue;
+               }
+               heads = (const char **) argv;
+               nr_heads = argc - i;
+               break;
+       }
+       if (!dest)
+               usage(send_pack_usage);
+       /*
+        * --all and --mirror are incompatible; neither makes sense
+        * with any refspecs.
+        */
+       if ((heads && (args.send_all || args.send_mirror)) ||
+                                       (args.send_all && args.send_mirror))
+               usage(send_pack_usage);
+
+       if (remote_name) {
+               remote = remote_get(remote_name);
+               if (!remote_has_url(remote, dest)) {
+                       die("Destination %s is not a uri for %s",
+                           dest, remote_name);
+               }
+       }
+
+       return send_pack(&args, dest, remote, nr_heads, heads);
+}
+
+int send_pack(struct send_pack_args *my_args,
+             const char *dest, struct remote *remote,
+             int nr_heads, const char **heads)
+{
+       int fd[2], ret;
+       struct child_process *conn;
+
+       memcpy(&args, my_args, sizeof(args));
+
+       verify_remote_names(nr_heads, heads);
+
+       conn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0);
+       ret = do_send_pack(fd[0], fd[1], remote, dest, nr_heads, heads);
+       close(fd[0]);
+       close(fd[1]);
+       ret |= finish_connect(conn);
+       return !!ret;
+}
index cbb0f04e85627a5509bb2ba8cbf04a54d4d20c41..114c684d246e975852bb5446ea2e7ab682ea036c 100644 (file)
 #include "refs.h"
 #include "tag.h"
 #include "run-command.h"
-
-static const char builtin_tag_usage[] =
-  "git-tag [-n [<num>]] -l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg> | -F <file>] <tagname> [<head>]";
+#include "parse-options.h"
+
+static const char * const git_tag_usage[] = {
+       "git-tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
+       "git-tag -d <tagname>...",
+       "git-tag [-n [<num>]] -l [<pattern>]",
+       "git-tag -v <tagname>...",
+       NULL
+};
 
 static char signingkey[1000];
 
@@ -276,7 +282,7 @@ static void write_tag_body(int fd, const unsigned char *sha1)
 
 static void create_tag(const unsigned char *object, const char *tag,
                       struct strbuf *buf, int message, int sign,
-                          unsigned char *prev, unsigned char *result)
+                      unsigned char *prev, unsigned char *result)
 {
        enum object_type type;
        char header_buf[1024];
@@ -335,105 +341,101 @@ static void create_tag(const unsigned char *object, const char *tag,
                die("unable to write tag file");
 }
 
+struct msg_arg {
+       int given;
+       struct strbuf buf;
+};
+
+static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
+{
+       struct msg_arg *msg = opt->value;
+
+       if (!arg)
+               return -1;
+       if (msg->buf.len)
+               strbuf_addstr(&(msg->buf), "\n\n");
+       strbuf_addstr(&(msg->buf), arg);
+       msg->given = 1;
+       return 0;
+}
+
 int cmd_tag(int argc, const char **argv, const char *prefix)
 {
        struct strbuf buf;
        unsigned char object[20], prev[20];
-       int annotate = 0, sign = 0, force = 0, lines = 0, message = 0;
        char ref[PATH_MAX];
        const char *object_ref, *tag;
-       int i;
        struct ref_lock *lock;
 
+       int annotate = 0, sign = 0, force = 0, lines = 0,
+                                       delete = 0, verify = 0;
+       char *list = NULL, *msgfile = NULL, *keyid = NULL;
+       const char *no_pattern = "NO_PATTERN";
+       struct msg_arg msg = { 0, STRBUF_INIT };
+       struct option options[] = {
+               { OPTION_STRING, 'l', NULL, &list, "pattern", "list tag names",
+                       PARSE_OPT_OPTARG, NULL, (intptr_t) no_pattern },
+               { OPTION_INTEGER, 'n', NULL, &lines, NULL,
+                               "print n lines of each tag message",
+                               PARSE_OPT_OPTARG, NULL, 1 },
+               OPT_BOOLEAN('d', NULL, &delete, "delete tags"),
+               OPT_BOOLEAN('v', NULL, &verify, "verify tags"),
+
+               OPT_GROUP("Tag creation options"),
+               OPT_BOOLEAN('a', NULL, &annotate,
+                                       "annotated tag, needs a message"),
+               OPT_CALLBACK('m', NULL, &msg, "msg",
+                            "message for the tag", parse_msg_arg),
+               OPT_STRING('F', NULL, &msgfile, "file", "message in a file"),
+               OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
+               OPT_STRING('u', NULL, &keyid, "key-id",
+                                       "use another key to sign the tag"),
+               OPT_BOOLEAN('f', NULL, &force, "replace the tag if exists"),
+               OPT_END()
+       };
+
        git_config(git_tag_config);
-       strbuf_init(&buf, 0);
 
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
+       argc = parse_options(argc, argv, options, git_tag_usage, 0);
 
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "-a")) {
-                       annotate = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-s")) {
-                       annotate = 1;
-                       sign = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-f")) {
-                       force = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-n")) {
-                       if (i + 1 == argc || *argv[i + 1] == '-')
-                               /* no argument */
-                               lines = 1;
-                       else
-                               lines = isdigit(*argv[++i]) ?
-                                       atoi(argv[i]) : 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-m")) {
-                       annotate = 1;
-                       i++;
-                       if (i == argc)
-                               die("option -m needs an argument.");
-                       if (message)
-                               die("only one -F or -m option is allowed.");
-                       strbuf_addstr(&buf, argv[i]);
-                       message = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-F")) {
-                       annotate = 1;
-                       i++;
-                       if (i == argc)
-                               die("option -F needs an argument.");
-                       if (message)
-                               die("only one -F or -m option is allowed.");
-
-                       if (!strcmp(argv[i], "-")) {
+       if (sign)
+               annotate = 1;
+
+       if (list)
+               return list_tags(list == no_pattern ? NULL : list, lines);
+       if (delete)
+               return for_each_tag_name(argv, delete_tag);
+       if (verify)
+               return for_each_tag_name(argv, verify_tag);
+
+       strbuf_init(&buf, 0);
+       if (msg.given || msgfile) {
+               if (msg.given && msgfile)
+                       die("only one -F or -m option is allowed.");
+               annotate = 1;
+               if (msg.given)
+                       strbuf_addbuf(&buf, &(msg.buf));
+               else {
+                       if (!strcmp(msgfile, "-")) {
                                if (strbuf_read(&buf, 0, 1024) < 0)
-                                       die("cannot read %s", argv[i]);
+                                       die("cannot read %s", msgfile);
                        } else {
-                               if (strbuf_read_file(&buf, argv[i], 1024) < 0)
+                               if (strbuf_read_file(&buf, msgfile, 1024) < 0)
                                        die("could not open or read '%s': %s",
-                                               argv[i], strerror(errno));
+                                               msgfile, strerror(errno));
                        }
-                       message = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-u")) {
-                       annotate = 1;
-                       sign = 1;
-                       i++;
-                       if (i == argc)
-                               die("option -u needs an argument.");
-                       if (strlcpy(signingkey, argv[i], sizeof(signingkey))
-                                                       >= sizeof(signingkey))
-                               die("argument to option -u too long");
-                       continue;
                }
-               if (!strcmp(arg, "-l"))
-                       return list_tags(argv[i + 1], lines);
-               if (!strcmp(arg, "-d"))
-                       return for_each_tag_name(argv + i + 1, delete_tag);
-               if (!strcmp(arg, "-v"))
-                       return for_each_tag_name(argv + i + 1, verify_tag);
-               usage(builtin_tag_usage);
        }
 
-       if (i == argc) {
+       if (argc == 0) {
                if (annotate)
-                       usage(builtin_tag_usage);
+                       usage_with_options(git_tag_usage, options);
                return list_tags(NULL, lines);
        }
-       tag = argv[i++];
+       tag = argv[0];
 
-       object_ref = i < argc ? argv[i] : "HEAD";
-       if (i + 1 < argc)
+       object_ref = argc == 2 ? argv[1] : "HEAD";
+       if (argc > 2)
                die("too many params");
 
        if (get_sha1(object_ref, object))
@@ -450,7 +452,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                die("tag '%s' already exists", tag);
 
        if (annotate)
-               create_tag(object, tag, &buf, message, sign, prev, object);
+               create_tag(object, tag, &buf, msg.given || msgfile,
+                          sign, prev, object);
 
        lock = lock_any_ref_for_update(ref, prev, 0);
        if (!lock)
index bcb54aaded9707bf7b0d77456f15728db8b4b8ee..98ada7b7f705d70ad27cccfaaf3c21c13f66dcc4 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -24,6 +24,7 @@ extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
 extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
 extern int cmd_cherry(int argc, const char **argv, const char *prefix);
 extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
+extern int cmd_clean(int argc, const char **argv, const char *prefix);
 extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
 extern int cmd_describe(int argc, const char **argv, const char *prefix);
@@ -48,6 +49,7 @@ extern int cmd_log(int argc, const char **argv, const char *prefix);
 extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
 extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
 extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
@@ -70,6 +72,7 @@ extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
 extern int cmd_revert(int argc, const char **argv, const char *prefix);
 extern int cmd_rm(int argc, const char **argv, const char *prefix);
 extern int cmd_runstatus(int argc, const char **argv, const char *prefix);
+extern int cmd_send_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_shortlog(int argc, const char **argv, const char *prefix);
 extern int cmd_show(int argc, const char **argv, const char *prefix);
 extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
diff --git a/cache.h b/cache.h
index 33ebccf48d0d8963ab5b262bfb87312e6542ed75..aaa135bfde23cd2529e3707e4499be7f75917336 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -290,6 +290,7 @@ extern int refresh_index(struct index_state *, unsigned int flags, const char **
 
 struct lock_file {
        struct lock_file *next;
+       int fd;
        pid_t owner;
        char on_list;
        char filename[PATH_MAX];
@@ -499,8 +500,20 @@ struct ref {
        struct ref *next;
        unsigned char old_sha1[20];
        unsigned char new_sha1[20];
-       unsigned char force;
-       unsigned char merge;
+       unsigned int force:1,
+               merge:1,
+               nonfastforward:1,
+               deletion:1;
+       enum {
+               REF_STATUS_NONE = 0,
+               REF_STATUS_OK,
+               REF_STATUS_REJECT_NONFASTFORWARD,
+               REF_STATUS_REJECT_NODELETE,
+               REF_STATUS_UPTODATE,
+               REF_STATUS_REMOTE_REJECT,
+               REF_STATUS_EXPECTING_REPORT,
+       } status;
+       char *remote_status;
        struct ref *peer_ref; /* when renaming */
        char name[FLEX_ARRAY]; /* more */
 };
@@ -509,8 +522,10 @@ struct ref {
 #define REF_HEADS      (1u << 1)
 #define REF_TAGS       (1u << 2)
 
+extern struct ref *find_ref_by_name(struct ref *list, const char *name);
+
 #define CONNECT_VERBOSE       (1u << 0)
-extern struct child_process *git_connect(int fd[2], char *url, const char *prog, int flags);
+extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
 extern int finish_connect(struct child_process *conn);
 extern int path_match(const char *path, int nr, char **match);
 extern int get_ack(int fd, unsigned char *result_sha1);
@@ -556,6 +571,7 @@ extern int git_config_bool(const char *, const char *);
 extern int git_config_set(const char *, const char *);
 extern int git_config_set_multivar(const char *, const char *, const char *, int);
 extern int git_config_rename_section(const char *, const char *);
+extern const char *git_etc_gitconfig(void);
 extern int check_repository_format_version(const char *var, const char *value);
 
 #define MAX_GITNAME (1000)
index 56e99fc0f4750174299303b27237737e09549933..ed96213c44265289c26d46edaaf740cebd0b4c86 100644 (file)
--- a/config.c
+++ b/config.c
@@ -6,6 +6,7 @@
  *
  */
 #include "cache.h"
+#include "exec_cmd.h"
 
 #define MAXNAME (256)
 
@@ -459,6 +460,21 @@ int git_config_from_file(config_fn_t fn, const char *filename)
        return ret;
 }
 
+const char *git_etc_gitconfig(void)
+{
+       static const char *system_wide;
+       if (!system_wide) {
+               system_wide = ETC_GITCONFIG;
+               if (!is_absolute_path(system_wide)) {
+                       /* interpret path relative to exec-dir */
+                       const char *exec_path = git_exec_path();
+                       system_wide = prefix_path(exec_path, strlen(exec_path),
+                                               system_wide);
+               }
+       }
+       return system_wide;
+}
+
 int git_config(config_fn_t fn)
 {
        int ret = 0;
@@ -471,8 +487,8 @@ int git_config(config_fn_t fn)
         * config file otherwise. */
        filename = getenv(CONFIG_ENVIRONMENT);
        if (!filename) {
-               if (!access(ETC_GITCONFIG, R_OK))
-                       ret += git_config_from_file(fn, ETC_GITCONFIG);
+               if (!access(git_etc_gitconfig(), R_OK))
+                       ret += git_config_from_file(fn, git_etc_gitconfig());
                home = getenv("HOME");
                filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
                if (!filename)
index 7bcf1a488c05280b4ae1f8aad43b82ba33e2ea94..5f8a15b9f9580bea6f350e8af468f81cf2535c9e 100644 (file)
@@ -415,7 +415,7 @@ GIT_PARSE_WITH(iconv))
 # times (my ext3 doesn't).
 #
 # Define USE_STDEV below if you want git to care about the underlying device
-# change being considered an inode change from the update-cache perspective.
+# change being considered an inode change from the update-index perspective.
 
 
 ## Output files
index 44e423dafd5c5e6566124fd1bb05f3d419b19a40..3aefd4ace590082b85bd3c4b9b41b8d1f1c72268 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -36,6 +36,11 @@ static int check_ref(const char *name, int len, unsigned int flags)
        return !(flags & ~REF_NORMAL);
 }
 
+int check_ref_type(const struct ref *ref, int flags)
+{
+       return check_ref(ref->name, strlen(ref->name), flags);
+}
+
 /*
  * Read all the refs from the other end
  */
@@ -476,9 +481,10 @@ char *get_port(char *host)
  *
  * If it returns, the connect is successful; it just dies on errors.
  */
-struct child_process *git_connect(int fd[2], char *url,
+struct child_process *git_connect(int fd[2], const char *url_orig,
                                  const char *prog, int flags)
 {
+       char *url = xstrdup(url_orig);
        char *host, *path = url;
        char *end;
        int c;
@@ -568,6 +574,7 @@ struct child_process *git_connect(int fd[2], char *url,
                             prog, path, 0,
                             target_host, 0);
                free(target_host);
+               free(url);
                if (free_path)
                        free(path);
                return NULL;
@@ -619,6 +626,7 @@ struct child_process *git_connect(int fd[2], char *url,
        fd[0] = conn->out; /* read from child's stdout */
        fd[1] = conn->in;  /* write to child's stdin */
        strbuf_release(&cmd);
+       free(url);
        if (free_path)
                free(path);
        return conn;
index 599b2fc5711bf6022fdb635b08baac16068de7dd..58e0e53cd6f6382e9665d20b53b26cd1da42fc88 100755 (executable)
@@ -551,6 +551,20 @@ _git_describe ()
 
 _git_diff ()
 {
+       local cur="${COMP_WORDS[COMP_CWORD]}"
+       case "$cur" in
+       --*)
+               __gitcomp "--cached --stat --numstat --shortstat --summary
+                       --patch-with-stat --name-only --name-status --color
+                       --no-color --color-words --no-renames --check
+                       --full-index --binary --abbrev --diff-filter
+                       --find-copies-harder --pickaxe-all --pickaxe-regex
+                       --text --ignore-space-at-eol --ignore-space-change
+                       --ignore-all-space --exit-code --quiet --ext-diff
+                       --no-ext-diff"
+               return
+               ;;
+       esac
        __git_complete_file
 }
 
diff --git a/contrib/examples/git-clean.sh b/contrib/examples/git-clean.sh
new file mode 100755 (executable)
index 0000000..01c95e9
--- /dev/null
@@ -0,0 +1,118 @@
+#!/bin/sh
+#
+# Copyright (c) 2005-2006 Pavel Roskin
+#
+
+OPTIONS_KEEPDASHDASH=
+OPTIONS_SPEC="\
+git-clean [options] <paths>...
+
+Clean untracked files from the working directory
+
+When optional <paths>... arguments are given, the paths
+affected are further limited to those that match them.
+--
+d remove directories as well
+f override clean.requireForce and clean anyway
+n don't remove anything, just show what would be done
+q be quiet, only report errors
+x remove ignored files as well
+X remove only ignored files"
+
+SUBDIRECTORY_OK=Yes
+. git-sh-setup
+require_work_tree
+
+ignored=
+ignoredonly=
+cleandir=
+rmf="rm -f --"
+rmrf="rm -rf --"
+rm_refuse="echo Not removing"
+echo1="echo"
+
+disabled=$(git config --bool clean.requireForce)
+
+while test $# != 0
+do
+       case "$1" in
+       -d)
+               cleandir=1
+               ;;
+       -f)
+               disabled=false
+               ;;
+       -n)
+               disabled=false
+               rmf="echo Would remove"
+               rmrf="echo Would remove"
+               rm_refuse="echo Would not remove"
+               echo1=":"
+               ;;
+       -q)
+               echo1=":"
+               ;;
+       -x)
+               ignored=1
+               ;;
+       -X)
+               ignoredonly=1
+               ;;
+       --)
+               shift
+               break
+               ;;
+       *)
+               usage # should not happen
+               ;;
+       esac
+       shift
+done
+
+# 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
+
+if [ "$ignored,$ignoredonly" = "1,1" ]; then
+       die "-x and -X cannot be set together"
+fi
+
+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"} ${excludes_file:+"$excludes_file"} \
+       -- "$@" |
+while read -r file; do
+       if [ -d "$file" -a ! -L "$file" ]; then
+               if [ -z "$cleandir" ]; then
+                       $rm_refuse "$file"
+                       continue
+               fi
+               $echo1 "Removing $file"
+               $rmrf "$file"
+       else
+               $echo1 "Removing $file"
+               $rmf "$file"
+       fi
+done
diff --git a/contrib/examples/git-ls-remote.sh b/contrib/examples/git-ls-remote.sh
new file mode 100755 (executable)
index 0000000..fec70bb
--- /dev/null
@@ -0,0 +1,142 @@
+#!/bin/sh
+#
+
+usage () {
+    echo >&2 "usage: $0 [--heads] [--tags] [-u|--upload-pack <upload-pack>]"
+    echo >&2 "          <repository> <refs>..."
+    exit 1;
+}
+
+die () {
+    echo >&2 "$*"
+    exit 1
+}
+
+exec=
+while test $# != 0
+do
+  case "$1" in
+  -h|--h|--he|--hea|--head|--heads)
+  heads=heads; shift ;;
+  -t|--t|--ta|--tag|--tags)
+  tags=tags; shift ;;
+  -u|--u|--up|--upl|--uploa|--upload|--upload-|--upload-p|--upload-pa|\
+  --upload-pac|--upload-pack)
+       shift
+       exec="--upload-pack=$1"
+       shift;;
+  -u=*|--u=*|--up=*|--upl=*|--uplo=*|--uploa=*|--upload=*|\
+  --upload-=*|--upload-p=*|--upload-pa=*|--upload-pac=*|--upload-pack=*)
+       exec=--upload-pack=$(expr "z$1" : 'z-[^=]*=\(.*\)')
+       shift;;
+  --)
+  shift; break ;;
+  -*)
+  usage ;;
+  *)
+  break ;;
+  esac
+done
+
+case "$#" in 0) usage ;; esac
+
+case ",$heads,$tags," in
+,,,) heads=heads tags=tags other=other ;;
+esac
+
+. git-parse-remote
+peek_repo="$(get_remote_url "$@")"
+shift
+
+tmp=.ls-remote-$$
+trap "rm -fr $tmp-*" 0 1 2 3 15
+tmpdir=$tmp-d
+
+case "$peek_repo" in
+http://* | https://* | ftp://* )
+       if [ -n "$GIT_SSL_NO_VERIFY" -o \
+               "`git config --bool http.sslVerify`" = false ]; then
+               curl_extra_args="-k"
+       fi
+       if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \
+               "`git config --bool http.noEPSV`" = true ]; then
+               curl_extra_args="${curl_extra_args} --disable-epsv"
+       fi
+       curl -nsf $curl_extra_args --header "Pragma: no-cache" "$peek_repo/info/refs" ||
+               echo "failed    slurping"
+       ;;
+
+rsync://* )
+       mkdir $tmpdir &&
+       rsync -rlq "$peek_repo/HEAD" $tmpdir &&
+       rsync -rq "$peek_repo/refs" $tmpdir || {
+               echo "failed    slurping"
+               exit
+       }
+       head=$(cat "$tmpdir/HEAD") &&
+       case "$head" in
+       ref:' '*)
+               head=$(expr "z$head" : 'zref: \(.*\)') &&
+               head=$(cat "$tmpdir/$head") || exit
+       esac &&
+       echo "$head     HEAD"
+       (cd $tmpdir && find refs -type f) |
+       while read path
+       do
+               tr -d '\012' <"$tmpdir/$path"
+               echo "  $path"
+       done &&
+       rm -fr $tmpdir
+       ;;
+
+* )
+       if test -f "$peek_repo" ; then
+               git bundle list-heads "$peek_repo" ||
+               echo "failed    slurping"
+       else
+               git-peek-remote $exec "$peek_repo" ||
+               echo "failed    slurping"
+       fi
+       ;;
+esac |
+sort -t '      ' -k 2 |
+while read sha1 path
+do
+       case "$sha1" in
+       failed)
+               exit 1 ;;
+       esac
+       case "$path" in
+       refs/heads/*)
+               group=heads ;;
+       refs/tags/*)
+               group=tags ;;
+       *)
+               group=other ;;
+       esac
+       case ",$heads,$tags,$other," in
+       *,$group,*)
+               ;;
+       *)
+               continue;;
+       esac
+       case "$#" in
+       0)
+               match=yes ;;
+       *)
+               match=no
+               for pat
+               do
+                       case "/$path" in
+                       */$pat )
+                               match=yes
+                               break ;;
+                       esac
+               done
+       esac
+       case "$match" in
+       no)
+               continue ;;
+       esac
+       echo "$sha1     $path"
+done
index c81a790aa60ea08144dd0899d1aee2ec334a1825..29dba4ba3a57c15bd430bd23c1cebe78e6dc03be 100755 (executable)
@@ -9,6 +9,6 @@
 # because the current index is what we will be committing as the
 # merge result.
 
-git diff-index --quiet --cached HEAD || exit 2
+git diff-index --quiet --cached HEAD -- || exit 2
 
 exit 0
index f8e936ae1008fdb7ef8e1722afb839d84c724f6c..d85d8f34ba055ad7e02bf5d61ae4a675ce0be1d3 100644 (file)
@@ -231,7 +231,7 @@ static int handle_diff_files_args(struct rev_info *revs,
 static int is_outside_repo(const char *path, int nongit, const char *prefix)
 {
        int i;
-       if (nongit || !strcmp(path, "-") || path[0] == '/')
+       if (nongit || !strcmp(path, "-") || is_absolute_path(path))
                return 1;
        if (prefixcmp(path, "../"))
                return 0;
index f9ebea56406090af207f79951618742dcd7d397f..3d377251bef8ea843b7a7fa41f98d611daecbcc1 100644 (file)
@@ -244,28 +244,35 @@ static int find_identical_files(struct file_similarity *src,
         * Walk over all the destinations ...
         */
        do {
-               struct diff_filespec *one = dst->filespec;
+               struct diff_filespec *target = dst->filespec;
                struct file_similarity *p, *best;
-               int i = 100;
+               int i = 100, best_score = -1;
 
                /*
                 * .. to find the best source match
                 */
                best = NULL;
                for (p = src; p; p = p->next) {
-                       struct diff_filespec *two = p->filespec;
+                       int score;
+                       struct diff_filespec *source = p->filespec;
 
                        /* False hash collission? */
-                       if (hashcmp(one->sha1, two->sha1))
+                       if (hashcmp(source->sha1, target->sha1))
                                continue;
                        /* Non-regular files? If so, the modes must match! */
-                       if (!S_ISREG(one->mode) || !S_ISREG(two->mode)) {
-                               if (one->mode != two->mode)
+                       if (!S_ISREG(source->mode) || !S_ISREG(target->mode)) {
+                               if (source->mode != target->mode)
                                        continue;
                        }
-                       best = p;
-                       if (basename_same(one, two))
-                               break;
+                       /* Give higher scores to sources that haven't been used already */
+                       score = !source->rename_used;
+                       score += basename_same(source, target);
+                       if (score > best_score) {
+                               best = p;
+                               best_score = score;
+                               if (score == 2)
+                                       break;
+                       }
 
                        /* Too many identical alternatives? Pick one */
                        if (!--i)
@@ -488,6 +495,19 @@ void diffcore_rename(struct diff_options *options)
        }
        /* cost matrix sorted by most to least similar pair */
        qsort(mx, num_create * num_src, sizeof(*mx), score_compare);
+       for (i = 0; i < num_create * num_src; i++) {
+               struct diff_rename_dst *dst = &rename_dst[mx[i].dst];
+               struct diff_filespec *src;
+               if (dst->pair)
+                       continue; /* already done, either exact or fuzzy. */
+               if (mx[i].score < minimum_score)
+                       break; /* there is no more usable pair. */
+               src = rename_src[mx[i].src].one;
+               if (src->rename_used)
+                       continue;
+               record_rename_pair(mx[i].dst, mx[i].src, mx[i].score);
+               rename_count++;
+       }
        for (i = 0; i < num_create * num_src; i++) {
                struct diff_rename_dst *dst = &rename_dst[mx[i].dst];
                if (dst->pair)
diff --git a/dir.c b/dir.c
index 225fdfb52c432b7af1af1cdf9aa9a31f8022e3ec..d448902909a7da216fbd49cecc505e68c0ba5e5f 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -144,17 +144,14 @@ void add_exclude(const char *string, const char *base,
                x->flags |= EXC_FLAG_NOWILDCARD;
        if (*string == '*' && no_wildcard(string+1))
                x->flags |= EXC_FLAG_ENDSWITH;
-       if (which->nr == which->alloc) {
-               which->alloc = alloc_nr(which->alloc);
-               which->excludes = xrealloc(which->excludes,
-                                          which->alloc * sizeof(x));
-       }
+       ALLOC_GROW(which->excludes, which->nr + 1, which->alloc);
        which->excludes[which->nr++] = x;
 }
 
 static int add_excludes_from_file_1(const char *fname,
                                    const char *base,
                                    int baselen,
+                                   char **buf_p,
                                    struct exclude_list *which)
 {
        struct stat st;
@@ -175,6 +172,8 @@ static int add_excludes_from_file_1(const char *fname,
                goto err;
        close(fd);
 
+       if (buf_p)
+               *buf_p = buf;
        buf[size++] = '\n';
        entry = buf;
        for (i = 0; i < size; i++) {
@@ -196,31 +195,63 @@ static int add_excludes_from_file_1(const char *fname,
 
 void add_excludes_from_file(struct dir_struct *dir, const char *fname)
 {
-       if (add_excludes_from_file_1(fname, "", 0,
+       if (add_excludes_from_file_1(fname, "", 0, NULL,
                                     &dir->exclude_list[EXC_FILE]) < 0)
                die("cannot use %s as an exclude file", fname);
 }
 
-int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen)
+static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
 {
-       char exclude_file[PATH_MAX];
-       struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
-       int current_nr = el->nr;
-
-       if (dir->exclude_per_dir) {
-               memcpy(exclude_file, base, baselen);
-               strcpy(exclude_file + baselen, dir->exclude_per_dir);
-               add_excludes_from_file_1(exclude_file, base, baselen, el);
+       struct exclude_list *el;
+       struct exclude_stack *stk = NULL;
+       int current;
+
+       if ((!dir->exclude_per_dir) ||
+           (baselen + strlen(dir->exclude_per_dir) >= PATH_MAX))
+               return; /* too long a path -- ignore */
+
+       /* Pop the ones that are not the prefix of the path being checked. */
+       el = &dir->exclude_list[EXC_DIRS];
+       while ((stk = dir->exclude_stack) != NULL) {
+               if (stk->baselen <= baselen &&
+                   !strncmp(dir->basebuf, base, stk->baselen))
+                       break;
+               dir->exclude_stack = stk->prev;
+               while (stk->exclude_ix < el->nr)
+                       free(el->excludes[--el->nr]);
+               free(stk->filebuf);
+               free(stk);
        }
-       return current_nr;
-}
 
-void pop_exclude_per_directory(struct dir_struct *dir, int stk)
-{
-       struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
+       /* Read from the parent directories and push them down. */
+       current = stk ? stk->baselen : -1;
+       while (current < baselen) {
+               struct exclude_stack *stk = xcalloc(1, sizeof(*stk));
+               const char *cp;
 
-       while (stk < el->nr)
-               free(el->excludes[--el->nr]);
+               if (current < 0) {
+                       cp = base;
+                       current = 0;
+               }
+               else {
+                       cp = strchr(base + current + 1, '/');
+                       if (!cp)
+                               die("oops in prep_exclude");
+                       cp++;
+               }
+               stk->prev = dir->exclude_stack;
+               stk->baselen = cp - base;
+               stk->exclude_ix = el->nr;
+               memcpy(dir->basebuf + current, base + current,
+                      stk->baselen - current);
+               strcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);
+               add_excludes_from_file_1(dir->basebuf,
+                                        dir->basebuf, stk->baselen,
+                                        &stk->filebuf, el);
+               dir->exclude_stack = stk;
+               current = stk->baselen;
+       }
+       dir->basebuf[baselen] = '\0';
 }
 
 /* Scan the list and let the last match determines the fate.
@@ -287,6 +318,7 @@ int excluded(struct dir_struct *dir, const char *pathname)
        const char *basename = strrchr(pathname, '/');
        basename = (basename) ? basename+1 : pathname;
 
+       prep_exclude(dir, pathname, basename-pathname);
        for (st = EXC_CMDL; st <= EXC_FILE; st++) {
                switch (excluded_1(pathname, pathlen, basename, &dir->exclude_list[st])) {
                case 0:
@@ -504,13 +536,10 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
        int contents = 0;
 
        if (fdir) {
-               int exclude_stk;
                struct dirent *de;
                char fullname[PATH_MAX + 1];
                memcpy(fullname, base, baselen);
 
-               exclude_stk = push_exclude_per_directory(dir, base, baselen);
-
                while ((de = readdir(fdir)) != NULL) {
                        int len, dtype;
                        int exclude;
@@ -584,8 +613,6 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                }
 exit_early:
                closedir(fdir);
-
-               pop_exclude_per_directory(dir, exclude_stk);
        }
 
        return contents;
@@ -654,47 +681,18 @@ static void free_simplify(struct path_simplify *simplify)
 int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
 {
        struct path_simplify *simplify = create_simplify(pathspec);
-       char *pp = NULL;
-
-       /*
-        * Make sure to do the per-directory exclude for all the
-        * directories leading up to our base.
-        */
-       if (baselen) {
-               if (dir->exclude_per_dir) {
-                       char *p;
-                       pp = xmalloc(baselen+1);
-                       memcpy(pp, base, baselen+1);
-                       p = pp;
-                       while (1) {
-                               char save = *p;
-                               *p = 0;
-                               push_exclude_per_directory(dir, pp, p-pp);
-                               *p++ = save;
-                               if (!save)
-                                       break;
-                               p = strchr(p, '/');
-                               if (p)
-                                       p++;
-                               else
-                                       p = pp + baselen;
-                       }
-               }
-       }
 
        read_directory_recursive(dir, path, base, baselen, 0, simplify);
        free_simplify(simplify);
-       free(pp);
        qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
        qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);
        return dir->nr;
 }
 
-int
-file_exists(const char *f)
+int file_exists(const char *f)
 {
-  struct stat sb;
-  return stat(f, &sb) == 0;
+       struct stat sb;
+       return stat(f, &sb) == 0;
 }
 
 /*
diff --git a/dir.h b/dir.h
index 82009dc13e4b685bc6f4f6e9206d621c7d361c65..d8814dccb2dd57af21d75c91a52026e09fdf1b95 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -1,17 +1,6 @@
 #ifndef DIR_H
 #define DIR_H
 
-/*
- * We maintain three exclude pattern lists:
- * EXC_CMDL lists patterns explicitly given on the command line.
- * EXC_DIRS lists patterns obtained from per-directory ignore files.
- * EXC_FILE lists patterns from fallback ignore files.
- */
-#define EXC_CMDL 0
-#define EXC_DIRS 1
-#define EXC_FILE 2
-
-
 struct dir_entry {
        unsigned int len;
        char name[FLEX_ARRAY]; /* more */
@@ -34,6 +23,13 @@ struct exclude_list {
        } **excludes;
 };
 
+struct exclude_stack {
+       struct exclude_stack *prev;
+       char *filebuf;
+       int baselen;
+       int exclude_ix;
+};
+
 struct dir_struct {
        int nr, alloc;
        int ignored_nr, ignored_alloc;
@@ -48,6 +44,18 @@ struct dir_struct {
        /* Exclude info */
        const char *exclude_per_dir;
        struct exclude_list exclude_list[3];
+       /*
+        * We maintain three exclude pattern lists:
+        * EXC_CMDL lists patterns explicitly given on the command line.
+        * EXC_DIRS lists patterns obtained from per-directory ignore files.
+        * EXC_FILE lists patterns from fallback ignore files.
+        */
+#define EXC_CMDL 0
+#define EXC_DIRS 1
+#define EXC_FILE 2
+
+       struct exclude_stack *exclude_stack;
+       char basebuf[PATH_MAX];
 };
 
 extern int common_prefix(const char **pathspec);
@@ -58,8 +66,6 @@ extern int common_prefix(const char **pathspec);
 extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
 
 extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen, const char **pathspec);
-extern int push_exclude_per_directory(struct dir_struct *, const char *, int);
-extern void pop_exclude_per_directory(struct dir_struct *, int);
 
 extern int excluded(struct dir_struct *, const char *);
 extern void add_excludes_from_file(struct dir_struct *, const char *fname);
index 4126f0e857bad273d6f44cabe34565de860bd119..76c1c844a95d8af19f64a3278706daf907dace01 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -218,7 +218,7 @@ fi
 
 case "$resolved" in
 '')
-       files=$(git diff-index --cached --name-only HEAD) || exit
+       files=$(git diff-index --cached --name-only HEAD --) || exit
        if [ "$files" ]; then
           echo "Dirty index: cannot apply patches (dirty: $files)" >&2
           exit 1
@@ -352,7 +352,7 @@ do
                case "$resolved$interactive" in
                tt)
                        # This is used only for interactive view option.
-                       git diff-index -p --cached HEAD >"$dotest/patch"
+                       git diff-index -p --cached HEAD -- >"$dotest/patch"
                        ;;
                esac
        esac
@@ -411,7 +411,7 @@ do
                # trust what the user has in the index file and the
                # working tree.
                resolved=
-               git diff-index --quiet --cached HEAD && {
+               git diff-index --quiet --cached HEAD -- && {
                        echo "No changes - did you forget to use 'git add'?"
                        stop_here_user_resolve $this
                }
@@ -433,7 +433,7 @@ do
                then
                    # Applying the patch to an earlier tree and merging the
                    # result may have produced the same tree as ours.
-                   git diff-index --quiet --cached HEAD && {
+                   git diff-index --quiet --cached HEAD -- && {
                        echo No changes -- Patch already applied.
                        go_next
                        continue
index 3aac8164c62c1f4af8df76832322df9f25a05b38..7a6521ec3c62b04fe6d4c3e80c418ccc02b4ec65 100755 (executable)
@@ -37,7 +37,7 @@ sq() {
 }
 
 bisect_autostart() {
-       test -d "$GIT_DIR/refs/bisect" || {
+       test -f "$GIT_DIR/BISECT_NAMES" || {
                echo >&2 'You need to start by "git bisect start"'
                if test -t 0
                then
@@ -72,7 +72,7 @@ bisect_start() {
                ;;
        refs/heads/*)
                [ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
-               echo "$head" | sed 's#^refs/heads/##' >"$GIT_DIR/head-name"
+               echo "${head#refs/heads/}" >"$GIT_DIR/head-name"
                ;;
        *)
                die "Bad HEAD - strange symbolic ref"
@@ -83,7 +83,6 @@ bisect_start() {
        # Get rid of any old bisect state
        #
        bisect_clean_state
-       mkdir "$GIT_DIR/refs/bisect"
 
        #
        # Check for one bad and then some good revisions.
@@ -131,7 +130,7 @@ bisect_write() {
                good|skip)      tag="$state"-"$rev" ;;
                *)              die "Bad bisect_write argument: $state" ;;
        esac
-       echo "$rev" >"$GIT_DIR/refs/bisect/$tag"
+       git update-ref "refs/bisect/$tag" "$rev"
        echo "# $state: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
        test -z "$nolog" && echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
 }
@@ -192,7 +191,7 @@ bisect_next_check() {
                ;;
        *)
                THEN=''
-               test -d "$GIT_DIR/refs/bisect" || {
+               test -f "$GIT_DIR/BISECT_NAMES" || {
                        echo >&2 'You need to start by "git bisect start".'
                        THEN='then '
                }
@@ -276,8 +275,7 @@ exit_if_skipped_commits () {
        if expr "$_tried" : ".*[|].*" > /dev/null ; then
                echo "There are only 'skip'ped commit left to test."
                echo "The first bad commit could be any of:"
-               echo "$_tried" | sed -e 's/[|]/\
-/g'
+               echo "$_tried" | tr '[|]' '[\012]'
                echo "We cannot bisect more!"
                exit 2
        fi
@@ -318,20 +316,23 @@ bisect_next() {
        exit_if_skipped_commits "$bisect_rev"
 
        echo "Bisecting: $bisect_nr revisions left to test after this"
-       echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect"
+       git branch -f new-bisect "$bisect_rev"
        git checkout -q new-bisect || exit
-       mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
-       GIT_DIR="$GIT_DIR" git symbolic-ref HEAD refs/heads/bisect
+       git branch -M new-bisect bisect
        git show-branch "$bisect_rev"
 }
 
 bisect_visualize() {
        bisect_next_check fail
-       not=`cd "$GIT_DIR/refs" && echo bisect/good-*`
-       eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
+       not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*")
+       eval gitk refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
 }
 
 bisect_reset() {
+       test -f "$GIT_DIR/BISECT_NAMES" || {
+               echo "We are not bisecting."
+               return
+       }
        case "$#" in
        0) if [ -s "$GIT_DIR/head-name" ]; then
               branch=`cat "$GIT_DIR/head-name"`
@@ -351,8 +352,12 @@ bisect_reset() {
 }
 
 bisect_clean_state() {
-       rm -fr "$GIT_DIR/refs/bisect"
-       rm -f "$GIT_DIR/refs/heads/bisect"
+       # There may be some refs packed during bisection.
+       git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* refs/heads/bisect |
+       while read ref hash
+       do
+               git update-ref -d $ref $hash
+       done
        rm -f "$GIT_DIR/BISECT_LOG"
        rm -f "$GIT_DIR/BISECT_NAMES"
        rm -f "$GIT_DIR/BISECT_RUN"
index aa724ac1a3859791187d257da6445d2b4adc1de8..f6d58ac044e5afb09855d687d3c1bcc8e2273be3 100755 (executable)
@@ -175,7 +175,7 @@ detach_warn=
 describe_detached_head () {
        test -n "$quiet" || {
                printf >&2 "$1 "
-               GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2"
+               GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2" --
        }
 }
 
@@ -266,7 +266,7 @@ if [ "$?" -eq 0 ]; then
        if test -n "$branch"
        then
                old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'`
-               GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from $old_branch_name to $branch" HEAD "refs/heads/$branch"
+               GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from ${old_branch_name:-$old} to $branch" HEAD "refs/heads/$branch"
                if test -n "$quiet"
                then
                        true    # nothing
@@ -278,7 +278,8 @@ if [ "$?" -eq 0 ]; then
                fi
        elif test -n "$detached"
        then
-               git update-ref --no-deref -m "checkout: moving to $arg" HEAD "$detached" ||
+               old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'`
+               git update-ref --no-deref -m "checkout: moving from ${old_branch_name:-$old} to $arg" HEAD "$detached" ||
                        die "Cannot detach HEAD"
                if test -n "$detach_warn"
                then
diff --git a/git-clean.sh b/git-clean.sh
deleted file mode 100755 (executable)
index 01c95e9..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005-2006 Pavel Roskin
-#
-
-OPTIONS_KEEPDASHDASH=
-OPTIONS_SPEC="\
-git-clean [options] <paths>...
-
-Clean untracked files from the working directory
-
-When optional <paths>... arguments are given, the paths
-affected are further limited to those that match them.
---
-d remove directories as well
-f override clean.requireForce and clean anyway
-n don't remove anything, just show what would be done
-q be quiet, only report errors
-x remove ignored files as well
-X remove only ignored files"
-
-SUBDIRECTORY_OK=Yes
-. git-sh-setup
-require_work_tree
-
-ignored=
-ignoredonly=
-cleandir=
-rmf="rm -f --"
-rmrf="rm -rf --"
-rm_refuse="echo Not removing"
-echo1="echo"
-
-disabled=$(git config --bool clean.requireForce)
-
-while test $# != 0
-do
-       case "$1" in
-       -d)
-               cleandir=1
-               ;;
-       -f)
-               disabled=false
-               ;;
-       -n)
-               disabled=false
-               rmf="echo Would remove"
-               rmrf="echo Would remove"
-               rm_refuse="echo Would not remove"
-               echo1=":"
-               ;;
-       -q)
-               echo1=":"
-               ;;
-       -x)
-               ignored=1
-               ;;
-       -X)
-               ignoredonly=1
-               ;;
-       --)
-               shift
-               break
-               ;;
-       *)
-               usage # should not happen
-               ;;
-       esac
-       shift
-done
-
-# 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
-
-if [ "$ignored,$ignoredonly" = "1,1" ]; then
-       die "-x and -X cannot be set together"
-fi
-
-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"} ${excludes_file:+"$excludes_file"} \
-       -- "$@" |
-while read -r file; do
-       if [ -d "$file" -a ! -L "$file" ]; then
-               if [ -z "$cleandir" ]; then
-                       $rm_refuse "$file"
-                       continue
-               fi
-               $echo1 "Removing $file"
-               $rmrf "$file"
-       else
-               $echo1 "Removing $file"
-               $rmf "$file"
-       fi
-done
index 86c896296686c94db6e9d0f2238a72aa383f647f..79eb10eacba955e0c58a0a540c4ac577f84953e9 100644 (file)
@@ -67,6 +67,8 @@
 #include <fnmatch.h>
 #include <sys/poll.h>
 #include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
 #include <assert.h>
 #include <regex.h>
 #include <netinet/in.h>
index efa6a0c41ad5a253047789e8ac2a30bf57f8646d..92648f40c98096d624a3a80feab4d3a818089b5a 100755 (executable)
@@ -108,10 +108,6 @@ sub read_repo_config {
             }
                }
        }
-    if (@ARGV == 0) {
-        chomp(my $module = `git-repo-config --get cvsimport.module`);
-        push(@ARGV, $module);
-    }
 }
 
 my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:";
@@ -119,6 +115,10 @@ sub read_repo_config {
 getopts($opts) or usage();
 usage if $opt_h;
 
+if (@ARGV == 0) {
+               chomp(my $module = `git-repo-config --get cvsimport.module`);
+               push(@ARGV, $module) if $? == 0;
+}
 @ARGV <= 1 or usage("You can't specify more than one CVS module");
 
 if ($opt_d) {
@@ -527,18 +527,12 @@ sub is_sha1 {
        return $s =~ /^[a-f0-9]{40}$/;
 }
 
-sub get_headref ($$) {
-    my $name    = shift;
-    my $git_dir = shift;
-
-    my $f = "$git_dir/$remote/$name";
-    if (open(my $fh, $f)) {
-           chomp(my $r = <$fh>);
-           is_sha1($r) or die "Cannot get head id for $name ($r): $!";
-           return $r;
-    }
-    die "unable to open $f: $!" unless $! == POSIX::ENOENT;
-    return undef;
+sub get_headref ($) {
+       my $name = shift;
+       my $r = `git rev-parse --verify '$name' 2>/dev/null`;
+       return undef unless $? == 0;
+       chomp $r;
+       return $r;
 }
 
 -d $git_tree
@@ -698,7 +692,8 @@ ()
 $ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
 
 sub commit {
-       if ($branch eq $opt_o && !$index{branch} && !get_headref($branch, $git_dir)) {
+       if ($branch eq $opt_o && !$index{branch} &&
+               !get_headref("$remote/$branch")) {
            # looks like an initial commit
            # use the index primed by git-init
            $ENV{GIT_INDEX_FILE} = "$git_dir/index";
@@ -722,7 +717,7 @@ sub commit {
        update_index(@old, @new);
        @old = @new = ();
        my $tree = write_tree();
-       my $parent = get_headref($last_branch, $git_dir);
+       my $parent = get_headref("$remote/$last_branch");
        print "Parent ID " . ($parent ? $parent : "(empty)") . "\n" if $opt_v;
 
        my @commit_args;
@@ -733,7 +728,7 @@ sub commit {
        foreach my $rx (@mergerx) {
                next unless $logmsg =~ $rx && $1;
                my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
-               if (my $sha1 = get_headref($mparent, $git_dir)) {
+               if (my $sha1 = get_headref("$remote/$mparent")) {
                        push @commit_args, '-p', $mparent;
                        print "Merge parent branch: $mparent\n" if $opt_v;
                }
@@ -870,29 +865,27 @@ sub commit {
                                print STDERR "Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\n";
                                $ancestor = $opt_o;
                        }
-                       if (-f "$git_dir/$remote/$branch") {
+                       if (defined get_headref("$remote/$branch")) {
                                print STDERR "Branch $branch already exists!\n";
                                $state=11;
                                next;
                        }
-                       unless (open(H,"$git_dir/$remote/$ancestor")) {
+                       my $id = get_headref("$remote/$ancestor");
+                       if (!$id) {
                                print STDERR "Branch $ancestor does not exist!\n";
                                $ignorebranch{$branch} = 1;
                                $state=11;
                                next;
                        }
-                       chomp(my $id = <H>);
-                       close(H);
-                       unless (open(H,"> $git_dir/$remote/$branch")) {
-                               print STDERR "Could not create branch $branch: $!\n";
+
+                       system(qw(git update-ref -m cvsimport),
+                               "$remote/$branch", $id);
+                       if($? != 0) {
+                               print STDERR "Could not create branch $branch\n";
                                $ignorebranch{$branch} = 1;
                                $state=11;
                                next;
                        }
-                       print H "$id\n"
-                               or die "Could not write branch $branch: $!";
-                       close(H)
-                               or die "Could not write branch $branch: $!";
                }
                $last_branch = $branch if $branch ne $last_branch;
                $state = 9;
@@ -1004,7 +997,7 @@ sub commit {
        $orig_branch = "master";
        print "DONE; creating $orig_branch branch\n" if $opt_v;
        system("git-update-ref", "refs/heads/master", "$remote/$opt_o")
-               unless -f "$git_dir/refs/heads/master";
+               unless defined get_headref('refs/heads/master');
        system("git-symbolic-ref", "$remote/HEAD", "$remote/$opt_o")
                if ($opt_r && $opt_o ne 'HEAD');
        system('git-update-ref', 'HEAD', "$orig_branch");
index c9f515d0ee0f36fc44f667f17674462ced7c2c9e..674a25d27e50b421e516e8ca6e1190d04e323e9c 100755 (executable)
@@ -8,6 +8,9 @@
 # a new branch. You can specify a number of filters to modify the commits,
 # files and trees.
 
+# The following functions will also be available in the commit filter:
+
+functions=$(cat << \EOF
 warn () {
         echo "$*" >&2
 }
@@ -46,6 +49,10 @@ die()
        echo "$*" >&2
        exit 1
 }
+EOF
+)
+
+eval "$functions"
 
 # When piped a commit, output a script to set the ident of either
 # "author" or "committer
@@ -80,11 +87,6 @@ set_ident () {
        echo "[ -n \"\$GIT_${uid}_NAME\" ] || export GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\""
 }
 
-# This script can be sourced by the commit filter to get the functions
-test "a$SOURCE_FUNCTIONS" = a1 && return
-this_script="$(cd "$(dirname "$0")"; pwd)"/$(basename "$0")
-export this_script
-
 USAGE="[--env-filter <command>] [--tree-filter <command>] \
 [--index-filter <command>] [--parent-filter <command>] \
 [--msg-filter <command>] [--commit-filter <command>] \
@@ -96,7 +98,7 @@ OPTIONS_SPEC=
 . git-sh-setup
 
 git diff-files --quiet &&
-       git diff-index --cached --quiet HEAD ||
+       git diff-index --cached --quiet HEAD -- ||
        die "Cannot rewrite branch(es) with a dirty working directory."
 
 tempdir=.git-rewrite
@@ -156,7 +158,7 @@ do
                filter_msg="$OPTARG"
                ;;
        --commit-filter)
-               filter_commit='SOURCE_FUNCTIONS=1 . "$this_script";'" $OPTARG"
+               filter_commit="$functions; $OPTARG"
                ;;
        --tag-name-filter)
                filter_tag_name="$OPTARG"
diff --git a/git-ls-remote.sh b/git-ls-remote.sh
deleted file mode 100755 (executable)
index fec70bb..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-#!/bin/sh
-#
-
-usage () {
-    echo >&2 "usage: $0 [--heads] [--tags] [-u|--upload-pack <upload-pack>]"
-    echo >&2 "          <repository> <refs>..."
-    exit 1;
-}
-
-die () {
-    echo >&2 "$*"
-    exit 1
-}
-
-exec=
-while test $# != 0
-do
-  case "$1" in
-  -h|--h|--he|--hea|--head|--heads)
-  heads=heads; shift ;;
-  -t|--t|--ta|--tag|--tags)
-  tags=tags; shift ;;
-  -u|--u|--up|--upl|--uploa|--upload|--upload-|--upload-p|--upload-pa|\
-  --upload-pac|--upload-pack)
-       shift
-       exec="--upload-pack=$1"
-       shift;;
-  -u=*|--u=*|--up=*|--upl=*|--uplo=*|--uploa=*|--upload=*|\
-  --upload-=*|--upload-p=*|--upload-pa=*|--upload-pac=*|--upload-pack=*)
-       exec=--upload-pack=$(expr "z$1" : 'z-[^=]*=\(.*\)')
-       shift;;
-  --)
-  shift; break ;;
-  -*)
-  usage ;;
-  *)
-  break ;;
-  esac
-done
-
-case "$#" in 0) usage ;; esac
-
-case ",$heads,$tags," in
-,,,) heads=heads tags=tags other=other ;;
-esac
-
-. git-parse-remote
-peek_repo="$(get_remote_url "$@")"
-shift
-
-tmp=.ls-remote-$$
-trap "rm -fr $tmp-*" 0 1 2 3 15
-tmpdir=$tmp-d
-
-case "$peek_repo" in
-http://* | https://* | ftp://* )
-       if [ -n "$GIT_SSL_NO_VERIFY" -o \
-               "`git config --bool http.sslVerify`" = false ]; then
-               curl_extra_args="-k"
-       fi
-       if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \
-               "`git config --bool http.noEPSV`" = true ]; then
-               curl_extra_args="${curl_extra_args} --disable-epsv"
-       fi
-       curl -nsf $curl_extra_args --header "Pragma: no-cache" "$peek_repo/info/refs" ||
-               echo "failed    slurping"
-       ;;
-
-rsync://* )
-       mkdir $tmpdir &&
-       rsync -rlq "$peek_repo/HEAD" $tmpdir &&
-       rsync -rq "$peek_repo/refs" $tmpdir || {
-               echo "failed    slurping"
-               exit
-       }
-       head=$(cat "$tmpdir/HEAD") &&
-       case "$head" in
-       ref:' '*)
-               head=$(expr "z$head" : 'zref: \(.*\)') &&
-               head=$(cat "$tmpdir/$head") || exit
-       esac &&
-       echo "$head     HEAD"
-       (cd $tmpdir && find refs -type f) |
-       while read path
-       do
-               tr -d '\012' <"$tmpdir/$path"
-               echo "  $path"
-       done &&
-       rm -fr $tmpdir
-       ;;
-
-* )
-       if test -f "$peek_repo" ; then
-               git bundle list-heads "$peek_repo" ||
-               echo "failed    slurping"
-       else
-               git-peek-remote $exec "$peek_repo" ||
-               echo "failed    slurping"
-       fi
-       ;;
-esac |
-sort -t '      ' -k 2 |
-while read sha1 path
-do
-       case "$sha1" in
-       failed)
-               exit 1 ;;
-       esac
-       case "$path" in
-       refs/heads/*)
-               group=heads ;;
-       refs/tags/*)
-               group=tags ;;
-       *)
-               group=other ;;
-       esac
-       case ",$heads,$tags,$other," in
-       *,$group,*)
-               ;;
-       *)
-               continue;;
-       esac
-       case "$#" in
-       0)
-               match=yes ;;
-       *)
-               match=no
-               for pat
-               do
-                       case "/$path" in
-                       */$pat )
-                               match=yes
-                               break ;;
-                       esac
-               done
-       esac
-       case "$match" in
-       no)
-               continue ;;
-       esac
-       echo "$sha1     $path"
-done
index bf44b6af58574273df9e7f549136a735f6e549fe..e9cd6fd999695daa1c5ba49835f53041d4131f3a 100755 (executable)
@@ -53,7 +53,7 @@ require_clean_work_tree () {
        git rev-parse --verify HEAD > /dev/null &&
        git update-index --refresh &&
        git diff-files --quiet &&
-       git diff-index --cached --quiet HEAD ||
+       git diff-index --cached --quiet HEAD -- ||
        die "Working tree is dirty"
 }
 
@@ -356,7 +356,7 @@ do
                git rev-parse --verify HEAD > /dev/null &&
                git update-index --refresh &&
                git diff-files --quiet &&
-               ! git diff-index --cached --quiet HEAD &&
+               ! git diff-index --cached --quiet HEAD -- &&
                . "$DOTEST"/author-script && {
                        test ! -f "$DOTEST"/amend || git reset --soft HEAD^
                } &&
index df5fd65d56bde3ed046c9ed40e62e8a17ede2e8e..bdcea0ed703057e08fd4204245f4f0c8a308f8d0 100755 (executable)
@@ -61,7 +61,7 @@ continue_merge () {
        fi
 
        cmt=`cat "$dotest/current"`
-       if ! git diff-index --quiet HEAD
+       if ! git diff-index --quiet HEAD --
        then
                if ! git-commit -C "$cmt"
                then
@@ -179,6 +179,7 @@ do
                exit
                ;;
        --skip)
+               git reset --hard HEAD || exit $?
                if test -d "$dotest"
                then
                        git rerere clear
@@ -284,7 +285,7 @@ fi
 
 # The tree must be really really clean.
 git update-index --refresh || exit
-diff=$(git diff-index --cached --name-status -r HEAD)
+diff=$(git diff-index --cached --name-status -r HEAD --)
 case "$diff" in
 ?*)    echo "cannot rebase: your index is not up-to-date"
        echo "$diff"
index 534eb168abf8b0b8ae5c2195bf85b66d9cb6b21e..b1529e28b1c4eb8b236bd497c83690c20621d369 100755 (executable)
@@ -15,7 +15,7 @@ trap 'rm -f "$TMP-*"' 0
 ref_stash=refs/stash
 
 no_changes () {
-       git diff-index --quiet --cached HEAD &&
+       git diff-index --quiet --cached HEAD -- &&
        git diff-files --quiet
 }
 
@@ -37,7 +37,7 @@ create_stash () {
        # state of the base commit
        if b_commit=$(git rev-parse --verify HEAD)
        then
-               head=$(git log --abbrev-commit --pretty=oneline -n 1 HEAD)
+               head=$(git log --no-color --abbrev-commit --pretty=oneline -n 1 HEAD --)
        else
                die "You do not have the initial commit yet"
        fi
@@ -108,7 +108,7 @@ have_stash () {
 
 list_stash () {
        have_stash || return 0
-       git log --pretty=oneline -g "$@" $ref_stash |
+       git log --no-color --pretty=oneline -g "$@" $ref_stash -- |
        sed -n -e 's/^[.0-9a-f]* refs\///p'
 }
 
index 43e1591cef4e69a1d06463ad996190b89df6cfe6..9f884eb2132c76b86475b97256e0ed565f84bb2e 100755 (executable)
@@ -35,6 +35,7 @@
 push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor';
 push @SVN::Git::Fetcher::ISA, 'SVN::Delta::Editor';
 use Carp qw/croak/;
+use Digest::MD5;
 use IO::File qw//;
 use File::Basename qw/dirname basename/;
 use File::Path qw/mkpath/;
@@ -48,8 +49,7 @@ BEGIN
        foreach (qw/command command_oneline command_noisy command_output_pipe
                    command_input_pipe command_close_pipe/) {
                for my $package ( qw(SVN::Git::Editor SVN::Git::Fetcher
-                       Git::SVN::Migration Git::SVN::Log Git::SVN
-                       Git::SVN::Util),
+                       Git::SVN::Migration Git::SVN::Log Git::SVN),
                        __PACKAGE__) {
                        *{"${package}::$_"} = \&{"Git::$_"};
                }
@@ -81,6 +81,7 @@ BEGIN
                'quiet|q' => \$_q,
                'repack-flags|repack-args|repack-opts=s' =>
                   \$Git::SVN::_repack_flags,
+               'use-log-author' => \$Git::SVN::_use_log_author,
                %remote_opts );
 
 my ($_trunk, $_tags, $_branches, $_stdlayout);
@@ -142,6 +143,9 @@ BEGIN
        'show-ignore' => [ \&cmd_show_ignore, "Show svn:ignore listings",
                        { 'revision|r=i' => \$_revision
                        } ],
+       'show-externals' => [ \&cmd_show_externals, "Show svn:externals listings",
+                       { 'revision|r=i' => \$_revision
+                       } ],
        'multi-fetch' => [ \&cmd_multi_fetch,
                           "Deprecated alias for $0 fetch --all",
                           { 'revision|r=s' => \$_revision, %fc_opts } ],
@@ -193,23 +197,6 @@ BEGIN
        }
 };
 
-my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
-
-read_repo_config(\%opts);
-Getopt::Long::Configure('pass_through') if ($cmd && $cmd eq 'log');
-my $rv = GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version,
-                    'minimize-connections' => \$Git::SVN::Migration::_minimize,
-                    'id|i=s' => \$Git::SVN::default_ref_id,
-                    'svn-remote|remote|R=s' => sub {
-                       $Git::SVN::no_reuse_existing = 1;
-                       $Git::SVN::default_repo_id = $_[1] });
-exit 1 if (!$rv && $cmd && $cmd ne 'log');
-
-usage(0) if $_help;
-version() if $_version;
-usage(1) unless defined $cmd;
-load_authors() if $_authors;
-
 # make sure we're always running
 unless ($cmd =~ /(?:clone|init|multi-init)$/) {
        unless (-d $ENV{GIT_DIR}) {
@@ -231,6 +218,24 @@ BEGIN
                $ENV{GIT_DIR} = $git_dir;
        }
 }
+
+my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
+
+read_repo_config(\%opts);
+Getopt::Long::Configure('pass_through') if ($cmd && $cmd eq 'log');
+my $rv = GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version,
+                    'minimize-connections' => \$Git::SVN::Migration::_minimize,
+                    'id|i=s' => \$Git::SVN::default_ref_id,
+                    'svn-remote|remote|R=s' => sub {
+                       $Git::SVN::no_reuse_existing = 1;
+                       $Git::SVN::default_repo_id = $_[1] });
+exit 1 if (!$rv && $cmd && $cmd ne 'log');
+
+usage(0) if $_help;
+version() if $_version;
+usage(1) unless defined $cmd;
+load_authors() if $_authors;
+
 unless ($cmd =~ /^(?:clone|init|multi-init|commit-diff)$/) {
        Git::SVN::Migration::migration_check();
 }
@@ -545,6 +550,8 @@ sub cmd_rebase {
                exit 1;
        }
        unless ($_local) {
+               # rebase will checkout for us, so no need to do it explicitly
+               $_no_checkout = 'true';
                $_fetch_all ? $gs->fetch_all : $gs->fetch;
        }
        command_noisy(rebase_cmd(), $gs->refname);
@@ -565,6 +572,21 @@ sub cmd_show_ignore {
        });
 }
 
+sub cmd_show_externals {
+       my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
+       $gs ||= Git::SVN->new;
+       my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum);
+       $gs->prop_walk($gs->{path}, $r, sub {
+               my ($gs, $path, $props) = @_;
+               print STDOUT "\n# $path\n";
+               my $s = $props->{'svn:externals'} or return;
+               $s =~ s/[\r\n]+/\n/g;
+               chomp $s;
+               $s =~ s#^#$path#gm;
+               print STDOUT "$s\n";
+       });
+}
+
 sub cmd_create_ignore {
        my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
        $gs ||= Git::SVN->new;
@@ -840,19 +862,19 @@ sub cmd_info {
                            command_output_pipe(qw(cat-file blob), "HEAD:$path");
                        if ($file_type eq "link") {
                                my $file_name = <$fh>;
-                               $checksum = Git::SVN::Util::md5sum("link $file_name");
+                               $checksum = md5sum("link $file_name");
                        } else {
-                               $checksum = Git::SVN::Util::md5sum($fh);
+                               $checksum = md5sum($fh);
                        }
                        command_close_pipe($fh, $ctx);
                } elsif ($file_type eq "link") {
                        my $file_name =
                            command(qw(cat-file blob), "HEAD:$path");
                        $checksum =
-                           Git::SVN::Util::md5sum("link " . $file_name);
+                           md5sum("link " . $file_name);
                } else {
                        open FILE, "<", $path or die $!;
-                       $checksum = Git::SVN::Util::md5sum(\*FILE);
+                       $checksum = md5sum(\*FILE);
                        close FILE or die $!;
                }
                $result .= "Checksum: " . $checksum . "\n";
@@ -1193,11 +1215,6 @@ sub find_file_type_and_diff_status {
        return ("file", $diff_status);
 }
 
-package Git::SVN::Util;
-use strict;
-use warnings;
-use Digest::MD5;
-
 sub md5sum {
        my $arg = shift;
        my $ref = ref $arg;
@@ -1219,7 +1236,8 @@ package Git::SVN;
 use warnings;
 use vars qw/$default_repo_id $default_ref_id $_no_metadata $_follow_parent
             $_repack $_repack_flags $_use_svm_props $_head
-            $_use_svnsync_props $no_reuse_existing $_minimize_url/;
+            $_use_svnsync_props $no_reuse_existing $_minimize_url
+           $_use_log_author/;
 use Carp qw/croak/;
 use File::Path qw/mkpath/;
 use File::Copy qw/copy/;
@@ -2059,11 +2077,17 @@ sub do_git_commit {
                croak "$log_entry->{revision} = $c already exists! ",
                      "Why are we refetching it?\n";
        }
-       $ENV{GIT_AUTHOR_NAME} = $ENV{GIT_COMMITTER_NAME} = $log_entry->{name};
-       $ENV{GIT_AUTHOR_EMAIL} = $ENV{GIT_COMMITTER_EMAIL} =
-                                                         $log_entry->{email};
+       $ENV{GIT_AUTHOR_NAME} = $log_entry->{name};
+       $ENV{GIT_AUTHOR_EMAIL} = $log_entry->{email};
        $ENV{GIT_AUTHOR_DATE} = $ENV{GIT_COMMITTER_DATE} = $log_entry->{date};
 
+       $ENV{GIT_COMMITTER_NAME} = (defined $log_entry->{commit_name})
+                                               ? $log_entry->{commit_name}
+                                               : $log_entry->{name};
+       $ENV{GIT_COMMITTER_EMAIL} = (defined $log_entry->{commit_email})
+                                               ? $log_entry->{commit_email}
+                                               : $log_entry->{email};
+
        my $tree = $log_entry->{tree};
        if (!defined $tree) {
                $tree = $self->tmp_index_do(sub {
@@ -2351,7 +2375,17 @@ sub make_log_entry {
        $log_entry{log} .= "\n";
        my $author = $log_entry{author} = check_author($log_entry{author});
        my ($name, $email) = defined $::users{$author} ? @{$::users{$author}}
-                                                      : ($author, undef);
+                                                      : ($author, undef);
+
+       my ($commit_name, $commit_email) = ($name, $email);
+       if ($_use_log_author) {
+               if ($log_entry{log} =~ /From:\s+(.*?)\s+<(.*)>\s*\n/) {
+                       ($name, $email) = ($1, $2);
+               } elsif ($log_entry{log} =~
+                                     /Signed-off-by:\s+(.*?)\s+<(.*)>\s*\n/) {
+                       ($name, $email) = ($1, $2);
+               }
+       }
        if (defined $headrev && $self->use_svm_props) {
                if ($self->rewrite_root) {
                        die "Can't have both 'useSvmProps' and 'rewriteRoot' ",
@@ -2374,23 +2408,28 @@ sub make_log_entry {
                remove_username($full_url);
                $log_entry{metadata} = "$full_url\@$r $uuid";
                $log_entry{svm_revision} = $r;
-               $email ||= "$author\@$uuid"
+               $email ||= "$author\@$uuid";
+               $commit_email ||= "$author\@$uuid";
        } elsif ($self->use_svnsync_props) {
                my $full_url = $self->svnsync->{url};
                $full_url .= "/$self->{path}" if length $self->{path};
                remove_username($full_url);
                my $uuid = $self->svnsync->{uuid};
                $log_entry{metadata} = "$full_url\@$rev $uuid";
-               $email ||= "$author\@$uuid"
+               $email ||= "$author\@$uuid";
+               $commit_email ||= "$author\@$uuid";
        } else {
                my $url = $self->metadata_url;
                remove_username($url);
                $log_entry{metadata} = "$url\@$rev " .
                                       $self->ra->get_uuid;
                $email ||= "$author\@" . $self->ra->get_uuid;
+               $commit_email ||= "$author\@" . $self->ra->get_uuid;
        }
        $log_entry{name} = $name;
        $log_entry{email} = $email;
+       $log_entry{commit_name} = $commit_name;
+       $log_entry{commit_email} = $commit_email;
        \%log_entry;
 }
 
@@ -2947,7 +2986,7 @@ sub apply_textdelta {
 
                if (defined $exp) {
                        seek $base, 0, 0 or croak $!;
-                       my $got = Git::SVN::Util::md5sum($base);
+                       my $got = ::md5sum($base);
                        die "Checksum mismatch: $fb->{path} $fb->{blob}\n",
                            "expected: $exp\n",
                            "     got: $got\n" if ($got ne $exp);
@@ -2966,7 +3005,7 @@ sub close_file {
        if (my $fh = $fb->{fh}) {
                if (defined $exp) {
                        seek($fh, 0, 0) or croak $!;
-                       my $got = Git::SVN::Util::md5sum($fh);
+                       my $got = ::md5sum($fh);
                        if ($got ne $exp) {
                                die "Checksum mismatch: $path\n",
                                    "expected: $exp\n    got: $got\n";
@@ -3321,7 +3360,7 @@ sub chg_file {
        $fh->flush == 0 or croak $!;
        seek $fh, 0, 0 or croak $!;
 
-       my $exp = Git::SVN::Util::md5sum($fh);
+       my $exp = ::md5sum($fh);
        seek $fh, 0, 0 or croak $!;
 
        my $pool = SVN::Pool->new;
diff --git a/git.c b/git.c
index 80c2f14a8b805008fc822e9483d2277e69bdcbe3..01bbbc73258dc66d8d3a8253cc9328b5b18346aa 100644 (file)
--- a/git.c
+++ b/git.c
@@ -256,7 +256,7 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv)
 
        status = p->fn(argc, argv, prefix);
        if (status)
-               return status;
+               return status & 0xff;
 
        /* Somebody closed stdout? */
        if (fstat(fileno(stdout), &st))
@@ -293,6 +293,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },
                { "cherry", cmd_cherry, RUN_SETUP },
                { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
+               { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
                { "commit-tree", cmd_commit_tree, RUN_SETUP },
                { "config", cmd_config },
                { "count-objects", cmd_count_objects, RUN_SETUP },
@@ -321,6 +322,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "log", cmd_log, RUN_SETUP | USE_PAGER },
                { "ls-files", cmd_ls_files, RUN_SETUP },
                { "ls-tree", cmd_ls_tree, RUN_SETUP },
+               { "ls-remote", cmd_ls_remote },
                { "mailinfo", cmd_mailinfo },
                { "mailsplit", cmd_mailsplit },
                { "merge-base", cmd_merge_base, RUN_SETUP },
@@ -329,6 +331,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
                { "name-rev", cmd_name_rev, RUN_SETUP },
                { "pack-objects", cmd_pack_objects, RUN_SETUP },
+               { "peek-remote", cmd_ls_remote },
                { "pickaxe", cmd_blame, RUN_SETUP },
                { "prune", cmd_prune, RUN_SETUP },
                { "prune-packed", cmd_prune_packed, RUN_SETUP },
@@ -343,6 +346,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
                { "rm", cmd_rm, RUN_SETUP },
                { "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE },
+               { "send-pack", cmd_send_pack, RUN_SETUP },
                { "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
                { "show-branch", cmd_show_branch, RUN_SETUP },
                { "show", cmd_show, RUN_SETUP | USE_PAGER },
diff --git a/help.c b/help.c
index 8217d97787b2ba38a5327c1204797044b2b4cd72..d340b6a1b6c4ecb8132e81fe306883aa18feb090 100644 (file)
--- a/help.c
+++ b/help.c
@@ -7,7 +7,6 @@
 #include "builtin.h"
 #include "exec_cmd.h"
 #include "common-cmds.h"
-#include <sys/ioctl.h>
 
 /* most GUI terminals set COLUMNS (although some don't export it) */
 static int term_columns(void)
index 9314621a11030e66eeba7f0da6b88a813e1ba759..78283b4de3b9111d87368df747daa96b5f6c09c3 100644 (file)
@@ -78,7 +78,7 @@ static struct curl_slist *no_pragma_header;
 static struct curl_slist *default_headers;
 
 static int push_verbosely;
-static int push_all;
+static int push_all = MATCH_REFS_NONE;
 static int force_all;
 static int dry_run;
 
@@ -433,7 +433,7 @@ static void start_fetch_packed(struct transfer_request *request)
        packfile = fopen(request->tmpfile, "a");
        if (!packfile) {
                fprintf(stderr, "Unable to open local file %s for pack",
-                       filename);
+                       request->tmpfile);
                remote->can_update_info_refs = 0;
                free(url);
                return;
@@ -941,7 +941,7 @@ static int fetch_index(unsigned char *sha1)
        indexfile = fopen(tmpfile, "a");
        if (!indexfile)
                return error("Unable to open local file %s for pack index",
-                            filename);
+                            tmpfile);
 
        slot = get_active_slot();
        slot->results = &results;
@@ -2300,7 +2300,7 @@ int main(int argc, char **argv)
 
                if (*arg == '-') {
                        if (!strcmp(arg, "--all")) {
-                               push_all = 1;
+                               push_all = MATCH_REFS_ALL;
                                continue;
                        }
                        if (!strcmp(arg, "--force")) {
@@ -2393,7 +2393,7 @@ int main(int argc, char **argv)
        if (!remote_tail)
                remote_tail = &remote_refs;
        if (match_refs(local_refs, remote_refs, &remote_tail,
-                      nr_refspec, refspec, push_all))
+                      nr_refspec, (const char **) refspec, push_all))
                return -1;
        if (!remote_refs) {
                fprintf(stderr, "No refs in common and none specified; doing nothing.\n");
index 444aebf5268c982255f73b151ff5c03bdffecdbe..a3fb596542eea6d85a6cb32add100e8d71e7713a 100644 (file)
@@ -405,7 +405,7 @@ static int fetch_index(struct walker *walker, struct alt_base *repo, unsigned ch
        indexfile = fopen(tmpfile, "a");
        if (!indexfile)
                return error("Unable to open local file %s for pack index",
-                            filename);
+                            tmpfile);
 
        slot = get_active_slot();
        slot->results = &results;
@@ -770,7 +770,7 @@ static int fetch_pack(struct walker *walker, struct alt_base *repo, unsigned cha
        packfile = fopen(tmpfile, "a");
        if (!packfile)
                return error("Unable to open local file %s for pack",
-                            filename);
+                            tmpfile);
 
        slot = get_active_slot();
        slot->results = &results;
index 9a1f64d8d71d13ee6be48c539d79764066288f07..f45d3ed54454635b84cfe9b8c1e7d99d33ffd4d9 100644 (file)
@@ -12,8 +12,10 @@ static void remove_lock_file(void)
 
        while (lock_file_list) {
                if (lock_file_list->owner == me &&
-                   lock_file_list->filename[0])
+                   lock_file_list->filename[0]) {
+                       close(lock_file_list->fd);
                        unlink(lock_file_list->filename);
+               }
                lock_file_list = lock_file_list->next;
        }
 }
@@ -92,7 +94,7 @@ static char *resolve_symlink(char *p, size_t s)
                        return p;
                }
 
-               if (link[0] == '/') {
+               if (is_absolute_path(link)) {
                        /* absolute path simply replaces p */
                        if (link_len < s)
                                strcpy(p, link);
@@ -120,8 +122,6 @@ static char *resolve_symlink(char *p, size_t s)
 
 static int lock_file(struct lock_file *lk, const char *path)
 {
-       int fd;
-
        if (strlen(path) >= sizeof(lk->filename)) return -1;
        strcpy(lk->filename, path);
        /*
@@ -130,8 +130,8 @@ static int lock_file(struct lock_file *lk, const char *path)
         */
        resolve_symlink(lk->filename, sizeof(lk->filename)-5);
        strcat(lk->filename, ".lock");
-       fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
-       if (0 <= fd) {
+       lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
+       if (0 <= lk->fd) {
                if (!lock_file_list) {
                        signal(SIGINT, remove_lock_file_on_signal);
                        atexit(remove_lock_file);
@@ -148,7 +148,7 @@ static int lock_file(struct lock_file *lk, const char *path)
        }
        else
                lk->filename[0] = 0;
-       return fd;
+       return lk->fd;
 }
 
 int hold_lock_file_for_update(struct lock_file *lk, const char *path, int die_on_error)
@@ -163,6 +163,7 @@ int commit_lock_file(struct lock_file *lk)
 {
        char result_file[PATH_MAX];
        int i;
+       close(lk->fd);
        strcpy(result_file, lk->filename);
        i = strlen(result_file) - 5; /* .lock */
        result_file[i] = 0;
@@ -194,7 +195,9 @@ int commit_locked_index(struct lock_file *lk)
 
 void rollback_lock_file(struct lock_file *lk)
 {
-       if (lk->filename[0])
+       if (lk->filename[0]) {
+               close(lk->fd);
                unlink(lk->filename);
+       }
        lk->filename[0] = 0;
 }
diff --git a/pager.c b/pager.c
index 8bac9d990381f5664333a92f68b0b8cd97d43855..fb7a1a625abf07b0d896a270286ee422a700c14c 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -1,7 +1,5 @@
 #include "cache.h"
 
-#include <sys/select.h>
-
 /*
  * This is split up from the rest of git so that we might do
  * something different on Windows, for example.
diff --git a/peek-remote.c b/peek-remote.c
deleted file mode 100644 (file)
index 8d20f7c..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-#include "cache.h"
-#include "refs.h"
-#include "pkt-line.h"
-
-static const char peek_remote_usage[] =
-"git-peek-remote [--upload-pack=<git-upload-pack>] [<host>:]<directory>";
-static const char *uploadpack = "git-upload-pack";
-
-static int peek_remote(int fd[2], unsigned flags)
-{
-       struct ref *ref;
-
-       get_remote_heads(fd[0], &ref, 0, NULL, flags);
-       packet_flush(fd[1]);
-
-       while (ref) {
-               printf("%s      %s\n", sha1_to_hex(ref->old_sha1), ref->name);
-               ref = ref->next;
-       }
-       return 0;
-}
-
-int main(int argc, char **argv)
-{
-       int i, ret;
-       char *dest = NULL;
-       int fd[2];
-       struct child_process *conn;
-       int nongit = 0;
-       unsigned flags = 0;
-
-       setup_git_directory_gently(&nongit);
-
-       for (i = 1; i < argc; i++) {
-               char *arg = argv[i];
-
-               if (*arg == '-') {
-                       if (!prefixcmp(arg, "--upload-pack=")) {
-                               uploadpack = arg + 14;
-                               continue;
-                       }
-                       if (!prefixcmp(arg, "--exec=")) {
-                               uploadpack = arg + 7;
-                               continue;
-                       }
-                       if (!strcmp("--tags", arg)) {
-                               flags |= REF_TAGS;
-                               continue;
-                       }
-                       if (!strcmp("--heads", arg)) {
-                               flags |= REF_HEADS;
-                               continue;
-                       }
-                       if (!strcmp("--refs", arg)) {
-                               flags |= REF_NORMAL;
-                               continue;
-                       }
-                       usage(peek_remote_usage);
-               }
-               dest = arg;
-               break;
-       }
-
-       if (!dest || i != argc - 1)
-               usage(peek_remote_usage);
-
-       conn = git_connect(fd, dest, uploadpack, 0);
-       ret = peek_remote(fd, flags);
-       close(fd[0]);
-       close(fd[1]);
-       ret |= finish_connect(conn);
-       return !!ret;
-}
index dca92c8adb52e212ef96410d3efd4deaa28e385f..7468460f9a6d29d5c4bf14db4921bf28e23b6814 100644 (file)
@@ -549,6 +549,37 @@ sub config_bool {
        };
 }
 
+=item config_int ( VARIABLE )
+
+Retrieve the integer configuration C<VARIABLE>. The return value
+is simple decimal number.  An optional value suffix of 'k', 'm',
+or 'g' in the config file will cause the value to be multiplied
+by 1024, 1048576 (1024^2), or 1073741824 (1024^3) prior to output.
+It would return C<undef> if configuration variable is not defined,
+
+Must be called on a repository instance.
+
+This currently wraps command('config') so it is not so fast.
+
+=cut
+
+sub config_int {
+       my ($self, $var) = @_;
+       $self->repo_path()
+               or throw Error::Simple("not a repository");
+
+       try {
+               return $self->command_oneline('config', '--int', '--get', $var);
+       } catch Git::Error::Command with {
+               my $E = shift;
+               if ($E->value() == 1) {
+                       # Key not found.
+                       return undef;
+               } else {
+                       throw $E;
+               }
+       };
+}
 
 =item ident ( TYPE | IDENTSTR )
 
index 38e35c06b9e73376adde597c4fe28490a2e886b1..fba4cf82353ff43eae7430c863680f481e03dcb0 100644 (file)
@@ -200,12 +200,14 @@ static const char *update(struct command *cmd)
        }
 
        if (is_null_sha1(new_sha1)) {
+               if (!parse_object(old_sha1)) {
+                       warning ("Allowing deletion of corrupt ref.");
+                       old_sha1 = NULL;
+               }
                if (delete_ref(name, old_sha1)) {
                        error("failed to delete %s", name);
                        return "failed to delete";
                }
-               fprintf(stderr, "%s: %s -> deleted\n", name,
-                       sha1_to_hex(old_sha1));
                return NULL; /* good */
        }
        else {
@@ -217,8 +219,6 @@ static const char *update(struct command *cmd)
                if (write_ref_sha1(lock, new_sha1, "push")) {
                        return "failed to write"; /* error() already called */
                }
-               fprintf(stderr, "%s: %s -> %s\n", name,
-                       sha1_to_hex(old_sha1), sha1_to_hex(new_sha1));
                return NULL; /* good */
        }
 }
diff --git a/refs.c b/refs.c
index ae532540cf2592aee2be7610ac811082c3a9b6be..54ec98d153889f40313dba9a5ee8f07ddd0e160a 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1433,3 +1433,11 @@ int update_ref(const char *action, const char *refname,
        }
        return 0;
 }
+
+struct ref *find_ref_by_name(struct ref *list, const char *name)
+{
+       for ( ; list; list = list->next)
+               if (!strcmp(list->name, name))
+                       return list;
+       return NULL;
+}
index bec2ba1adbed02af54572be924dcfbb47bf9c423..bb010590837fd6ea188e64c5263bb1fe12ab93f4 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -485,7 +485,7 @@ struct ref *alloc_ref(unsigned namelen)
        return ret;
 }
 
-static struct ref *copy_ref(struct ref *ref)
+static struct ref *copy_ref(const struct ref *ref)
 {
        struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1);
        memcpy(ret, ref, sizeof(struct ref) + strlen(ref->name) + 1);
@@ -493,6 +493,18 @@ static struct ref *copy_ref(struct ref *ref)
        return ret;
 }
 
+struct ref *copy_ref_list(const struct ref *ref)
+{
+       struct ref *ret = NULL;
+       struct ref **tail = &ret;
+       while (ref) {
+               *tail = copy_ref(ref);
+               ref = ref->next;
+               tail = &((*tail)->next);
+       }
+       return ret;
+}
+
 void free_refs(struct ref *ref)
 {
        struct ref *next;
@@ -684,14 +696,6 @@ static int match_explicit_refs(struct ref *src, struct ref *dst,
        return -errs;
 }
 
-static struct ref *find_ref_by_name(struct ref *list, const char *name)
-{
-       for ( ; list; list = list->next)
-               if (!strcmp(list->name, name))
-                       return list;
-       return NULL;
-}
-
 static const struct refspec *check_pattern_match(const struct refspec *rs,
                                                 int rs_nr,
                                                 const struct ref *src)
@@ -710,10 +714,12 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
  * without thinking.
  */
 int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
-              int nr_refspec, char **refspec, int all)
+              int nr_refspec, const char **refspec, int flags)
 {
        struct refspec *rs =
                parse_ref_spec(nr_refspec, (const char **) refspec);
+       int send_all = flags & MATCH_REFS_ALL;
+       int send_mirror = flags & MATCH_REFS_MIRROR;
 
        if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
                return -1;
@@ -730,7 +736,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
                        if (!pat)
                                continue;
                }
-               else if (prefixcmp(src->name, "refs/heads/"))
+               else if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
                        /*
                         * "matching refs"; traditionally we pushed everything
                         * including refs outside refs/heads/ hierarchy, but
@@ -751,10 +757,13 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
                if (dst_peer && dst_peer->peer_ref)
                        /* We're already sending something to this ref. */
                        goto free_name;
-               if (!dst_peer && !nr_refspec && !all)
-                       /* Remote doesn't have it, and we have no
+
+               if (!dst_peer && !nr_refspec && !(send_all || send_mirror))
+                       /*
+                        * Remote doesn't have it, and we have no
                         * explicit pattern, and we don't have
-                        * --all. */
+                        * --all nor --mirror.
+                        */
                        goto free_name;
                if (!dst_peer) {
                        /* Create a new one and link it */
@@ -810,10 +819,10 @@ int branch_merge_matches(struct branch *branch,
        return ref_matches_abbrev(branch->merge[i]->src, refname);
 }
 
-static struct ref *get_expanded_map(struct ref *remote_refs,
+static struct ref *get_expanded_map(const struct ref *remote_refs,
                                    const struct refspec *refspec)
 {
-       struct ref *ref;
+       const struct ref *ref;
        struct ref *ret = NULL;
        struct ref **tail = &ret;
 
@@ -824,7 +833,7 @@ static struct ref *get_expanded_map(struct ref *remote_refs,
                if (strchr(ref->name, '^'))
                        continue; /* a dereference item */
                if (!prefixcmp(ref->name, refspec->src)) {
-                       char *match;
+                       const char *match;
                        struct ref *cpy = copy_ref(ref);
                        match = ref->name + remote_prefix_len;
 
@@ -842,9 +851,9 @@ static struct ref *get_expanded_map(struct ref *remote_refs,
        return ret;
 }
 
-static struct ref *find_ref_by_name_abbrev(struct ref *refs, const char *name)
+static const struct ref *find_ref_by_name_abbrev(const struct ref *refs, const char *name)
 {
-       struct ref *ref;
+       const struct ref *ref;
        for (ref = refs; ref; ref = ref->next) {
                if (ref_matches_abbrev(name, ref->name))
                        return ref;
@@ -852,9 +861,9 @@ static struct ref *find_ref_by_name_abbrev(struct ref *refs, const char *name)
        return NULL;
 }
 
-struct ref *get_remote_ref(struct ref *remote_refs, const char *name)
+struct ref *get_remote_ref(const struct ref *remote_refs, const char *name)
 {
-       struct ref *ref = find_ref_by_name_abbrev(remote_refs, name);
+       const struct ref *ref = find_ref_by_name_abbrev(remote_refs, name);
 
        if (!ref)
                return NULL;
@@ -887,7 +896,7 @@ static struct ref *get_local_ref(const char *name)
        return ret;
 }
 
-int get_fetch_map(struct ref *remote_refs,
+int get_fetch_map(const struct ref *remote_refs,
                  const struct refspec *refspec,
                  struct ref ***tail,
                  int missing_ok)
index 878b4ecc32a2a4b5be4e0444ae4510e1a7ab01cb..b10036cae6f89e087da56979e2248e1e5c5d42d3 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -44,6 +44,10 @@ struct refspec {
 
 struct ref *alloc_ref(unsigned namelen);
 
+struct ref *copy_ref_list(const struct ref *ref);
+
+int check_ref_type(const struct ref *ref, int flags);
+
 /*
  * Frees the entire list and peers of elements.
  */
@@ -57,7 +61,7 @@ void ref_remove_duplicates(struct ref *ref_map);
 struct refspec *parse_ref_spec(int nr_refspec, const char **refspec);
 
 int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
-              int nr_refspec, char **refspec, int all);
+              int nr_refspec, const char **refspec, int all);
 
 /*
  * Given a list of the remote refs and the specification of things to
@@ -71,10 +75,10 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
  * missing_ok is usually false, but when we are adding branch.$name.merge
  * it is Ok if the branch is not at the remote anymore.
  */
-int get_fetch_map(struct ref *remote_refs, const struct refspec *refspec,
+int get_fetch_map(const struct ref *remote_refs, const struct refspec *refspec,
                  struct ref ***tail, int missing_ok);
 
-struct ref *get_remote_ref(struct ref *remote_refs, const char *name);
+struct ref *get_remote_ref(const struct ref *remote_refs, const char *name);
 
 /*
  * For the given remote, reads the refspec's src and sets the other fields.
@@ -98,4 +102,11 @@ struct branch *branch_get(const char *name);
 int branch_has_merge_config(struct branch *branch);
 int branch_merge_matches(struct branch *, int n, const char *);
 
+/* Flags to match_refs. */
+enum match_refs_flags {
+       MATCH_REFS_NONE         = 0,
+       MATCH_REFS_ALL          = (1 << 0),
+       MATCH_REFS_MIRROR       = (1 << 1),
+};
+
 #endif
diff --git a/send-pack.c b/send-pack.c
deleted file mode 100644 (file)
index b74fd45..0000000
+++ /dev/null
@@ -1,461 +0,0 @@
-#include "cache.h"
-#include "commit.h"
-#include "tag.h"
-#include "refs.h"
-#include "pkt-line.h"
-#include "run-command.h"
-#include "remote.h"
-
-static const char send_pack_usage[] =
-"git-send-pack [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
-"  --all and explicit <ref> specification are mutually exclusive.";
-static const char *receivepack = "git-receive-pack";
-static int verbose;
-static int send_all;
-static int force_update;
-static int use_thin_pack;
-static int dry_run;
-
-/*
- * Make a pack stream and spit it out into file descriptor fd
- */
-static int pack_objects(int fd, struct ref *refs)
-{
-       /*
-        * The child becomes pack-objects --revs; we feed
-        * the revision parameters to it via its stdin and
-        * let its stdout go back to the other end.
-        */
-       const char *args[] = {
-               "pack-objects",
-               "--all-progress",
-               "--revs",
-               "--stdout",
-               NULL,
-               NULL,
-       };
-       struct child_process po;
-
-       if (use_thin_pack)
-               args[4] = "--thin";
-       memset(&po, 0, sizeof(po));
-       po.argv = args;
-       po.in = -1;
-       po.out = fd;
-       po.git_cmd = 1;
-       if (start_command(&po))
-               die("git-pack-objects failed (%s)", strerror(errno));
-
-       /*
-        * We feed the pack-objects we just spawned with revision
-        * parameters by writing to the pipe.
-        */
-       while (refs) {
-               char buf[42];
-
-               if (!is_null_sha1(refs->old_sha1) &&
-                   has_sha1_file(refs->old_sha1)) {
-                       memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
-                       buf[0] = '^';
-                       buf[41] = '\n';
-                       if (!write_or_whine(po.in, buf, 42,
-                                               "send-pack: send refs"))
-                               break;
-               }
-               if (!is_null_sha1(refs->new_sha1)) {
-                       memcpy(buf, sha1_to_hex(refs->new_sha1), 40);
-                       buf[40] = '\n';
-                       if (!write_or_whine(po.in, buf, 41,
-                                               "send-pack: send refs"))
-                               break;
-               }
-               refs = refs->next;
-       }
-
-       if (finish_command(&po))
-               return error("pack-objects died with strange error");
-       return 0;
-}
-
-static void unmark_and_free(struct commit_list *list, unsigned int mark)
-{
-       while (list) {
-               struct commit_list *temp = list;
-               temp->item->object.flags &= ~mark;
-               list = temp->next;
-               free(temp);
-       }
-}
-
-static int ref_newer(const unsigned char *new_sha1,
-                    const unsigned char *old_sha1)
-{
-       struct object *o;
-       struct commit *old, *new;
-       struct commit_list *list, *used;
-       int found = 0;
-
-       /* Both new and old must be commit-ish and new is descendant of
-        * old.  Otherwise we require --force.
-        */
-       o = deref_tag(parse_object(old_sha1), NULL, 0);
-       if (!o || o->type != OBJ_COMMIT)
-               return 0;
-       old = (struct commit *) o;
-
-       o = deref_tag(parse_object(new_sha1), NULL, 0);
-       if (!o || o->type != OBJ_COMMIT)
-               return 0;
-       new = (struct commit *) o;
-
-       if (parse_commit(new) < 0)
-               return 0;
-
-       used = list = NULL;
-       commit_list_insert(new, &list);
-       while (list) {
-               new = pop_most_recent_commit(&list, 1);
-               commit_list_insert(new, &used);
-               if (new == old) {
-                       found = 1;
-                       break;
-               }
-       }
-       unmark_and_free(list, 1);
-       unmark_and_free(used, 1);
-       return found;
-}
-
-static struct ref *local_refs, **local_tail;
-static struct ref *remote_refs, **remote_tail;
-
-static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
-{
-       struct ref *ref;
-       int len = strlen(refname) + 1;
-       ref = xcalloc(1, sizeof(*ref) + len);
-       hashcpy(ref->new_sha1, sha1);
-       memcpy(ref->name, refname, len);
-       *local_tail = ref;
-       local_tail = &ref->next;
-       return 0;
-}
-
-static void get_local_heads(void)
-{
-       local_tail = &local_refs;
-       for_each_ref(one_local_ref, NULL);
-}
-
-static int receive_status(int in)
-{
-       char line[1000];
-       int ret = 0;
-       int len = packet_read_line(in, line, sizeof(line));
-       if (len < 10 || memcmp(line, "unpack ", 7)) {
-               fprintf(stderr, "did not receive status back\n");
-               return -1;
-       }
-       if (memcmp(line, "unpack ok\n", 10)) {
-               fputs(line, stderr);
-               ret = -1;
-       }
-       while (1) {
-               len = packet_read_line(in, line, sizeof(line));
-               if (!len)
-                       break;
-               if (len < 3 ||
-                   (memcmp(line, "ok", 2) && memcmp(line, "ng", 2))) {
-                       fprintf(stderr, "protocol error: %s\n", line);
-                       ret = -1;
-                       break;
-               }
-               if (!memcmp(line, "ok", 2))
-                       continue;
-               fputs(line, stderr);
-               ret = -1;
-       }
-       return ret;
-}
-
-static void update_tracking_ref(struct remote *remote, struct ref *ref)
-{
-       struct refspec rs;
-       int will_delete_ref;
-
-       rs.src = ref->name;
-       rs.dst = NULL;
-
-       if (!ref->peer_ref)
-               return;
-
-       will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
-
-       if (!will_delete_ref &&
-                       !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1))
-               return;
-
-       if (!remote_find_tracking(remote, &rs)) {
-               fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
-               if (is_null_sha1(ref->peer_ref->new_sha1)) {
-                       if (delete_ref(rs.dst, NULL))
-                               error("Failed to delete");
-               } else
-                       update_ref("update by push", rs.dst,
-                                       ref->new_sha1, NULL, 0, 0);
-               free(rs.dst);
-       }
-}
-
-static int send_pack(int in, int out, struct remote *remote, int nr_refspec, char **refspec)
-{
-       struct ref *ref;
-       int new_refs;
-       int ret = 0;
-       int ask_for_status_report = 0;
-       int allow_deleting_refs = 0;
-       int expect_status_report = 0;
-
-       /* No funny business with the matcher */
-       remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL);
-       get_local_heads();
-
-       /* Does the other end support the reporting? */
-       if (server_supports("report-status"))
-               ask_for_status_report = 1;
-       if (server_supports("delete-refs"))
-               allow_deleting_refs = 1;
-
-       /* match them up */
-       if (!remote_tail)
-               remote_tail = &remote_refs;
-       if (match_refs(local_refs, remote_refs, &remote_tail,
-                      nr_refspec, refspec, send_all))
-               return -1;
-
-       if (!remote_refs) {
-               fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
-                       "Perhaps you should specify a branch such as 'master'.\n");
-               return 0;
-       }
-
-       /*
-        * Finally, tell the other end!
-        */
-       new_refs = 0;
-       for (ref = remote_refs; ref; ref = ref->next) {
-               char old_hex[60], *new_hex;
-               int will_delete_ref;
-
-               if (!ref->peer_ref)
-                       continue;
-
-
-               will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
-               if (will_delete_ref && !allow_deleting_refs) {
-                       error("remote does not support deleting refs");
-                       ret = -2;
-                       continue;
-               }
-               if (!will_delete_ref &&
-                   !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
-                       if (verbose)
-                               fprintf(stderr, "'%s': up-to-date\n", ref->name);
-                       continue;
-               }
-
-               /* This part determines what can overwrite what.
-                * The rules are:
-                *
-                * (0) you can always use --force or +A:B notation to
-                *     selectively force individual ref pairs.
-                *
-                * (1) if the old thing does not exist, it is OK.
-                *
-                * (2) if you do not have the old thing, you are not allowed
-                *     to overwrite it; you would not know what you are losing
-                *     otherwise.
-                *
-                * (3) if both new and old are commit-ish, and new is a
-                *     descendant of old, it is OK.
-                *
-                * (4) regardless of all of the above, removing :B is
-                *     always allowed.
-                */
-
-               if (!force_update &&
-                   !will_delete_ref &&
-                   !is_null_sha1(ref->old_sha1) &&
-                   !ref->force) {
-                       if (!has_sha1_file(ref->old_sha1) ||
-                           !ref_newer(ref->peer_ref->new_sha1,
-                                      ref->old_sha1)) {
-                               /* We do not have the remote ref, or
-                                * we know that the remote ref is not
-                                * an ancestor of what we are trying to
-                                * push.  Either way this can be losing
-                                * commits at the remote end and likely
-                                * we were not up to date to begin with.
-                                */
-                               error("remote '%s' is not an ancestor of\n"
-                                     " local  '%s'.\n"
-                                     " Maybe you are not up-to-date and "
-                                     "need to pull first?",
-                                     ref->name,
-                                     ref->peer_ref->name);
-                               ret = -2;
-                               continue;
-                       }
-               }
-               hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
-               if (!will_delete_ref)
-                       new_refs++;
-               strcpy(old_hex, sha1_to_hex(ref->old_sha1));
-               new_hex = sha1_to_hex(ref->new_sha1);
-
-               if (!dry_run) {
-                       if (ask_for_status_report) {
-                               packet_write(out, "%s %s %s%c%s",
-                                       old_hex, new_hex, ref->name, 0,
-                                       "report-status");
-                               ask_for_status_report = 0;
-                               expect_status_report = 1;
-                       }
-                       else
-                               packet_write(out, "%s %s %s",
-                                       old_hex, new_hex, ref->name);
-               }
-               if (will_delete_ref)
-                       fprintf(stderr, "deleting '%s'\n", ref->name);
-               else {
-                       fprintf(stderr, "updating '%s'", ref->name);
-                       if (strcmp(ref->name, ref->peer_ref->name))
-                               fprintf(stderr, " using '%s'",
-                                       ref->peer_ref->name);
-                       fprintf(stderr, "\n  from %s\n  to   %s\n",
-                               old_hex, new_hex);
-               }
-       }
-
-       packet_flush(out);
-       if (new_refs && !dry_run)
-               ret = pack_objects(out, remote_refs);
-       close(out);
-
-       if (expect_status_report) {
-               if (receive_status(in))
-                       ret = -4;
-       }
-
-       if (!dry_run && remote && ret == 0) {
-               for (ref = remote_refs; ref; ref = ref->next)
-                       update_tracking_ref(remote, ref);
-       }
-
-       if (!new_refs && ret == 0)
-               fprintf(stderr, "Everything up-to-date\n");
-       return ret;
-}
-
-static void verify_remote_names(int nr_heads, char **heads)
-{
-       int i;
-
-       for (i = 0; i < nr_heads; i++) {
-               const char *remote = strchr(heads[i], ':');
-
-               remote = remote ? (remote + 1) : heads[i];
-               switch (check_ref_format(remote)) {
-               case 0: /* ok */
-               case -2: /* ok but a single level -- that is fine for
-                         * a match pattern.
-                         */
-               case -3: /* ok but ends with a pattern-match character */
-                       continue;
-               }
-               die("remote part of refspec is not a valid name in %s",
-                   heads[i]);
-       }
-}
-
-int main(int argc, char **argv)
-{
-       int i, nr_heads = 0;
-       char *dest = NULL;
-       char **heads = NULL;
-       int fd[2], ret;
-       struct child_process *conn;
-       char *remote_name = NULL;
-       struct remote *remote = NULL;
-
-       setup_git_directory();
-       git_config(git_default_config);
-
-       argv++;
-       for (i = 1; i < argc; i++, argv++) {
-               char *arg = *argv;
-
-               if (*arg == '-') {
-                       if (!prefixcmp(arg, "--receive-pack=")) {
-                               receivepack = arg + 15;
-                               continue;
-                       }
-                       if (!prefixcmp(arg, "--exec=")) {
-                               receivepack = arg + 7;
-                               continue;
-                       }
-                       if (!prefixcmp(arg, "--remote=")) {
-                               remote_name = arg + 9;
-                               continue;
-                       }
-                       if (!strcmp(arg, "--all")) {
-                               send_all = 1;
-                               continue;
-                       }
-                       if (!strcmp(arg, "--dry-run")) {
-                               dry_run = 1;
-                               continue;
-                       }
-                       if (!strcmp(arg, "--force")) {
-                               force_update = 1;
-                               continue;
-                       }
-                       if (!strcmp(arg, "--verbose")) {
-                               verbose = 1;
-                               continue;
-                       }
-                       if (!strcmp(arg, "--thin")) {
-                               use_thin_pack = 1;
-                               continue;
-                       }
-                       usage(send_pack_usage);
-               }
-               if (!dest) {
-                       dest = arg;
-                       continue;
-               }
-               heads = argv;
-               nr_heads = argc - i;
-               break;
-       }
-       if (!dest)
-               usage(send_pack_usage);
-       if (heads && send_all)
-               usage(send_pack_usage);
-       verify_remote_names(nr_heads, heads);
-
-       if (remote_name) {
-               remote = remote_get(remote_name);
-               if (!remote_has_url(remote, dest)) {
-                       die("Destination %s is not a uri for %s",
-                           dest, remote_name);
-               }
-       }
-
-       conn = git_connect(fd, dest, receivepack, verbose ? CONNECT_VERBOSE : 0);
-       ret = send_pack(fd[0], fd[1], remote, nr_heads, heads);
-       close(fd[0]);
-       close(fd[1]);
-       ret |= finish_connect(conn);
-       return !!ret;
-}
diff --git a/send-pack.h b/send-pack.h
new file mode 100644 (file)
index 0000000..8ff1dc3
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef SEND_PACK_H
+#define SEND_PACK_H
+
+struct send_pack_args {
+       const char *receivepack;
+       unsigned verbose:1,
+               send_all:1,
+               send_mirror:1,
+               force_update:1,
+               use_thin_pack:1,
+               dry_run:1;
+};
+
+int send_pack(struct send_pack_args *args,
+             const char *dest, struct remote *remote,
+             int nr_heads, const char **heads);
+
+#endif
index 0d1312ca56d52daa3fc692984d4d3abaf3425791..a051e49a9ea2f605bdc278394de731ff4c55e627 100644 (file)
@@ -35,7 +35,7 @@ static int update_info_refs(int force)
        safe_create_leading_directories(path0);
        info_ref_fp = fopen(path1, "w");
        if (!info_ref_fp)
-               return error("unable to update %s", path0);
+               return error("unable to update %s", path1);
        for_each_ref(add_info_ref, NULL);
        fclose(info_ref_fp);
        adjust_shared_perm(path1);
diff --git a/setup.c b/setup.c
index 43cd3f94ea31a7a2a8f8a07cbb67598bb1e52c39..2c7b5cb200414a0fa3f2727c68ea60e51b39c261 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -59,7 +59,7 @@ const char *prefix_path(const char *prefix, int len, const char *path)
 const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
 {
        static char path[PATH_MAX];
-       if (!pfx || !*pfx || arg[0] == '/')
+       if (!pfx || !*pfx || is_absolute_path(arg))
                return arg;
        memcpy(path, pfx, pfx_len);
        strcpy(path + pfx_len, arg);
index f007874cbb034ec9efa7f73c42831e0037d452fa..b0c24356ae7358561300c9bb3a3f56ba502b2a3b 100644 (file)
 
 #ifdef NO_C99_FORMAT
 #define SZ_FMT "lu"
+static unsigned long sz_fmt(size_t s) { return (unsigned long)s; }
 #else
 #define SZ_FMT "zu"
+static size_t sz_fmt(size_t s) { return s; }
 #endif
 
 const unsigned char null_sha1[20];
@@ -86,7 +88,7 @@ int safe_create_leading_directories(char *path)
        char *pos = path;
        struct stat st;
 
-       if (*pos == '/')
+       if (is_absolute_path(path))
                pos++;
 
        while (pos) {
@@ -253,7 +255,7 @@ static int link_alt_odb_entry(const char * entry, int len, const char * relative
        int entlen = pfxlen + 43;
        int base_len = -1;
 
-       if (*entry != '/' && relative_base) {
+       if (!is_absolute_path(entry) && relative_base) {
                /* Relative alt-odb */
                if (base_len < 0)
                        base_len = strlen(relative_base) + 1;
@@ -262,7 +264,7 @@ static int link_alt_odb_entry(const char * entry, int len, const char * relative
        }
        ent = xmalloc(sizeof(*ent) + entlen);
 
-       if (*entry != '/' && relative_base) {
+       if (!is_absolute_path(entry) && relative_base) {
                memcpy(ent->base, relative_base, base_len - 1);
                ent->base[base_len - 1] = '/';
                memcpy(ent->base + base_len, entry, len);
@@ -333,7 +335,7 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
                while (cp < ep && *cp != sep)
                        cp++;
                if (last != cp) {
-                       if ((*last != '/') && depth) {
+                       if (!is_absolute_path(last) && depth) {
                                error("%s: ignoring relative alternate object store %s",
                                                relative_base, last);
                        } else {
@@ -423,9 +425,9 @@ void pack_report(void)
                "pack_report: getpagesize()            = %10" SZ_FMT "\n"
                "pack_report: core.packedGitWindowSize = %10" SZ_FMT "\n"
                "pack_report: core.packedGitLimit      = %10" SZ_FMT "\n",
-               (size_t) getpagesize(),
-               packed_git_window_size,
-               packed_git_limit);
+               sz_fmt(getpagesize()),
+               sz_fmt(packed_git_window_size),
+               sz_fmt(packed_git_limit));
        fprintf(stderr,
                "pack_report: pack_used_ctr            = %10u\n"
                "pack_report: pack_mmap_calls          = %10u\n"
@@ -435,7 +437,7 @@ void pack_report(void)
                pack_used_ctr,
                pack_mmap_calls,
                pack_open_windows, peak_pack_open_windows,
-               pack_mapped, peak_pack_mapped);
+               sz_fmt(pack_mapped), sz_fmt(peak_pack_mapped));
 }
 
 static int check_packed_git_idx(const char *path,  struct packed_git *p)
index 2ee5a00ea794e31946ee3228bc66ecc8e7d3b38f..657f68104d52558668119234a0637ac2bca33c0a 100755 (executable)
@@ -36,7 +36,6 @@ test_expect_failure 'rebase with git am -3 (default)' '
 '
 
 test_expect_success 'rebase --skip with am -3' '
-       git reset --hard HEAD &&
        git rebase --skip
        '
 
@@ -57,7 +56,6 @@ test_expect_success 'checkout skip-merge' 'git checkout -f skip-merge'
 test_expect_failure 'rebase with --merge' 'git rebase --merge master'
 
 test_expect_success 'rebase --skip with --merge' '
-       git reset --hard HEAD &&
        git rebase --skip
        '
 
index 245fb3babddfeaeb361d9822780915460dfe1bab..73da45f18c2c5a58828c56c561e27012aa901a9a 100755 (executable)
@@ -20,6 +20,13 @@ LF='
 '
 DQ='"'
 
+echo foo > "Name and an${HT}HT"
+test -f "Name and an${HT}HT" || {
+       # since FAT/NTFS does not allow tabs in filenames, skip this test
+       say 'Your filesystem does not allow tabs in filenames, test skipped.'
+       test_done
+}
+
 for_each_name () {
        for name in \
            Name "Name and a${LF}LF" "Name and an${HT}HT" "Name${DQ}" \
index 7d92ae3e996b31f8fa9e0b62f40a04ec1940cf70..c44b27aeb24816a346f0aa84d70546a0ffd83b2a 100755 (executable)
@@ -16,7 +16,7 @@ cat path0 >path1
 chmod +x path1
 
 test_expect_success \
-    'update-cache --add two files with and without +x.' \
+    'update-index --add two files with and without +x.' \
     'git update-index --add path0 path1'
 
 mv path0 path0-
index 063e79257a6e96d9021fa41d18a632560a272776..2fe50bc7ce050f32affa287c5c6e3ab7b1a66f45 100755 (executable)
@@ -27,7 +27,7 @@ Line 15
 '
 
 test_expect_success \
-    'update-cache --add a file.' \
+    'update-index --add a file.' \
     'git update-index --add path0'
 
 test_expect_success \
index 5836e3a899d62c3c86482ea254eb53c015a52823..1287d2ad175fffe342386eaf1c04bc8d26fd72f4 100755 (executable)
@@ -119,7 +119,7 @@ test_expect_success \
     'compare_diff_raw expected current'
 
 test_expect_success \
-    'run diff with -B' \
+    'run diff with -B -M' \
     'git diff-index -B -M "$tree" >current'
 
 # This should not mistake file0 as the copy source of new file1
index de587517f434d3801d5e0aad94d4e084cbdd5b06..90ab54f0f586c87ace077be87fba396c8f2781a0 100644 (file)
@@ -90,7 +90,7 @@ diff --git a/Documentation/git.txt b/Documentation/git.txt
 diff --git a/Makefile b/Makefile
 --- a/Makefile
 +++ b/Makefile
-@@ -30,7 +30,7 @@ PROG=   git-update-cache git-diff-files 
+@@ -30,7 +30,7 @@ PROG=   git-update-index git-diff-files
        git-checkout-cache git-diff-tree git-rev-tree git-ls-files \
        git-check-files git-ls-tree git-merge-base git-merge-cache \
        git-unpack-file git-export git-diff-cache git-convert-cache \
index cfdc80885b30a8b9b184004072886adc1f88457a..f5c7d601fc955b15d15012a5b49df83364b6ebf4 100644 (file)
@@ -9,7 +9,7 @@ diff --git a/Makefile b/Makefile
 -      git-deltafy-script
 +      git-deltafy-script git-fetch-script
  
- PROG=   git-update-cache git-diff-files git-init-db git-write-tree \
+ PROG=   git-update-index git-diff-files git-init-db git-write-tree \
        git-read-tree git-commit-tree git-cat-file git-fsck-cache \
 diff --git a/git-pull-script b/git-fetch-script
 similarity index 87%
index de11623d1babf50952e45b46015ae45bb07ac2bf..5f6ddc105950eac20bcacb7987fabff015320ee3 100644 (file)
@@ -200,7 +200,7 @@ diff a/Documentation/git.txt b/Documentation/git.txt
 diff a/Makefile b/Makefile
 --- a/Makefile
 +++ b/Makefile
-@@ -30,7 +30,7 @@ PROG=   git-update-cache git-diff-files 
+@@ -30,7 +30,7 @@ PROG=   git-update-index git-diff-files
        git-checkout-cache git-diff-tree git-rev-tree git-ls-files \
        git-check-files git-ls-tree git-merge-base git-merge-cache \
        git-unpack-file git-export git-diff-cache git-convert-cache \
index d9753637fc23822795bca72c26b71d0ba71e6552..a72729a712038dc64f55d154eb9a94713e3269c9 100644 (file)
@@ -8,7 +8,7 @@ diff a/Makefile b/Makefile
 -      git-deltafy-script
 +      git-deltafy-script git-fetch-script
  
- PROG=   git-update-cache git-diff-files git-init-db git-write-tree \
+ PROG=   git-update-index git-diff-files git-init-db git-write-tree \
        git-read-tree git-commit-tree git-cat-file git-fsck-cache \
 diff a/git-fetch-script b/git-fetch-script
 --- /dev/null
index 65571e05496eb3710cb89d93f5b95d34b77d1998..b540f7295a1bb48bf044d297201b07aca9fb5005 100755 (executable)
@@ -24,7 +24,7 @@ cat >gpatch.file <<\EOF &&
 +++ file1+     2007-02-21 01:07:44.000000000 -0800
 @@ -1 +1 @@
 -A
-+B
++B 
 EOF
 
 sed -e 's|file1|sub/&|' gpatch.file >gpatch-sub.file &&
index ba7579c2510fc4a4d2de4d79037640b5df644bd6..f1106e65422823288f25add874d366afa5ea99f1 100755 (executable)
@@ -187,49 +187,51 @@ test_expect_success \
                        test-3-${packname_3}.idx'
 
 test_expect_success \
-    'corrupt a pack and see if verify catches' \
+    'verify-pack catches mismatched .idx and .pack files' \
     'cat test-1-${packname_1}.idx >test-3.idx &&
      cat test-2-${packname_2}.pack >test-3.pack &&
      if git verify-pack test-3.idx
      then false
      else :;
-     fi &&
+     fi'
 
-     : PACK_SIGNATURE &&
-     cat test-1-${packname_1}.pack >test-3.pack &&
+test_expect_success \
+    'verify-pack catches a corrupted pack signature' \
+    'cat test-1-${packname_1}.pack >test-3.pack &&
      dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=2 &&
      if git verify-pack test-3.idx
      then false
      else :;
-     fi &&
+     fi'
 
-     : PACK_VERSION &&
-     cat test-1-${packname_1}.pack >test-3.pack &&
+test_expect_success \
+    'verify-pack catches a corrupted pack version' \
+    'cat test-1-${packname_1}.pack >test-3.pack &&
      dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=7 &&
      if git verify-pack test-3.idx
      then false
      else :;
-     fi &&
+     fi'
 
-     : TYPE/SIZE byte of the first packed object data &&
-     cat test-1-${packname_1}.pack >test-3.pack &&
+test_expect_success \
+    'verify-pack catches a corrupted type/size of the 1st packed object data' \
+    'cat test-1-${packname_1}.pack >test-3.pack &&
      dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=12 &&
      if git verify-pack test-3.idx
      then false
      else :;
-     fi &&
+     fi'
 
-     : sum of the index file itself &&
-     l=`wc -c <test-3.idx` &&
+test_expect_success \
+    'verify-pack catches a corrupted sum of the index file itself' \
+    'l=`wc -c <test-3.idx` &&
      l=`expr $l - 20` &&
      cat test-1-${packname_1}.pack >test-3.pack &&
      dd if=/dev/zero of=test-3.idx count=20 bs=1 conv=notrunc seek=$l &&
      if git verify-pack test-3.pack
      then false
      else :;
-     fi &&
-
-     :'
+     fi'
 
 test_expect_success \
     'build pack index for an existing pack' \
index 4f58c4c3f93b1629a564f8b23ad672100d64798d..2a2878b57229016ad473ccfd65ff7f609ba7d966 100755 (executable)
@@ -61,17 +61,33 @@ test_expect_success \
 
 test_expect_success \
     'index v2: force some 64-bit offsets with pack-objects' \
-    'pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list) &&
-     git verify-pack -v "test-3-${pack3}.pack"'
+    'pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list)'
+
+have_64bits=
+if msg=$(git verify-pack -v "test-3-${pack3}.pack" 2>&1) ||
+       ! echo "$msg" | grep "pack too large .* off_t"
+then
+       have_64bits=t
+else
+       say "skipping tests concerning 64-bit offsets"
+fi
+
+test "$have_64bits" &&
+test_expect_success \
+    'index v2: verify a pack with some 64-bit offsets' \
+    'git verify-pack -v "test-3-${pack3}.pack"'
 
+test "$have_64bits" &&
 test_expect_failure \
     '64-bit offsets: should be different from previous index v2 results' \
     'cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"'
 
+test "$have_64bits" &&
 test_expect_success \
     'index v2: force some 64-bit offsets with index-pack' \
     'git-index-pack --index-version=2,0x40000 -o 3.idx "test-1-${pack1}.pack"'
 
+test "$have_64bits" &&
 test_expect_success \
     '64-bit offsets: index-pack result should match pack-objects one' \
     'cmp "test-3-${pack3}.idx" "3.idx"'
@@ -116,11 +132,11 @@ test_expect_failure \
 test_expect_success \
     '[index v2] 1) stream pack to repository' \
     'rm -f .git/objects/pack/* &&
-     git-index-pack --index-version=2,0x40000 --stdin < "test-1-${pack1}.pack" &&
+     git-index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" &&
      git prune-packed &&
      git count-objects | ( read nr rest && test "$nr" -eq 1 ) &&
      cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" &&
-     cmp "test-3-${pack1}.idx"  ".git/objects/pack/pack-${pack1}.idx"'
+     cmp "test-2-${pack1}.idx"  ".git/objects/pack/pack-${pack1}.idx"'
 
 test_expect_success \
     '[index v2] 2) create a stealth corruption in a delta base reference' \
diff --git a/t/t5404-tracking-branches.sh b/t/t5404-tracking-branches.sh
new file mode 100755 (executable)
index 0000000..1493a92
--- /dev/null
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+test_description='tracking branch update checks for git push'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       echo 1 >file &&
+       git add file &&
+       git commit -m 1 &&
+       git branch b1 &&
+       git branch b2 &&
+       git clone . aa &&
+       git checkout b1 &&
+       echo b1 >>file &&
+       git commit -a -m b1 &&
+       git checkout b2 &&
+       echo b2 >>file &&
+       git commit -a -m b2
+'
+
+test_expect_success 'prepare pushable branches' '
+       cd aa &&
+       b1=$(git rev-parse origin/b1) &&
+       b2=$(git rev-parse origin/b2) &&
+       git checkout -b b1 origin/b1 &&
+       echo aa-b1 >>file &&
+       git commit -a -m aa-b1 &&
+       git checkout -b b2 origin/b2 &&
+       echo aa-b2 >>file &&
+       git commit -a -m aa-b2 &&
+       git checkout master &&
+       echo aa-master >>file &&
+       git commit -a -m aa-master
+'
+
+test_expect_success 'mixed-success push returns error' '! git push'
+
+test_expect_success 'check tracking branches updated correctly after push' '
+       test "$(git rev-parse origin/master)" = "$(git rev-parse master)"
+'
+
+test_expect_success 'check tracking branches not updated for failed refs' '
+       test "$(git rev-parse origin/b1)" = "$b1" &&
+       test "$(git rev-parse origin/b2)" = "$b2"
+'
+
+test_expect_success 'deleted branches have their tracking branches removed' '
+       git push origin :b1 &&
+       test "$(git rev-parse origin/b1)" = "origin/b1"
+'
+
+test_done
diff --git a/t/t5405-send-pack-rewind.sh b/t/t5405-send-pack-rewind.sh
new file mode 100755 (executable)
index 0000000..86abc62
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='forced push to replace commit we do not have'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+       >file1 && git add file1 && test_tick &&
+       git commit -m Initial &&
+
+       mkdir another && (
+               cd another &&
+               git init &&
+               git fetch .. master:master
+       ) &&
+
+       >file2 && git add file2 && test_tick &&
+       git commit -m Second
+
+'
+
+test_expect_success 'non forced push should die not segfault' '
+
+       (
+               cd another &&
+               git push .. master:master
+               test $? = 1
+       )
+
+'
+
+test_expect_success 'forced push should succeed' '
+
+       (
+               cd another &&
+               git push .. +master:master
+       )
+
+'
+
+test_done
diff --git a/t/t5406-remote-rejects.sh b/t/t5406-remote-rejects.sh
new file mode 100755 (executable)
index 0000000..46b2cb4
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='remote push rejects are reported by client'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       mkdir .git/hooks &&
+       (echo "#!/bin/sh" ; echo "exit 1") >.git/hooks/update &&
+       chmod +x .git/hooks/update &&
+       echo 1 >file &&
+       git add file &&
+       git commit -m 1 &&
+       git clone . child &&
+       cd child &&
+       echo 2 >file &&
+       git commit -a -m 2
+'
+
+test_expect_success 'push reports error' '! git push 2>stderr'
+
+test_expect_success 'individual ref reports error' 'grep rejected stderr'
+
+test_done
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
new file mode 100755 (executable)
index 0000000..6ec5f7c
--- /dev/null
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+test_description='git ls-remote'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+       >file &&
+       git add file &&
+       test_tick &&
+       git commit -m initial &&
+       git tag mark &&
+       git show-ref --tags -d | sed -e "s/ /   /" >expected.tag &&
+       (
+               echo "$(git rev-parse HEAD)     HEAD"
+               git show-ref -d | sed -e "s/ /  /"
+       ) >expected.all &&
+
+       git remote add self $(pwd)/.git
+
+'
+
+test_expect_success 'ls-remote --tags .git' '
+
+       git ls-remote --tags .git >actual &&
+       diff -u expected.tag actual
+
+'
+
+test_expect_success 'ls-remote .git' '
+
+       git ls-remote .git >actual &&
+       diff -u expected.all actual
+
+'
+
+test_expect_success 'ls-remote --tags self' '
+
+       git ls-remote --tags self >actual &&
+       diff -u expected.tag actual
+
+'
+
+test_expect_success 'ls-remote self' '
+
+       git ls-remote self >actual &&
+       diff -u expected.all actual
+
+'
+
+test_done
index 86f9b5346a0d7d8807d8c37c063690540873813a..987c9d21caf2a3d7653157f5e6d74f0841863c8b 100755 (executable)
@@ -247,9 +247,8 @@ test_expect_success 'push with colon-less refspec (4)' '
 test_expect_success 'push with dry-run' '
 
        mk_test heads/master &&
-       cd testrepo &&
-       old_commit=$(git show-ref -s --verify refs/heads/master) &&
-       cd .. &&
+       (cd testrepo &&
+        old_commit=$(git show-ref -s --verify refs/heads/master)) &&
        git push --dry-run testrepo &&
        check_push_result $old_commit heads/master
 '
@@ -257,28 +256,40 @@ test_expect_success 'push with dry-run' '
 test_expect_success 'push updates local refs' '
 
        rm -rf parent child &&
-       mkdir parent && cd parent && git init &&
-               echo one >foo && git add foo && git commit -m one &&
-       cd .. &&
-       git clone parent child && cd child &&
+       mkdir parent &&
+       (cd parent && git init &&
+               echo one >foo && git add foo && git commit -m one) &&
+       git clone parent child &&
+       (cd child &&
                echo two >foo && git commit -a -m two &&
                git push &&
-       test $(git rev-parse master) = $(git rev-parse remotes/origin/master)
+       test $(git rev-parse master) = $(git rev-parse remotes/origin/master))
 
 '
 
 test_expect_success 'push does not update local refs on failure' '
 
        rm -rf parent child &&
-       mkdir parent && cd parent && git init &&
+       mkdir parent &&
+       (cd parent && git init &&
                echo one >foo && git add foo && git commit -m one &&
                echo exit 1 >.git/hooks/pre-receive &&
-               chmod +x .git/hooks/pre-receive &&
-       cd .. &&
-       git clone parent child && cd child &&
-               echo two >foo && git commit -a -m two || exit 1
-               git push && exit 1
-       test $(git rev-parse master) != $(git rev-parse remotes/origin/master)
+               chmod +x .git/hooks/pre-receive) &&
+       git clone parent child &&
+       (cd child &&
+               echo two >foo && git commit -a -m two &&
+               ! git push &&
+               test $(git rev-parse master) != \
+                       $(git rev-parse remotes/origin/master))
+
+'
+
+test_expect_success 'allow deleting an invalid remote ref' '
+
+       pwd &&
+       rm -f testrepo/.git/objects/??/* &&
+       git push testrepo :refs/heads/master &&
+       (cd testrepo && ! git rev-parse --verify refs/heads/master)
 
 '
 
diff --git a/t/t5517-push-mirror.sh b/t/t5517-push-mirror.sh
new file mode 100755 (executable)
index 0000000..ed3fec1
--- /dev/null
@@ -0,0 +1,228 @@
+#!/bin/sh
+
+test_description='pushing to a mirror repository'
+
+. ./test-lib.sh
+
+D=`pwd`
+
+invert () {
+       if "$@"; then
+               return 1
+       else
+               return 0
+       fi
+}
+
+mk_repo_pair () {
+       rm -rf master mirror &&
+       mkdir mirror &&
+       (
+               cd mirror &&
+               git init
+       ) &&
+       mkdir master &&
+       (
+               cd master &&
+               git init &&
+               git config remote.up.url ../mirror
+       )
+}
+
+
+# BRANCH tests
+test_expect_success 'push mirror creates new branches' '
+
+       mk_repo_pair &&
+       (
+               cd master &&
+               echo one >foo && git add foo && git commit -m one &&
+               git push --mirror up
+       ) &&
+       master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
+       mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
+       test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror updates existing branches' '
+
+       mk_repo_pair &&
+       (
+               cd master &&
+               echo one >foo && git add foo && git commit -m one &&
+               git push --mirror up &&
+               echo two >foo && git add foo && git commit -m two &&
+               git push --mirror up
+       ) &&
+       master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
+       mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
+       test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror force updates existing branches' '
+
+       mk_repo_pair &&
+       (
+               cd master &&
+               echo one >foo && git add foo && git commit -m one &&
+               git push --mirror up &&
+               echo two >foo && git add foo && git commit -m two &&
+               git push --mirror up &&
+               git reset --hard HEAD^
+               git push --mirror up
+       ) &&
+       master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
+       mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
+       test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror removes branches' '
+
+       mk_repo_pair &&
+       (
+               cd master &&
+               echo one >foo && git add foo && git commit -m one &&
+               git branch remove master &&
+               git push --mirror up &&
+               git branch -D remove
+               git push --mirror up
+       ) &&
+       (
+               cd mirror &&
+               invert git show-ref -s --verify refs/heads/remove
+       )
+
+'
+
+test_expect_success 'push mirror adds, updates and removes branches together' '
+
+       mk_repo_pair &&
+       (
+               cd master &&
+               echo one >foo && git add foo && git commit -m one &&
+               git branch remove master &&
+               git push --mirror up &&
+               git branch -D remove &&
+               git branch add master &&
+               echo two >foo && git add foo && git commit -m two &&
+               git push --mirror up
+       ) &&
+       master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
+       master_add=$(cd master && git show-ref -s --verify refs/heads/add) &&
+       mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
+       mirror_add=$(cd mirror && git show-ref -s --verify refs/heads/add) &&
+       test "$master_master" = "$mirror_master" &&
+       test "$master_add" = "$mirror_add" &&
+       (
+               cd mirror &&
+               invert git show-ref -s --verify refs/heads/remove
+       )
+
+'
+
+
+# TAG tests
+test_expect_success 'push mirror creates new tags' '
+
+       mk_repo_pair &&
+       (
+               cd master &&
+               echo one >foo && git add foo && git commit -m one &&
+               git tag -f tmaster master &&
+               git push --mirror up
+       ) &&
+       master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
+       mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
+       test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror updates existing tags' '
+
+       mk_repo_pair &&
+       (
+               cd master &&
+               echo one >foo && git add foo && git commit -m one &&
+               git tag -f tmaster master &&
+               git push --mirror up &&
+               echo two >foo && git add foo && git commit -m two &&
+               git tag -f tmaster master &&
+               git push --mirror up
+       ) &&
+       master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
+       mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
+       test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror force updates existing tags' '
+
+       mk_repo_pair &&
+       (
+               cd master &&
+               echo one >foo && git add foo && git commit -m one &&
+               git tag -f tmaster master &&
+               git push --mirror up &&
+               echo two >foo && git add foo && git commit -m two &&
+               git tag -f tmaster master &&
+               git push --mirror up &&
+               git reset --hard HEAD^
+               git tag -f tmaster master &&
+               git push --mirror up
+       ) &&
+       master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
+       mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
+       test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror removes tags' '
+
+       mk_repo_pair &&
+       (
+               cd master &&
+               echo one >foo && git add foo && git commit -m one &&
+               git tag -f tremove master &&
+               git push --mirror up &&
+               git tag -d tremove
+               git push --mirror up
+       ) &&
+       (
+               cd mirror &&
+               invert git show-ref -s --verify refs/tags/tremove
+       )
+
+'
+
+test_expect_success 'push mirror adds, updates and removes tags together' '
+
+       mk_repo_pair &&
+       (
+               cd master &&
+               echo one >foo && git add foo && git commit -m one &&
+               git tag -f tmaster master &&
+               git tag -f tremove master &&
+               git push --mirror up &&
+               git tag -d tremove &&
+               git tag tadd master &&
+               echo two >foo && git add foo && git commit -m two &&
+               git tag -f tmaster master &&
+               git push --mirror up
+       ) &&
+       master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
+       master_add=$(cd master && git show-ref -s --verify refs/tags/tadd) &&
+       mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
+       mirror_add=$(cd mirror && git show-ref -s --verify refs/tags/tadd) &&
+       test "$master_master" = "$mirror_master" &&
+       test "$master_add" = "$mirror_add" &&
+       (
+               cd mirror &&
+               invert git show-ref -s --verify refs/tags/tremove
+       )
+
+'
+
+test_done
index 53956c08e2754eb68a2a8311b26e16f5ee891308..2ba4b00e526eb00c5d236777f896772c6cad538b 100755 (executable)
@@ -71,6 +71,43 @@ test_expect_success 'bisect start with one bad and good' '
        git bisect next
 '
 
+test_expect_success 'bisect reset: back in the master branch' '
+       git bisect reset &&
+       echo "* master" > branch.expect &&
+       git branch > branch.output &&
+       cmp branch.expect branch.output
+'
+
+test_expect_success 'bisect reset: back in another branch' '
+       git checkout -b other &&
+       git bisect start &&
+       git bisect good $HASH1 &&
+       git bisect bad $HASH3 &&
+       git bisect reset &&
+       echo "  master" > branch.expect &&
+       echo "* other" >> branch.expect &&
+       git branch > branch.output &&
+       cmp branch.expect branch.output
+'
+
+test_expect_success 'bisect reset when not bisecting' '
+       git bisect reset &&
+       git branch > branch.output &&
+       cmp branch.expect branch.output
+'
+
+test_expect_success 'bisect reset removes packed refs' '
+       git bisect reset &&
+       git bisect start &&
+       git bisect good $HASH1 &&
+       git bisect bad $HASH3 &&
+       git pack-refs --all --prune &&
+       git bisect next &&
+       git bisect reset &&
+       test -z "$(git for-each-ref "refs/bisect/*")" &&
+       test -z "$(git for-each-ref "refs/heads/bisect")"
+'
+
 # $HASH1 is good, $HASH4 is bad, we skip $HASH3
 # but $HASH2 is bad,
 # so we should find $HASH2 as the first bad commit
@@ -167,7 +204,7 @@ test_expect_success 'bisect skip: add line and then a new test' '
        git bisect skip &&
        git bisect good > my_bisect_log.txt &&
        grep "$HASH5 is first bad commit" my_bisect_log.txt &&
-       git bisect log > log_to_replay.txt
+       git bisect log > log_to_replay.txt &&
        git bisect reset
 '
 
index 2089351f7d157ce96a07ed1c6c1465fc58819ec0..5f60b22d872b5d034e137e0c6c7c5a9d664e57ee 100755 (executable)
@@ -114,7 +114,7 @@ test_expect_success 'use index-filter to move into a subdirectory' '
 
 test_expect_success 'stops when msg filter fails' '
        old=$(git rev-parse HEAD) &&
-       ! git-filter-branch -f --msg-filter false &&
+       ! git-filter-branch -f --msg-filter false HEAD &&
        test $old = $(git rev-parse HEAD) &&
        rm -rf .git-rewrite
 '
index 5f7e388d7add06b8392997037a86c4d0f8524ceb..c7130c4dcc31dda1f70160c81804096676943af4 100755 (executable)
@@ -339,20 +339,14 @@ test_expect_success \
 '
 
 test_expect_success \
-       'trying to create tags giving many -m or -F options should fail' '
+       'trying to create tags giving both -m or -F options should fail' '
        echo "message file 1" >msgfile1 &&
        echo "message file 2" >msgfile2 &&
        ! tag_exists msgtag &&
-       ! git-tag -m "message 1" -m "message 2" msgtag &&
-       ! tag_exists msgtag &&
-       ! git-tag -F msgfile1 -F msgfile2 msgtag &&
-       ! tag_exists msgtag &&
        ! git-tag -m "message 1" -F msgfile1 msgtag &&
        ! tag_exists msgtag &&
        ! git-tag -F msgfile1 -m "message 1" msgtag &&
        ! tag_exists msgtag &&
-       ! git-tag -F msgfile1 -m "message 1" -F msgfile2 msgtag &&
-       ! tag_exists msgtag &&
        ! git-tag -m "message 1" -F msgfile1 -m "message 2" msgtag &&
        ! tag_exists msgtag
 '
@@ -673,6 +667,22 @@ test_expect_success 'creating a signed tag with -F - should succeed' '
        git diff expect actual
 '
 
+cat >fakeeditor <<'EOF'
+#!/bin/sh
+test -n "$1" && exec >"$1"
+echo A signed tag message
+echo from a fake editor.
+EOF
+chmod +x fakeeditor
+get_tag_header implied-annotate $commit commit $time >expect
+./fakeeditor >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success '-s implies annotated tag' '
+       GIT_EDITOR=./fakeeditor git-tag -s implied-annotate &&
+       get_tag_msg implied-annotate >actual &&
+       git diff expect actual
+'
+
 test_expect_success \
        'trying to create a signed tag with non-existing -F file should fail' '
        ! test -f nonexistingfile &&
index 9dba104b1f8bf9125f1a947a2fd115b684a94095..31a6f63399a97902e493c453a3f3117bc44ef2f7 100755 (executable)
@@ -79,7 +79,8 @@ test_expect_success \
 
 cat >editor <<\EOF
 #!/bin/sh
-sed -i -e "s/a file/an amend commit/g" $1
+sed -e "s/a file/an amend commit/g" < $1 > $1-
+mv $1- $1
 EOF
 chmod 755 editor
 
@@ -98,7 +99,8 @@ test_expect_success \
 
 cat >editor <<\EOF
 #!/bin/sh
-sed -i -e "s/amend/older/g" $1
+sed -e "s/amend/older/g"  < $1 > $1-
+mv $1- $1
 EOF
 chmod 755 editor
 
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
new file mode 100755 (executable)
index 0000000..29fee2d
--- /dev/null
@@ -0,0 +1,122 @@
+#!/bin/sh
+
+test_description='git-cvsimport basic tests'
+. ./test-lib.sh
+
+if ! ( type cvs && type cvsps ) >/dev/null 2>&1
+then
+       test_expect_success 'skipping cvsimport tests, cvs/cvsps not found' ''
+       test_done
+       exit
+fi
+
+CVSROOT=$(pwd)/cvsroot
+export CVSROOT
+# for clean cvsps cache
+HOME=$(pwd)
+export HOME
+
+test_expect_success 'setup cvsroot' 'cvs init'
+
+test_expect_success 'setup a cvs module' '
+
+       mkdir $CVSROOT/module &&
+       cvs co -d module-cvs module &&
+       cd module-cvs &&
+       cat <<EOF >o_fortuna &&
+O Fortuna
+velut luna
+statu variabilis,
+
+semper crescis
+aut decrescis;
+vita detestabilis
+
+nunc obdurat
+et tunc curat
+ludo mentis aciem,
+
+egestatem,
+potestatem
+dissolvit ut glaciem.
+EOF
+       cvs add o_fortuna &&
+       cat <<EOF >message &&
+add "O Fortuna" lyrics
+
+These public domain lyrics make an excellent sample text.
+EOF
+       cvs commit -F message &&
+       cd ..
+'
+
+test_expect_success 'import a trivial module' '
+
+       git cvsimport -a -z 0 -C module-git module &&
+       git diff module-cvs/o_fortuna module-git/o_fortuna
+
+'
+
+test_expect_success 'pack refs' 'cd module-git && git gc && cd ..'
+
+test_expect_success 'update cvs module' '
+
+       cd module-cvs &&
+       cat <<EOF >o_fortuna &&
+O Fortune,
+like the moon
+you are changeable,
+
+ever waxing
+and waning;
+hateful life
+
+first oppresses
+and then soothes
+as fancy takes it;
+
+poverty
+and power
+it melts them like ice.
+EOF
+       cat <<EOF >message &&
+translate to English
+
+My Latin is terrible.
+EOF
+       cvs commit -F message &&
+       cd ..
+'
+
+test_expect_success 'update git module' '
+
+       cd module-git &&
+       git cvsimport -a -z 0 module &&
+       git merge origin &&
+       cd .. &&
+       git diff module-cvs/o_fortuna module-git/o_fortuna
+
+'
+
+test_expect_success 'update cvs module' '
+
+       cd module-cvs &&
+               echo 1 >tick &&
+               cvs add tick &&
+               cvs commit -m 1
+       cd ..
+
+'
+
+test_expect_success 'cvsimport.module config works' '
+
+       cd module-git &&
+               git config cvsimport.module module &&
+               git cvsimport -a -z0 &&
+               git merge origin &&
+       cd .. &&
+       git diff module-cvs/tick module-git/tick
+
+'
+
+test_done
index a19279b3e41653b519b8c3b266bc7845f3648c4b..7092bae26342d273f28d52ac330c270a22067f65 100644 (file)
@@ -13,7 +13,7 @@
 
 if git-rev-parse --verify HEAD 2>/dev/null
 then
-       git-diff-index -p -M --cached HEAD
+       git-diff-index -p -M --cached HEAD --
 else
        # NEEDSWORK: we should produce a diff with an empty tree here
        # if we want to do the same verification for the initial import.
diff --git a/trace.c b/trace.c
index 0d89dbe7794dcdfb8519e6d083f74bf792976c6a..d3d1b6d55e86aab94ddb9712f5eee743b3bfa9a8 100644 (file)
--- a/trace.c
+++ b/trace.c
@@ -37,7 +37,7 @@ static int get_trace_fd(int *need_close)
                return STDERR_FILENO;
        if (strlen(trace) == 1 && isdigit(*trace))
                return atoi(trace);
-       if (*trace == '/') {
+       if (is_absolute_path(trace)) {
                int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666);
                if (fd == -1) {
                        fprintf(stderr,
index 43b9e7c410882d82fd6b08532e30df9b82b3ffb1..50db9807d003162c81f391e08f247bac7ce410c9 100644 (file)
@@ -6,6 +6,7 @@
 #endif
 #include "pkt-line.h"
 #include "fetch-pack.h"
+#include "send-pack.h"
 #include "walker.h"
 #include "bundle.h"
 #include "dir.h"
@@ -141,7 +142,7 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list)
        }
 }
 
-static struct ref *get_refs_via_rsync(const struct transport *transport)
+static struct ref *get_refs_via_rsync(struct transport *transport)
 {
        struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
        struct ref dummy, *tail = &dummy;
@@ -283,6 +284,9 @@ static int rsync_transport_push(struct transport *transport,
        struct child_process rsync;
        const char *args[10];
 
+       if (flags & TRANSPORT_PUSH_MIRROR)
+               return error("rsync transport does not support mirror mode");
+
        /* first push the objects */
 
        strbuf_addstr(&buf, transport->url);
@@ -388,6 +392,9 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons
        int argc;
        int err;
 
+       if (flags & TRANSPORT_PUSH_MIRROR)
+               return error("http transport does not support mirror mode");
+
        argv = xmalloc((refspec_nr + 12) * sizeof(char *));
        argv[0] = "http-push";
        argc = 1;
@@ -432,7 +439,7 @@ static int missing__target(int code, int result)
 
 #define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
 
-static struct ref *get_refs_via_curl(const struct transport *transport)
+static struct ref *get_refs_via_curl(struct transport *transport)
 {
        struct buffer buffer;
        char *data, *start, *mid;
@@ -529,7 +536,7 @@ struct bundle_transport_data {
        struct bundle_header header;
 };
 
-static struct ref *get_refs_from_bundle(const struct transport *transport)
+static struct ref *get_refs_from_bundle(struct transport *transport)
 {
        struct bundle_transport_data *data = transport->data;
        struct ref *result = NULL;
@@ -601,7 +608,7 @@ static int set_git_option(struct transport *connection,
        return 1;
 }
 
-static struct ref *get_refs_via_connect(const struct transport *transport)
+static struct ref *get_refs_via_connect(struct transport *transport)
 {
        struct git_transport_data *data = transport->data;
        struct ref *refs;
@@ -654,50 +661,17 @@ static int fetch_refs_via_pack(struct transport *transport,
 static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags)
 {
        struct git_transport_data *data = transport->data;
-       const char **argv;
-       char *rem;
-       int argc;
-       int err;
+       struct send_pack_args args;
 
-       argv = xmalloc((refspec_nr + 12) * sizeof(char *));
-       argv[0] = "send-pack";
-       argc = 1;
-       if (flags & TRANSPORT_PUSH_ALL)
-               argv[argc++] = "--all";
-       if (flags & TRANSPORT_PUSH_FORCE)
-               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);
-               argv[argc++] = rp;
-       }
-       if (data->thin)
-               argv[argc++] = "--thin";
-       rem = xmalloc(strlen(transport->remote->name) + 10);
-       sprintf(rem, "--remote=%s", transport->remote->name);
-       argv[argc++] = rem;
-       argv[argc++] = transport->url;
-       while (refspec_nr--)
-               argv[argc++] = *refspec++;
-       argv[argc] = NULL;
-       err = run_command_v_opt(argv, RUN_GIT_CMD);
-       switch (err) {
-       case -ERR_RUN_COMMAND_FORK:
-               error("unable to fork for %s", argv[0]);
-       case -ERR_RUN_COMMAND_EXEC:
-               error("unable to exec %s", argv[0]);
-               break;
-       case -ERR_RUN_COMMAND_WAITPID:
-       case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
-       case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
-       case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
-               error("%s died with strange error", argv[0]);
-       }
-       return !!err;
+       args.receivepack = data->receivepack;
+       args.send_all = !!(flags & TRANSPORT_PUSH_ALL);
+       args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);
+       args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
+       args.use_thin_pack = data->thin;
+       args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE);
+       args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
+
+       return send_pack(&args, transport->url, transport->remote, refspec_nr, refspec);
 }
 
 static int disconnect_git(struct transport *transport)
@@ -789,7 +763,7 @@ int transport_push(struct transport *transport,
        return transport->push(transport, refspec_nr, refspec, flags);
 }
 
-struct ref *transport_get_remote_refs(struct transport *transport)
+const struct ref *transport_get_remote_refs(struct transport *transport)
 {
        if (!transport->remote_refs)
                transport->remote_refs = transport->get_refs_list(transport);
index 2f80ab4b03df181dc8b041b1aace5832aa84deb1..6fb4526cda5fc1ee1178fdf1d0840c90b88aa26c 100644 (file)
@@ -8,7 +8,7 @@ struct transport {
        struct remote *remote;
        const char *url;
        void *data;
-       struct ref *remote_refs;
+       const struct ref *remote_refs;
 
        /**
         * Returns 0 if successful, positive if the option is not
@@ -18,7 +18,7 @@ struct transport {
        int (*set_option)(struct transport *connection, const char *name,
                          const char *value);
 
-       struct ref *(*get_refs_list)(const struct transport *transport);
+       struct ref *(*get_refs_list)(struct transport *transport);
        int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs);
        int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags);
 
@@ -30,7 +30,8 @@ struct transport {
 #define TRANSPORT_PUSH_ALL 1
 #define TRANSPORT_PUSH_FORCE 2
 #define TRANSPORT_PUSH_DRY_RUN 4
-#define TRANSPORT_PUSH_VERBOSE 8
+#define TRANSPORT_PUSH_MIRROR 8
+#define TRANSPORT_PUSH_VERBOSE 16
 
 /* Returns a transport suitable for the url */
 struct transport *transport_get(struct remote *, const char *);
@@ -62,7 +63,7 @@ int transport_set_option(struct transport *transport, const char *name,
 int transport_push(struct transport *connection,
                   int refspec_nr, const char **refspec, int flags);
 
-struct ref *transport_get_remote_refs(struct transport *transport);
+const struct ref *transport_get_remote_refs(struct transport *transport);
 
 int transport_fetch_refs(struct transport *transport, struct ref *refs);
 void transport_unlock_pack(struct transport *transport);
index aea16adde846b404900555f5f0dd6d87daea8bbf..e9eb795d64b9cd6a6940995d41dd9c3f3239df02 100644 (file)
@@ -71,12 +71,8 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
        int remove;
        int baselen = strlen(base);
        int src_size = len + 1;
-       int i_stk = i_stk;
        int retval = 0;
 
-       if (o->dir)
-               i_stk = push_exclude_per_directory(o->dir, base, strlen(base));
-
        do {
                int i;
                const char *first;
@@ -255,8 +251,6 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
        } while (1);
 
  leave_directory:
-       if (o->dir)
-               pop_exclude_per_directory(o->dir, i_stk);
        return retval;
 }