aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Documentation/Makefile4
-rw-r--r--Documentation/asciidoc.conf5
-rw-r--r--Documentation/cvs-migration.txt19
-rw-r--r--Documentation/diff-format.txt15
-rw-r--r--Documentation/diff-options.txt19
-rw-r--r--Documentation/diffcore.txt8
-rw-r--r--Documentation/git-add.txt2
-rw-r--r--Documentation/git-apply.txt5
-rw-r--r--Documentation/git-applymbox.txt12
-rw-r--r--Documentation/git-archimport.txt23
-rw-r--r--Documentation/git-bisect.txt2
-rw-r--r--Documentation/git-branch.txt2
-rw-r--r--Documentation/git-cat-file.txt3
-rw-r--r--Documentation/git-checkout-index.txt1
-rw-r--r--Documentation/git-cherry-pick.txt3
-rw-r--r--Documentation/git-clone-pack.txt14
-rw-r--r--Documentation/git-clone.txt3
-rw-r--r--Documentation/git-commit-tree.txt3
-rw-r--r--Documentation/git-commit.txt3
-rw-r--r--Documentation/git-convert-objects.txt5
-rw-r--r--Documentation/git-cvsimport.txt5
-rw-r--r--Documentation/git-daemon.txt2
-rw-r--r--Documentation/git-diff-files.txt1
-rw-r--r--Documentation/git-diff-index.txt1
-rw-r--r--Documentation/git-diff-stages.txt1
-rw-r--r--Documentation/git-diff-tree.txt1
-rw-r--r--Documentation/git-fetch-pack.txt3
-rw-r--r--Documentation/git-fetch.txt1
-rw-r--r--Documentation/git-format-patch.txt6
-rw-r--r--Documentation/git-fsck-objects.txt11
-rw-r--r--Documentation/git-grep.txt1
-rw-r--r--Documentation/git-hash-object.txt1
-rw-r--r--Documentation/git-http-fetch.txt5
-rw-r--r--Documentation/git-index-pack.txt44
-rw-r--r--Documentation/git-init-db.txt1
-rw-r--r--Documentation/git-local-fetch.txt5
-rw-r--r--Documentation/git-log.txt3
-rw-r--r--Documentation/git-ls-files.txt2
-rw-r--r--Documentation/git-ls-remote.txt1
-rw-r--r--Documentation/git-ls-tree.txt1
-rw-r--r--Documentation/git-mailsplit.txt13
-rw-r--r--Documentation/git-merge-base.txt1
-rw-r--r--Documentation/git-merge-index.txt1
-rw-r--r--Documentation/git-merge-one-file.txt1
-rw-r--r--Documentation/git-merge.txt1
-rw-r--r--Documentation/git-mktag.txt1
-rw-r--r--Documentation/git-octopus.txt1
-rw-r--r--Documentation/git-pack-objects.txt3
-rw-r--r--Documentation/git-peek-remote.txt3
-rw-r--r--Documentation/git-prune-packed.txt1
-rw-r--r--Documentation/git-prune.txt1
-rw-r--r--Documentation/git-pull.txt1
-rw-r--r--Documentation/git-read-tree.txt1
-rw-r--r--Documentation/git-receive-pack.txt1
-rw-r--r--Documentation/git-rename.txt1
-rw-r--r--Documentation/git-repack.txt1
-rw-r--r--Documentation/git-resolve.txt3
-rw-r--r--Documentation/git-rev-list.txt1
-rw-r--r--Documentation/git-rev-parse.txt25
-rw-r--r--Documentation/git-send-email.txt1
-rw-r--r--Documentation/git-send-pack.txt1
-rw-r--r--Documentation/git-shortlog.txt3
-rw-r--r--Documentation/git-show-branch.txt27
-rw-r--r--Documentation/git-show-index.txt3
-rw-r--r--Documentation/git-ssh-fetch.txt1
-rw-r--r--Documentation/git-ssh-upload.txt1
-rw-r--r--Documentation/git-status.txt3
-rw-r--r--Documentation/git-svnimport.txt18
-rw-r--r--Documentation/git-tag.txt24
-rw-r--r--Documentation/git-tar-tree.txt1
-rw-r--r--Documentation/git-unpack-file.txt1
-rw-r--r--Documentation/git-unpack-objects.txt1
-rw-r--r--Documentation/git-update-server-info.txt1
-rw-r--r--Documentation/git-upload-pack.txt1
-rw-r--r--Documentation/git-var.txt1
-rw-r--r--Documentation/git-verify-pack.txt5
-rw-r--r--Documentation/git-whatchanged.txt3
-rw-r--r--Documentation/git-write-tree.txt1
-rw-r--r--Documentation/git.txt14
-rw-r--r--Documentation/glossary.txt2
-rw-r--r--Documentation/hooks.txt3
-rw-r--r--Documentation/pull-fetch-param.txt2
-rw-r--r--Documentation/repository-layout.txt5
-rw-r--r--Documentation/tutorial.txt98
-rw-r--r--Makefile80
-rw-r--r--apply.c78
-rw-r--r--cache.h62
-rw-r--r--check-ref-format.c17
-rw-r--r--clone-pack.c223
-rw-r--r--commit-tree.c5
-rw-r--r--commit.c1
-rw-r--r--compat/mmap.c50
-rw-r--r--config.c236
-rw-r--r--connect.c13
-rw-r--r--convert-objects.c1
-rw-r--r--ctype.c23
-rw-r--r--daemon.c2
-rw-r--r--date.c1
-rw-r--r--debian/control2
-rw-r--r--diff-files.c11
-rw-r--r--diff-index.c11
-rw-r--r--diff-tree.c2
-rw-r--r--diff.c77
-rw-r--r--diff.h2
-rw-r--r--environment.c75
-rw-r--r--fetch-pack.c2
-rw-r--r--fetch.c2
-rw-r--r--fsck-objects.c5
-rwxr-xr-xgit-am.sh341
-rwxr-xr-xgit-applymbox.sh17
-rwxr-xr-xgit-applypatch.sh73
-rwxr-xr-xgit-branch.sh63
-rwxr-xr-xgit-checkout.sh2
-rwxr-xr-xgit-commit.sh30
-rwxr-xr-xgit-cvsimport.perl7
-rwxr-xr-xgit-fetch.sh30
-rwxr-xr-xgit-findtags.perl94
-rwxr-xr-xgit-format-patch.sh169
-rwxr-xr-xgit-ls-remote.sh2
-rwxr-xr-xgit-merge-recursive.py2
-rwxr-xr-xgit-parse-remote.sh8
-rwxr-xr-xgit-rename.perl2
-rwxr-xr-xgit-repack.sh6
-rwxr-xr-xgit-sh-setup.sh2
-rwxr-xr-xgit-shortlog.perl143
-rwxr-xr-xgit-status.sh24
-rwxr-xr-xgit-svnimport.perl20
-rwxr-xr-xgit-tag.sh20
-rwxr-xr-xgit-verify-tag.sh8
-rwxr-xr-xgit.sh7
-rw-r--r--http-fetch.c1118
-rw-r--r--ident.c31
-rw-r--r--index-pack.c462
-rw-r--r--index.c10
-rw-r--r--local-fetch.c5
-rw-r--r--ls-tree.c55
-rw-r--r--mailinfo.c4
-rw-r--r--mailsplit.c172
-rw-r--r--pack-objects.c39
-rw-r--r--patch-id.c5
-rw-r--r--peek-remote.c2
-rw-r--r--prune-packed.c5
-rw-r--r--quote.c56
-rw-r--r--quote.h11
-rw-r--r--read-cache.c12
-rw-r--r--read-tree.c31
-rw-r--r--receive-pack.c4
-rw-r--r--refs.c58
-rw-r--r--rev-list.c21
-rw-r--r--rev-parse.c1
-rw-r--r--rsh.c51
-rw-r--r--send-pack.c2
-rw-r--r--server-info.c17
-rw-r--r--sha1_file.c223
-rw-r--r--sha1_name.c123
-rw-r--r--show-branch.c59
-rw-r--r--t/Makefile8
-rwxr-xr-xt/diff-lib.sh10
-rwxr-xr-xt/t0000-basic.sh2
-rw-r--r--t/t1200-tutorial.sh160
-rw-r--r--t/t3101-ls-tree-dirname.sh160
-rwxr-xr-xt/t4000-diff-format.sh3
-rwxr-xr-xt/t4001-diff-rename.sh3
-rwxr-xr-xt/t4004-diff-rename-symlink.sh3
-rwxr-xr-xt/t5000-tar-tree.sh6
-rwxr-xr-xt/t5300-pack-object.sh18
-rwxr-xr-xt/t5400-send-pack.sh3
-rwxr-xr-xt/t6001-rev-list-merge-order.sh2
-rwxr-xr-xt/t6002-rev-list-bisect.sh2
-rwxr-xr-xt/t6003-rev-list-topo-order.sh2
-rwxr-xr-xt/test-lib.sh9
-rw-r--r--templates/.gitignore1
-rw-r--r--templates/Makefile19
-rw-r--r--update-index.c132
-rw-r--r--update-ref.c1
-rw-r--r--upload-pack.c58
-rw-r--r--var.c11
-rw-r--r--verify-pack.c4
179 files changed, 4546 insertions, 1231 deletions
diff --git a/.gitignore b/.gitignore
index d190c0ad0..5ad4a2766 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
git
git-add
+git-am
git-apply
git-applymbox
git-applypatch
@@ -25,6 +26,7 @@ git-diff-stages
git-diff-tree
git-fetch
git-fetch-pack
+git-findtags
git-fmt-merge-msg
git-format-patch
git-fsck-objects
@@ -32,6 +34,7 @@ git-get-tar-commit-id
git-grep
git-hash-object
git-http-fetch
+git-index-pack
git-init-db
git-local-fetch
git-log
diff --git a/Documentation/Makefile b/Documentation/Makefile
index bb21d6af4..3cfa360a9 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -17,14 +17,14 @@ DOC_HTML += $(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES))
DOC_MAN1=$(patsubst %.txt,%.1,$(MAN1_TXT))
DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT))
-prefix=$(HOME)
+prefix?=$(HOME)
bin=$(prefix)/bin
mandir=$(prefix)/man
man1=$(mandir)/man1
man7=$(mandir)/man7
# DESTDIR=
-INSTALL=install
+INSTALL?=install
#
# Please note that there is a minor bug in asciidoc.
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
index baefb2f0d..fa0877d48 100644
--- a/Documentation/asciidoc.conf
+++ b/Documentation/asciidoc.conf
@@ -7,6 +7,9 @@
# Show GIT link as: <command>(<section>); if section is defined, else just show
# the command.
+[attributes]
+caret=^
+
ifdef::backend-docbook[]
[gitlink-inlinemacro]
{0%{target}}
@@ -19,3 +22,5 @@ ifdef::backend-xhtml11[]
[gitlink-inlinemacro]
<a href="{target}.html">{target}{0?({0})}</a>
endif::backend-xhtml11[]
+
+
diff --git a/Documentation/cvs-migration.txt b/Documentation/cvs-migration.txt
index 7c4dbef00..57436f007 100644
--- a/Documentation/cvs-migration.txt
+++ b/Documentation/cvs-migration.txt
@@ -1,6 +1,5 @@
-Git for CVS users
+git for CVS users
=================
-v0.99.5, Aug 2005
Ok, so you're a CVS user. That's ok, it's a treatable condition, and the
first step to recovery is admitting you have a problem. The fact that
@@ -8,7 +7,7 @@ you are reading this file means that you may be well on that path
already.
The thing about CVS is that it absolutely sucks as a source control
-manager, and you'll thus be happy with almost anything else. Git,
+manager, and you'll thus be happy with almost anything else. git,
however, may be a bit 'too' different (read: "good") for your taste, and
does a lot of things differently.
@@ -16,7 +15,7 @@ One particular suckage of CVS is very hard to work around: CVS is
basically a tool for tracking 'file' history, while git is a tool for
tracking 'project' history. This sometimes causes problems if you are
used to doing very strange things in CVS, in particular if you're doing
-things like making branches of just a subset of the project. Git can't
+things like making branches of just a subset of the project. git can't
track that, since git never tracks things on the level of an individual
file, only on the whole project level.
@@ -24,7 +23,7 @@ The good news is that most people don't do that, and in fact most sane
people think it's a bug in CVS that makes it tag (and check in changes)
one file at a time. So most projects you'll ever see will use CVS
'as if' it was sane. In which case you'll find it very easy indeed to
-move over to Git.
+move over to git.
First off: this is not a git tutorial. See
link:tutorial.html[Documentation/tutorial.txt] for how git
@@ -33,7 +32,7 @@ and notes on converting from CVS to git.
Second: CVS has the notion of a "repository" as opposed to the thing
that you're actually working in (your working directory, or your
-"checked out tree"). Git does not have that notion at all, and all git
+"checked out tree"). git does not have that notion at all, and all git
working directories 'are' the repositories. However, you can easily
emulate the CVS model by having one special "global repository", which
people can synchronize with. See details later, but in the meantime
@@ -50,7 +49,7 @@ gone through the git tutorial, and generally familiarized yourself with
how to commit stuff etc in git) is to create a git'ified version of your
CVS archive.
-Happily, that's very easy indeed. Git will do it for you, although git
+Happily, that's very easy indeed. git will do it for you, although git
will need the help of a program called "cvsps":
http://www.cobite.com/cvsps/
@@ -136,7 +135,7 @@ technically possible, and there are at least two specialized scripts out
there that can be used to get equivalent information (see the git
mailing list archives for details).
-Git has a couple of alternatives, though, that you may find sufficient
+git has a couple of alternatives, though, that you may find sufficient
or even superior depending on your use. One is called "git-whatchanged"
(for obvious reasons) and the other one is called "pickaxe" ("a tool for
the software archeologist").
@@ -209,7 +208,7 @@ show anything for commits that do not touch this "if" statement.
Also, in the original context, the same statement might have
appeared at first in a different file and later the file was
renamed to "a-file.c". CVS annotate would not help you to go
-back across such a rename, but GIT would still help you in such
+back across such a rename, but git would still help you in such
a situation. For that, you can give the -C flag to
git-diff-tree, like this:
@@ -229,7 +228,7 @@ does rename or copy would not show in the output, and if the
"o-file.c", it would find the commit that changed the statement
when it was in "o-file.c".
-NOTE: The current versions of "git-diff-tree -C" is not eager
+NOTE: The current version of "git-diff-tree -C" is not eager
enough to find copies, and it will miss the fact that a-file.c
was created by copying o-file.c unless o-file.c was somehow
changed in the same commit.
diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt
index 6e9fa8cdb..bfe634dcd 100644
--- a/Documentation/diff-format.txt
+++ b/Documentation/diff-format.txt
@@ -1,8 +1,8 @@
The output format from "git-diff-index", "git-diff-tree" and
"git-diff-files" are very similar.
-These commands all compare two sets of things; what are
-compared are different:
+These commands all compare two sets of things; what is
+compared differs:
git-diff-index <tree-ish>::
compares the <tree-ish> and the files on the filesystem.
@@ -46,7 +46,7 @@ That is, from the left to the right:
. path for "dst"; only exists for C or R.
. an LF or a NUL when '-z' option is used, to terminate the record.
-<sha1> is shown as all 0's if new is a file on the filesystem
+<sha1> is shown as all 0's if a file is new on the filesystem
and it is out of sync with the cache.
Example:
@@ -91,7 +91,7 @@ For a path that is added, removed, or modified,
where:
<old|new>-file:: are files GIT_EXTERNAL_DIFF can use to read the
- contents of <old|ne>,
+ contents of <old|new>,
<old|new>-hex:: are the 40-hexdigit SHA1 hashes,
<old|new>-mode:: are the octal representation of the file modes.
@@ -106,7 +106,7 @@ For a path that is unmerged, 'GIT_EXTERNAL_DIFF' is called with 1
parameter, <path>.
-Git specific extension to diff format
+git specific extension to diff format
-------------------------------------
What -p option produces is slightly different from the
@@ -121,12 +121,11 @@ The `a/` and `b/` filenames are the same unless rename/copy is
involved. Especially, even for a creation or a deletion,
`/dev/null` is _not_ used in place of `a/` or `b/` filenames.
+
-When rename/copy is involved, `file1` and `file2` shows the
+When rename/copy is involved, `file1` and `file2` show the
name of the source file of the rename/copy and the name of
the file that rename/copy produces, respectively.
-2. It is followed by extended header lines that are one or
- more of:
+2. It is followed by one or more extended header lines:
old mode <mode>
new mode <mode>
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 613a60d60..06500d04b 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -5,9 +5,8 @@
Synonym for "-p".
-r::
- Look recursively in subdirectories; this flag does not
- mean anything to commands other than "git-diff-tree";
- other diff commands always look at all the subdirectories.
+ Look recursively in subdirectories; only used by "git-diff-tree";
+ other diff commands always work recursively.
-z::
\0 line termination on output
@@ -28,26 +27,26 @@
Detect copies as well as renames.
--find-copies-harder::
- By default, -C option finds copies only if the original
- file of the copy was modified in the same changeset for
- performance reasons. This flag makes the command
+ For performance reasons, by default, -C option finds copies only
+ if the original file of the copy was modified in the same
+ changeset. This flag makes the command
inspect unmodified files as candidates for the source of
copy. This is a very expensive operation for large
projects, so use it with caution.
-l<num>::
-M and -C options require O(n^2) processing time where n
- in the number of potential rename/copy targets. This
+ is the number of potential rename/copy targets. This
option prevents rename/copy detection from running if
- the number of rename/copy targets exceed the specified
+ the number of rename/copy targets exceeds the specified
number.
-S<string>::
- Look for differences that contains the change in <string>.
+ Look for differences that contain the change in <string>.
--pickaxe-all::
When -S finds a change, show all the changes in that
- changeset, not just the files that contains the change
+ changeset, not just the files that contain the change
in <string>.
-O<orderfile>::
diff --git a/Documentation/diffcore.txt b/Documentation/diffcore.txt
index a1f03df8f..7c2168a99 100644
--- a/Documentation/diffcore.txt
+++ b/Documentation/diffcore.txt
@@ -177,7 +177,7 @@ diffcore-merge-broken
---------------------
This transformation is used to merge filepairs broken by
-diffcore-break, and were not transformed into rename/copy by
+diffcore-break, and not transformed into rename/copy by
diffcore-rename, back into a single modification. This always
runs when diffcore-break is used.
@@ -206,10 +206,10 @@ like these:
* -B/60 (the same as above, since diffcore-break defaults to 50%).
Note that earlier implementation left a broken pair as a separate
-creation and deletion patches. This was unnecessary hack and
+creation and deletion patches. This was an unnecessary hack and
the latest implementation always merges all the broken pairs
back into modifications, but the resulting patch output is
-formatted differently to still let the reviewing easier for such
+formatted differently for easier review in case of such
a complete rewrite by showing the entire contents of old version
prefixed with '-', followed by the entire contents of new
version prefixed with '+'.
@@ -250,7 +250,7 @@ pattern. Filepairs that match a glob pattern on an earlier line
in the file are output before ones that match a later line, and
filepairs that do not match any glob pattern are output last.
-As an example, typical orderfile for the core GIT probably
+As an example, typical orderfile for the core git probably
would look like this:
------------------------------------------------
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index ae1ea762f..4a03b4cfc 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -11,7 +11,7 @@ SYNOPSIS
DESCRIPTION
-----------
-A simple wrapper to git-update-index to add files to the cache for people used
+A simple wrapper for git-update-index to add files to the cache for people used
to do "cvs add".
OPTIONS
diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
index cb676348f..61d9dacc6 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -1,10 +1,9 @@
git-apply(1)
============
-v0.1, June 2005
NAME
----
-git-apply - Apply patch on a GIT index file and a work tree
+git-apply - Apply patch on a git index file and a work tree
SYNOPSIS
@@ -13,7 +12,7 @@ SYNOPSIS
DESCRIPTION
-----------
-Reads supplied diff output and applies it on a GIT index file
+Reads supplied diff output and applies it on a git index file
and a work tree.
OPTIONS
diff --git a/Documentation/git-applymbox.txt b/Documentation/git-applymbox.txt
index 5022643ad..8f01ca6a1 100644
--- a/Documentation/git-applymbox.txt
+++ b/Documentation/git-applymbox.txt
@@ -8,7 +8,7 @@ git-applymbox - Apply a series of patches in a mailbox
SYNOPSIS
--------
-'git-applymbox' [-u] [-k] [-q] ( -c .dotest/<num> | <mbox> ) [ <signoff> ]
+'git-applymbox' [-u] [-k] [-q] [-m] ( -c .dotest/<num> | <mbox> ) [ <signoff> ]
DESCRIPTION
-----------
@@ -22,7 +22,7 @@ OPTIONS
-q::
Apply patches interactively. The user will be given
opportunity to edit the log message and the patch before
- attempting to apply patch in each e-mail message.
+ attempting to apply it.
-k::
Usually the program 'cleans up' the Subject: header line
@@ -33,6 +33,14 @@ OPTIONS
munging, and is most useful when used to read back 'git
format-patch --mbox' output.
+-m::
+ Patches are applied with `git-apply` command, and unless
+ it cleanly applies without fuzz, the processing fails.
+ With this flag, if a tree that the patch applies cleanly
+ is found in a repository, the patch is applied to the
+ tree and then a 3-way merge between the resulting tree
+ and the current tree.
+
-u::
By default, the commit log message, author name and
author email are taken from the e-mail without any
diff --git a/Documentation/git-archimport.txt b/Documentation/git-archimport.txt
index 605473174..fcda0125a 100644
--- a/Documentation/git-archimport.txt
+++ b/Documentation/git-archimport.txt
@@ -3,7 +3,7 @@ git-archimport(1)
NAME
----
-git-archimport - Import an Arch repository into GIT
+git-archimport - Import an Arch repository into git
SYNOPSIS
@@ -20,31 +20,34 @@ it will just import it as a regular commit. If it can find it, it will mark it
as a merge whenever possible (see discussion below).
The script expects you to provide the key roots where it can start the import
-from an 'initial import' or 'tag' type of Arch commit. It will follow and import
-new branches within the provided roots.
+from an 'initial import' or 'tag' type of Arch commit. It will follow and
+import new branches within the provided roots.
It expects to be dealing with one project only. If it sees
-branches that have different roots, it will refuse to run. In that case, edit your
-<archive/branch> parameters to define clearly the scope of the import.
+branches that have different roots, it will refuse to run. In that case,
+edit your <archive/branch> parameters to define clearly the scope of the
+import.
-`git-archimport` uses `tla` extensively in the background to access the Arch repository.
+`git-archimport` uses `tla` extensively in the background to access the
+Arch repository.
Make sure you have a recent version of `tla` available in the path. `tla` must
know about the repositories you pass to `git-archimport`.
For the initial import `git-archimport` expects to find itself in an empty
directory. To follow the development of a project that uses Arch, rerun
-`git-archimport` with the same parameters as the initial import to perform incremental imports.
+`git-archimport` with the same parameters as the initial import to perform
+incremental imports.
MERGES
------
-Patch merge data from Arch is used to mark merges in GIT as well. GIT
+Patch merge data from Arch is used to mark merges in git as well. git
does not care much about tracking patches, and only considers a merge when a
branch incorporates all the commits since the point they forked. The end result
-is that GIT will have a good idea of how far branches have diverged. So the
+is that git will have a good idea of how far branches have diverged. So the
import process does lose some patch-trading metadata.
Fortunately, when you try and merge branches imported from Arch,
-GIT will find a good merge base, and it has a good chance of identifying
+git will find a good merge base, and it has a good chance of identifying
patches that have been traded out-of-sequence between the branches.
OPTIONS
diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt
index ede06dac3..39fa665d9 100644
--- a/Documentation/git-bisect.txt
+++ b/Documentation/git-bisect.txt
@@ -76,7 +76,7 @@ During the bisection process, you can say
to see the currently remaining suspects in `gitk`.
-The good/bad you told the command is logged, and `git bisect
+The good/bad input is logged, and `git bisect
log` shows what you have done so far. You can truncate its
output somewhere and save it in a file, and run
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index a2a0cfb7b..a7121a4c6 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -23,7 +23,7 @@ OPTIONS
The name of the branch to create.
start-point::
- Where to make the branch; defaults to HEAD.
+ Where to create the branch; defaults to HEAD.
Author
------
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index 44983b692..ab4dcae21 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -1,6 +1,5 @@
git-cat-file(1)
===============
-v0.1, May 2005
NAME
----
@@ -32,7 +31,7 @@ OPTIONS
<type>::
Typically this matches the real type of <object> but asking
- for a type that can trivially dereferenced from the given
+ for a type that can trivially be dereferenced from the given
<object> is also permitted. An example is to ask for a
"tree" with <object> being a commit object that contains it,
or to ask for a "blob" with <object> being a tag object that
diff --git a/Documentation/git-checkout-index.txt b/Documentation/git-checkout-index.txt
index 1ba6fb2d9..613dfabe7 100644
--- a/Documentation/git-checkout-index.txt
+++ b/Documentation/git-checkout-index.txt
@@ -1,6 +1,5 @@
git-checkout-index(1)
=====================
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index cd5b97d62..26e046779 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -1,6 +1,5 @@
git-cherry-pick(1)
==================
-v0.99.5 Aug 2005
NAME
----
@@ -22,7 +21,7 @@ OPTIONS
Commit to cherry-pick.
-r::
- Usuall the command appends which commit was
+ Usually the command appends which commit was
cherry-picked after the original commit message when
making a commit. This option, '--replay', causes it to
use the original commit message intact. This is useful
diff --git a/Documentation/git-clone-pack.txt b/Documentation/git-clone-pack.txt
index 83d17a055..b58165a5f 100644
--- a/Documentation/git-clone-pack.txt
+++ b/Documentation/git-clone-pack.txt
@@ -1,6 +1,5 @@
git-clone-pack(1)
=================
-v0.1, July 2005
NAME
----
@@ -9,7 +8,7 @@ git-clone-pack - Clones a repository by receiving packed objects.
SYNOPSIS
--------
-'git-clone-pack' [-q] [--exec=<git-upload-pack>] [<host>:]<directory> [<head>...]
+'git-clone-pack' [-q] [--keep] [--exec=<git-upload-pack>] [<host>:]<directory> [<head>...]
DESCRIPTION
-----------
@@ -24,12 +23,17 @@ OPTIONS
Pass '-q' flag to 'git-unpack-objects'; this makes the
cloning process less verbose.
+--keep::
+ Do not invoke 'git-unpack-objects' on received data, but
+ create a single packfile out of it instead, and store it
+ in the object database.
+
--exec=<git-upload-pack>::
Use this to specify the path to 'git-upload-pack' on the
- remote side, if is not found on your $PATH.
- Installations of sshd ignores the user's environment
+ remote side, if it is not found on your $PATH.
+ Installations of sshd ignore the user's environment
setup scripts for login shells (e.g. .bash_profile) and
- your privately installed GIT may not be found on the system
+ your privately installed git may not be found on the system
default $PATH. Another workaround suggested is to set
up your $PATH in ".bashrc", but this flag is for people
who do not want to pay the overhead for non-interactive
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 7d713c738..dd92cdefc 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -1,6 +1,5 @@
git-clone(1)
============
-v0.1, July 2005
NAME
----
@@ -9,7 +8,7 @@ git-clone - Clones a repository.
SYNOPSIS
--------
-'git clone' [-l [-s]] [-q] [-n] [-u <upload-pack>] <repository> <directory>
+'git-clone' [-l [-s]] [-q] [-n] [-u <upload-pack>] <repository> <directory>
DESCRIPTION
-----------
diff --git a/Documentation/git-commit-tree.txt b/Documentation/git-commit-tree.txt
index 895f73364..d9b0ab071 100644
--- a/Documentation/git-commit-tree.txt
+++ b/Documentation/git-commit-tree.txt
@@ -1,6 +1,5 @@
git-commit-tree(1)
==================
-v0.1, May 2005
NAME
----
@@ -36,7 +35,7 @@ OPTIONS
An existing tree object
-p <parent commit>::
- Each '-p' indicates a the id of a parent commit object.
+ Each '-p' indicates the id of a parent commit object.
Commit Information
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 790a8eb0d..1edc278c6 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -1,6 +1,5 @@
git-commit(1)
=============
-v0.99.4, Aug 2005
NAME
----
@@ -8,7 +7,7 @@ git-commit - Record your changes
SYNOPSIS
--------
-'git commit' [-a] [-s] [-v] [(-c | -C) <commit> | -F <file> | -m <msg>] [-e] <file>...
+'git-commit' [-a] [-s] [-v] [(-c | -C) <commit> | -F <file> | -m <msg>] [-e] <file>...
DESCRIPTION
-----------
diff --git a/Documentation/git-convert-objects.txt b/Documentation/git-convert-objects.txt
index 6ce62dc67..b1220c06e 100644
--- a/Documentation/git-convert-objects.txt
+++ b/Documentation/git-convert-objects.txt
@@ -1,10 +1,9 @@
git-convert-objects(1)
======================
-v0.1, May 2005
NAME
----
-git-convert-objects - Converts old-style GIT repository
+git-convert-objects - Converts old-style git repository
SYNOPSIS
@@ -13,7 +12,7 @@ SYNOPSIS
DESCRIPTION
-----------
-Converts old-style GIT repository to the latest format
+Converts old-style git repository to the latest format
Author
diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt
index b5753a46d..f5248c91c 100644
--- a/Documentation/git-cvsimport.txt
+++ b/Documentation/git-cvsimport.txt
@@ -1,6 +1,5 @@
git-cvsimport(1)
================
-v0.1, July 2005
NAME
----
@@ -11,7 +10,7 @@ SYNOPSIS
--------
'git-cvsimport' [ -o <branch-for-HEAD> ] [ -h ] [ -v ]
[ -d <CVSROOT> ] [ -p <options-for-cvsps> ]
- [ -C <GIT_repository> ] [ -i ] [ -k ]
+ [ -C <git_repository> ] [ -i ] [ -k ]
[ -s <subst> ] [ -m ] [ -M regex ] [ <CVS_module> ]
@@ -31,7 +30,7 @@ OPTIONS
are supported.
-C <target-dir>::
- The GIT repository to import to. If the directory doesn't
+ The git repository to import to. If the directory doesn't
exist, it will be created. Default is the current directory.
-i::
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index 065f2aa72..a013a58a7 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -3,7 +3,7 @@ git-daemon(1)
NAME
----
-git-daemon - A really simple server for GIT repositories.
+git-daemon - A really simple server for git repositories.
SYNOPSIS
--------
diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt
index eb9fb7425..e3873888f 100644
--- a/Documentation/git-diff-files.txt
+++ b/Documentation/git-diff-files.txt
@@ -1,6 +1,5 @@
git-diff-files(1)
=================
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git-diff-index.txt b/Documentation/git-diff-index.txt
index 8b6a953c0..2fc3eed71 100644
--- a/Documentation/git-diff-index.txt
+++ b/Documentation/git-diff-index.txt
@@ -1,6 +1,5 @@
git-diff-index(1)
=================
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git-diff-stages.txt b/Documentation/git-diff-stages.txt
index 276d7bdc4..28c60fc7e 100644
--- a/Documentation/git-diff-stages.txt
+++ b/Documentation/git-diff-stages.txt
@@ -1,6 +1,5 @@
git-diff-stages(1)
==================
-v0.1, June 2005
NAME
----
diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt
index 339a92287..dcfb9e18c 100644
--- a/Documentation/git-diff-tree.txt
+++ b/Documentation/git-diff-tree.txt
@@ -1,6 +1,5 @@
git-diff-tree(1)
================
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt
index 1d281820c..ea6faab05 100644
--- a/Documentation/git-fetch-pack.txt
+++ b/Documentation/git-fetch-pack.txt
@@ -1,6 +1,5 @@
git-fetch-pack(1)
=================
-v0.1, July 2005
NAME
----
@@ -35,7 +34,7 @@ OPTIONS
remote side, if is not found on your $PATH.
Installations of sshd ignores the user's environment
setup scripts for login shells (e.g. .bash_profile) and
- your privately installed GIT may not be found on the system
+ your privately installed git may not be found on the system
default $PATH. Another workaround suggested is to set
up your $PATH in ".bashrc", but this flag is for people
who do not want to pay the overhead for non-interactive
diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt
index 1fa9f4dbf..c0b5aac5f 100644
--- a/Documentation/git-fetch.txt
+++ b/Documentation/git-fetch.txt
@@ -1,6 +1,5 @@
git-fetch(1)
============
-v0.99.5, Aug 2005
NAME
----
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index a1483ffd0..f3ef4c1e0 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -8,7 +8,7 @@ git-format-patch - Prepare patches for e-mail submission.
SYNOPSIS
--------
-'git-format-patch' [-n][-o <dir>][-k][--mbox][--diff-options] <his> [<mine>]
+'git-format-patch' [-n][-o <dir>|--stdout][-k][--mbox][--diff-options] <his> [<mine>]
DESCRIPTION
-----------
@@ -54,6 +54,10 @@ OPTIONS
concatenated together and fed to `git-applymbox`.
Implies --author and --date.
+--stdout::
+ This flag generates the mbox formatted output to the
+ standard output, instead of saving them into a file per
+ patch and implies --mbox.
Author
------
diff --git a/Documentation/git-fsck-objects.txt b/Documentation/git-fsck-objects.txt
index ba251a515..5dc9dbdd7 100644
--- a/Documentation/git-fsck-objects.txt
+++ b/Documentation/git-fsck-objects.txt
@@ -1,6 +1,5 @@
git-fsck-objects(1)
===================
-v0.1, May 2005
NAME
----
@@ -42,22 +41,22 @@ index file and all SHA1 references in .git/refs/* as heads.
($GIT_DIR/objects), making sure that it is consistent and
complete without referring to objects found in alternate
object pools listed in GIT_ALTERNATE_OBJECT_DIRECTORIES,
- nor packed GIT archives found in $GIT_DIR/objects/pack;
+ nor packed git archives found in $GIT_DIR/objects/pack;
cannot be used with --full.
--full::
Check not just objects in GIT_OBJECT_DIRECTORY
($GIT_DIR/objects), but also the ones found in alternate
object pools listed in GIT_ALTERNATE_OBJECT_DIRECTORIES,
- and in packed GIT archives found in $GIT_DIR/objects/pack
+ and in packed git archives found in $GIT_DIR/objects/pack
and corresponding pack subdirectories in alternate
object pools; cannot be used with --standalone.
--strict::
Enable more strict checking, namely to catch a file mode
recorded with g+w bit set, which was created by older
- versions of GIT. Existing repositories, including the
- Linux kernel, GIT itself, and sparse repository have old
+ versions of git. Existing repositories, including the
+ Linux kernel, git itself, and sparse repository have old
objects that triggers this check, but it is recommended
to check new projects with this flag.
@@ -81,7 +80,7 @@ Any corrupt objects you will have to find in backups or other archives
the hopes that somebody else has the object you have corrupted).
Of course, "valid tree" doesn't mean that it wasn't generated by some
-evil person, and the end result might be crap. Git is a revision
+evil person, and the end result might be crap. git is a revision
tracking system, not a quality assurance system ;)
Extracted Diagnostics
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index 5f082167c..017579348 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -1,6 +1,5 @@
git-grep(1)
===========
-v0.99.6, Sep 2005
NAME
----
diff --git a/Documentation/git-hash-object.txt b/Documentation/git-hash-object.txt
index 935cc66b3..9239f1113 100644
--- a/Documentation/git-hash-object.txt
+++ b/Documentation/git-hash-object.txt
@@ -1,6 +1,5 @@
git-hash-object(1)
==================
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git-http-fetch.txt b/Documentation/git-http-fetch.txt
index c03427256..088624f6c 100644
--- a/Documentation/git-http-fetch.txt
+++ b/Documentation/git-http-fetch.txt
@@ -1,10 +1,9 @@
git-http-fetch(1)
=================
-v0.1, May 2005
NAME
----
-git-http-fetch - Downloads a remote GIT repository via HTTP
+git-http-fetch - Downloads a remote git repository via HTTP
SYNOPSIS
@@ -13,7 +12,7 @@ SYNOPSIS
DESCRIPTION
-----------
-Downloads a remote GIT repository via HTTP.
+Downloads a remote git repository via HTTP.
-c::
Get the commit objects.
diff --git a/Documentation/git-index-pack.txt b/Documentation/git-index-pack.txt
new file mode 100644
index 000000000..71ce55727
--- /dev/null
+++ b/Documentation/git-index-pack.txt
@@ -0,0 +1,44 @@
+git-index-pack(1)
+=================
+
+NAME
+----
+git-index-pack - Build pack index file for an existing packed archive
+
+
+SYNOPSIS
+--------
+'git-index-pack' [-o <index-file>] <pack-file>
+
+
+DESCRIPTION
+-----------
+Reads a packed archive (.pack) from the specified file, and
+builds a pack index file (.idx) for it. The packed archive
+together with the pack index can then be placed in the
+objects/pack/ directory of a git repository.
+
+
+OPTIONS
+-------
+-o <index-file>::
+ Write the generated pack index into the specified
+ file. Without this option the name of pack index
+ file is constructed from the name of packed archive
+ file by replacing .pack with .idx (and the program
+ fails if the name of packed archive does not end
+ with .pack).
+
+
+Author
+------
+Written by Sergey Vlasov <vsu@altlinux.ru>
+
+Documentation
+-------------
+Documentation by Sergey Vlasov
+
+GIT
+---
+Part of the gitlink:git[7] suite
+
diff --git a/Documentation/git-init-db.txt b/Documentation/git-init-db.txt
index ef2d04a61..ef1826ae6 100644
--- a/Documentation/git-init-db.txt
+++ b/Documentation/git-init-db.txt
@@ -1,6 +1,5 @@
git-init-db(1)
==============
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git-local-fetch.txt b/Documentation/git-local-fetch.txt
index ccf973592..87abec1c4 100644
--- a/Documentation/git-local-fetch.txt
+++ b/Documentation/git-local-fetch.txt
@@ -1,10 +1,9 @@
git-local-fetch(1)
==================
-v0.1, May 2005
NAME
----
-git-local-fetch - Duplicates another GIT repository on a local system
+git-local-fetch - Duplicates another git repository on a local system
SYNOPSIS
@@ -13,7 +12,7 @@ SYNOPSIS
DESCRIPTION
-----------
-Duplicates another GIT repository on a local system.
+Duplicates another git repository on a local system.
OPTIONS
-------
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 2a0e5acea..13a399830 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -1,6 +1,5 @@
git-log(1)
==========
-v0.99.4, Aug 2005
NAME
----
@@ -9,7 +8,7 @@ git-log - Show commit logs
SYNOPSIS
--------
-'git log' <option>...
+'git-log' <option>...
DESCRIPTION
-----------
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 940285f8c..f4501d62c 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -50,7 +50,7 @@ OPTIONS
-k|--killed::
Show files on the filesystem that need to be removed due
- to file/directory conflicts for checkout-cache to
+ to file/directory conflicts for checkout-index to
succeed.
-z::
diff --git a/Documentation/git-ls-remote.txt b/Documentation/git-ls-remote.txt
index 89bd609d6..c0a80d408 100644
--- a/Documentation/git-ls-remote.txt
+++ b/Documentation/git-ls-remote.txt
@@ -1,6 +1,5 @@
git-ls-remote(1)
================
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt
index 0d159fd5c..cdc5654a9 100644
--- a/Documentation/git-ls-tree.txt
+++ b/Documentation/git-ls-tree.txt
@@ -1,6 +1,5 @@
git-ls-tree(1)
==============
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git-mailsplit.txt b/Documentation/git-mailsplit.txt
index 557d2e905..03a947766 100644
--- a/Documentation/git-mailsplit.txt
+++ b/Documentation/git-mailsplit.txt
@@ -7,7 +7,7 @@ git-mailsplit - Totally braindamaged mbox splitter program.
SYNOPSIS
--------
-'git-mailsplit' <mbox> <directory>
+'git-mailsplit' [-d<prec>] [<mbox>] <directory>
DESCRIPTION
-----------
@@ -17,14 +17,23 @@ directory so you can process them further from there.
OPTIONS
-------
<mbox>::
- Mbox file to split.
+ Mbox file to split. If not given, the mbox is read from
+ the standard input.
<directory>::
Directory in which to place the individual messages.
+-d<prec>::
+ Instead of the default 4 digits with leading zeros,
+ different precision can be specified for the generated
+ filenames.
+
+
Author
------
Written by Linus Torvalds <torvalds@osdl.org>
+and Junio C Hamano <junkio@cox.net>
+
Documentation
--------------
diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt
index e4692163e..d1d56f194 100644
--- a/Documentation/git-merge-base.txt
+++ b/Documentation/git-merge-base.txt
@@ -1,6 +1,5 @@
git-merge-base(1)
=================
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git-merge-index.txt b/Documentation/git-merge-index.txt
index 5caee90ad..d072fdaa4 100644
--- a/Documentation/git-merge-index.txt
+++ b/Documentation/git-merge-index.txt
@@ -1,6 +1,5 @@
git-merge-index(1)
==================
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git-merge-one-file.txt b/Documentation/git-merge-one-file.txt
index 712739777..86aad37c6 100644
--- a/Documentation/git-merge-one-file.txt
+++ b/Documentation/git-merge-one-file.txt
@@ -1,6 +1,5 @@
git-merge-one-file(1)
=====================
-v0.99.4, Aug 2005
NAME
----
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index dca363dd5..a007a8b15 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -1,6 +1,5 @@
git-merge(1)
============
-v0.99.6, Sep 2005
NAME
----
diff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt
index 44ed4b540..2860a3d1b 100644
--- a/Documentation/git-mktag.txt
+++ b/Documentation/git-mktag.txt
@@ -1,6 +1,5 @@
git-mktag(1)
============
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git-octopus.txt b/Documentation/git-octopus.txt
index 881c317ac..6e32ea347 100644
--- a/Documentation/git-octopus.txt
+++ b/Documentation/git-octopus.txt
@@ -1,6 +1,5 @@
git-octopus(1)
==============
-v0.99.5, Aug 2005
NAME
----
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index 44aba940d..caf5d0d1e 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -1,6 +1,5 @@
git-pack-objects(1)
===================
-v0.1, July 2005
NAME
----
@@ -31,7 +30,7 @@ transport by their peers.
Placing both in the pack/ subdirectory of $GIT_OBJECT_DIRECTORY (or
any of the directories on $GIT_ALTERNATE_OBJECT_DIRECTORIES)
-enables GIT to read from such an archive.
+enables git to read from such an archive.
OPTIONS
diff --git a/Documentation/git-peek-remote.txt b/Documentation/git-peek-remote.txt
index c1527f1bc..915d3f8a0 100644
--- a/Documentation/git-peek-remote.txt
+++ b/Documentation/git-peek-remote.txt
@@ -1,6 +1,5 @@
git-peek-remote(1)
==================
-v0.1, July 2005
NAME
----
@@ -23,7 +22,7 @@ OPTIONS
remote side, if it is not found on your $PATH. Some
installations of sshd ignores the user's environment
setup scripts for login shells (e.g. .bash_profile) and
- your privately installed GIT may not be found on the system
+ your privately installed git may not be found on the system
default $PATH. Another workaround suggested is to set
up your $PATH in ".bashrc", but this flag is for people
who do not want to pay the overhead for non-interactive
diff --git a/Documentation/git-prune-packed.txt b/Documentation/git-prune-packed.txt
index 5c48a0937..28a1500d3 100644
--- a/Documentation/git-prune-packed.txt
+++ b/Documentation/git-prune-packed.txt
@@ -1,6 +1,5 @@
git-prune-packed(1)
=====================
-v0.1, August 2005
NAME
----
diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt
index d1676cbfc..3367c9b21 100644
--- a/Documentation/git-prune.txt
+++ b/Documentation/git-prune.txt
@@ -1,6 +1,5 @@
git-prune(1)
============
-v0.99.5, Aug 2005
NAME
----
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index c35d2eb54..952779292 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -1,6 +1,5 @@
git-pull(1)
===========
-v0.99.4, Aug 2005
NAME
----
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index 5653bacca..7db5fb579 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -1,6 +1,5 @@
git-read-tree(1)
================
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt
index fb4b76b6c..8afde1437 100644
--- a/Documentation/git-receive-pack.txt
+++ b/Documentation/git-receive-pack.txt
@@ -1,6 +1,5 @@
git-receive-pack(1)
===================
-v0.1, July 2005
NAME
----
diff --git a/Documentation/git-rename.txt b/Documentation/git-rename.txt
index 21928dc07..583cb0315 100644
--- a/Documentation/git-rename.txt
+++ b/Documentation/git-rename.txt
@@ -1,6 +1,5 @@
git-rename(1)
=============
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index bd830ada2..0c1ae49ed 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -1,6 +1,5 @@
git-repack(1)
=============
-v0.99.5, August 2005
NAME
----
diff --git a/Documentation/git-resolve.txt b/Documentation/git-resolve.txt
index 7d3eb7903..4e57c2b28 100644
--- a/Documentation/git-resolve.txt
+++ b/Documentation/git-resolve.txt
@@ -1,6 +1,5 @@
git-resolve(1)
==============
-v0.99.5, Aug 2005
NAME
----
@@ -9,7 +8,7 @@ git-resolve - Merge two commits
SYNOPSIS
--------
-'git resolve' <current> <merged> <message>
+'git-resolve' <current> <merged> <message>
DESCRIPTION
-----------
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index f386a3a79..7dbb7c660 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -1,6 +1,5 @@
git-rev-list(1)
===============
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index e2d94ff33..694f3a09d 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -54,13 +54,13 @@ OPTIONS
`git-diff-\*`).
--not::
- When showing object names, prefix them with '^' and
- strip '^' prefix from the object names that already have
+ When showing object names, prefix them with '{caret}' and
+ strip '{caret}' prefix from the object names that already have
one.
--symbolic::
Usually the object names are output in SHA1 form (with
- possible '^' prefix); this option makes them output in a
+ possible '{caret}' prefix); this option makes them output in a
form as close to the original input as possible.
@@ -91,24 +91,25 @@ what is called an 'extended SHA1' syntax.
* A symbolic ref name. E.g. 'master' typically means the commit
object referenced by $GIT_DIR/refs/heads/master. If you
happen to have both heads/master and tags/master, you can
- explicitly say 'heads/master' to tell GIT which one you mean.
+ explicitly say 'heads/master' to tell git which one you mean.
-* A suffix '^' to a revision parameter means the first parent of
- that commit object. '^<n>' means the <n>th parent (i.e.
- 'rev^'
- is equivalent to 'rev^1'). As a special rule,
- 'rev^0' means the commit itself and is used when 'rev' is the
+* A suffix '{caret}' to a revision parameter means the first parent of
+ that commit object. '{caret}<n>' means the <n>th parent (i.e.
+ 'rev{caret}'
+ is equivalent to 'rev{caret}1'). As a special rule,
+ 'rev{caret}0' means the commit itself and is used when 'rev' is the
object name of a tag object that refers to a commit object.
* A suffix '~<n>' to a revision parameter means the commit
object that is the <n>th generation grand-parent of the named
commit object, following only the first parent. I.e. rev~3 is
- equivalent to rev^^^ which is equivalent to rev^1^1^1.
+ equivalent to rev{caret}{caret}{caret} which is equivalent to\
+ rev{caret}1{caret}1{caret}1.
-'git-rev-parse' also accepts a prefix '^' to revision parameter,
+'git-rev-parse' also accepts a prefix '{caret}' to revision parameter,
which is passed to 'git-rev-list'. Two revision parameters
concatenated with '..' is a short-hand for writing a range
-between them. I.e. 'r1..r2' is equivalent to saying '^r1 r2'
+between them. I.e. 'r1..r2' is equivalent to saying '{caret}r1 r2'
Author
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index b95e33db5..b9bec55e5 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -1,6 +1,5 @@
git-send-email(1)
=================
-v0.1, July 2005
NAME
----
diff --git a/Documentation/git-send-pack.txt b/Documentation/git-send-pack.txt
index 219dfc2ef..577f06a21 100644
--- a/Documentation/git-send-pack.txt
+++ b/Documentation/git-send-pack.txt
@@ -1,6 +1,5 @@
git-send-pack(1)
================
-v0.1, July 2005
NAME
----
diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt
index a852e9b86..65ca77fbf 100644
--- a/Documentation/git-shortlog.txt
+++ b/Documentation/git-shortlog.txt
@@ -1,6 +1,5 @@
git-shortlog(1)
===============
-v0.99.4, Aug 2005
NAME
----
@@ -9,7 +8,7 @@ git-shortlog - Summarize 'git log' output.
SYNOPSIS
--------
-'git log --pretty=short | git shortlog'
+'git-log --pretty=short | git shortlog'
DESCRIPTION
-----------
diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt
index 5b80d5aa2..c6c97b21c 100644
--- a/Documentation/git-show-branch.txt
+++ b/Documentation/git-show-branch.txt
@@ -1,6 +1,5 @@
git-show-branch(1)
==================
-v0.99.5, Aug 2005
NAME
----
@@ -8,7 +7,7 @@ git-show-branch - Show branches and their commits.
SYNOPSIS
--------
-'git show-branch [--all] [--heads] [--tags] [--more=<n> | --list | --independent | --merge-base] <reference>...'
+'git-show-branch [--all] [--heads] [--tags] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] <reference>...'
DESCRIPTION
-----------
@@ -45,6 +44,15 @@ OPTIONS
Among the <reference>s given, display only the ones that
cannot be reached from any other <reference>.
+--no-name::
+ Do not show naming strings for each commit.
+
+--sha1-name::
+ Instead of naming the commits using the path to reach
+ them from heads (e.g. "master~2" to mean the grandparent
+ of "master"), name them with the unique prefix of their
+ object names.
+
Note that --more, --list, --independent and --merge-base options
are mutually exclusive.
@@ -89,21 +97,6 @@ whose commit message is "Add 'git show-branch'. "fixes" branch
adds one commit 'Introduce "reset type"'. "mhf" branch has many
other commits.
-When only one head is given, the output format changes slightly
-to conserve space. The '+' sign to show which commit is
-reachable from which head and the first N lines to show the list
-of heads being displayed are both meaningless so they are
-omitted. Also the label given to each commit does not repeat
-the name of the branch because it is obvious.
-
-------------------------------------------------
-$ git show-branch --more=4 master
-[master] Add 'git show-branch'.
-[~1] Add a new extended SHA1 syntax <name>~<num>
-[~2] Fix "git-diff A B"
-[~3] git-ls-files: generalized pathspecs
-[~4] Make "git-ls-files" work in subdirectories
-------------------------------------------------
Author
------
diff --git a/Documentation/git-show-index.txt b/Documentation/git-show-index.txt
index 72720ada2..be09b62be 100644
--- a/Documentation/git-show-index.txt
+++ b/Documentation/git-show-index.txt
@@ -1,6 +1,5 @@
git-show-index(1)
=================
-v0.1, July 2005
NAME
----
@@ -14,7 +13,7 @@ SYNOPSIS
DESCRIPTION
-----------
-Reads given idx file for packed GIT archive created with
+Reads given idx file for packed git archive created with
git-pack-objects command, and dumps its contents.
The information it outputs is subset of what you can get from
diff --git a/Documentation/git-ssh-fetch.txt b/Documentation/git-ssh-fetch.txt
index e3887ace0..b7116b30e 100644
--- a/Documentation/git-ssh-fetch.txt
+++ b/Documentation/git-ssh-fetch.txt
@@ -1,6 +1,5 @@
git-ssh-fetch(1)
================
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git-ssh-upload.txt b/Documentation/git-ssh-upload.txt
index b62501981..702674e45 100644
--- a/Documentation/git-ssh-upload.txt
+++ b/Documentation/git-ssh-upload.txt
@@ -1,6 +1,5 @@
git-ssh-upload(1)
=================
-v0.1, Jun 2005
NAME
----
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index 6d49a5aa0..753fc0866 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -1,6 +1,5 @@
git-status(1)
=============
-v0.99.4, Aug 2005
NAME
----
@@ -9,7 +8,7 @@ git-status - Show working tree status.
SYNOPSIS
--------
-'git status'
+'git-status'
DESCRIPTION
-----------
diff --git a/Documentation/git-svnimport.txt b/Documentation/git-svnimport.txt
index a27a83596..88bdc08eb 100644
--- a/Documentation/git-svnimport.txt
+++ b/Documentation/git-svnimport.txt
@@ -29,7 +29,7 @@ directories for branches, and "/tags/FOO" directories for tags.
Other subdirectories are ignored.
git-svnimport creates a file ".git/svn2git", which is required for
-incremental SVN imports.
+incremental SVN imports.
OPTIONS
-------
@@ -49,27 +49,27 @@ When importing incementally, you might need to edit the .git/svn2git file.
-t <trunk_subdir>::
Name the SVN trunk. Default "trunk".
-
+
-T <tag_subdir>::
Name the SVN subdirectory for tags. Default "tags".
-
+
-b <branch_subdir>::
Name the SVN subdirectory for branches. Default "branches".
-
+
-o <branch-for-HEAD>::
The 'trunk' branch from SVN is imported to the 'origin' branch within
the git repository. Use this option if you want to import into a
different branch.
--m::
+-m::
Attempt to detect merges based on the commit message. This option
- will enable default regexes that try to capture the name source
- branch name from the commit message.
+ will enable default regexes that try to capture the name source
+ branch name from the commit message.
-M <regex>::
Attempt to detect merges based on the commit message with a custom
- regex. It can be used with -m to also see the default regexes.
- You must escape forward slashes.
+ regex. It can be used with -m to also see the default regexes.
+ You must escape forward slashes.
-l <max_num_changes>::
Limit the number of SVN changesets we pull before quitting.
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 1e0d4f5f4..3984812ce 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -1,28 +1,34 @@
git-tag(1)
==========
-v0.99.4, Aug 2005
NAME
----
git-tag - Create a tag object signed with GPG
-
SYNOPSIS
--------
-'git-tag' [-s | -a] [-f] <name>
+'git-tag' [-a | -s | -u <key-id>] [-f] [-m <msg>] <name> [<head>]
DESCRIPTION
-----------
-Adds a "tag" reference in .git/refs/tags/
+Adds a 'tag' reference in .git/refs/tags/
+
+Unless `-f` is given, the tag must not yet exist in
+`.git/refs/tags/` directory.
-Unless "-f" is given, the tag must not yet exist in ".git/refs/tags"
+If one of `-a`, `-s`, or `-u <key-id>` is passed, the command
+creates a 'tag' object, and requires the tag message. Unless
+`-m <msg>` is given, an editor is started for the user to type
+in the tag message.
-If "-s" or "-a" is passed, the user will be prompted for a tag message.
-and a tag object is created. Otherwise just the SHA1 object
-name of the commit object is written.
+Otherwise just the SHA1 object name of the commit object is
+written (i.e. an lightweight tag).
-A GnuPG signed tag object will be created when "-s" is used.
+A GnuPG signed tag object will be created when `-s` or `-u
+<key-id>` is used. When `-u <key-id>` is not used, the
+committer identity for the current user is used to find the
+GnuPG key for signing.
Author
diff --git a/Documentation/git-tar-tree.txt b/Documentation/git-tar-tree.txt
index 480a0cf0b..2139b6ff8 100644
--- a/Documentation/git-tar-tree.txt
+++ b/Documentation/git-tar-tree.txt
@@ -1,6 +1,5 @@
git-tar-tree(1)
===============
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git-unpack-file.txt b/Documentation/git-unpack-file.txt
index 3903b2d99..213dc8196 100644
--- a/Documentation/git-unpack-file.txt
+++ b/Documentation/git-unpack-file.txt
@@ -1,6 +1,5 @@
git-unpack-file(1)
==================
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git-unpack-objects.txt b/Documentation/git-unpack-objects.txt
index 9b982d996..b716ba1ad 100644
--- a/Documentation/git-unpack-objects.txt
+++ b/Documentation/git-unpack-objects.txt
@@ -1,6 +1,5 @@
git-unpack-objects(1)
=====================
-v0.1, July 2005
NAME
----
diff --git a/Documentation/git-update-server-info.txt b/Documentation/git-update-server-info.txt
index 2efd5400a..3d0dea07f 100644
--- a/Documentation/git-update-server-info.txt
+++ b/Documentation/git-update-server-info.txt
@@ -1,6 +1,5 @@
git-update-server-info(1)
=========================
-v0.1, July 2005
NAME
----
diff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt
index 98815b6a1..3d8f8ef66 100644
--- a/Documentation/git-upload-pack.txt
+++ b/Documentation/git-upload-pack.txt
@@ -1,6 +1,5 @@
git-upload-pack(1)
==================
-v0.1, July 2005
NAME
----
diff --git a/Documentation/git-var.txt b/Documentation/git-var.txt
index c1c717208..c22d34f5f 100644
--- a/Documentation/git-var.txt
+++ b/Documentation/git-var.txt
@@ -1,6 +1,5 @@
git-var(1)
==========
-v0.1, July 2005
NAME
----
diff --git a/Documentation/git-verify-pack.txt b/Documentation/git-verify-pack.txt
index b100aa765..cd74ffd39 100644
--- a/Documentation/git-verify-pack.txt
+++ b/Documentation/git-verify-pack.txt
@@ -1,10 +1,9 @@
git-verify-pack(1)
==================
-v0.1, June 2005
NAME
----
-git-verify-pack - Validate packed GIT archive files.
+git-verify-pack - Validate packed git archive files.
SYNOPSIS
@@ -14,7 +13,7 @@ SYNOPSIS
DESCRIPTION
-----------
-Reads given idx file for packed GIT archive created with
+Reads given idx file for packed git archive created with
git-pack-objects command and verifies idx file and the
corresponding pack file.
diff --git a/Documentation/git-whatchanged.txt b/Documentation/git-whatchanged.txt
index 056a9697c..8a2212de6 100644
--- a/Documentation/git-whatchanged.txt
+++ b/Documentation/git-whatchanged.txt
@@ -1,6 +1,5 @@
git-whatchanged(1)
==================
-v0.99.4, Aug 2005
NAME
----
@@ -9,7 +8,7 @@ git-whatchanged - Show logs with difference each commit introduces.
SYNOPSIS
--------
-'git whatchanged' <option>...
+'git-whatchanged' <option>...
DESCRIPTION
-----------
diff --git a/Documentation/git-write-tree.txt b/Documentation/git-write-tree.txt
index 71e16d128..51be44d1f 100644
--- a/Documentation/git-write-tree.txt
+++ b/Documentation/git-write-tree.txt
@@ -1,6 +1,5 @@
git-write-tree(1)
=================
-v0.1, May 2005
NAME
----
diff --git a/Documentation/git.txt b/Documentation/git.txt
index e14102127..796c4f61e 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -1,6 +1,5 @@
git(7)
======
-v0.99.6, Sep 2005
NAME
----
@@ -69,6 +68,9 @@ gitlink:git-commit-tree[1]::
gitlink:git-hash-object[1]::
Computes the object ID from a file.
+gitlink:git-index-pack.html[1]::
+ Build pack index file for an existing packed archive.
+
gitlink:git-init-db[1]::
Creates an empty git object database
@@ -147,7 +149,7 @@ gitlink:git-var[1]::
Displays a git logical variable
gitlink:git-verify-pack[1]::
- Validates packed GIT archive files
+ Validates packed git archive files
The interrogate commands may create files - and you can force them to
touch the working file set - but in general they don't
@@ -164,11 +166,11 @@ gitlink:git-fetch-pack[1]::
Updates from a remote repository.
gitlink:git-http-fetch[1]::
- Downloads a remote GIT repository via HTTP
+ Downloads a remote git repository via HTTP
Previously this command was known as git-http-pull.
gitlink:git-local-fetch[1]::
- Duplicates another GIT repository on a local system
+ Duplicates another git repository on a local system
Previously this command was known as git-local-pull.
gitlink:git-peek-remote[1]::
@@ -323,7 +325,7 @@ gitlink:git-archimport[1]::
Previously this command was known as git-archimport-script.
gitlink:git-convert-objects[1]::
- Converts old-style GIT repository
+ Converts old-style git repository
Previously this command was known as git-convert-cache.
gitlink:git-cvsimport[1]::
@@ -361,7 +363,7 @@ gitlink:git-count-objects[1]::
Previously this command was known as git-count-objects-script.
gitlink:git-daemon[1]::
- A really simple server for GIT repositories.
+ A really simple server for git repositories.
gitlink:git-get-tar-commit-id[1]::
Extract commit ID from an archive created using git-tar-tree.
diff --git a/Documentation/glossary.txt b/Documentation/glossary.txt
index a069b7bb0..eb7b47102 100644
--- a/Documentation/glossary.txt
+++ b/Documentation/glossary.txt
@@ -1,5 +1,5 @@
object::
- The unit of storage in GIT. It is uniquely identified by
+ The unit of storage in git. It is uniquely identified by
the SHA1 of its contents. Consequently, an object can not
be changed.
diff --git a/Documentation/hooks.txt b/Documentation/hooks.txt
index 57f472087..7ee3571bc 100644
--- a/Documentation/hooks.txt
+++ b/Documentation/hooks.txt
@@ -1,6 +1,5 @@
-Hooks used by GIT
+Hooks used by git
=================
-v0.99.6, Sep 2005
Hooks are little scripts you can place in `$GIT_DIR/hooks`
directory to trigger action at certain points. When
diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt
index 8dbddbf63..51222b6f3 100644
--- a/Documentation/pull-fetch-param.txt
+++ b/Documentation/pull-fetch-param.txt
@@ -6,7 +6,7 @@
===============================================================
- Rsync URL: rsync://remote.machine/path/to/repo.git/
- HTTP(s) URL: http://remote.machine/path/to/repo.git/
-- GIT URL: git://remote.machine/path/to/repo.git/
+- git URL: git://remote.machine/path/to/repo.git/
or remote.machine:/path/to/repo.git/
- Local directory: /path/to/repo.git/
===============================================================
diff --git a/Documentation/repository-layout.txt b/Documentation/repository-layout.txt
index d20fa80d8..1b5f22824 100644
--- a/Documentation/repository-layout.txt
+++ b/Documentation/repository-layout.txt
@@ -1,6 +1,5 @@
-GIT repository layout
+git repository layout
=====================
-v0.99.5, Sep 2005
You may find these things in your git repository (`.git`
directory for a repository associated with your working tree, or
@@ -120,7 +119,7 @@ info/grafts::
info/exclude::
This file, by convention among Porcelains, stores the
exclude pattern list. `git status` looks at it, but
- otherwise it is not looked at by any of the core GIT
+ otherwise it is not looked at by any of the core git
commands.
remotes::
diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt
index 36f42e051..b9f737e96 100644
--- a/Documentation/tutorial.txt
+++ b/Documentation/tutorial.txt
@@ -1,6 +1,5 @@
A short git tutorial
====================
-v0.99.5, Aug 2005
Introduction
------------
@@ -52,7 +51,9 @@ your new project. You will now have a `.git` directory, and you can
inspect that with `ls`. For your new empty project, it should show you
three entries, among other things:
- - a symlink called `HEAD`, pointing to `refs/heads/master`
+ - a symlink called `HEAD`, pointing to `refs/heads/master` (if your
+ platform does not have native symlinks, it is a file containing the
+ line "ref: refs/heads/master")
+
Don't worry about the fact that the file that the `HEAD` link points to
doesn't even exist yet -- you haven't created the commit that will
@@ -161,7 +162,7 @@ you'll have to use the object name, not the filename of the object:
git-cat-file -t 557db03de997c86a4a028e1ebd3a1ceb225be238
where the `-t` tells `git-cat-file` to tell you what the "type" of the
-object is. Git will tell you that you have a "blob" object (ie just a
+object is. git will tell you that you have a "blob" object (ie just a
regular file), and you can see the contents with
git-cat-file "blob" 557db03
@@ -228,6 +229,7 @@ which will spit out
------------
diff --git a/hello b/hello
+index 557db03..263414f 100644
--- a/hello
+++ b/hello
@@ -1 +1,2 @@
@@ -290,13 +292,16 @@ also wants to get a commit message
on its standard input, and it will write out the resulting object name for the
commit to its standard output.
-And this is where we start using the `.git/HEAD` file. The `HEAD` file is
-supposed to contain the reference to the top-of-tree, and since that's
-exactly what `git-commit-tree` spits out, we can do this all with a simple
-shell pipeline:
+And this is where we create the `.git/refs/heads/master` file
+which is pointed at by `HEAD`. This file is supposed to contain
+the reference to the top-of-tree of the master branch, and since
+that's exactly what `git-commit-tree` spits out, we can do this
+all with a sequence of simple shell commands:
------------------------------------------------
-echo "Initial commit" | git-commit-tree $(git-write-tree) > .git/HEAD
+tree=$(git-write-tree)
+commit=$(echo 'Initial commit' | git-commit-tree $tree)
+git-update-ref HEAD $(commit)
------------------------------------------------
which will say:
@@ -378,7 +383,7 @@ come from the working tree or not.
This is not hard to understand, as soon as you realize that git simply
never knows (or cares) about files that it is not told about
-explicitly. Git will never go *looking* for files to compare, it
+explicitly. git will never go *looking* for files to compare, it
expects you to tell it what the files are, and that's what the index
is there for.
================
@@ -544,7 +549,7 @@ name for the state at that point.
Copying repositories
--------------------
-Git repositories are normally totally self-sufficient, and it's worth noting
+git repositories are normally totally self-sufficient, and it's worth noting
that unlike CVS, for example, there is no separate notion of
"repository" and "working tree". A git repository normally *is* the
working tree, with the local git information hidden in the `.git`
@@ -692,7 +697,9 @@ other point in the history than the current `HEAD`, you can do so by
just telling `git checkout` what the base of the checkout would be.
In other words, if you have an earlier tag or branch, you'd just do
- git checkout -b mybranch earlier-commit
+------------
+git checkout -b mybranch earlier-commit
+------------
and it would create the new branch `mybranch` at the earlier commit,
and check out the state at that time.
@@ -700,17 +707,29 @@ and check out the state at that time.
You can always just jump back to your original `master` branch by doing
- git checkout master
+------------
+git checkout master
+------------
(or any other branch-name, for that matter) and if you forget which
branch you happen to be on, a simple
- ls -l .git/HEAD
+------------
+ls -l .git/HEAD
+------------
-will tell you where it's pointing. To get the list of branches
-you have, you can say
+will tell you where it's pointing (Note that on platforms with bad or no
+symlink support, you have to execute
- git branch
+------------
+cat .git/HEAD
+------------
+
+instead). To get the list of branches you have, you can say
+
+------------
+git branch
+------------
which is nothing more than a simple script around `ls .git/refs/heads`.
There will be asterisk in front of the branch you are currently on.
@@ -718,7 +737,9 @@ There will be asterisk in front of the branch you are currently on.
Sometimes you may wish to create a new branch _without_ actually
checking it out and switching to it. If so, just use the command
- git branch <branchname> [startingpoint]
+------------
+git branch <branchname> [startingpoint]
+------------
which will simply _create_ the branch, but will not do anything further.
You can then later -- once you decide that you want to actually develop
@@ -844,7 +865,6 @@ $ git show-branch master mybranch
! [mybranch] Some work.
--
+ [master] Merged "mybranch" changes.
-+ [master~1] Some fun.
++ [mybranch] Some work.
------------------------------------------------
@@ -871,8 +891,10 @@ Now, let's pretend you are the one who did all the work in
to the `master` branch. Let's go back to `mybranch`, and run
resolve to get the "upstream changes" back to your branch.
- git checkout mybranch
- git resolve HEAD master "Merge upstream changes."
+------------
+git checkout mybranch
+git resolve HEAD master "Merge upstream changes."
+------------
This outputs something like this (the actual commit object names
would be different)
@@ -951,7 +973,7 @@ This transport is the same as SSH transport but uses `sh` to run
both ends on the local machine instead of running other end on
the remote machine via `ssh`.
-GIT Native::
+git Native::
`git://remote.machine/path/to/repo.git/`
+
This transport was designed for anonymous downloading. Like SSH
@@ -972,13 +994,13 @@ necessary objects. Because of this behaviour, they are
sometimes also called 'commit walkers'.
+
The 'commit walkers' are sometimes also called 'dumb
-transports', because they do not require any GIT aware smart
-server like GIT Native transport does. Any stock HTTP server
+transports', because they do not require any git aware smart
+server like git Native transport does. Any stock HTTP server
would suffice.
+
There are (confusingly enough) `git-ssh-fetch` and `git-ssh-upload`
programs, which are 'commit walkers'; they outlived their
-usefulness when GIT Native and SSH transports were introduced,
+usefulness when git Native and SSH transports were introduced,
and not used by `git pull` or `git push` scripts.
Once you fetch from the remote repository, you `resolve` that
@@ -1082,19 +1104,23 @@ done only once.
on the remote machine. The communication between the two over
the network internally uses an SSH connection.
-Your private repository's GIT directory is usually `.git`, but
+Your private repository's git directory is usually `.git`, but
your public repository is often named after the project name,
i.e. `<project>.git`. Let's create such a public repository for
project `my-git`. After logging into the remote machine, create
an empty directory:
- mkdir my-git.git
+------------
+mkdir my-git.git
+------------
-Then, make that directory into a GIT repository by running
+Then, make that directory into a git repository by running
`git init-db`, but this time, since its name is not the usual
`.git`, we do things slightly differently:
- GIT_DIR=my-git.git git-init-db
+------------
+GIT_DIR=my-git.git git-init-db
+------------
Make sure this directory is available for others you want your
changes to be pulled by via the transport of your choice. Also
@@ -1118,7 +1144,9 @@ Your "public repository" is now ready to accept your changes.
Come back to the machine you have your private repository. From
there, run this command:
- git push <public-host>:/path/to/my-git.git master
+------------
+git push <public-host>:/path/to/my-git.git master
+------------
This synchronizes your public repository to match the named
branch head (i.e. `master` in this case) and objects reachable
@@ -1128,7 +1156,9 @@ As a real example, this is how I update my public git
repository. Kernel.org mirror network takes care of the
propagation to other publicly visible machines:
- git push master.kernel.org:/pub/scm/git/git.git/
+------------
+git push master.kernel.org:/pub/scm/git/git.git/
+------------
Packing your repository
@@ -1141,7 +1171,9 @@ not so convenient to transport over the network. Since git objects are
immutable once they are created, there is a way to optimize the
storage by "packing them together". The command
- git repack
+------------
+git repack
+------------
will do it for you. If you followed the tutorial examples, you
would have accumulated about 17 objects in `.git/objects/??/`
@@ -1165,7 +1197,9 @@ Our programs are always perfect ;-).
Once you have packed objects, you do not need to leave the
unpacked objects that are contained in the pack file anymore.
- git prune-packed
+------------
+git prune-packed
+------------
would remove them for you.
diff --git a/Makefile b/Makefile
index 13b949b09..5ee72bc92 100644
--- a/Makefile
+++ b/Makefile
@@ -27,6 +27,8 @@
# Define NEEDS_SOCKET if linking with libc is not enough (SunOS,
# Patrick Mauritz).
#
+# Define NO_MMAP if you want to avoid mmap.
+#
# Define WITH_OWN_SUBPROCESS_PY if you want to use with python 2.3.
#
# Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
@@ -85,14 +87,14 @@ SCRIPT_SH = \
git-repack.sh git-request-pull.sh git-reset.sh \
git-resolve.sh git-revert.sh git-sh-setup.sh git-status.sh \
git-tag.sh git-verify-tag.sh git-whatchanged.sh git.sh \
- git-applymbox.sh git-applypatch.sh \
+ git-applymbox.sh git-applypatch.sh git-am.sh \
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
git-merge-resolve.sh git-grep.sh
SCRIPT_PERL = \
git-archimport.perl git-cvsimport.perl git-relink.perl \
git-rename.perl git-shortlog.perl git-fmt-merge-msg.perl \
- git-svnimport.perl
+ git-findtags.perl git-svnimport.perl
SCRIPT_PYTHON = \
git-merge-recursive.py
@@ -109,7 +111,7 @@ PROGRAMS = \
git-convert-objects$X git-diff-files$X \
git-diff-index$X git-diff-stages$X \
git-diff-tree$X git-fetch-pack$X git-fsck-objects$X \
- git-hash-object$X git-init-db$X \
+ git-hash-object$X git-index-pack$X git-init-db$X \
git-local-fetch$X git-ls-files$X git-ls-tree$X git-merge-base$X \
git-merge-index$X git-mktag$X git-pack-objects$X git-patch-id$X \
git-peek-remote$X git-prune-packed$X git-read-tree$X \
@@ -119,7 +121,7 @@ PROGRAMS = \
git-ssh-upload$X git-tar-tree$X git-unpack-file$X \
git-unpack-objects$X git-update-index$X git-update-server-info$X \
git-upload-pack$X git-verify-pack$X git-write-tree$X \
- git-update-ref$X git-symbolic-ref$X \
+ git-update-ref$X git-symbolic-ref$X git-check-ref-format$X \
$(SIMPLE_PROGRAMS)
# Backward compatibility -- to be removed after 1.0
@@ -157,19 +159,34 @@ LIB_OBJS = \
object.o pack-check.o patch-delta.o path.o pkt-line.o \
quote.o read-cache.o refs.o run-command.o \
server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
- tag.o tree.o usage.o $(DIFF_OBJS)
+ tag.o tree.o usage.o config.o environment.o ctype.o \
+ $(DIFF_OBJS)
LIBS = $(LIB_FILE)
LIBS += -lz
+# Shell quote;
+# Result of this needs to be placed inside ''
+shq = $(subst ','\'',$(1))
+# This has surrounding ''
+shellquote = '$(call shq,$(1))'
+
#
# Platform specific tweaks
#
-ifeq ($(shell uname -s),Darwin)
+
+# We choose to avoid "if .. else if .. else .. endif endif"
+# because maintaining the nesting to match is a pain. If
+# we had "elif" things would have been much nicer...
+uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
+uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
+uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not')
+
+ifeq ($(uname_S),Darwin)
NEEDS_SSL_WITH_CRYPTO = YesPlease
NEEDS_LIBICONV = YesPlease
endif
-ifeq ($(shell uname -s),SunOS)
+ifeq ($(uname_S),SunOS)
NEEDS_SOCKET = YesPlease
NEEDS_NSL = YesPlease
SHELL_PATH = /bin/bash
@@ -179,20 +196,23 @@ ifeq ($(shell uname -s),SunOS)
TAR = gtar
PLATFORM_DEFINES += -D__EXTENSIONS__
endif
-ifeq ($(shell uname -o),Cygwin)
+ifeq ($(uname_O),Cygwin)
NO_STRCASESTR = YesPlease
NEEDS_LIBICONV = YesPlease
NO_IPV6 = YesPlease
X = .exe
PLATFORM_DEFINES += -DUSE_SYMLINK_HEAD=0
endif
-ifneq (,$(findstring arm,$(shell uname -m)))
- ARM_SHA1 = YesPlease
-endif
-ifeq ($(shell uname -s),OpenBSD)
+ifeq ($(uname_S),OpenBSD)
+ NO_STRCASESTR = YesPlease
NEEDS_LIBICONV = YesPlease
PLATFORM_DEFINES += -I/usr/local/include -L/usr/local/lib
endif
+ifneq (,$(findstring arm,$(uname_M)))
+ ARM_SHA1 = YesPlease
+endif
+
+-include config.mak
ifndef NO_CURL
ifdef CURLDIR
@@ -226,7 +246,7 @@ ifndef NO_OPENSSL
OPENSSL_LINK =
endif
else
- DEFINES += '-DNO_OPENSSL'
+ DEFINES += -DNO_OPENSSL
MOZILLA_SHA1 = 1
OPENSSL_LIBSSL =
endif
@@ -256,9 +276,13 @@ ifdef NEEDS_NSL
SIMPLE_LIB += -lnsl
endif
ifdef NO_STRCASESTR
- DEFINES += -Dstrcasestr=gitstrcasestr
+ DEFINES += -Dstrcasestr=gitstrcasestr -DNO_STRCASESTR=1
LIB_OBJS += compat/strcasestr.o
endif
+ifdef NO_MMAP
+ DEFINES += -Dmmap=gitfakemmap -Dmunmap=gitfakemunmap -DNO_MMAP
+ LIB_OBJS += compat/mmap.o
+endif
ifdef NO_IPV6
DEFINES += -DNO_IPV6 -Dsockaddr_storage=sockaddr_in
endif
@@ -281,14 +305,14 @@ endif
endif
endif
-DEFINES += '-DSHA1_HEADER=$(SHA1_HEADER)'
+DEFINES += -DSHA1_HEADER=$(call shellquote,$(SHA1_HEADER))
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
$(patsubst %.py,%,$(SCRIPT_PYTHON)) \
gitk
-export TAR INSTALL DESTDIR SHELL_PATH
+export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
### Build rules
all: $(PROGRAMS) $(SCRIPTS)
@@ -298,7 +322,7 @@ all:
git: git.sh Makefile
rm -f $@+ $@
- sed -e '1s|#!.*/sh|#!$(SHELL_PATH)|' \
+ sed -e '1s|#!.*/sh|#!$(call shq,$(SHELL_PATH))|' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-e 's/@@X@@/$(X)/g' \
$(GIT_LIST_TWEAK) <$@.sh >$@+
@@ -307,22 +331,22 @@ git: git.sh Makefile
$(filter-out git,$(patsubst %.sh,%,$(SCRIPT_SH))) : % : %.sh
rm -f $@
- sed -e '1s|#!.*/sh|#!$(SHELL_PATH)|' \
+ sed -e '1s|#!.*/sh|#!$(call shq,$(SHELL_PATH))|' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
$@.sh >$@
chmod +x $@
$(patsubst %.perl,%,$(SCRIPT_PERL)) : % : %.perl
rm -f $@
- sed -e '1s|#!.*perl|#!$(PERL_PATH)|' \
+ sed -e '1s|#!.*perl|#!$(call shq,$(PERL_PATH))|' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
$@.perl >$@
chmod +x $@
$(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
rm -f $@
- sed -e '1s|#!.*python|#!$(PYTHON_PATH)|' \
- -e 's|@@GIT_PYTHON_PATH@@|$(GIT_PYTHON_DIR)|g' \
+ sed -e '1s|#!.*python|#!$(call shq,$(PYTHON_PATH))|' \
+ -e 's|@@GIT_PYTHON_PATH@@|$(call shq,$(GIT_PYTHON_DIR))|g' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
$@.py >$@
chmod +x $@
@@ -352,7 +376,7 @@ git-rev-list$X: LIBS += $(OPENSSL_LIBSSL)
init-db.o: init-db.c
$(CC) -c $(ALL_CFLAGS) \
- -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir)"' $*.c
+ -DDEFAULT_GIT_TEMPLATE_DIR=$(call shellquote,"$(template_dir)") $*.c
$(LIB_OBJS): $(LIB_H)
$(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H)
@@ -384,13 +408,13 @@ check:
### Installation rules
install: $(PROGRAMS) $(SCRIPTS)
- $(INSTALL) -d -m755 $(DESTDIR)$(bindir)
- $(INSTALL) $(PROGRAMS) $(SCRIPTS) $(DESTDIR)$(bindir)
- $(INSTALL) git-revert $(DESTDIR)$(bindir)/git-cherry-pick
- sh ./cmd-rename.sh $(DESTDIR)$(bindir)
+ $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(bindir))
+ $(INSTALL) $(PROGRAMS) $(SCRIPTS) $(call shellquote,$(DESTDIR)$(bindir))
+ $(INSTALL) git-revert $(call shellquote,$(DESTDIR)$(bindir)/git-cherry-pick)
+ sh ./cmd-rename.sh $(call shellquote,$(DESTDIR)$(bindir))
$(MAKE) -C templates install
- $(INSTALL) -d -m755 $(DESTDIR)$(GIT_PYTHON_DIR)
- $(INSTALL) $(PYMODULES) $(DESTDIR)$(GIT_PYTHON_DIR)
+ $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR))
+ $(INSTALL) $(PYMODULES) $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR))
install-doc:
$(MAKE) -C Documentation install
diff --git a/apply.c b/apply.c
index 7be504135..f4d00f283 100644
--- a/apply.c
+++ b/apply.c
@@ -6,7 +6,6 @@
* This applies patches on top of some (arbitrary) version of the SCM.
*
*/
-#include <ctype.h>
#include <fnmatch.h>
#include "cache.h"
@@ -14,6 +13,7 @@
// files that are being modified, but doesn't apply the patch
// --stat does just a diffstat, and doesn't actually apply
// --show-files shows the directory changes
+// --show-index-info shows the old and new index info for paths if available.
//
static int check_index = 0;
static int write_index = 0;
@@ -22,8 +22,9 @@ static int summary = 0;
static int check = 0;
static int apply = 1;
static int show_files = 0;
+static int show_index_info = 0;
static const char apply_usage[] =
-"git-apply [--stat] [--summary] [--check] [--index] [--apply] [--show-files] <patch>...";
+"git-apply [--stat] [--summary] [--check] [--index] [--apply] [--show-files] [--show-index-info] <patch>...";
/*
* For "diff-stat" like behaviour, we keep track of the biggest change
@@ -56,6 +57,8 @@ struct patch {
struct fragment *fragments;
char *result;
unsigned long resultsize;
+ char old_sha1_prefix[41];
+ char new_sha1_prefix[41];
struct patch *next;
};
@@ -334,6 +337,38 @@ static int gitdiff_dissimilarity(const char *line, struct patch *patch)
return 0;
}
+static int gitdiff_index(const char *line, struct patch *patch)
+{
+ /* index line is N hexadecimal, "..", N hexadecimal,
+ * and optional space with octal mode.
+ */
+ const char *ptr, *eol;
+ int len;
+
+ ptr = strchr(line, '.');
+ if (!ptr || ptr[1] != '.' || 40 <= ptr - line)
+ return 0;
+ len = ptr - line;
+ memcpy(patch->old_sha1_prefix, line, len);
+ patch->old_sha1_prefix[len] = 0;
+
+ line = ptr + 2;
+ ptr = strchr(line, ' ');
+ eol = strchr(line, '\n');
+
+ if (!ptr || eol < ptr)
+ ptr = eol;
+ len = ptr - line;
+
+ if (40 <= len)
+ return 0;
+ memcpy(patch->new_sha1_prefix, line, len);
+ patch->new_sha1_prefix[len] = 0;
+ if (*ptr == ' ')
+ patch->new_mode = patch->old_mode = strtoul(ptr+1, NULL, 8);
+ return 0;
+}
+
/*
* This is normal for a diff that doesn't change anything: we'll fall through
* into the next diff. Tell the parser to break out.
@@ -438,6 +473,7 @@ static int parse_git_header(char *line, int len, unsigned int size, struct patch
{ "rename to ", gitdiff_renamedst },
{ "similarity index ", gitdiff_similarity },
{ "dissimilarity index ", gitdiff_dissimilarity },
+ { "index ", gitdiff_index },
{ "", gitdiff_unrecognized },
};
int i;
@@ -1136,6 +1172,36 @@ static void show_file_list(struct patch *patch)
}
}
+static inline int is_null_sha1(const unsigned char *sha1)
+{
+ return !memcmp(sha1, null_sha1, 20);
+}
+
+static void show_index_list(struct patch *list)
+{
+ struct patch *patch;
+
+ /* Once we start supporting the reverse patch, it may be
+ * worth showing the new sha1 prefix, but until then...
+ */
+ for (patch = list; patch; patch = patch->next) {
+ const unsigned char *sha1_ptr;
+ unsigned char sha1[20];
+ const char *name;
+
+ name = patch->old_name ? patch->old_name : patch->new_name;
+ if (patch->is_new)
+ sha1_ptr = null_sha1;
+ else if (get_sha1(patch->old_sha1_prefix, sha1))
+ die("sha1 information is lacking or useless (%s).",
+ name);
+ else
+ sha1_ptr = sha1;
+ printf("%06o %s %s\n",patch->old_mode,
+ sha1_to_hex(sha1_ptr), name);
+ }
+}
+
static void stat_patch_list(struct patch *patch)
{
int files, adds, dels;
@@ -1476,6 +1542,9 @@ static int apply_patch(int fd)
if (show_files)
show_file_list(list);
+ if (show_index_info)
+ show_index_list(list);
+
if (diffstat)
stat_patch_list(list);
@@ -1534,6 +1603,11 @@ int main(int argc, char **argv)
show_files = 1;
continue;
}
+ if (!strcmp(arg, "--show-index-info")) {
+ apply = 0;
+ show_index_info = 1;
+ continue;
+ }
fd = open(arg, O_RDONLY);
if (fd < 0)
usage(apply_usage);
diff --git a/cache.h b/cache.h
index ec2a1610b..d77601682 100644
--- a/cache.h
+++ b/cache.h
@@ -11,7 +11,9 @@
#include <string.h>
#include <errno.h>
#include <limits.h>
+#ifndef NO_MMAP
#include <sys/mman.h>
+#endif
#include <sys/param.h>
#include <netinet/in.h>
#include <sys/types.h>
@@ -165,6 +167,7 @@ extern int ce_match_stat(struct cache_entry *ce, struct stat *st);
extern int ce_modified(struct cache_entry *ce, struct stat *st);
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type);
+extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
struct cache_file {
@@ -175,6 +178,8 @@ extern int hold_index_file_for_update(struct cache_file *, const char *path);
extern int commit_index_file(struct cache_file *);
extern void rollback_index_file(struct cache_file *);
+extern int trust_executable_bit;
+
#define MTIME_CHANGED 0x0001
#define CTIME_CHANGED 0x0002
#define OWNER_CHANGED 0x0004
@@ -189,6 +194,7 @@ extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)
extern char *sha1_file_name(const unsigned char *sha1);
extern char *sha1_pack_name(const unsigned char *sha1);
extern char *sha1_pack_index_name(const unsigned char *sha1);
+extern const char *find_unique_abbrev(const unsigned char *sha1, int);
extern const unsigned char null_sha1[20];
int git_mkstemp(char *path, size_t n, const char *template);
@@ -218,6 +224,7 @@ extern int read_tree(void *buffer, unsigned long size, int stage, const char **p
extern int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
size_t bufsize, size_t *bufposn);
extern int write_sha1_to_fd(int fd, const unsigned char *sha1);
+extern int move_temp_to_file(const char *tmpfile, char *filename);
extern int has_sha1_pack(const unsigned char *sha1);
extern int has_sha1_file(const unsigned char *sha1);
@@ -306,6 +313,7 @@ extern struct packed_git {
void *pack_base;
unsigned int pack_last_used;
unsigned int pack_use_cnt;
+ int pack_local;
unsigned char sha1[20];
char pack_name[0]; /* something like ".git/objects/pack/xxxxx.pack" */
} *packed_git;
@@ -331,7 +339,7 @@ extern int path_match(const char *path, int nr, char **match);
extern int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
int nr_refspec, char **refspec, int all);
extern int get_ack(int fd, unsigned char *result_sha1);
-extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match);
+extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, int ignore_funny);
extern struct packed_git *parse_pack_index(unsigned char *sha1);
extern struct packed_git *parse_pack_index_file(const unsigned char *sha1,
@@ -345,7 +353,7 @@ extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
extern int use_packed_git(struct packed_git *);
extern void unuse_packed_git(struct packed_git *);
-extern struct packed_git *add_packed_git(char *, int);
+extern struct packed_git *add_packed_git(char *, int, int);
extern int num_packed_objects(const struct packed_git *p);
extern int nth_packed_object_sha1(const struct packed_git *, int, unsigned char*);
extern int find_pack_entry_one(const unsigned char *, struct pack_entry *, struct packed_git *);
@@ -355,4 +363,54 @@ extern void packed_object_info_detail(struct pack_entry *, char *, unsigned long
/* Dumb servers support */
extern int update_server_info(int);
+#ifdef NO_MMAP
+
+#ifndef PROT_READ
+#define PROT_READ 1
+#define PROT_WRITE 2
+#define MAP_PRIVATE 1
+#define MAP_FAILED ((void*)-1)
+#endif
+
+extern void *gitfakemmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);
+extern int gitfakemunmap(void *start, size_t length);
+
+#endif
+
+typedef int (*config_fn_t)(const char *, const char *);
+extern int git_default_config(const char *, const char *);
+extern int git_config(config_fn_t fn);
+extern int git_config_int(const char *, const char *);
+extern int git_config_bool(const char *, const char *);
+
+#define MAX_GITNAME (1000)
+extern char git_default_email[MAX_GITNAME];
+extern char git_default_name[MAX_GITNAME];
+
+/* Sane ctype - no locale, and works with signed chars */
+#undef isspace
+#undef isdigit
+#undef isalpha
+#undef isalnum
+#undef tolower
+#undef toupper
+extern unsigned char sane_ctype[256];
+#define GIT_SPACE 0x01
+#define GIT_DIGIT 0x02
+#define GIT_ALPHA 0x04
+#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
+#define isspace(x) sane_istest(x,GIT_SPACE)
+#define isdigit(x) sane_istest(x,GIT_DIGIT)
+#define isalpha(x) sane_istest(x,GIT_ALPHA)
+#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
+#define tolower(x) sane_case((unsigned char)(x), 0x20)
+#define toupper(x) sane_case((unsigned char)(x), 0)
+
+static inline int sane_case(int x, int high)
+{
+ if (sane_istest(x, GIT_ALPHA))
+ x = (x & ~0x20) | high;
+ return x;
+}
+
#endif /* CACHE_H */
diff --git a/check-ref-format.c b/check-ref-format.c
new file mode 100644
index 000000000..a0adb3dcb
--- /dev/null
+++ b/check-ref-format.c
@@ -0,0 +1,17 @@
+/*
+ * GIT - The information manager from hell
+ */
+
+#include "cache.h"
+#include "refs.h"
+
+#include <stdio.h>
+
+int main(int ac, char **av)
+{
+ if (ac != 2)
+ usage("git-check-ref-format refname");
+ if (check_ref_format(av[1]))
+ exit(1);
+ return 0;
+}
diff --git a/clone-pack.c b/clone-pack.c
index 49820c657..f9b263a44 100644
--- a/clone-pack.c
+++ b/clone-pack.c
@@ -4,7 +4,9 @@
#include <sys/wait.h>
static int quiet;
-static const char clone_pack_usage[] = "git-clone-pack [-q] [--exec=<git-upload-pack>] [<host>:]<directory> [<heads>]*";
+static int keep_pack;
+static const char clone_pack_usage[] =
+"git-clone-pack [-q] [--keep] [--exec=<git-upload-pack>] [<host>:]<directory> [<heads>]*";
static const char *exec = "git-upload-pack";
static void clone_handshake(int fd[2], struct ref *ref)
@@ -34,6 +36,12 @@ static void write_one_ref(struct ref *ref)
int fd;
char *hex;
+ if (!strncmp(ref->name, "refs/", 5) &&
+ check_ref_format(ref->name + 5)) {
+ error("refusing to create funny ref '%s' locally", ref->name);
+ return;
+ }
+
if (safe_create_leading_directories(path))
die("unable to create leading directory for %s", ref->name);
fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0666);
@@ -51,6 +59,7 @@ static void write_refs(struct ref *ref)
struct ref *head = NULL, *head_ptr, *master_ref;
char *head_path;
+ /* Upload-pack must report HEAD first */
if (!strcmp(ref->name, "HEAD")) {
head = ref;
ref = ref->next;
@@ -60,17 +69,21 @@ static void write_refs(struct ref *ref)
while (ref) {
if (is_master(ref))
master_ref = ref;
- if (head && !memcmp(ref->old_sha1, head->old_sha1, 20)) {
- if (!head_ptr || ref == master_ref)
- head_ptr = ref;
- }
+ if (head &&
+ !memcmp(ref->old_sha1, head->old_sha1, 20) &&
+ !strncmp(ref->name, "refs/heads/",11) &&
+ (!head_ptr || ref == master_ref))
+ head_ptr = ref;
+
write_one_ref(ref);
ref = ref->next;
}
- if (!head)
+ if (!head) {
+ fprintf(stderr, "No HEAD in remote.\n");
return;
+ }
- head_path = git_path("HEAD");
+ head_path = strdup(git_path("HEAD"));
if (!head_ptr) {
/*
* If we had a master ref, and it wasn't HEAD, we need to undo the
@@ -82,6 +95,7 @@ static void write_refs(struct ref *ref)
unlink(head_path);
}
write_one_ref(head);
+ free(head_path);
return;
}
@@ -89,27 +103,22 @@ static void write_refs(struct ref *ref)
if (master_ref)
return;
+ fprintf(stderr, "Setting HEAD to %s\n", head_ptr->name);
+
/*
* Uhhuh. Other end didn't have master. We start HEAD off with
* the first branch with the same value.
*/
- unlink(head_path);
- if (symlink(head_ptr->name, head_path) < 0)
+ if (create_symref(head_path, head_ptr->name) < 0)
die("unable to link HEAD to %s", head_ptr->name);
+ free(head_path);
}
-static int clone_pack(int fd[2], int nr_match, char **match)
+static int clone_by_unpack(int fd[2])
{
- struct ref *refs;
int status;
pid_t pid;
- get_remote_heads(fd[0], &refs, nr_match, match);
- if (!refs) {
- packet_flush(fd[1]);
- die("no matching remote head");
- }
- clone_handshake(fd, refs);
pid = fork();
if (pid < 0)
die("git-clone-pack: unable to fork off git-unpack-objects");
@@ -131,7 +140,6 @@ static int clone_pack(int fd[2], int nr_match, char **match)
int code = WEXITSTATUS(status);
if (code)
die("git-unpack-objects died with error code %d", code);
- write_refs(refs);
return 0;
}
if (WIFSIGNALED(status)) {
@@ -141,6 +149,180 @@ static int clone_pack(int fd[2], int nr_match, char **match)
die("Sherlock Holmes! git-unpack-objects died of unnatural causes %d!", status);
}
+static int finish_pack(const char *pack_tmp_name)
+{
+ int pipe_fd[2];
+ pid_t pid;
+ char idx[PATH_MAX];
+ char final[PATH_MAX];
+ char hash[41];
+ unsigned char sha1[20];
+ char *cp;
+ int err = 0;
+
+ if (pipe(pipe_fd) < 0)
+ die("git-clone-pack: unable to set up pipe");
+
+ strcpy(idx, pack_tmp_name); /* ".git/objects/pack-XXXXXX" */
+ cp = strrchr(idx, '/');
+ memcpy(cp, "/pidx", 5);
+
+ pid = fork();
+ if (pid < 0)
+ die("git-clone-pack: unable to fork off git-index-pack");
+ if (!pid) {
+ close(0);
+ dup2(pipe_fd[1], 1);
+ close(pipe_fd[0]);
+ close(pipe_fd[1]);
+ execlp("git-index-pack","git-index-pack",
+ "-o", idx, pack_tmp_name, NULL);
+ error("cannot exec git-index-pack <%s> <%s>",
+ idx, pack_tmp_name);
+ exit(1);
+ }
+ close(pipe_fd[1]);
+ if (read(pipe_fd[0], hash, 40) != 40) {
+ error("git-clone-pack: unable to read from git-index-pack");
+ err = 1;
+ }
+ close(pipe_fd[0]);
+
+ for (;;) {
+ int status, code;
+ int retval = waitpid(pid, &status, 0);
+
+ if (retval < 0) {
+ if (errno == EINTR)
+ continue;
+ error("waitpid failed (%s)", strerror(retval));
+ goto error_die;
+ }
+ if (WIFSIGNALED(status)) {
+ int sig = WTERMSIG(status);
+ error("git-index-pack died of signal %d", sig);
+ goto error_die;
+ }
+ if (!WIFEXITED(status)) {
+ error("git-index-pack died of unnatural causes %d",
+ status);
+ goto error_die;
+ }
+ code = WEXITSTATUS(status);
+ if (code) {
+ error("git-index-pack died with error code %d", code);
+ goto error_die;
+ }
+ if (err)
+ goto error_die;
+ break;
+ }
+ hash[40] = 0;
+ if (get_sha1_hex(hash, sha1)) {
+ error("git-index-pack reported nonsense '%s'", hash);
+ goto error_die;
+ }
+ /* Now we have pack in pack_tmp_name[], and
+ * idx in idx[]; rename them to their final names.
+ */
+ snprintf(final, sizeof(final),
+ "%s/pack/pack-%s.pack", get_object_directory(), hash);
+ move_temp_to_file(pack_tmp_name, final);
+ chmod(final, 0444);
+ snprintf(final, sizeof(final),
+ "%s/pack/pack-%s.idx", get_object_directory(), hash);
+ move_temp_to_file(idx, final);
+ chmod(final, 0444);
+ return 0;
+
+ error_die:
+ unlink(idx);
+ unlink(pack_tmp_name);
+ exit(1);
+}
+
+static int clone_without_unpack(int fd[2])
+{
+ char tmpfile[PATH_MAX];
+ int ofd, ifd;
+
+ ifd = fd[0];
+ snprintf(tmpfile, sizeof(tmpfile),
+ "%s/pack-XXXXXX", get_object_directory());
+ ofd = mkstemp(tmpfile);
+ if (ofd < 0)
+ return error("unable to create temporary file %s", tmpfile);
+
+ while (1) {
+ char buf[8192];
+ ssize_t sz, wsz, pos;
+ sz = read(ifd, buf, sizeof(buf));
+ if (sz == 0)
+ break;
+ if (sz < 0) {
+ error("error reading pack (%s)", strerror(errno));
+ close(ofd);
+ unlink(tmpfile);
+ return -1;
+ }
+ pos = 0;
+ while (pos < sz) {
+ wsz = write(ofd, buf + pos, sz - pos);
+ if (wsz < 0) {
+ error("error writing pack (%s)",
+ strerror(errno));
+ close(ofd);
+ unlink(tmpfile);
+ return -1;
+ }
+ pos += wsz;
+ }
+ }
+ close(ofd);
+ return finish_pack(tmpfile);
+}
+
+static int clone_pack(int fd[2], int nr_match, char **match)
+{
+ struct ref *refs;
+ int status;
+
+ get_remote_heads(fd[0], &refs, nr_match, match, 1);
+ if (!refs) {
+ packet_flush(fd[1]);
+ die("no matching remote head");
+ }
+ clone_handshake(fd, refs);
+
+ if (keep_pack)
+ status = clone_without_unpack(fd);
+ else
+ status = clone_by_unpack(fd);
+
+ if (!status)
+ write_refs(refs);
+ return status;
+}
+
+static int clone_options(const char *var, const char *value)
+{
+ if (!strcmp("clone.keeppack", var)) {
+ keep_pack = git_config_bool(var, value);
+ return 0;
+ }
+ if (!strcmp("clone.quiet", var)) {
+ quiet = git_config_bool(var, value);
+ return 0;
+ }
+ /*
+ * Put other local option parsing for this program
+ * here ...
+ */
+
+ /* Fall back on the default ones */
+ return git_default_config(var, value);
+}
+
int main(int argc, char **argv)
{
int i, ret, nr_heads;
@@ -148,6 +330,7 @@ int main(int argc, char **argv)
int fd[2];
pid_t pid;
+ git_config(clone_options);
nr_heads = 0;
heads = NULL;
for (i = 1; i < argc; i++) {
@@ -162,6 +345,10 @@ int main(int argc, char **argv)
exec = arg + 7;
continue;
}
+ if (!strcmp("--keep", arg)) {
+ keep_pack = 1;
+ continue;
+ }
usage(clone_pack_usage);
}
dest = arg;
diff --git a/commit-tree.c b/commit-tree.c
index b1ef0b590..ea0fdd44e 100644
--- a/commit-tree.c
+++ b/commit-tree.c
@@ -7,7 +7,6 @@
#include <pwd.h>
#include <time.h>
-#include <ctype.h>
#define BLOCKING (1ul << 14)
@@ -89,6 +88,9 @@ int main(int argc, char **argv)
char *buffer;
unsigned int size;
+ setup_ident();
+ git_config(git_default_config);
+
if (argc < 2 || get_sha1_hex(argv[1], tree_sha1) < 0)
usage(commit_tree_usage);
@@ -104,7 +106,6 @@ int main(int argc, char **argv)
}
if (!parents)
fprintf(stderr, "Committing initial tree %s\n", argv[1]);
- setup_ident();
init_buffer(&buffer, &size);
add_buffer(&buffer, &size, "tree %s\n", sha1_to_hex(tree_sha1));
diff --git a/commit.c b/commit.c
index f735f981b..8f403180e 100644
--- a/commit.c
+++ b/commit.c
@@ -1,4 +1,3 @@
-#include <ctype.h>
#include "tag.h"
#include "commit.h"
#include "cache.h"
diff --git a/compat/mmap.c b/compat/mmap.c
new file mode 100644
index 000000000..a051c4767
--- /dev/null
+++ b/compat/mmap.c
@@ -0,0 +1,50 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include "../cache.h"
+
+void *gitfakemmap(void *start, size_t length, int prot , int flags, int fd, off_t offset)
+{
+ int n = 0;
+
+ if (start != NULL || !(flags & MAP_PRIVATE))
+ die("Invalid usage of gitfakemmap.");
+
+ if (lseek(fd, offset, SEEK_SET) < 0) {
+ errno = EINVAL;
+ return MAP_FAILED;
+ }
+
+ start = xmalloc(length);
+ if (start == NULL) {
+ errno = ENOMEM;
+ return MAP_FAILED;
+ }
+
+ while (n < length) {
+ int count = read(fd, start+n, length-n);
+
+ if (count == 0) {
+ memset(start+n, 0, length-n);
+ break;
+ }
+
+ if (count < 0) {
+ free(start);
+ errno = EACCES;
+ return MAP_FAILED;
+ }
+
+ n += count;
+ }
+
+ return start;
+}
+
+int gitfakemunmap(void *start, size_t length)
+{
+ free(start);
+ return 0;
+}
+
diff --git a/config.c b/config.c
new file mode 100644
index 000000000..519fecfee
--- /dev/null
+++ b/config.c
@@ -0,0 +1,236 @@
+
+#include "cache.h"
+
+#define MAXNAME (256)
+
+static FILE *config_file;
+static int config_linenr;
+static int get_next_char(void)
+{
+ int c;
+ FILE *f;
+
+ c = '\n';
+ if ((f = config_file) != NULL) {
+ c = fgetc(f);
+ if (c == '\n')
+ config_linenr++;
+ if (c == EOF) {
+ config_file = NULL;
+ c = '\n';
+ }
+ }
+ return c;
+}
+
+static char *parse_value(void)
+{
+ static char value[1024];
+ int quote = 0, comment = 0, len = 0, space = 0;
+
+ for (;;) {
+ int c = get_next_char();
+ if (len >= sizeof(value))
+ return NULL;
+ if (c == '\n') {
+ if (quote)
+ return NULL;
+ value[len] = 0;
+ return value;
+ }
+ if (comment)
+ continue;
+ if (isspace(c) && !quote) {
+ space = 1;
+ continue;
+ }
+ if (space) {
+ if (len)
+ value[len++] = ' ';
+ space = 0;
+ }
+ if (c == '\\') {
+ c = get_next_char();
+ switch (c) {
+ case '\n':
+ continue;
+ case 't':
+ c = '\t';
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ /* Some characters escape as themselves */
+ case '\\': case '"':
+ break;
+ /* Reject unknown escape sequences */
+ default:
+ return NULL;
+ }
+ value[len++] = c;
+ continue;
+ }
+ if (c == '"') {
+ quote = 1-quote;
+ continue;
+ }
+ if (!quote) {
+ if (c == ';' || c == '#') {
+ comment = 1;
+ continue;
+ }
+ }
+ value[len++] = c;
+ }
+}
+
+static int get_value(config_fn_t fn, char *name, unsigned int len)
+{
+ int c;
+ char *value;
+
+ /* Get the full name */
+ for (;;) {
+ c = get_next_char();
+ if (c == EOF)
+ break;
+ if (!isalnum(c))
+ break;
+ name[len++] = tolower(c);
+ if (len >= MAXNAME)
+ return -1;
+ }
+ name[len] = 0;
+ while (c == ' ' || c == '\t')
+ c = get_next_char();
+
+ value = NULL;
+ if (c != '\n') {
+ if (c != '=')
+ return -1;
+ value = parse_value();
+ if (!value)
+ return -1;
+ }
+ return fn(name, value);
+}
+
+static int get_base_var(char *name)
+{
+ int baselen = 0;
+
+ for (;;) {
+ int c = get_next_char();
+ if (c == EOF)
+ return -1;
+ if (c == ']')
+ return baselen;
+ if (!isalnum(c))
+ return -1;
+ if (baselen > MAXNAME / 2)
+ return -1;
+ name[baselen++] = tolower(c);
+ }
+}
+
+static int git_parse_file(config_fn_t fn)
+{
+ int comment = 0;
+ int baselen = 0;
+ static char var[MAXNAME];
+
+ for (;;) {
+ int c = get_next_char();
+ if (c == '\n') {
+ /* EOF? */
+ if (!config_file)
+ return 0;
+ comment = 0;
+ continue;
+ }
+ if (comment || isspace(c))
+ continue;
+ if (c == '#' || c == ';') {
+ comment = 1;
+ continue;
+ }
+ if (c == '[') {
+ baselen = get_base_var(var);
+ if (baselen <= 0)
+ break;
+ var[baselen++] = '.';
+ var[baselen] = 0;
+ continue;
+ }
+ if (!isalpha(c))
+ break;
+ var[baselen] = tolower(c);
+ if (get_value(fn, var, baselen+1) < 0)
+ break;
+ }
+ die("bad config file line %d", config_linenr);
+}
+
+int git_config_int(const char *name, const char *value)
+{
+ if (value && *value) {
+ char *end;
+ int val = strtol(value, &end, 0);
+ if (!*end)
+ return val;
+ }
+ die("bad config value for '%s'", name);
+}
+
+int git_config_bool(const char *name, const char *value)
+{
+ if (!value)
+ return 1;
+ if (!*value)
+ return 0;
+ if (!strcasecmp(value, "true"))
+ return 1;
+ if (!strcasecmp(value, "false"))
+ return 0;
+ return git_config_int(name, value) != 0;
+}
+
+int git_default_config(const char *var, const char *value)
+{
+ /* This needs a better name */
+ if (!strcmp(var, "core.filemode")) {
+ trust_executable_bit = git_config_bool(var, value);
+ return 0;
+ }
+
+ if (!strcmp(var, "user.name")) {
+ strncpy(git_default_name, value, sizeof(git_default_name));
+ return 0;
+ }
+
+ if (!strcmp(var, "user.email")) {
+ strncpy(git_default_email, value, sizeof(git_default_email));
+ return 0;
+ }
+
+ /* Add other config variables here.. */
+ return 0;
+}
+
+int git_config(config_fn_t fn)
+{
+ int ret;
+ FILE *f = fopen(git_path("config"), "r");
+
+ ret = -1;
+ if (f) {
+ config_file = f;
+ config_linenr = 1;
+ ret = git_parse_file(fn);
+ fclose(f);
+ }
+ return ret;
+}
diff --git a/connect.c b/connect.c
index b157cf1cc..b171c5dbc 100644
--- a/connect.c
+++ b/connect.c
@@ -1,6 +1,7 @@
#include "cache.h"
#include "pkt-line.h"
#include "quote.h"
+#include "refs.h"
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -10,7 +11,8 @@
/*
* Read all the refs from the other end
*/
-struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match)
+struct ref **get_remote_heads(int in, struct ref **list,
+ int nr_match, char **match, int ignore_funny)
{
*list = NULL;
for (;;) {
@@ -29,6 +31,11 @@ struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **ma
if (len < 42 || get_sha1_hex(buffer, old_sha1) || buffer[40] != ' ')
die("protocol error: expected sha/ref, got '%s'", buffer);
name = buffer + 41;
+
+ if (ignore_funny && 45 < len && !memcmp(name, "refs/", 5) &&
+ check_ref_format(name + 5))
+ continue;
+
if (nr_match && !path_match(name, nr_match, match))
continue;
ref = xcalloc(1, sizeof(*ref) + len - 40);
@@ -284,6 +291,10 @@ static enum protocol get_protocol(const char *name)
return PROTO_SSH;
if (!strcmp(name, "git"))
return PROTO_GIT;
+ if (!strcmp(name, "git+ssh"))
+ return PROTO_SSH;
+ if (!strcmp(name, "ssh+git"))
+ return PROTO_SSH;
die("I don't handle protocol '%s'", name);
}
diff --git a/convert-objects.c b/convert-objects.c
index 9ad0c7767..a892013f0 100644
--- a/convert-objects.c
+++ b/convert-objects.c
@@ -1,6 +1,5 @@
#define _XOPEN_SOURCE /* glibc2 needs this */
#include <time.h>
-#include <ctype.h>
#include "cache.h"
struct entry {
diff --git a/ctype.c b/ctype.c
new file mode 100644
index 000000000..56bdffa63
--- /dev/null
+++ b/ctype.c
@@ -0,0 +1,23 @@
+/*
+ * Sane locale-independent, ASCII ctype.
+ *
+ * No surprises, and works with signed and unsigned chars.
+ */
+#include "cache.h"
+
+#define SS GIT_SPACE
+#define AA GIT_ALPHA
+#define DD GIT_DIGIT
+
+unsigned char sane_ctype[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, SS, SS, 0, 0, SS, 0, 0, /* 0-15 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-15 */
+ SS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32-15 */
+ DD, DD, DD, DD, DD, DD, DD, DD, DD, DD, 0, 0, 0, 0, 0, 0, /* 48-15 */
+ 0, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, /* 64-15 */
+ AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, 0, 0, 0, 0, 0, /* 80-15 */
+ 0, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, /* 96-15 */
+ AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, 0, 0, 0, 0, 0, /* 112-15 */
+ /* Nothing in the 128.. range */
+};
+
diff --git a/daemon.c b/daemon.c
index f285a8c98..11fa3ed11 100644
--- a/daemon.c
+++ b/daemon.c
@@ -142,7 +142,7 @@ static int upload(char *dir, int dirlen)
* is ok with us doing this.
*/
if ((!export_all_trees && access("git-daemon-export-ok", F_OK)) ||
- access("objects/00", X_OK) ||
+ access("objects/", X_OK) ||
access("HEAD", R_OK)) {
logerror("Not a valid git-daemon-enabled repository: '%s'", dir);
return -1;
diff --git a/date.c b/date.c
index b21cadc4d..63f5a0919 100644
--- a/date.c
+++ b/date.c
@@ -4,7 +4,6 @@
* Copyright (C) Linus Torvalds, 2005
*/
-#include <ctype.h>
#include <time.h>
#include "cache.h"
diff --git a/debian/control b/debian/control
index 0ec89338b..ff33827c1 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,7 @@ Source: git-core
Section: devel
Priority: optional
Maintainer: Junio C Hamano <junkio@cox.net>
-Build-Depends-Indep: libz-dev, libssl-dev, libcurl3-dev, asciidoc (>= 6.0.3), xmlto, debhelper (>= 4.0.0), bc
+Build-Depends-Indep: libz-dev, libssl-dev, libcurl3-dev|libcurl3-gnutls-dev|libcurl3-openssl-dev, asciidoc (>= 6.0.3), xmlto, debhelper (>= 4.0.0), bc
Standards-Version: 3.6.1
Package: git-core
diff --git a/diff-files.c b/diff-files.c
index 5e598322f..8a8f9b6dc 100644
--- a/diff-files.c
+++ b/diff-files.c
@@ -38,6 +38,7 @@ int main(int argc, const char **argv)
const char *prefix = setup_git_directory();
int entries, i;
+ git_config(git_default_config);
diff_setup(&diff_options);
while (1 < argc && argv[1][0] == '-') {
if (!strcmp(argv[1], "-q"))
@@ -80,7 +81,7 @@ int main(int argc, const char **argv)
for (i = 0; i < entries; i++) {
struct stat st;
- unsigned int oldmode;
+ unsigned int oldmode, newmode;
struct cache_entry *ce = active_cache[i];
int changed;
@@ -110,7 +111,13 @@ int main(int argc, const char **argv)
if (!changed && !diff_options.find_copies_harder)
continue;
oldmode = ntohl(ce->ce_mode);
- show_modified(oldmode, DIFF_FILE_CANON_MODE(st.st_mode),
+
+ newmode = DIFF_FILE_CANON_MODE(st.st_mode);
+ if (!trust_executable_bit &&
+ S_ISREG(newmode) && S_ISREG(oldmode) &&
+ ((newmode ^ oldmode) == 0111))
+ newmode = oldmode;
+ show_modified(oldmode, newmode,
ce->sha1, (changed ? null_sha1 : ce->sha1),
ce->name);
}
diff --git a/diff-index.c b/diff-index.c
index 62b36cc8e..c9a9f4c74 100644
--- a/diff-index.c
+++ b/diff-index.c
@@ -15,7 +15,7 @@ static void show_file(const char *prefix,
}
static int get_stat_data(struct cache_entry *ce,
- unsigned char **sha1p, unsigned int *modep)
+ unsigned char ** sha1p, unsigned int *modep)
{
unsigned char *sha1 = ce->sha1;
unsigned int mode = ce->ce_mode;
@@ -35,6 +35,10 @@ static int get_stat_data(struct cache_entry *ce,
changed = ce_match_stat(ce, &st);
if (changed) {
mode = create_ce_mode(st.st_mode);
+ if (!trust_executable_bit &&
+ S_ISREG(mode) && S_ISREG(ce->ce_mode) &&
+ ((mode ^ ce->ce_mode) == 0111))
+ mode = ce->ce_mode;
sha1 = no_sha1;
}
}
@@ -49,7 +53,9 @@ static void show_new_file(struct cache_entry *new)
unsigned char *sha1;
unsigned int mode;
- /* New file in the index: it might actually be different in the working copy */
+ /* New file in the index: it might actually be different in
+ * the working copy.
+ */
if (get_stat_data(new, &sha1, &mode) < 0)
return;
@@ -174,6 +180,7 @@ int main(int argc, const char **argv)
int allow_options = 1;
int i;
+ git_config(git_default_config);
diff_setup(&diff_options);
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
diff --git a/diff-tree.c b/diff-tree.c
index b2d74eb1d..851722037 100644
--- a/diff-tree.c
+++ b/diff-tree.c
@@ -1,4 +1,3 @@
-#include <ctype.h>
#include "cache.h"
#include "diff.h"
#include "commit.h"
@@ -408,6 +407,7 @@ int main(int argc, const char **argv)
unsigned char sha1[2][20];
const char *prefix = setup_git_directory();
+ git_config(git_default_config);
nr_sha1 = 0;
diff_setup(&diff_options);
diff --git a/diff.c b/diff.c
index 7d06b035a..cbb86320a 100644
--- a/diff.c
+++ b/diff.c
@@ -596,15 +596,31 @@ static void run_external_diff(const char *pgm,
remove_tempfile();
}
+static void diff_fill_sha1_info(struct diff_filespec *one)
+{
+ if (DIFF_FILE_VALID(one)) {
+ if (!one->sha1_valid) {
+ struct stat st;
+ if (stat(one->path, &st) < 0)
+ die("stat %s", one->path);
+ if (index_path(one->sha1, one->path, &st, 0))
+ die("cannot hash %s\n", one->path);
+ }
+ }
+ else
+ memset(one->sha1, 0, 20);
+}
+
static void run_diff(struct diff_filepair *p)
{
const char *pgm = external_diff();
- char msg_[PATH_MAX*2+200], *xfrm_msg;
+ char msg[PATH_MAX*2+300], *xfrm_msg;
struct diff_filespec *one;
struct diff_filespec *two;
const char *name;
const char *other;
int complete_rewrite = 0;
+ int len;
if (DIFF_PAIR_UNMERGED(p)) {
/* unmerged */
@@ -616,39 +632,60 @@ static void run_diff(struct diff_filepair *p)
name = p->one->path;
other = (strcmp(name, p->two->path) ? p->two->path : NULL);
one = p->one; two = p->two;
+
+ diff_fill_sha1_info(one);
+ diff_fill_sha1_info(two);
+
+ len = 0;
switch (p->status) {
case DIFF_STATUS_COPIED:
- sprintf(msg_,
- "similarity index %d%%\n"
- "copy from %s\n"
- "copy to %s",
- (int)(0.5 + p->score * 100.0/MAX_SCORE),
- name, other);
- xfrm_msg = msg_;
+ len += snprintf(msg + len, sizeof(msg) - len,
+ "similarity index %d%%\n"
+ "copy from %s\n"
+ "copy to %s\n",
+ (int)(0.5 + p->score * 100.0/MAX_SCORE),
+ name, other);
break;
case DIFF_STATUS_RENAMED:
- sprintf(msg_,
- "similarity index %d%%\n"
- "rename from %s\n"
- "rename to %s",
- (int)(0.5 + p->score * 100.0/MAX_SCORE),
- name, other);
- xfrm_msg = msg_;
+ len += snprintf(msg + len, sizeof(msg) - len,
+ "similarity index %d%%\n"
+ "rename from %s\n"
+ "rename to %s\n",
+ (int)(0.5 + p->score * 100.0/MAX_SCORE),
+ name, other);
break;
case DIFF_STATUS_MODIFIED:
if (p->score) {
- sprintf(msg_,
- "dissimilarity index %d%%",
- (int)(0.5 + p->score * 100.0/MAX_SCORE));
- xfrm_msg = msg_;
+ len += snprintf(msg + len, sizeof(msg) - len,
+ "dissimilarity index %d%%\n",
+ (int)(0.5 + p->score *
+ 100.0/MAX_SCORE));
complete_rewrite = 1;
break;
}
/* fallthru */
default:
- xfrm_msg = NULL;
+ /* nothing */
+ ;
}
+ if (memcmp(one->sha1, two->sha1, 20)) {
+ char one_sha1[41];
+ memcpy(one_sha1, sha1_to_hex(one->sha1), 41);
+
+ len += snprintf(msg + len, sizeof(msg) - len,
+ "index %.7s..%.7s", one_sha1,
+ sha1_to_hex(two->sha1));
+ if (one->mode == two->mode)
+ len += snprintf(msg + len, sizeof(msg) - len,
+ " %06o", one->mode);
+ len += snprintf(msg + len, sizeof(msg) - len, "\n");
+ }
+
+ if (len)
+ msg[--len] = 0;
+ xfrm_msg = len ? msg : NULL;
+
if (!pgm &&
DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
(S_IFMT & one->mode) != (S_IFMT & two->mode)) {
diff --git a/diff.h b/diff.h
index 7f4079c87..2f4a7b463 100644
--- a/diff.h
+++ b/diff.h
@@ -103,7 +103,7 @@ extern void diff_flush(struct diff_options*);
/* these are not diff-raw status letters proper, but used by
* diffcore-filter insn to specify additional restrictions.
*/
-#define DIFF_STATUS_FILTER_AON 'A'
+#define DIFF_STATUS_FILTER_AON '*'
#define DIFF_STATUS_FILTER_BROKEN 'B'
#endif /* DIFF_H */
diff --git a/environment.c b/environment.c
new file mode 100644
index 000000000..1dc7af56c
--- /dev/null
+++ b/environment.c
@@ -0,0 +1,75 @@
+/*
+ * We put all the git config variables in this same object
+ * file, so that programs can link against the config parser
+ * without having to link against all the rest of git.
+ *
+ * In particular, no need to bring in libz etc unless needed,
+ * even if you might want to know where the git directory etc
+ * are.
+ */
+#include "cache.h"
+
+char git_default_email[MAX_GITNAME];
+char git_default_name[MAX_GITNAME];
+int trust_executable_bit = 1;
+
+static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir,
+ *git_graft_file;
+static void setup_git_env(void)
+{
+ git_dir = getenv(GIT_DIR_ENVIRONMENT);
+ if (!git_dir)
+ git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
+ git_object_dir = getenv(DB_ENVIRONMENT);
+ if (!git_object_dir) {
+ git_object_dir = xmalloc(strlen(git_dir) + 9);
+ sprintf(git_object_dir, "%s/objects", git_dir);
+ }
+ git_refs_dir = xmalloc(strlen(git_dir) + 6);
+ sprintf(git_refs_dir, "%s/refs", git_dir);
+ git_index_file = getenv(INDEX_ENVIRONMENT);
+ if (!git_index_file) {
+ git_index_file = xmalloc(strlen(git_dir) + 7);
+ sprintf(git_index_file, "%s/index", git_dir);
+ }
+ git_graft_file = getenv(GRAFT_ENVIRONMENT);
+ if (!git_graft_file)
+ git_graft_file = strdup(git_path("info/grafts"));
+}
+
+char *get_git_dir(void)
+{
+ if (!git_dir)
+ setup_git_env();
+ return git_dir;
+}
+
+char *get_object_directory(void)
+{
+ if (!git_object_dir)
+ setup_git_env();
+ return git_object_dir;
+}
+
+char *get_refs_directory(void)
+{
+ if (!git_refs_dir)
+ setup_git_env();
+ return git_refs_dir;
+}
+
+char *get_index_file(void)
+{
+ if (!git_index_file)
+ setup_git_env();
+ return git_index_file;
+}
+
+char *get_graft_file(void)
+{
+ if (!git_graft_file)
+ setup_git_env();
+ return git_graft_file;
+}
+
+
diff --git a/fetch-pack.c b/fetch-pack.c
index 582f967a7..953c0cf44 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -81,7 +81,7 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
int status;
pid_t pid;
- get_remote_heads(fd[0], &ref, nr_match, match);
+ get_remote_heads(fd[0], &ref, nr_match, match, 1);
if (!ref) {
packet_flush(fd[1]);
die("no matching remote head");
diff --git a/fetch.c b/fetch.c
index 3e073d358..73bde07ae 100644
--- a/fetch.c
+++ b/fetch.c
@@ -165,7 +165,7 @@ static int loop(void)
* the queue because we needed to fetch it first.
*/
if (! (obj->flags & TO_SCAN)) {
- if (!has_sha1_file(obj->sha1) && fetch(obj->sha1)) {
+ if (fetch(obj->sha1)) {
report_missing(obj->type
? obj->type
: "object", obj->sha1);
diff --git a/fsck-objects.c b/fsck-objects.c
index 65cec7d12..17d05363e 100644
--- a/fsck-objects.c
+++ b/fsck-objects.c
@@ -329,9 +329,8 @@ static int fsck_dir(int i, char *path)
DIR *dir = opendir(path);
struct dirent *de;
- if (!dir) {
- return error("missing sha1 directory '%s'", path);
- }
+ if (!dir)
+ return 0;
while ((de = readdir(dir)) != NULL) {
char name[100];
diff --git a/git-am.sh b/git-am.sh
new file mode 100755
index 000000000..c562159a1
--- /dev/null
+++ b/git-am.sh
@@ -0,0 +1,341 @@
+#!/bin/sh
+#
+#
+. git-sh-setup || die "Not a git archive"
+
+files=$(git-diff-index --cached --name-only HEAD) || exit
+if [ "$files" ]; then
+ echo "Dirty index: cannot apply patches (dirty: $files)" >&2
+ exit 1
+fi
+
+usage () {
+ echo >&2 "usage: $0 [--signoff] [--dotest=<dir>] [--utf8] [--3way] <mbox>"
+ echo >&2 " or, when resuming"
+ echo >&2 " $0 [--skip]"
+ exit 1;
+}
+
+stop_here () {
+ echo "$1" >"$dotest/next"
+ exit 1
+}
+
+go_next () {
+ rm -f "$dotest/$msgnum" "$dotest/msg" "$dotest/msg-clean" \
+ "$dotest/patch" "$dotest/info"
+ echo "$next" >"$dotest/next"
+ this=$next
+}
+
+fall_back_3way () {
+ O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
+
+ rm -fr "$dotest"/patch-merge-*
+ mkdir "$dotest/patch-merge-tmp-dir"
+
+ # First see if the patch records the index info that we can use.
+ if git-apply --show-index-info "$dotest/patch" \
+ >"$dotest/patch-merge-index-info" 2>/dev/null &&
+ GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
+ git-update-index --index-info <"$dotest/patch-merge-index-info" &&
+ GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
+ git-write-tree >"$dotest/patch-merge-base+" &&
+ # index has the base tree now.
+ (
+ cd "$dotest/patch-merge-tmp-dir" &&
+ GIT_INDEX_FILE="../patch-merge-tmp-index" \
+ GIT_OBJECT_DIRECTORY="$O_OBJECT" \
+ git-apply --index <../patch
+ )
+ then
+ echo Using index info to reconstruct a base tree...
+ mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base"
+ mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index"
+ else
+ # Otherwise, try nearby trees that can be used to apply the
+ # patch.
+ (
+ N=10
+
+ # Hoping the patch is against our recent commits...
+ git-rev-list --max-count=$N HEAD
+
+ # or hoping the patch is against known tags...
+ git-ls-remote --tags .
+ ) |
+ while read base junk
+ do
+ # See if we have it as a tree...
+ git-cat-file tree "$base" >/dev/null 2>&1 || continue
+
+ rm -fr "$dotest"/patch-merge-* &&
+ mkdir "$dotest/patch-merge-tmp-dir" || break
+ (
+ cd "$dotest/patch-merge-tmp-dir" &&
+ GIT_INDEX_FILE=../patch-merge-tmp-index &&
+ GIT_OBJECT_DIRECTORY="$O_OBJECT" &&
+ export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY &&
+ git-read-tree "$base" &&
+ git-apply --index &&
+ mv ../patch-merge-tmp-index ../patch-merge-index &&
+ echo "$base" >../patch-merge-base
+ ) <"$dotest/patch" 2>/dev/null && break
+ done
+ fi
+
+ test -f "$dotest/patch-merge-index" &&
+ his_tree=$(GIT_INDEX_FILE="$dotest/patch-merge-index" git-write-tree) &&
+ orig_tree=$(cat "$dotest/patch-merge-base") &&
+ rm -fr "$dotest"/patch-merge-* || exit 1
+
+ echo Falling back to patching base and 3-way merge...
+
+ # This is not so wrong. Depending on which base we picked,
+ # orig_tree may be wildly different from ours, but his_tree
+ # has the same set of wildly different changes in parts the
+ # patch did not touch, so resolve ends up cancelling them,
+ # saying that we reverted all those changes.
+
+ git-merge-resolve $orig_tree -- HEAD $his_tree || {
+ echo Failed to merge in the changes.
+ exit 1
+ }
+}
+
+prec=4
+dotest=.dotest sign= utf8= keep= skip= interactive=
+
+while case "$#" in 0) break;; esac
+do
+ case "$1" in
+ -d=*|--d=*|--do=*|--dot=*|--dote=*|--dotes=*|--dotest=*)
+ dotest=`expr "$1" : '-[^=]*=\(.*\)'`; shift ;;
+ -d|--d|--do|--dot|--dote|--dotes|--dotest)
+ case "$#" in 1) usage ;; esac; shift
+ dotest="$1"; shift;;
+
+ -i|--i|--in|--int|--inte|--inter|--intera|--interac|--interact|\
+ --interacti|--interactiv|--interactive)
+ interactive=t; shift ;;
+
+ -3|--3|--3w|--3wa|--3way)
+ threeway=t; shift ;;
+ -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
+ sign=t; shift ;;
+ -u|--u|--ut|--utf|--utf8)
+ utf8=t; shift ;;
+ -k|--k|--ke|--kee|--keep)
+ keep=t; shift ;;
+
+ --sk|--ski|--skip)
+ skip=t; shift ;;
+
+ --)
+ shift; break ;;
+ -*)
+ usage ;;
+ *)
+ break ;;
+ esac
+done
+
+if test -d "$dotest" &&
+ last=$(cat "$dotest/last") &&
+ next=$(cat "$dotest/next") &&
+ test $# != 0 &&
+ test "$next" -gt "$last"
+then
+ rm -fr "$dotest"
+fi
+
+if test -d "$dotest"
+then
+ test ",$#," = ",0," ||
+ die "previous dotest directory $dotest still exists but mbox given."
+else
+ # Make sure we are not given --skip
+ test ",$skip," = ,, ||
+ die "we are not resuming."
+
+ # Start afresh.
+ mkdir -p "$dotest" || exit
+
+ # cat does the right thing for us, including '-' to mean
+ # standard input.
+ cat "$@" |
+ git-mailsplit -d$prec "$dotest/" >"$dotest/last" || {
+ rm -fr "$dotest"
+ exit 1
+ }
+
+ echo "$sign" >"$dotest/sign"
+ echo "$utf8" >"$dotest/utf8"
+ echo "$keep" >"$dotest/keep"
+ echo 1 >"$dotest/next"
+fi
+
+if test "$(cat "$dotest/utf8")" = t
+then
+ utf8=-u
+fi
+if test "$(cat "$dotest/keep")" = t
+then
+ keep=-k
+fi
+if test "$(cat "$dotest/sign")" = t
+then
+ SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e '
+ s/>.*/>/
+ s/^/Signed-off-by: /'
+ `
+else
+ SIGNOFF=
+fi
+
+last=`cat "$dotest/last"`
+this=`cat "$dotest/next"`
+if test "$skip" = t
+then
+ this=`expr "$this" + 1`
+fi
+
+if test "$this" -gt "$last"
+then
+ echo Nothing to do.
+ rm -fr "$dotest"
+ exit
+fi
+
+while test "$this" -le "$last"
+do
+ msgnum=`printf "%0${prec}d" $this`
+ next=`expr "$this" + 1`
+ test -f "$dotest/$msgnum" || {
+ go_next
+ continue
+ }
+ git-mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
+ <"$dotest/$msgnum" >"$dotest/info" ||
+ stop_here $this
+ git-stripspace < "$dotest/msg" > "$dotest/msg-clean"
+
+ GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")"
+ GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")"
+ GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")"
+ SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
+
+ case "$keep_subject" in -k) SUBJECT="[PATCH] $SUBJECT" ;; esac
+ if test '' != "$SIGNOFF"
+ then
+ LAST_SIGNED_OFF_BY=`
+ sed -ne '/^Signed-off-by: /p' "$dotest/msg-clean" |
+ tail -n 1
+ `
+ ADD_SIGNOFF=$(test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
+ test '' = "$LAST_SIGNED_OFF_BY" && echo
+ echo "$SIGNOFF"
+ })
+ else
+ ADD_SIGNOFF=
+ fi
+ {
+ echo "$SUBJECT"
+ if test -s "$dotest/msg-clean"
+ then
+ echo
+ cat "$dotest/msg-clean"
+ fi
+ if test '' != "$ADD_SIGNOFF"
+ then
+ echo "$ADD_SIGNOFF"
+ fi
+ } >"$dotest/final-commit"
+
+ if test "$interactive" = t
+ then
+ test -t 0 ||
+ die "cannot be interactive without stdin connected to a terminal."
+ action=again
+ while test "$action" = again
+ do
+ echo "Commit Body is:"
+ echo "--------------------------"
+ cat "$dotest/final-commit"
+ echo "--------------------------"
+ echo -n "Apply? [y]es/[n]o/[e]dit/[a]ccept all "
+ read reply
+ case "$reply" in
+ y*|Y*) action=yes ;;
+ a*|A*) action=yes interactive= ;;
+ n*|N*) action=skip ;;
+ e*|E*) "${VISUAL:-${EDITOR:-vi}}" "$dotest/final-commit"
+ action=again ;;
+ esac
+ done
+ else
+ action=yes
+ fi
+
+ if test $action = skip
+ then
+ go_next
+ continue
+ fi
+
+ if test -x "$GIT_DIR"/hooks/applypatch-msg
+ then
+ "$GIT_DIR"/hooks/applypatch-msg "$dotest/final-commit" ||
+ stop_here $this
+ fi
+
+ echo
+ echo "Applying '$SUBJECT'"
+ echo
+
+ git-apply --index "$dotest/patch"; apply_status=$?
+ if test $apply_status = 1 && test "$threeway" = t
+ then
+ if (fall_back_3way)
+ then
+ # Applying the patch to an earlier tree and merging the
+ # result may have produced the same tree as ours.
+ changed="$(git-diff-index --cached --name-only -z HEAD)"
+ if test '' = "$changed"
+ then
+ echo No changes -- Patch already applied.
+ go_next
+ continue
+ fi
+ # clear apply_status -- we have successfully merged.
+ apply_status=0
+ fi
+ fi
+ if test $apply_status != 0
+ then
+ echo Patch failed at $msgnum.
+ stop_here $this
+ fi
+
+ if test -x "$GIT_DIR"/hooks/pre-applypatch
+ then
+ "$GIT_DIR"/hooks/pre-applypatch || stop_here $this
+ fi
+
+ tree=$(git-write-tree) &&
+ echo Wrote tree $tree &&
+ parent=$(git-rev-parse --verify HEAD) &&
+ commit=$(git-commit-tree $tree -p $parent <"$dotest/final-commit") &&
+ echo Committed: $commit &&
+ git-update-ref HEAD $commit $parent ||
+ stop_here $this
+
+ if test -x "$GIT_DIR"/hooks/post-applypatch
+ then
+ "$GIT_DIR"/hooks/post-applypatch
+ fi
+
+ go_next
+done
+
+rm -fr "$dotest"
diff --git a/git-applymbox.sh b/git-applymbox.sh
index e2bfd0287..4e77132ab 100755
--- a/git-applymbox.sh
+++ b/git-applymbox.sh
@@ -9,8 +9,6 @@
## You give it a mbox-format collection of emails, and it will try to
## apply them to the kernel using "applypatch"
##
-## applymbox [-u] [-k] [-q] (-c .dotest/msg-number | mail_archive) [Signoff_file]"
-##
## The patch application may fail in the middle. In which case:
## (1) look at .dotest/patch and fix it up to apply
## (2) re-run applymbox with -c .dotest/msg-number for the current one.
@@ -21,7 +19,7 @@
. git-sh-setup || die "Not a git archive"
usage () {
- echo >&2 "applymbox [-u] [-k] [-q] (-c .dotest/<num> | mbox) [signoff]"
+ echo >&2 "applymbox [-u] [-k] [-q] [-m] (-c .dotest/<num> | mbox) [signoff]"
exit 1
}
@@ -33,6 +31,7 @@ do
-k) keep_subject=-k ;;
-q) query_apply=t ;;
-c) continue="$2"; resume=f; shift ;;
+ -m) fallback_3way=t ;;
-*) usage ;;
*) break ;;
esac
@@ -43,7 +42,8 @@ case "$continue" in
'')
rm -rf .dotest
mkdir .dotest
- git-mailsplit "$1" .dotest || exit 1
+ num_msgs=$(git-mailsplit "$1" .dotest) || exit 1
+ echo "$num_msgs patch(es) to process."
shift
esac
@@ -56,6 +56,9 @@ fi
case "$query_apply" in
t) touch .dotest/.query_apply
esac
+case "$fall_back_3way" in
+t) : >.dotest/.3way
+esac
case "$keep_subject" in
-k) : >.dotest/.keep_subject
esac
@@ -80,7 +83,11 @@ do
do
git-applypatch .dotest/msg-clean .dotest/patch .dotest/info "$signoff"
case "$?" in
- 0 | 2 )
+ 0)
+ # Remove the cleanly applied one to reduce clutter.
+ rm -f .dotest/$i
+ ;;
+ 2)
# 2 is a special exit code from applypatch to indicate that
# the patch wasn't applied, but continue anyway
;;
diff --git a/git-applypatch.sh b/git-applypatch.sh
index 14635d9bc..66fd19ae2 100755
--- a/git-applypatch.sh
+++ b/git-applypatch.sh
@@ -22,6 +22,8 @@ query_apply=.dotest/.query_apply
## if this file exists.
keep_subject=.dotest/.keep_subject
+## We do not attempt the 3-way merge fallback unless this file exists.
+fall_back_3way=.dotest/.3way
MSGFILE=$1
PATCHFILE=$2
@@ -102,10 +104,79 @@ echo Applying "'$SUBJECT'"
echo
git-apply --index "$PATCHFILE" || {
+
+ # git-apply exits with status 1 when the patch does not apply,
+ # but it die()s with other failures, most notably upon corrupt
+ # patch. In the latter case, there is no point to try applying
+ # it to another tree and do 3-way merge.
+ test $? = 1 || exit 1
+
+ test -f "$fall_back_3way" || exit 1
+
# Here if we know which revision the patch applies to,
# we create a temporary working tree and index, apply the
# patch, and attempt 3-way merge with the resulting tree.
- exit 1
+
+ O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
+ rm -fr .patch-merge-*
+
+ (
+ N=10
+
+ # if the patch records the base tree...
+ sed -ne '
+ /^diff /q
+ /^applies-to: \([0-9a-f]*\)$/{
+ s//\1/p
+ q
+ }
+ ' "$PATCHFILE"
+
+ # or hoping the patch is against our recent commits...
+ git-rev-list --max-count=$N HEAD
+
+ # or hoping the patch is against known tags...
+ git-ls-remote --tags .
+ ) |
+ while read base junk
+ do
+ # Try it if we have it as a tree.
+ git-cat-file tree "$base" >/dev/null 2>&1 || continue
+
+ rm -fr .patch-merge-tmp-* &&
+ mkdir .patch-merge-tmp-dir || break
+ (
+ cd .patch-merge-tmp-dir &&
+ GIT_INDEX_FILE=../.patch-merge-tmp-index &&
+ GIT_OBJECT_DIRECTORY="$O_OBJECT" &&
+ export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY &&
+ git-read-tree "$base" &&
+ git-apply --index &&
+ mv ../.patch-merge-tmp-index ../.patch-merge-index &&
+ echo "$base" >../.patch-merge-base
+ ) <"$PATCHFILE" 2>/dev/null && break
+ done
+
+ test -f .patch-merge-index &&
+ his_tree=$(GIT_INDEX_FILE=.patch-merge-index git-write-tree) &&
+ orig_tree=$(cat .patch-merge-base) &&
+ rm -fr .patch-merge-* || exit 1
+
+ echo Falling back to patching base and 3-way merge using $orig_tree...
+
+ # This is not so wrong. Depending on which base we picked,
+ # orig_tree may be wildly different from ours, but his_tree
+ # has the same set of wildly different changes in parts the
+ # patch did not touch, so resolve ends up cancelling them,
+ # saying that we reverted all those changes.
+
+ if git-merge-resolve $orig_tree -- HEAD $his_tree
+ then
+ echo Done.
+ else
+ echo Failed to merge in the changes.
+ exit 1
+ fi
}
if test -x "$GIT_DIR"/hooks/pre-applypatch
diff --git a/git-branch.sh b/git-branch.sh
index 074229c20..e2db9063d 100755
--- a/git-branch.sh
+++ b/git-branch.sh
@@ -13,38 +13,42 @@ If two arguments, create a new branch <branchname> based off of <start-point>.
}
delete_branch () {
- option="$1" branch_name="$2"
+ option="$1"
+ shift
headref=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD |
sed -e 's|^refs/heads/||')
- case ",$headref," in
- ",$branch_name,")
- die "Cannot delete the branch you are on." ;;
- ,,)
- die "What branch are you on anyway?" ;;
- esac
- branch=$(cat "$GIT_DIR/refs/heads/$branch_name") &&
- branch=$(git-rev-parse --verify "$branch^0") ||
- die "Seriously, what branch are you talking about?"
- case "$option" in
- -D)
- ;;
- *)
- mbs=$(git-merge-base -a "$branch" HEAD | tr '\012' ' ')
- case " $mbs " in
- *' '$branch' '*)
- # the merge base of branch and HEAD contains branch --
- # which means that the HEAD contains everything in the HEAD.
+ for branch_name
+ do
+ case ",$headref," in
+ ",$branch_name,")
+ die "Cannot delete the branch you are on." ;;
+ ,,)
+ die "What branch are you on anyway?" ;;
+ esac
+ branch=$(cat "$GIT_DIR/refs/heads/$branch_name") &&
+ branch=$(git-rev-parse --verify "$branch^0") ||
+ die "Seriously, what branch are you talking about?"
+ case "$option" in
+ -D)
;;
*)
- echo >&2 "The branch '$branch_name' is not a strict subset of your current HEAD.
-If you are sure you want to delete it, run 'git branch -D $branch_name'."
- exit 1
+ mbs=$(git-merge-base -a "$branch" HEAD | tr '\012' ' ')
+ case " $mbs " in
+ *' '$branch' '*)
+ # the merge base of branch and HEAD contains branch --
+ # which means that the HEAD contains everything in the HEAD.
+ ;;
+ *)
+ echo >&2 "The branch '$branch_name' is not a strict subset of your current HEAD.
+ If you are sure you want to delete it, run 'git branch -D $branch_name'."
+ exit 1
+ ;;
+ esac
;;
esac
- ;;
- esac
- rm -f "$GIT_DIR/refs/heads/$branch_name"
- echo "Deleted branch $branch_name."
+ rm -f "$GIT_DIR/refs/heads/$branch_name"
+ echo "Deleted branch $branch_name."
+ done
exit 0
}
@@ -52,7 +56,7 @@ while case "$#,$1" in 0,*) break ;; *,-*) ;; *) break ;; esac
do
case "$1" in
-d | -D)
- delete_branch "$1" "$2"
+ delete_branch "$@"
exit
;;
--)
@@ -93,6 +97,9 @@ branchname="$1"
rev=$(git-rev-parse --verify "$head") || exit
-[ -e "$GIT_DIR/refs/heads/$branchname" ] && die "$branchname already exists"
+[ -e "$GIT_DIR/refs/heads/$branchname" ] &&
+ die "$branchname already exists."
+git-check-ref-format "heads/$branchname" ||
+ die "we do not like '$branchname' as a branch name."
echo $rev > "$GIT_DIR/refs/heads/$branchname"
diff --git a/git-checkout.sh b/git-checkout.sh
index c3825904b..2c053a33c 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -17,6 +17,8 @@ while [ "$#" != "0" ]; do
die "git checkout: -b needs a branch name"
[ -e "$GIT_DIR/refs/heads/$newbranch" ] &&
die "git checkout: branch $newbranch already exists"
+ git-check-ref-format "heads/$newbranch" ||
+ die "we do not like '$newbranch' as a branch name."
;;
"-f")
force=1
diff --git a/git-commit.sh b/git-commit.sh
index 5e85b5464..5bdee54a4 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -129,7 +129,7 @@ then
elif test "$use_commit" != ""
then
git-cat-file commit "$use_commit" | sed -e '1,/^$/d'
-fi | git-stripspace >.editmsg
+fi | git-stripspace >"$GIT_DIR"/COMMIT_EDITMSG
case "$signoff" in
t)
@@ -139,7 +139,7 @@ t)
s/>.*/>/
s/^/Signed-off-by: /
'
- } >>.editmsg
+ } >>"$GIT_DIR"/COMMIT_EDITMSG
;;
esac
@@ -153,7 +153,7 @@ if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
echo "# $GIT_DIR/MERGE_HEAD"
echo "# and try again"
echo "#"
-fi >>.editmsg
+fi >>"$GIT_DIR"/COMMIT_EDITMSG
PARENTS="-p HEAD"
if GIT_DIR="$GIT_DIR" git-rev-parse --verify HEAD >/dev/null 2>&1
@@ -197,16 +197,16 @@ else
fi
PARENTS=""
fi
-git-status >>.editmsg
-if [ "$?" != "0" -a ! -f $GIT_DIR/MERGE_HEAD ]
+git-status >>"$GIT_DIR"/COMMIT_EDITMSG
+if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ]
then
- rm -f .editmsg
+ rm -f "$GIT_DIR/COMMIT_EDITMSG"
git-status
exit 1
fi
case "$no_edit" in
'')
- ${VISUAL:-${EDITOR:-vi}} .editmsg
+ ${VISUAL:-${EDITOR:-vi}} "$GIT_DIR/COMMIT_EDITMSG"
;;
esac
@@ -214,16 +214,20 @@ case "$verify" in
t)
if test -x "$GIT_DIR"/hooks/commit-msg
then
- "$GIT_DIR"/hooks/commit-msg .editmsg || exit
+ "$GIT_DIR"/hooks/commit-msg "$GIT_DIR"/COMMIT_EDITMSG || exit
fi
esac
-grep -v '^#' < .editmsg | git-stripspace > .cmitmsg
-grep -v -i '^Signed-off-by' .cmitmsg >.cmitchk
-if test -s .cmitchk
+grep -v '^#' < "$GIT_DIR"/COMMIT_EDITMSG |
+git-stripspace > "$GIT_DIR"/COMMIT_MSG
+
+if cnt=`grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
+ git-stripspace |
+ wc -l` &&
+ test 0 -lt $cnt
then
tree=$(git-write-tree) &&
- commit=$(cat .cmitmsg | git-commit-tree $tree $PARENTS) &&
+ commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) &&
git-update-ref HEAD $commit $current &&
rm -f -- "$GIT_DIR/MERGE_HEAD"
else
@@ -231,7 +235,7 @@ else
false
fi
ret="$?"
-rm -f .cmitmsg .editmsg .cmitchk
+rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG"
if test -x "$GIT_DIR"/hooks/post-commit && test "$ret" = 0
then
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index cc0eed294..0621dc3e2 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -487,7 +487,10 @@ unless($pid) {
my @opt;
@opt = split(/,/,$opt_p) if defined $opt_p;
unshift @opt, '-z', $opt_z if defined $opt_z;
- exec("cvsps",@opt,"-u","-A","--cvs-direct",'--root',$opt_d,$cvs_tree);
+ unless ($opt_p =~ m/--no-cvs-direct/) {
+ push @opt, '--cvs-direct';
+ }
+ exec("cvsps",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
die "Could not start cvsps: $!\n";
}
@@ -587,7 +590,7 @@ my $commit = sub {
push @par, '-p', $mparent;
print OUT "Merge parent branch: $mparent\n" if $opt_v;
}
- }
+ }
}
exec("env",
diff --git a/git-fetch.sh b/git-fetch.sh
index 61da6a9e3..0cb1596f5 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -5,6 +5,10 @@
_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+LF='
+'
+IFS="$LF"
+
tags=
append=
force=
@@ -49,7 +53,7 @@ rsync_slurped_objects=
if test "" = "$append"
then
- : >$GIT_DIR/FETCH_HEAD
+ : >"$GIT_DIR/FETCH_HEAD"
fi
append_fetch_head () {
@@ -86,11 +90,11 @@ append_fetch_head () {
if git-cat-file commit "$head_" >/dev/null 2>&1
then
headc_=$(git-rev-parse --verify "$head_^0") || exit
- echo "$headc_ $not_for_merge_ $note_" >>$GIT_DIR/FETCH_HEAD
+ echo "$headc_ $not_for_merge_ $note_" >>"$GIT_DIR/FETCH_HEAD"
echo >&2 "* committish: $head_"
echo >&2 " $note_"
else
- echo "$head_ not-for-merge $note_" >>$GIT_DIR/FETCH_HEAD
+ echo "$head_ not-for-merge $note_" >>"$GIT_DIR/FETCH_HEAD"
echo >&2 "* non-commit: $head_"
echo >&2 " $note_"
fi
@@ -170,11 +174,15 @@ esac
reflist=$(get_remote_refs_for_fetch "$@")
if test "$tags"
then
- taglist=$(git-ls-remote --tags "$remote" | awk '{ print "."$2":"$2 }')
+ taglist=$(git-ls-remote --tags "$remote" |
+ sed -e '
+ /\^{}$/d
+ s/^[^ ]* //
+ s/.*/&:&/')
if test "$#" -gt 1
then
# remote URL plus explicit refspecs; we need to merge them.
- reflist="$reflist $taglist"
+ reflist="$reflist$LF$taglist"
else
# No explicit refspecs; fetch tags only.
reflist=$taglist
@@ -183,7 +191,7 @@ fi
for ref in $reflist
do
- refs="$refs $ref"
+ refs="$refs$LF$ref"
# These are relative path from $GIT_DIR, typically starting at refs/
# but may be HEAD
@@ -204,7 +212,7 @@ do
remote_name=$(expr "$ref" : '\([^:]*\):')
local_name=$(expr "$ref" : '[^:]*:\(.*\)')
- rref="$rref $remote_name"
+ rref="$rref$LF$remote_name"
# There are transports that can fetch only one head at a time...
case "$remote" in
@@ -212,7 +220,12 @@ do
if [ -n "$GIT_SSL_NO_VERIFY" ]; then
curl_extra_args="-k"
fi
- head=$(curl -nsf $curl_extra_args "$remote/$remote_name") &&
+ remote_name_quoted=$(perl -e '
+ my $u = $ARGV[0];
+ $u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg;
+ print "$u";
+ ' "$remote_name")
+ head=$(curl -nsf $curl_extra_args "$remote/$remote_name_quoted") &&
expr "$head" : "$_x40\$" >/dev/null ||
die "Failed to fetch $remote_name from $remote"
echo >&2 Fetching "$remote_name from $remote" using http
@@ -262,6 +275,7 @@ case "$remote" in
http://* | https://* | rsync://* )
;; # we are already done.
*)
+ IFS=" $LF"
(
git-fetch-pack "$remote" $rref || echo failed "$remote"
) |
diff --git a/git-findtags.perl b/git-findtags.perl
new file mode 100755
index 000000000..745affe3a
--- /dev/null
+++ b/git-findtags.perl
@@ -0,0 +1,94 @@
+#!/usr/bin/perl -w
+#
+# Copyright (c) 2005 Martin Langhoff
+#
+# Walk the tags and find if they match a commit
+# expects a SHA1 of a commit. Option -t enables
+# searching trees too.
+#
+
+use strict;
+use File::Basename;
+use File::Find;
+use Getopt::Std;
+
+my $git_dir = $ENV{GIT_DIR} || '.git';
+$git_dir =~ s|/$||; # chomp trailing slash
+
+# options
+our $opt_t;
+getopts("t") || usage();
+
+my @tagfiles = `find $git_dir/refs/tags -follow -type f`; # haystack
+my $target = shift @ARGV; # needle
+unless ($target) {
+ usage();
+}
+
+# drive the processing from the find hook
+# slower, safer (?) than the find utility
+find( { wanted => \&process,
+ no_chdir => 1,
+ follow => 1,
+ }, "$git_dir/refs/tags");
+
+
+sub process {
+ my ($dev,$ino,$mode,$nlink,$uid,$gid);
+
+ # process only regular files
+ unless ((($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) && -f _) {
+ return 1; # ignored anyway
+ }
+
+ my $tagfile = $_;
+ chomp $tagfile;
+ my $tagname = substr($tagfile, length($git_dir.'/refs/tags/'));
+
+ my $tagid = quickread($tagfile);
+ chomp $tagid;
+
+ # is it just a soft tag?
+ if ($tagid eq $target) {
+ print "$tagname\n";
+ return 1; # done with this tag
+ }
+
+ # grab the first 2 lines (the whole tag could be large)
+ my $tagobj = `git-cat-file tag $tagid | head -n2 `;
+ if ($tagobj =~ m/^type commit$/m) { # only deal with commits
+
+ if ($tagobj =~ m/^object $target$/m) { # match on the commit
+ print "$tagname\n";
+
+ } elsif ( $opt_t && # follow the commit
+ $tagobj =~ m/^object (\S+)$/m) { # and try to match trees
+ my $commitid = $1;
+ my $commitobj = `git-cat-file commit $commitid | head -n1`;
+ chomp $commitobj;
+ $commitobj =~ m/^tree (\S+)$/;
+ my $treeid = $1;
+ if ($target eq $treeid) {
+ print "$tagname\n";
+ }
+ }
+ }
+}
+
+sub quickread {
+ my $file = shift;
+ local $/; # undef: slurp mode
+ open FILE, "<$file"
+ or die "Cannot open $file : $!";
+ my $content = <FILE>;
+ close FILE;
+ return $content;
+}
+
+sub usage {
+ print STDERR <<END;
+Usage: ${\basename $0} # find tags for a commit or tree
+ [ -t ] <commit-or-tree-sha1>
+END
+ exit(1);
+}
diff --git a/git-format-patch.sh b/git-format-patch.sh
index 9378219d8..0207ab18a 100755
--- a/git-format-patch.sh
+++ b/git-format-patch.sh
@@ -6,7 +6,9 @@
. git-sh-setup || die "Not a git archive."
usage () {
- echo >&2 "usage: $0"' [-n] [-o dir] [--keep-subject] [--mbox] [--check] [--signoff] [-<diff options>...] upstream [ our-head ]
+ echo >&2 "usage: $0"' [-n] [-o dir | --stdout] [--keep-subject] [--mbox]
+ [--check] [--signoff] [-<diff options>...]
+ ( from..to ... | upstream [ our-head ] )
Prepare each commit with its patch since our-head forked from upstream,
one file per patch, for e-mail submission. Each output file is
@@ -49,6 +51,8 @@ do
numbered=t ;;
-s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
signoff=t ;;
+ --st|--std|--stdo|--stdou|--stdout)
+ stdout=t mbox=t date=t author=t ;;
-o=*|--o=*|--ou=*|--out=*|--outp=*|--outpu=*|--output=*|--output-=*|\
--output-d=*|--output-di=*|--output-dir=*|--output-dire=*|\
--output-direc=*|--output-direct=*|--output-directo=*|\
@@ -73,25 +77,70 @@ tt)
die '--keep-subject and --numbered are incompatible.' ;;
esac
-rev1= rev2=
-case "$#" in
-2)
- rev1="$1" rev2="$2" ;;
-1)
- case "$1" in
- *..*)
- rev1=`expr "$1" : '\(.*\)\.\.'`
- rev2=`expr "$1" : '.*\.\.\(.*\)'`
+tmp=.tmp-series$$
+trap 'rm -f $tmp-*' 0 1 2 3 15
+
+series=$tmp-series
+commsg=$tmp-commsg
+filelist=$tmp-files
+
+# Backward compatible argument parsing hack.
+#
+# Historically, we supported:
+# 1. "rev1" is equivalent to "rev1..HEAD"
+# 2. "rev1..rev2"
+# 3. "rev1" "rev2 is equivalent to "rev1..rev2"
+#
+# We want to take a sequence of "rev1..rev2" in general.
+
+case "$#,$1" in
+1,?*..?*)
+ # single "rev1..rev2"
;;
- *)
- rev1="$1"
- rev2="HEAD"
+1,*)
+ # single rev1
+ set x "$1..HEAD"
+ shift
+ ;;
+2,?*..?*)
+ # not traditional "rev1" "rev2"
+ ;;
+2,*)
+ set x "$1..$2"
+ shift
;;
- esac ;;
-*)
- usage ;;
esac
+# Now we have what we want in $@
+for revpair
+do
+ case "$revpair" in
+ ?*..?*)
+ rev1=`expr "$revpair" : '\(.*\)\.\.'`
+ rev2=`expr "$revpair" : '.*\.\.\(.*\)'`
+ ;;
+ *)
+ usage
+ ;;
+ esac
+ git-rev-parse --verify "$rev1^0" >/dev/null 2>&1 ||
+ die "Not a valid rev $rev1 ($revpair)"
+ git-rev-parse --verify "$rev2^0" >/dev/null 2>&1 ||
+ die "Not a valid rev $rev2 ($revpair)"
+ git-cherry -v "$rev1" "$rev2" |
+ while read sign rev comment
+ do
+ case "$sign" in
+ '-')
+ echo >&2 "Merged already: $comment"
+ ;;
+ *)
+ echo $rev
+ ;;
+ esac
+ done
+done >$series
+
me=`git-var GIT_AUTHOR_IDENT | sed -e 's/>.*/>/'`
case "$outdir" in
@@ -100,13 +149,6 @@ case "$outdir" in
esac
test -d "$outdir" || mkdir -p "$outdir" || exit
-tmp=.tmp-series$$
-trap 'rm -f $tmp-*' 0 1 2 3 15
-
-series=$tmp-series
-commsg=$tmp-commsg
-filelist=$tmp-files
-
titleScript='
/./d
/^$/n
@@ -128,38 +170,7 @@ whosepatchScript='
q
}'
-git-cherry -v "$rev1" "$rev2" |
-while read sign rev comment
-do
- case "$sign" in
- '-')
- echo >&2 "Merged already: $comment"
- ;;
- *)
- echo $rev
- ;;
- esac
-done >$series
-
-total=`wc -l <$series | tr -dc "[0-9]"`
-i=1
-while read commit
-do
- git-cat-file commit "$commit" | git-stripspace >$commsg
- title=`sed -ne "$titleScript" <$commsg`
- case "$numbered" in
- '') num= ;;
- *)
- case $total in
- 1) num= ;;
- *) num=' '`printf "%d/%d" $i $total` ;;
- esac
- esac
-
- file=`printf '%04d-%stxt' $i "$title"`
- i=`expr "$i" + 1`
- echo "* $file"
- {
+process_one () {
mailScript='
/./d
/^$/n'
@@ -178,6 +189,7 @@ do
echo 'From nobody Mon Sep 17 00:00:00 2001' ;# UNIX "From" line
;;
esac
+
eval "$(sed -ne "$whosepatchScript" $commsg)"
test "$author,$au" = ",$me" || {
mailScript="$mailScript"'
@@ -196,7 +208,9 @@ Date: '"$ad"
n
b body'
- sed -ne "$mailScript" <$commsg
+ (cat $commsg ; echo; echo) |
+ sed -ne "$mailScript" |
+ git-stripspace
test "$signoff" = "t" && {
offsigner=`git-var GIT_COMMITTER_IDENT | sed -e 's/>.*/>/'`
@@ -222,14 +236,39 @@ Date: '"$ad"
echo
;;
esac
- } >"$outdir$file"
- case "$check" in
- t)
- # This is slightly modified from Andrew Morton's Perfect Patch.
- # Lines you introduce should not have trailing whitespace.
- # Also check for an indentation that has SP before a TAB.
- grep -n '^+\([ ]* .*\|.*[ ]\)$' "$outdir$file"
-
- : do not exit with non-zero because we saw no problem in the last one.
+}
+
+total=`wc -l <$series | tr -dc "[0-9]"`
+i=1
+while read commit
+do
+ git-cat-file commit "$commit" | git-stripspace >$commsg
+ title=`sed -ne "$titleScript" <$commsg`
+ case "$numbered" in
+ '') num= ;;
+ *)
+ case $total in
+ 1) num= ;;
+ *) num=' '`printf "%d/%d" $i $total` ;;
+ esac
esac
+
+ file=`printf '%04d-%stxt' $i "$title"`
+ if test '' = "$stdout"
+ then
+ echo "* $file"
+ process_one >"$outdir$file"
+ if test t = "$check"
+ then
+ # This is slightly modified from Andrew Morton's Perfect Patch.
+ # Lines you introduce should not have trailing whitespace.
+ # Also check for an indentation that has SP before a TAB.
+ grep -n '^+\([ ]* .*\|.*[ ]\)$' "$outdir$file"
+ :
+ fi
+ else
+ echo >&2 "* $file"
+ process_one
+ fi
+ i=`expr "$i" + 1`
done <$series
diff --git a/git-ls-remote.sh b/git-ls-remote.sh
index bfbd5a4d5..f0f0b07f6 100755
--- a/git-ls-remote.sh
+++ b/git-ls-remote.sh
@@ -1,6 +1,6 @@
#!/bin/sh
#
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
usage () {
echo >&2 "usage: $0 [--heads] [--tags] <repository> <refs>..."
diff --git a/git-merge-recursive.py b/git-merge-recursive.py
index b80a86035..626d85493 100755
--- a/git-merge-recursive.py
+++ b/git-merge-recursive.py
@@ -4,7 +4,7 @@ import sys, math, random, os, re, signal, tempfile, stat, errno, traceback
from heapq import heappush, heappop
from sets import Set
-sys.path.append('@@GIT_PYTHON_PATH@@')
+sys.path.append('''@@GIT_PYTHON_PATH@@''')
from gitMergeCommon import *
originalIndexFile = os.environ.get('GIT_INDEX_FILE',
diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index 4d8a572a9..aea7b0e54 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
get_data_source () {
case "$1" in
@@ -94,6 +94,12 @@ canon_refs_list_for_fetch () {
heads/* | tags/* ) local="refs/$local" ;;
*) local="refs/heads/$local" ;;
esac
+
+ if local_ref_name=$(expr "$local" : 'refs/\(.*\)')
+ then
+ git-check-ref-format "$local_ref_name" ||
+ die "* refusing to create funny ref '$local_ref_name' locally"
+ fi
echo "${dot_prefix}${force}${remote}:${local}"
dot_prefix=.
done
diff --git a/git-rename.perl b/git-rename.perl
index a28c8c83b..3b1127b1b 100755
--- a/git-rename.perl
+++ b/git-rename.perl
@@ -15,7 +15,7 @@ sub usage($);
my $GIT_DIR = $ENV{'GIT_DIR'} || ".git";
unless ( -d $GIT_DIR && -d $GIT_DIR . "/objects" &&
- -d $GIT_DIR . "/objects/00" && -d $GIT_DIR . "/refs") {
+ -d $GIT_DIR . "/objects/" && -d $GIT_DIR . "/refs") {
usage("Git repository not found.");
}
diff --git a/git-repack.sh b/git-repack.sh
index b395d0ef3..49547a77c 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -5,13 +5,14 @@
. git-sh-setup || die "Not a git archive"
-no_update_info= all_into_one= remove_redundant=
+no_update_info= all_into_one= remove_redundant= local=
while case "$#" in 0) break ;; esac
do
case "$1" in
-n) no_update_info=t ;;
-a) all_into_one=t ;;
-d) remove_redandant=t ;;
+ -l) local=t ;;
*) break ;;
esac
shift
@@ -37,6 +38,9 @@ case ",$all_into_one," in
find . -type f \( -name '*.pack' -o -name '*.idx' \) -print`
;;
esac
+if [ "$local" ]; then
+ pack_objects="$pack_objects --local"
+fi
name=$(git-rev-list --objects $rev_list $(git-rev-parse $rev_parse) |
git-pack-objects --non-empty $pack_objects .tmp-pack) ||
exit 1
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index a0172686a..dbb98842b 100755
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -22,4 +22,4 @@ refs/*) : ;;
*) false ;;
esac &&
[ -d "$GIT_DIR/refs" ] &&
-[ -d "$GIT_OBJECT_DIRECTORY/00" ]
+[ -d "$GIT_OBJECT_DIRECTORY/" ]
diff --git a/git-shortlog.perl b/git-shortlog.perl
index 8f0984be0..0b14f833e 100755
--- a/git-shortlog.perl
+++ b/git-shortlog.perl
@@ -2,55 +2,13 @@
use strict;
-#
-# Even with git, we don't always have name translations.
-# So have an email->real name table to translate the
-# (hopefully few) missing names
-#
-my %mailmap = (
- 'R.Marek@sh.cvut.cz' => 'Rudolf Marek',
- 'Ralf.Wildenhues@gmx.de' => 'Ralf Wildenhues',
- 'aherrman@de.ibm.com' => 'Andreas Herrmann',
- 'akpm@osdl.org' => 'Andrew Morton',
- 'andrew.vasquez@qlogic.com' => 'Andrew Vasquez',
- 'aquynh@gmail.com' => 'Nguyen Anh Quynh',
- 'axboe@suse.de' => 'Jens Axboe',
- 'blaisorblade@yahoo.it' => 'Paolo \'Blaisorblade\' Giarrusso',
- 'bunk@stusta.de' => 'Adrian Bunk',
- 'domen@coderock.org' => 'Domen Puncer',
- 'dougg@torque.net' => 'Douglas Gilbert',
- 'dwmw2@shinybook.infradead.org' => 'David Woodhouse',
- 'ecashin@coraid.com' => 'Ed L Cashin',
- 'felix@derklecks.de' => 'Felix Moeller',
- 'fzago@systemfabricworks.com' => 'Frank Zago',
- 'gregkh@suse.de' => 'Greg Kroah-Hartman',
- 'hch@lst.de' => 'Christoph Hellwig',
- 'htejun@gmail.com' => 'Tejun Heo',
- 'jejb@mulgrave.(none)' => 'James Bottomley',
- 'jejb@titanic.il.steeleye.com' => 'James Bottomley',
- 'jgarzik@pretzel.yyz.us' => 'Jeff Garzik',
- 'johnpol@2ka.mipt.ru' => 'Evgeniy Polyakov',
- 'kay.sievers@vrfy.org' => 'Kay Sievers',
- 'minyard@acm.org' => 'Corey Minyard',
- 'mshah@teja.com' => 'Mitesh shah',
- 'pj@ludd.ltu.se' => 'Peter A Jonsson',
- 'rmps@joel.ist.utl.pt' => 'Rui Saraiva',
- 'santtu.hyrkko@gmail.com' => 'Santtu Hyrkkö',
- 'simon@thekelleys.org.uk' => 'Simon Kelley',
- 'ssant@in.ibm.com' => 'Sachin P Sant',
- 'terra@gnome.org' => 'Morten Welinder',
- 'tony.luck@intel.com' => 'Tony Luck',
- 'welinder@anemone.rentec.com' => 'Morten Welinder',
- 'welinder@darter.rentec.com' => 'Morten Welinder',
- 'welinder@troll.com' => 'Morten Welinder',
-);
-
+my (%mailmap);
+my (%email);
my (%map);
my $pstate = 1;
my $n_records = 0;
my $n_output = 0;
-
sub shortlog_entry($$) {
my ($name, $desc) = @_;
my $key = $name;
@@ -108,41 +66,35 @@ sub changelog_input {
if ($pstate == 1) {
my ($email);
- next unless /^[Aa]uthor:? (.*)<(.*)>.*$/;
-
+ next unless /^[Aa]uthor:?\s*(.*?)\s*<(.*)>/;
+
$n_records++;
-
+
$author = $1;
$email = $2;
$desc = undef;
- # trim trailing whitespace.
- # why doesn't chomp work?
- while ($author && ($author =~ /\s$/)) {
- chop $author;
- }
-
# cset author fixups
if (exists $mailmap{$email}) {
$author = $mailmap{$email};
} elsif (exists $mailmap{$author}) {
$author = $mailmap{$author};
- } elsif ((!$author) || ($author eq "")) {
+ } elsif (!$author) {
$author = $email;
}
-
+ $email{$author}{$email}++;
$pstate++;
}
-
+
# skip to blank line
elsif ($pstate == 2) {
next unless /^\s*$/;
$pstate++;
}
-
+
# skip to non-blank line
elsif ($pstate == 3) {
- next unless /^\s*(\S.*)$/;
+ next unless /^\s*?(.*)/;
# skip lines that are obviously not
# a 1-line cset description
@@ -150,9 +102,9 @@ sub changelog_input {
chomp;
$desc = $1;
-
+
&shortlog_entry($author, $desc);
-
+
$pstate = 1;
}
@@ -162,16 +114,87 @@ sub changelog_input {
}
}
+sub read_mailmap {
+ my ($fh, $mailmap) = @_;
+ while (<$fh>) {
+ chomp;
+ if (/^([^#].*?)\s*<(.*)>/) {
+ $mailmap->{$2} = $1;
+ }
+ }
+}
+
+sub setup_mailmap {
+ read_mailmap(\*DATA, \%mailmap);
+ if (-f '.mailmap') {
+ my $fh = undef;
+ open $fh, '<', '.mailmap';
+ read_mailmap($fh, \%mailmap);
+ close $fh;
+ }
+}
+
sub finalize {
#print "\n$n_records records parsed.\n";
if ($n_records != $n_output) {
die "parse error: input records != output records\n";
}
+ if (0) {
+ for my $author (sort keys %email) {
+ my $e = $email{$author};
+ for my $email (sort keys %$e) {
+ print STDERR "$author <$email>\n";
+ }
+ }
+ }
}
+&setup_mailmap;
&changelog_input;
&shortlog_output;
&finalize;
exit(0);
+
+__DATA__
+#
+# Even with git, we don't always have name translations.
+# So have an email->real name table to translate the
+# (hopefully few) missing names
+#
+Adrian Bunk <bunk@stusta.de>
+Andreas Herrmann <aherrman@de.ibm.com>
+Andrew Morton <akpm@osdl.org>
+Andrew Vasquez <andrew.vasquez@qlogic.com>
+Christoph Hellwig <hch@lst.de>
+Corey Minyard <minyard@acm.org>
+David Woodhouse <dwmw2@shinybook.infradead.org>
+Domen Puncer <domen@coderock.org>
+Douglas Gilbert <dougg@torque.net>
+Ed L Cashin <ecashin@coraid.com>
+Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+Felix Moeller <felix@derklecks.de>
+Frank Zago <fzago@systemfabricworks.com>
+Greg Kroah-Hartman <gregkh@suse.de>
+James Bottomley <jejb@mulgrave.(none)>
+James Bottomley <jejb@titanic.il.steeleye.com>
+Jeff Garzik <jgarzik@pretzel.yyz.us>
+Jens Axboe <axboe@suse.de>
+Kay Sievers <kay.sievers@vrfy.org>
+Mitesh shah <mshah@teja.com>
+Morten Welinder <terra@gnome.org>
+Morten Welinder <welinder@anemone.rentec.com>
+Morten Welinder <welinder@darter.rentec.com>
+Morten Welinder <welinder@troll.com>
+Nguyen Anh Quynh <aquynh@gmail.com>
+Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
+Peter A Jonsson <pj@ludd.ltu.se>
+Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
+Rudolf Marek <R.Marek@sh.cvut.cz>
+Rui Saraiva <rmps@joel.ist.utl.pt>
+Sachin P Sant <ssant@in.ibm.com>
+Santtu Hyrkk,Av(B <santtu.hyrkko@gmail.com>
+Simon Kelley <simon@thekelleys.org.uk>
+Tejun Heo <htejun@gmail.com>
+Tony Luck <tony.luck@intel.com>
diff --git a/git-status.sh b/git-status.sh
index 44398d760..fbdd37753 100755
--- a/git-status.sh
+++ b/git-status.sh
@@ -42,7 +42,15 @@ git-update-index -q --unmerged --refresh || exit
if GIT_DIR="$GIT_DIR" git-rev-parse --verify HEAD >/dev/null 2>&1
then
git-diff-index -M --cached HEAD |
- sed 's/^://' |
+ sed -e '
+ s/^://
+ h
+ s/^[^ ]*//
+ s/ /\\ /g
+ x
+ s/ .*$//
+ G
+ s/\n/ /' |
report "Updated but not checked in" "will commit"
committable="$?"
@@ -51,14 +59,24 @@ else
# Initial commit
#'
git-ls-files |
- sed 's/^/o o o o A /' |
+ sed -e '
+ s/ /\\ /g
+ s/^/o o o o A /' |
report "Updated but not checked in" "will commit"
committable="$?"
fi
git-diff-files |
-sed 's/^://' |
+sed -e '
+ s/^://
+ h
+ s/^[^ ]*//
+ s/ /\\ /g
+ x
+ s/ .*$//
+ G
+ s/\n/ /' |
report "Changed but not updated" "use git-update-index to mark for commit"
if grep -v '^#' "$GIT_DIR/info/exclude" >/dev/null 2>&1
diff --git a/git-svnimport.perl b/git-svnimport.perl
index 45486a852..20a85724c 100755
--- a/git-svnimport.perl
+++ b/git-svnimport.perl
@@ -108,7 +108,7 @@ sub conn {
sub file {
my($self,$path,$rev) = @_;
- my ($fh, $name) = tempfile('gitsvn.XXXXXX',
+ my ($fh, $name) = tempfile('gitsvn.XXXXXX',
DIR => File::Spec->tmpdir(), UNLINK => 1);
print "... $rev $path ...\n" if $opt_v;
@@ -163,9 +163,9 @@ sub getwd() {
sub get_headref($$) {
my $name = shift;
- my $git_dir = shift;
+ my $git_dir = shift;
my $sha;
-
+
if (open(C,"$git_dir/refs/heads/$name")) {
chomp($sha = <C>);
close(C);
@@ -235,7 +235,7 @@ EOM
$forward_master =
$opt_o ne 'master' && -f "$git_dir/refs/heads/master" &&
- system('cmp', '-s', "$git_dir/refs/heads/master",
+ system('cmp', '-s', "$git_dir/refs/heads/master",
"$git_dir/refs/heads/$opt_o") == 0;
# populate index
@@ -285,8 +285,8 @@ sub get_file($$$) {
$res = $lwp_ua->request($req);
if ($res->is_success) {
my $fh;
- ($fh, $name) = tempfile('gitsvn.XXXXXX',
- DIR => File::Spec->tmpdir(), UNLINK => 1);
+ ($fh, $name) = tempfile('gitsvn.XXXXXX',
+ DIR => File::Spec->tmpdir(), UNLINK => 1);
print $fh $res->content;
close($fh) or die "Could not write $name: $!\n";
} else {
@@ -352,7 +352,7 @@ sub copy_subdir($$$$$$) {
print "... found $path$p ...\n" if $opt_v;
push(@$new,[$mode,$sha1,$path.$p]);
}
- close($f) or
+ close($f) or
print STDERR "$newrev:$newbranch: could not list files in $oldpath \@ $rev\n";
}
@@ -567,7 +567,7 @@ sub commit {
push @par, '-p', $mparent;
print OUT "Merge parent branch: $mparent\n" if $opt_v;
}
- }
+ }
}
exec("env",
@@ -604,7 +604,7 @@ sub commit {
print "... no known parent\n" if $opt_v;
} elsif(not $tag) {
print "Writing to refs/heads/$dest\n" if $opt_v;
- open(C,">$git_dir/refs/heads/$dest") and
+ open(C,">$git_dir/refs/heads/$dest") and
print C ("$cid\n") and
close(C)
or die "Cannot write branch $dest for update: $!\n";
@@ -614,7 +614,7 @@ sub commit {
my($in, $out) = ('','');
$last_rev = "-" if %$changed_paths;
# the tag was 'complex', i.e. did not refer to a "real" revision
-
+
$dest =~ tr/_/\./ if $opt_u;
my $pid = open2($in, $out, 'git-mktag');
diff --git a/git-tag.sh b/git-tag.sh
index 76c1bcd8c..faa766799 100755
--- a/git-tag.sh
+++ b/git-tag.sh
@@ -4,7 +4,7 @@
. git-sh-setup || die "Not a git archive"
usage () {
- echo >&2 "Usage: git-tag [-a | -s] [-f] [-m "tag message"] tagname"
+ echo >&2 "Usage: git-tag [-a | -s | -u <key-id>] [-f] [-m <msg>] <tagname> [<head>]"
exit 1
}
@@ -12,6 +12,7 @@ annotate=
signed=
force=
message=
+username=
while case "$#" in 0) break ;; esac
do
case "$1" in
@@ -30,6 +31,12 @@ do
shift
message="$1"
;;
+ -u)
+ annotate=1
+ signed=1
+ shift
+ username="$1"
+ ;;
-*)
usage
;;
@@ -46,10 +53,13 @@ if [ -e "$GIT_DIR/refs/tags/$name" -a -z "$force" ]; then
die "tag '$name' already exists"
fi
shift
+git-check-ref-format "tags/$name" ||
+ die "we do not like '$name' as a tag name."
object=$(git-rev-parse --verify --default HEAD "$@") || exit 1
type=$(git-cat-file -t $object) || exit 1
tagger=$(git-var GIT_COMMITTER_IDENT) || exit 1
+: ${username:=$(expr "$tagger" : '\(.*>\)')}
trap 'rm -f .tmp-tag* .tagmsg .editmsg' 0
@@ -65,13 +75,15 @@ if [ "$annotate" ]; then
grep -v '^#' < .editmsg | git-stripspace > .tagmsg
- [ -s .tagmsg ] || exit
+ [ -s .tagmsg ] || {
+ echo >&2 "No tag message?"
+ exit 1
+ }
( echo -e "object $object\ntype $type\ntag $name\ntagger $tagger\n"; cat .tagmsg ) > .tmp-tag
rm -f .tmp-tag.asc .tagmsg
if [ "$signed" ]; then
- me=$(expr "$tagger" : '\(.*>\)') &&
- gpg -bsa -u "$me" .tmp-tag &&
+ gpg -bsa -u "$username" .tmp-tag &&
cat .tmp-tag.asc >>.tmp-tag ||
die "failed to sign the tag with GPG."
fi
diff --git a/git-verify-tag.sh b/git-verify-tag.sh
index 156c75bb3..ed4c89396 100755
--- a/git-verify-tag.sh
+++ b/git-verify-tag.sh
@@ -1,8 +1,12 @@
#!/bin/sh
. git-sh-setup || die "Not a git archive"
-tag=$(git-rev-parse $1) || exit 1
+type="$(git-cat-file -t "$1" 2>/dev/null)" ||
+ die "$1: no such object."
-git-cat-file tag $tag > .tmp-vtag || exit 1
+test "$type" = tag ||
+ die "$1: cannot verify a non-tag object of type $type."
+
+git-cat-file tag "$1" > .tmp-vtag || exit 1
cat .tmp-vtag | sed '/-----BEGIN PGP/Q' | gpg --verify .tmp-vtag - || exit 1
rm -f .tmp-vtag
diff --git a/git.sh b/git.sh
index 7400c1625..94940aea2 100755
--- a/git.sh
+++ b/git.sh
@@ -1,7 +1,7 @@
#!/bin/sh
cmd=
-path=$(dirname $0)
+path=$(dirname "$0")
case "$#" in
0) ;;
*) cmd="$1"
@@ -12,13 +12,14 @@ case "$#" in
exit 0 ;;
esac
- test -x $path/git-$cmd && exec $path/git-$cmd "$@"
+ test -x "$path/git-$cmd" && exec "$path/git-$cmd" "$@"
case '@@X@@' in
'')
;;
*)
- test -x $path/git-$cmd@@X@@ && exec $path/git-$cmd@@X@@ "$@"
+ test -x "$path/git-$cmd@@X@@" &&
+ exec "$path/git-$cmd@@X@@" "$@"
;;
esac
;;
diff --git a/http-fetch.c b/http-fetch.c
index 71a8c60b5..efa6e8232 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -6,6 +6,11 @@
#include <curl/curl.h>
#include <curl/easy.h>
+#if LIBCURL_VERSION_NUM >= 0x070908
+#define USE_CURL_MULTI
+#define DEFAULT_MAX_REQUESTS 5
+#endif
+
#if LIBCURL_VERSION_NUM < 0x070704
#define curl_global_cleanup() do { /* nothing */ } while(0)
#endif
@@ -13,16 +18,28 @@
#define curl_global_init(a) do { /* nothing */ } while(0)
#endif
+#if LIBCURL_VERSION_NUM < 0x070c04
+#define NO_CURL_EASY_DUPHANDLE
+#endif
+
#define PREV_BUF_SIZE 4096
#define RANGE_HEADER_SIZE 30
-static CURL *curl;
+static int active_requests = 0;
+static int data_received;
+
+#ifdef USE_CURL_MULTI
+static int max_requests = -1;
+static CURLM *curlm;
+#endif
+#ifndef NO_CURL_EASY_DUPHANDLE
+static CURL *curl_default;
+#endif
+static struct curl_slist *pragma_header;
static struct curl_slist *no_pragma_header;
static struct curl_slist *no_range_header;
static char curl_errorstr[CURL_ERROR_SIZE];
-static char *initial_base;
-
struct alt_base
{
char *base;
@@ -33,17 +50,56 @@ struct alt_base
static struct alt_base *alt = NULL;
-static SHA_CTX c;
-static z_stream stream;
+enum transfer_state {
+ WAITING,
+ ABORTED,
+ ACTIVE,
+ COMPLETE,
+};
-static int local;
-static int zret;
+struct transfer_request
+{
+ unsigned char sha1[20];
+ struct alt_base *repo;
+ char *url;
+ char filename[PATH_MAX];
+ char tmpfile[PATH_MAX];
+ int local;
+ enum transfer_state state;
+ CURLcode curl_result;
+ char errorstr[CURL_ERROR_SIZE];
+ long http_code;
+ unsigned char real_sha1[20];
+ SHA_CTX c;
+ z_stream stream;
+ int zret;
+ int rename;
+ struct active_request_slot *slot;
+ struct transfer_request *next;
+};
-static int curl_ssl_verify;
-static char *ssl_cert;
-static char *ssl_key;
-static char *ssl_capath;
-static char *ssl_cainfo;
+struct active_request_slot
+{
+ CURL *curl;
+ FILE *local;
+ int in_use;
+ int done;
+ CURLcode curl_result;
+ struct active_request_slot *next;
+};
+
+static struct transfer_request *request_queue_head = NULL;
+static struct active_request_slot *active_queue_head = NULL;
+
+static int curl_ssl_verify = -1;
+static char *ssl_cert = NULL;
+#if LIBCURL_VERSION_NUM >= 0x070902
+static char *ssl_key = NULL;
+#endif
+#if LIBCURL_VERSION_NUM >= 0x070908
+static char *ssl_capath = NULL;
+#endif
+static char *ssl_cainfo = NULL;
struct buffer
{
@@ -52,6 +108,60 @@ struct buffer
void *buffer;
};
+static int http_options(const char *var, const char *value)
+{
+ if (!strcmp("http.sslverify", var)) {
+ if (curl_ssl_verify == -1) {
+ curl_ssl_verify = git_config_bool(var, value);
+ }
+ return 0;
+ }
+
+ if (!strcmp("http.sslcert", var)) {
+ if (ssl_cert == NULL) {
+ ssl_cert = xmalloc(strlen(value)+1);
+ strcpy(ssl_cert, value);
+ }
+ return 0;
+ }
+#if LIBCURL_VERSION_NUM >= 0x070902
+ if (!strcmp("http.sslkey", var)) {
+ if (ssl_key == NULL) {
+ ssl_key = xmalloc(strlen(value)+1);
+ strcpy(ssl_key, value);
+ }
+ return 0;
+ }
+#endif
+#if LIBCURL_VERSION_NUM >= 0x070908
+ if (!strcmp("http.sslcapath", var)) {
+ if (ssl_capath == NULL) {
+ ssl_capath = xmalloc(strlen(value)+1);
+ strcpy(ssl_capath, value);
+ }
+ return 0;
+ }
+#endif
+ if (!strcmp("http.sslcainfo", var)) {
+ if (ssl_cainfo == NULL) {
+ ssl_cainfo = xmalloc(strlen(value)+1);
+ strcpy(ssl_cainfo, value);
+ }
+ return 0;
+ }
+
+#ifdef USE_CURL_MULTI
+ if (!strcmp("http.maxrequests", var)) {
+ if (max_requests == -1)
+ max_requests = git_config_int(var, value);
+ return 0;
+ }
+#endif
+
+ /* Fall back on the default ones */
+ return git_default_config(var, value);
+}
+
static size_t fwrite_buffer(void *ptr, size_t eltsize, size_t nmemb,
struct buffer *buffer)
{
@@ -60,81 +170,496 @@ static size_t fwrite_buffer(void *ptr, size_t eltsize, size_t nmemb,
size = buffer->size - buffer->posn;
memcpy(buffer->buffer + buffer->posn, ptr, size);
buffer->posn += size;
+ data_received++;
return size;
}
+static size_t fwrite_buffer_dynamic(const void *ptr, size_t eltsize,
+ size_t nmemb, struct buffer *buffer)
+{
+ size_t size = eltsize * nmemb;
+ if (size > buffer->size - buffer->posn) {
+ buffer->size = buffer->size * 3 / 2;
+ if (buffer->size < buffer->posn + size)
+ buffer->size = buffer->posn + size;
+ buffer->buffer = xrealloc(buffer->buffer, buffer->size);
+ }
+ memcpy(buffer->buffer + buffer->posn, ptr, size);
+ buffer->posn += size;
+ data_received++;
+ return size;
+}
+
static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
void *data)
{
unsigned char expn[4096];
size_t size = eltsize * nmemb;
int posn = 0;
+ struct transfer_request *request = (struct transfer_request *)data;
do {
- ssize_t retval = write(local, ptr + posn, size - posn);
+ ssize_t retval = write(request->local,
+ ptr + posn, size - posn);
if (retval < 0)
return posn;
posn += retval;
} while (posn < size);
- stream.avail_in = size;
- stream.next_in = ptr;
+ request->stream.avail_in = size;
+ request->stream.next_in = ptr;
do {
- stream.next_out = expn;
- stream.avail_out = sizeof(expn);
- zret = inflate(&stream, Z_SYNC_FLUSH);
- SHA1_Update(&c, expn, sizeof(expn) - stream.avail_out);
- } while (stream.avail_in && zret == Z_OK);
+ request->stream.next_out = expn;
+ request->stream.avail_out = sizeof(expn);
+ request->zret = inflate(&request->stream, Z_SYNC_FLUSH);
+ SHA1_Update(&request->c, expn,
+ sizeof(expn) - request->stream.avail_out);
+ } while (request->stream.avail_in && request->zret == Z_OK);
+ data_received++;
return size;
}
-void prefetch(unsigned char *sha1)
+#ifdef USE_CURL_MULTI
+static void process_curl_messages(void);
+static void process_request_queue(void);
+#endif
+
+static CURL* get_curl_handle(void)
{
+ CURL* result = curl_easy_init();
+
+ curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify);
+#if LIBCURL_VERSION_NUM >= 0x070907
+ curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
+#endif
+
+ if (ssl_cert != NULL)
+ curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
+#if LIBCURL_VERSION_NUM >= 0x070902
+ if (ssl_key != NULL)
+ curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
+#endif
+#if LIBCURL_VERSION_NUM >= 0x070908
+ if (ssl_capath != NULL)
+ curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);
+#endif
+ if (ssl_cainfo != NULL)
+ curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
+ curl_easy_setopt(result, CURLOPT_FAILONERROR, 1);
+
+ return result;
}
-int relink_or_rename(char *old, char *new) {
- int ret;
+static struct active_request_slot *get_active_slot(void)
+{
+ struct active_request_slot *slot = active_queue_head;
+ struct active_request_slot *newslot;
- ret = link(old, new);
- if (ret < 0) {
- /* Same Coda hack as in write_sha1_file(sha1_file.c) */
- ret = errno;
- if (ret == EXDEV && !rename(old, new))
- return 0;
+#ifdef USE_CURL_MULTI
+ int num_transfers;
+
+ /* Wait for a slot to open up if the queue is full */
+ while (active_requests >= max_requests) {
+ curl_multi_perform(curlm, &num_transfers);
+ if (num_transfers < active_requests) {
+ process_curl_messages();
+ }
+ }
+#endif
+
+ while (slot != NULL && slot->in_use) {
+ slot = slot->next;
}
- unlink(old);
- if (ret) {
- if (ret != EEXIST)
- return ret;
+ if (slot == NULL) {
+ newslot = xmalloc(sizeof(*newslot));
+#ifdef NO_CURL_EASY_DUPHANDLE
+ newslot->curl = get_curl_handle();
+#else
+ newslot->curl = curl_easy_duphandle(curl_default);
+#endif
+ newslot->in_use = 0;
+ newslot->next = NULL;
+
+ slot = active_queue_head;
+ if (slot == NULL) {
+ active_queue_head = newslot;
+ } else {
+ while (slot->next != NULL) {
+ slot = slot->next;
+ }
+ slot->next = newslot;
+ }
+ slot = newslot;
}
- return 0;
+ active_requests++;
+ slot->in_use = 1;
+ slot->done = 0;
+ slot->local = NULL;
+ curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
+ curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_range_header);
+ curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
+
+ return slot;
}
-static int got_alternates = 0;
+static int start_active_slot(struct active_request_slot *slot)
+{
+#ifdef USE_CURL_MULTI
+ CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);
+
+ if (curlm_result != CURLM_OK &&
+ curlm_result != CURLM_CALL_MULTI_PERFORM) {
+ active_requests--;
+ slot->in_use = 0;
+ return 0;
+ }
+#endif
+ return 1;
+}
+
+static void run_active_slot(struct active_request_slot *slot)
+{
+#ifdef USE_CURL_MULTI
+ int num_transfers;
+ long last_pos = 0;
+ long current_pos;
+ fd_set readfds;
+ fd_set writefds;
+ fd_set excfds;
+ int max_fd;
+ struct timeval select_timeout;
+ CURLMcode curlm_result;
+
+ while (!slot->done) {
+ data_received = 0;
+ do {
+ curlm_result = curl_multi_perform(curlm,
+ &num_transfers);
+ } while (curlm_result == CURLM_CALL_MULTI_PERFORM);
+ if (num_transfers < active_requests) {
+ process_curl_messages();
+ process_request_queue();
+ }
+
+ if (!data_received && slot->local != NULL) {
+ current_pos = ftell(slot->local);
+ if (current_pos > last_pos)
+ data_received++;
+ last_pos = current_pos;
+ }
+
+ if (!slot->done && !data_received) {
+ max_fd = 0;
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&excfds);
+ select_timeout.tv_sec = 0;
+ select_timeout.tv_usec = 50000;
+ select(max_fd, &readfds, &writefds,
+ &excfds, &select_timeout);
+ }
+ }
+#else
+ slot->curl_result = curl_easy_perform(slot->curl);
+ active_requests--;
+#endif
+}
+
+static void start_request(struct transfer_request *request)
+{
+ char *hex = sha1_to_hex(request->sha1);
+ char prevfile[PATH_MAX];
+ char *url;
+ char *posn;
+ int prevlocal;
+ unsigned char prev_buf[PREV_BUF_SIZE];
+ ssize_t prev_read = 0;
+ long prev_posn = 0;
+ char range[RANGE_HEADER_SIZE];
+ struct curl_slist *range_header = NULL;
+ struct active_request_slot *slot;
+
+ snprintf(prevfile, sizeof(prevfile), "%s.prev", request->filename);
+ unlink(prevfile);
+ rename(request->tmpfile, prevfile);
+ unlink(request->tmpfile);
+
+ request->local = open(request->tmpfile,
+ O_WRONLY | O_CREAT | O_EXCL, 0666);
+ /* This could have failed due to the "lazy directory creation";
+ * try to mkdir the last path component.
+ */
+ if (request->local < 0 && errno == ENOENT) {
+ char *dir = strrchr(request->tmpfile, '/');
+ if (dir) {
+ *dir = 0;
+ mkdir(request->tmpfile, 0777);
+ *dir = '/';
+ }
+ request->local = open(request->tmpfile,
+ O_WRONLY | O_CREAT | O_EXCL, 0666);
+ }
+
+ if (request->local < 0) {
+ request->state = ABORTED;
+ error("Couldn't create temporary file %s for %s: %s\n",
+ request->tmpfile, request->filename, strerror(errno));
+ return;
+ }
+
+ memset(&request->stream, 0, sizeof(request->stream));
+
+ inflateInit(&request->stream);
+
+ SHA1_Init(&request->c);
+
+ url = xmalloc(strlen(request->repo->base) + 50);
+ request->url = xmalloc(strlen(request->repo->base) + 50);
+ strcpy(url, request->repo->base);
+ posn = url + strlen(request->repo->base);
+ strcpy(posn, "objects/");
+ posn += 8;
+ memcpy(posn, hex, 2);
+ posn += 2;
+ *(posn++) = '/';
+ strcpy(posn, hex + 2);
+ strcpy(request->url, url);
+
+ /* If a previous temp file is present, process what was already
+ fetched. */
+ prevlocal = open(prevfile, O_RDONLY);
+ if (prevlocal != -1) {
+ do {
+ prev_read = read(prevlocal, prev_buf, PREV_BUF_SIZE);
+ if (prev_read>0) {
+ if (fwrite_sha1_file(prev_buf,
+ 1,
+ prev_read,
+ request) == prev_read) {
+ prev_posn += prev_read;
+ } else {
+ prev_read = -1;
+ }
+ }
+ } while (prev_read > 0);
+ close(prevlocal);
+ }
+ unlink(prevfile);
+
+ /* Reset inflate/SHA1 if there was an error reading the previous temp
+ file; also rewind to the beginning of the local file. */
+ if (prev_read == -1) {
+ memset(&request->stream, 0, sizeof(request->stream));
+ inflateInit(&request->stream);
+ SHA1_Init(&request->c);
+ if (prev_posn>0) {
+ prev_posn = 0;
+ lseek(request->local, SEEK_SET, 0);
+ ftruncate(request->local, 0);
+ }
+ }
+
+ slot = get_active_slot();
+ curl_easy_setopt(slot->curl, CURLOPT_FILE, request);
+ curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
+ curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);
+ curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+ curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
+
+ /* If we have successfully processed data from a previous fetch
+ attempt, only fetch the data we don't already have. */
+ if (prev_posn>0) {
+ if (get_verbosely)
+ fprintf(stderr,
+ "Resuming fetch of object %s at byte %ld\n",
+ hex, prev_posn);
+ sprintf(range, "Range: bytes=%ld-", prev_posn);
+ range_header = curl_slist_append(range_header, range);
+ curl_easy_setopt(slot->curl,
+ CURLOPT_HTTPHEADER, range_header);
+ }
+
+ /* Try to get the request started, abort the request on error */
+ if (!start_active_slot(slot)) {
+ request->state = ABORTED;
+ close(request->local);
+ free(request->url);
+ return;
+ }
+
+ request->slot = slot;
+ request->state = ACTIVE;
+}
+
+static void finish_request(struct transfer_request *request)
+{
+ fchmod(request->local, 0444);
+ close(request->local);
+
+ if (request->http_code == 416) {
+ fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
+ } else if (request->curl_result != CURLE_OK) {
+ return;
+ }
+
+ inflateEnd(&request->stream);
+ SHA1_Final(request->real_sha1, &request->c);
+ if (request->zret != Z_STREAM_END) {
+ unlink(request->tmpfile);
+ return;
+ }
+ if (memcmp(request->sha1, request->real_sha1, 20)) {
+ unlink(request->tmpfile);
+ return;
+ }
+ request->rename =
+ move_temp_to_file(request->tmpfile, request->filename);
+
+ if (request->rename == 0)
+ pull_say("got %s\n", sha1_to_hex(request->sha1));
+}
+
+static void release_request(struct transfer_request *request)
+{
+ struct transfer_request *entry = request_queue_head;
+
+ if (request == request_queue_head) {
+ request_queue_head = request->next;
+ } else {
+ while (entry->next != NULL && entry->next != request)
+ entry = entry->next;
+ if (entry->next == request)
+ entry->next = entry->next->next;
+ }
+
+ free(request->url);
+ free(request);
+}
+
+#ifdef USE_CURL_MULTI
+void process_curl_messages(void)
+{
+ int num_messages;
+ struct active_request_slot *slot;
+ struct transfer_request *request = NULL;
+ CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages);
+
+ while (curl_message != NULL) {
+ if (curl_message->msg == CURLMSG_DONE) {
+ slot = active_queue_head;
+ while (slot != NULL &&
+ slot->curl != curl_message->easy_handle)
+ slot = slot->next;
+ if (slot != NULL) {
+ curl_multi_remove_handle(curlm, slot->curl);
+ active_requests--;
+ slot->done = 1;
+ slot->in_use = 0;
+ slot->curl_result = curl_message->data.result;
+ request = request_queue_head;
+ while (request != NULL &&
+ request->slot != slot)
+ request = request->next;
+ } else {
+ fprintf(stderr, "Received DONE message for unknown request!\n");
+ }
+ if (request != NULL) {
+ request->curl_result =
+ curl_message->data.result;
+ curl_easy_getinfo(slot->curl,
+ CURLINFO_HTTP_CODE,
+ &request->http_code);
+ request->slot = NULL;
+
+ /* Use alternates if necessary */
+ if (request->http_code == 404 &&
+ request->repo->next != NULL) {
+ request->repo = request->repo->next;
+ start_request(request);
+ } else {
+ finish_request(request);
+ request->state = COMPLETE;
+ }
+ }
+ } else {
+ fprintf(stderr, "Unknown CURL message received: %d\n",
+ (int)curl_message->msg);
+ }
+ curl_message = curl_multi_info_read(curlm, &num_messages);
+ }
+}
+
+void process_request_queue(void)
+{
+ struct transfer_request *request = request_queue_head;
+ int num_transfers;
+
+ while (active_requests < max_requests && request != NULL) {
+ if (request->state == WAITING) {
+ if (has_sha1_file(request->sha1))
+ release_request(request);
+ else
+ start_request(request);
+ curl_multi_perform(curlm, &num_transfers);
+ }
+ request = request->next;
+ }
+}
+#endif
+
+void prefetch(unsigned char *sha1)
+{
+ struct transfer_request *newreq;
+ struct transfer_request *tail;
+ char *filename = sha1_file_name(sha1);
+
+ newreq = xmalloc(sizeof(*newreq));
+ memcpy(newreq->sha1, sha1, 20);
+ newreq->repo = alt;
+ newreq->url = NULL;
+ newreq->local = -1;
+ newreq->state = WAITING;
+ snprintf(newreq->filename, sizeof(newreq->filename), "%s", filename);
+ snprintf(newreq->tmpfile, sizeof(newreq->tmpfile),
+ "%s.temp", filename);
+ newreq->next = NULL;
+
+ if (request_queue_head == NULL) {
+ request_queue_head = newreq;
+ } else {
+ tail = request_queue_head;
+ while (tail->next != NULL) {
+ tail = tail->next;
+ }
+ tail->next = newreq;
+ }
+#ifdef USE_CURL_MULTI
+ process_request_queue();
+ process_curl_messages();
+#endif
+}
static int fetch_index(struct alt_base *repo, unsigned char *sha1)
{
+ char *hex = sha1_to_hex(sha1);
char *filename;
char *url;
char tmpfile[PATH_MAX];
- int ret;
long prev_posn = 0;
char range[RANGE_HEADER_SIZE];
struct curl_slist *range_header = NULL;
- CURLcode curl_result;
FILE *indexfile;
+ struct active_request_slot *slot;
if (has_pack_index(sha1))
return 0;
if (get_verbosely)
- fprintf(stderr, "Getting index for pack %s\n",
- sha1_to_hex(sha1));
+ fprintf(stderr, "Getting index for pack %s\n", hex);
url = xmalloc(strlen(repo->base) + 64);
- sprintf(url, "%s/objects/pack/pack-%s.idx",
- repo->base, sha1_to_hex(sha1));
+ sprintf(url, "%s/objects/pack/pack-%s.idx", repo->base, hex);
filename = sha1_pack_index_name(sha1);
snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
@@ -143,12 +668,13 @@ static int fetch_index(struct alt_base *repo, unsigned char *sha1)
return error("Unable to open local file %s for pack index",
filename);
- curl_easy_setopt(curl, CURLOPT_FILE, indexfile);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
- curl_easy_setopt(curl, CURLOPT_URL, url);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, no_pragma_header);
- curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errorstr);
-
+ slot = get_active_slot();
+ curl_easy_setopt(slot->curl, CURLOPT_FILE, indexfile);
+ curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
+ curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+ curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
+ slot->local = indexfile;
+
/* If there is data present from a previous transfer attempt,
resume where it left off */
prev_posn = ftell(indexfile);
@@ -156,30 +682,26 @@ static int fetch_index(struct alt_base *repo, unsigned char *sha1)
if (get_verbosely)
fprintf(stderr,
"Resuming fetch of index for pack %s at byte %ld\n",
- sha1_to_hex(sha1), prev_posn);
+ hex, prev_posn);
sprintf(range, "Range: bytes=%ld-", prev_posn);
range_header = curl_slist_append(range_header, range);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, range_header);
+ curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
}
- /* Clear out the Range: header after performing the request, so
- other curl requests don't inherit inappropriate header data */
- curl_result = curl_easy_perform(curl);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, no_range_header);
- if (curl_result != 0) {
- fclose(indexfile);
- return error("Unable to get pack index %s\n%s", url,
- curl_errorstr);
+ if (start_active_slot(slot)) {
+ run_active_slot(slot);
+ if (slot->curl_result != CURLE_OK) {
+ fclose(indexfile);
+ return error("Unable to get pack index %s\n%s", url,
+ curl_errorstr);
+ }
+ } else {
+ return error("Unable to start request");
}
fclose(indexfile);
- ret = relink_or_rename(tmpfile, filename);
- if (ret)
- return error("unable to write index filename %s: %s",
- filename, strerror(ret));
-
- return 0;
+ return move_temp_to_file(tmpfile, filename);
}
static int setup_index(struct alt_base *repo, unsigned char *sha1)
@@ -205,10 +727,13 @@ static int fetch_alternates(char *base)
char *data;
int i = 0;
int http_specific = 1;
- if (got_alternates)
- return 0;
+ struct alt_base *tail = alt;
+ static const char null_byte = '\0';
+
+ struct active_request_slot *slot;
+
data = xmalloc(4096);
- buffer.size = 4095;
+ buffer.size = 4096;
buffer.posn = 0;
buffer.buffer = data;
@@ -218,25 +743,39 @@ static int fetch_alternates(char *base)
url = xmalloc(strlen(base) + 31);
sprintf(url, "%s/objects/info/http-alternates", base);
- curl_easy_setopt(curl, CURLOPT_FILE, &buffer);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
- curl_easy_setopt(curl, CURLOPT_URL, url);
-
- if (curl_easy_perform(curl) || !buffer.posn) {
- http_specific = 0;
-
- sprintf(url, "%s/objects/info/alternates", base);
-
- curl_easy_setopt(curl, CURLOPT_FILE, &buffer);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
- curl_easy_setopt(curl, CURLOPT_URL, url);
-
- if (curl_easy_perform(curl)) {
- return 0;
+ slot = get_active_slot();
+ curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
+ curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
+ fwrite_buffer_dynamic);
+ curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+ if (start_active_slot(slot)) {
+ run_active_slot(slot);
+ if (slot->curl_result != CURLE_OK || !buffer.posn) {
+ http_specific = 0;
+
+ sprintf(url, "%s/objects/info/alternates", base);
+
+ slot = get_active_slot();
+ curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
+ curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
+ fwrite_buffer_dynamic);
+ curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+ if (start_active_slot(slot)) {
+ run_active_slot(slot);
+ if (slot->curl_result != CURLE_OK) {
+ free(buffer.buffer);
+ return 0;
+ }
+ }
}
+ } else {
+ free(buffer.buffer);
+ return 0;
}
- data[buffer.posn] = '\0';
+ fwrite_buffer_dynamic(&null_byte, 1, 1, &buffer);
+ buffer.posn--;
+ data = buffer.buffer;
while (i < buffer.posn) {
int posn = i;
@@ -283,18 +822,20 @@ static int fetch_alternates(char *base)
fprintf(stderr,
"Also look at %s\n", target);
newalt = xmalloc(sizeof(*newalt));
- newalt->next = alt;
+ newalt->next = NULL;
newalt->base = target;
newalt->got_indices = 0;
newalt->packs = NULL;
- alt = newalt;
+ while (tail->next != NULL)
+ tail = tail->next;
+ tail->next = newalt;
ret++;
}
}
i = posn + 1;
}
- got_alternates = 1;
-
+
+ free(buffer.buffer);
return ret;
}
@@ -306,6 +847,8 @@ static int fetch_indices(struct alt_base *repo)
char *data;
int i = 0;
+ struct active_request_slot *slot;
+
if (repo->got_indices)
return 0;
@@ -320,15 +863,24 @@ static int fetch_indices(struct alt_base *repo)
url = xmalloc(strlen(repo->base) + 21);
sprintf(url, "%s/objects/info/packs", repo->base);
- curl_easy_setopt(curl, CURLOPT_FILE, &buffer);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
- curl_easy_setopt(curl, CURLOPT_URL, url);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL);
- curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errorstr);
-
- if (curl_easy_perform(curl))
- return error("%s", curl_errorstr);
+ slot = get_active_slot();
+ curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
+ curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
+ fwrite_buffer_dynamic);
+ curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+ curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
+ if (start_active_slot(slot)) {
+ run_active_slot(slot);
+ if (slot->curl_result != CURLE_OK) {
+ free(buffer.buffer);
+ return error("%s", curl_errorstr);
+ }
+ } else {
+ free(buffer.buffer);
+ return error("Unable to start request");
+ }
+ data = buffer.buffer;
while (i < buffer.posn) {
switch (data[i]) {
case 'P':
@@ -348,6 +900,7 @@ static int fetch_indices(struct alt_base *repo)
i++;
}
+ free(buffer.buffer);
repo->got_indices = 1;
return 0;
}
@@ -364,7 +917,8 @@ static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
long prev_posn = 0;
char range[RANGE_HEADER_SIZE];
struct curl_slist *range_header = NULL;
- CURLcode curl_result;
+
+ struct active_request_slot *slot;
if (fetch_indices(repo))
return -1;
@@ -390,11 +944,12 @@ static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
return error("Unable to open local file %s for pack",
filename);
- curl_easy_setopt(curl, CURLOPT_FILE, packfile);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
- curl_easy_setopt(curl, CURLOPT_URL, url);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, no_pragma_header);
- curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errorstr);
+ slot = get_active_slot();
+ curl_easy_setopt(slot->curl, CURLOPT_FILE, packfile);
+ curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
+ curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+ curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
+ slot->local = packfile;
/* If there is data present from a previous transfer attempt,
resume where it left off */
@@ -406,25 +961,25 @@ static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
sha1_to_hex(target->sha1), prev_posn);
sprintf(range, "Range: bytes=%ld-", prev_posn);
range_header = curl_slist_append(range_header, range);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, range_header);
+ curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
}
- /* Clear out the Range: header after performing the request, so
- other curl requests don't inherit inappropriate header data */
- curl_result = curl_easy_perform(curl);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, no_range_header);
- if (curl_result != 0) {
- fclose(packfile);
- return error("Unable to get pack file %s\n%s", url,
- curl_errorstr);
+ if (start_active_slot(slot)) {
+ run_active_slot(slot);
+ if (slot->curl_result != CURLE_OK) {
+ fclose(packfile);
+ return error("Unable to get pack file %s\n%s", url,
+ curl_errorstr);
+ }
+ } else {
+ return error("Unable to start request");
}
fclose(packfile);
- ret = relink_or_rename(tmpfile, filename);
+ ret = move_temp_to_file(tmpfile, filename);
if (ret)
- return error("unable to write pack filename %s: %s",
- filename, strerror(ret));
+ return ret;
lst = &repo->packs;
while (*lst != target)
@@ -441,185 +996,175 @@ static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
static int fetch_object(struct alt_base *repo, unsigned char *sha1)
{
char *hex = sha1_to_hex(sha1);
- char *filename = sha1_file_name(sha1);
- unsigned char real_sha1[20];
- char tmpfile[PATH_MAX];
- char prevfile[PATH_MAX];
int ret;
- char *url;
- char *posn;
- int prevlocal;
- unsigned char prev_buf[PREV_BUF_SIZE];
- ssize_t prev_read = 0;
- long prev_posn = 0;
- char range[RANGE_HEADER_SIZE];
- struct curl_slist *range_header = NULL;
- CURLcode curl_result;
-
- snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
- snprintf(prevfile, sizeof(prevfile), "%s.prev", filename);
-
- if (unlink(prevfile) && (errno != ENOENT))
- return error("Failed to unlink %s (%s)",
- prevfile, strerror(errno));
- if (rename(tmpfile, prevfile) && (errno != ENOENT))
- return error("Failed to rename %s to %s (%s)",
- tmpfile, prevfile, strerror(errno));
+ struct transfer_request *request = request_queue_head;
- local = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
-
- /* Note: if another instance starts now, it will turn our new
- tmpfile into its prevfile. */
-
- if (local < 0)
- return error("Couldn't create temporary file %s for %s: %s\n",
- tmpfile, filename, strerror(errno));
-
- memset(&stream, 0, sizeof(stream));
-
- inflateInit(&stream);
-
- SHA1_Init(&c);
-
- curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
- curl_easy_setopt(curl, CURLOPT_FILE, NULL);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, no_pragma_header);
- curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errorstr);
-
- url = xmalloc(strlen(repo->base) + 50);
- strcpy(url, repo->base);
- posn = url + strlen(repo->base);
- strcpy(posn, "objects/");
- posn += 8;
- memcpy(posn, hex, 2);
- posn += 2;
- *(posn++) = '/';
- strcpy(posn, hex + 2);
+ while (request != NULL && memcmp(request->sha1, sha1, 20))
+ request = request->next;
+ if (request == NULL)
+ return error("Couldn't find request for %s in the queue", hex);
- curl_easy_setopt(curl, CURLOPT_URL, url);
+ if (has_sha1_file(request->sha1)) {
+ release_request(request);
+ return 0;
+ }
- /* If a previous temp file is present, process what was already
- fetched. */
- prevlocal = open(prevfile, O_RDONLY);
- if (prevlocal != -1) {
- do {
- prev_read = read(prevlocal, prev_buf, PREV_BUF_SIZE);
- if (prev_read>0) {
- if (fwrite_sha1_file(prev_buf,
- 1,
- prev_read,
- NULL) == prev_read) {
- prev_posn += prev_read;
- } else {
- prev_read = -1;
- }
- }
- } while (prev_read > 0);
- close(prevlocal);
+#ifdef USE_CURL_MULTI
+ while (request->state == WAITING) {
+ int num_transfers;
+ curl_multi_perform(curlm, &num_transfers);
+ if (num_transfers < active_requests) {
+ process_curl_messages();
+ process_request_queue();
+ }
}
- unlink(prevfile);
+#else
+ start_request(request);
+#endif
- /* Reset inflate/SHA1 if there was an error reading the previous temp
- file; also rewind to the beginning of the local file. */
- if (prev_read == -1) {
- memset(&stream, 0, sizeof(stream));
- inflateInit(&stream);
- SHA1_Init(&c);
- if (prev_posn>0) {
- prev_posn = 0;
- lseek(local, SEEK_SET, 0);
- ftruncate(local, 0);
+ while (request->state == ACTIVE) {
+ run_active_slot(request->slot);
+#ifndef USE_CURL_MULTI
+ request->curl_result = request->slot->curl_result;
+ curl_easy_getinfo(request->slot->curl,
+ CURLINFO_HTTP_CODE,
+ &request->http_code);
+ request->slot = NULL;
+
+ /* Use alternates if necessary */
+ if (request->http_code == 404 &&
+ request->repo->next != NULL) {
+ request->repo = request->repo->next;
+ start_request(request);
+ } else {
+ finish_request(request);
+ request->state = COMPLETE;
}
+#endif
}
- /* If we have successfully processed data from a previous fetch
- attempt, only fetch the data we don't already have. */
- if (prev_posn>0) {
- if (get_verbosely)
- fprintf(stderr,
- "Resuming fetch of object %s at byte %ld\n",
- hex, prev_posn);
- sprintf(range, "Range: bytes=%ld-", prev_posn);
- range_header = curl_slist_append(range_header, range);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, range_header);
+ if (request->state == ABORTED) {
+ release_request(request);
+ return error("Request for %s aborted", hex);
}
- /* Clear out the Range: header after performing the request, so
- other curl requests don't inherit inappropriate header data */
- curl_result = curl_easy_perform(curl);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, no_range_header);
- if (curl_result != 0) {
- return error("%s", curl_errorstr);
+ if (request->curl_result != CURLE_OK && request->http_code != 416) {
+ ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
+ request->errorstr, request->curl_result,
+ request->http_code, hex);
+ release_request(request);
+ return ret;
}
- fchmod(local, 0444);
- close(local);
- inflateEnd(&stream);
- SHA1_Final(real_sha1, &c);
- if (zret != Z_STREAM_END) {
- unlink(tmpfile);
- return error("File %s (%s) corrupt\n", hex, url);
+ if (request->zret != Z_STREAM_END) {
+ ret = error("File %s (%s) corrupt\n", hex, request->url);
+ release_request(request);
+ return ret;
}
- if (memcmp(sha1, real_sha1, 20)) {
- unlink(tmpfile);
+
+ if (memcmp(request->sha1, request->real_sha1, 20)) {
+ release_request(request);
return error("File %s has bad hash\n", hex);
}
- ret = relink_or_rename(tmpfile, filename);
- if (ret)
- return error("unable to write sha1 filename %s: %s",
- filename, strerror(ret));
- pull_say("got %s\n", hex);
+ if (request->rename < 0) {
+ ret = error("unable to write sha1 filename %s: %s",
+ request->filename,
+ strerror(request->rename));
+ release_request(request);
+ return ret;
+ }
+
+ release_request(request);
return 0;
}
int fetch(unsigned char *sha1)
{
struct alt_base *altbase = alt;
+
+ if (!fetch_object(altbase, sha1))
+ return 0;
while (altbase) {
- if (!fetch_object(altbase, sha1))
- return 0;
if (!fetch_pack(altbase, sha1))
return 0;
- if (fetch_alternates(altbase->base) > 0) {
- altbase = alt;
- continue;
- }
altbase = altbase->next;
}
return error("Unable to find %s under %s\n", sha1_to_hex(sha1),
- initial_base);
+ alt->base);
+}
+
+static inline int needs_quote(int ch)
+{
+ switch (ch) {
+ case '/': case '-': case '.':
+ case 'A'...'Z': case 'a'...'z': case '0'...'9':
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+static inline int hex(int v)
+{
+ if (v < 10) return '0' + v;
+ else return 'A' + v - 10;
+}
+
+static char *quote_ref_url(const char *base, const char *ref)
+{
+ const char *cp;
+ char *dp, *qref;
+ int len, baselen, ch;
+
+ baselen = strlen(base);
+ len = baselen + 6; /* "refs/" + NUL */
+ for (cp = ref; (ch = *cp) != 0; cp++, len++)
+ if (needs_quote(ch))
+ len += 2; /* extra two hex plus replacement % */
+ qref = xmalloc(len);
+ memcpy(qref, base, baselen);
+ memcpy(qref + baselen, "refs/", 5);
+ for (cp = ref, dp = qref + baselen + 5; (ch = *cp) != 0; cp++) {
+ if (needs_quote(ch)) {
+ *dp++ = '%';
+ *dp++ = hex((ch >> 4) & 0xF);
+ *dp++ = hex(ch & 0xF);
+ }
+ else
+ *dp++ = ch;
+ }
+ *dp = 0;
+
+ return qref;
}
int fetch_ref(char *ref, unsigned char *sha1)
{
- char *url, *posn;
+ char *url;
char hex[42];
struct buffer buffer;
- char *base = initial_base;
+ char *base = alt->base;
+ struct active_request_slot *slot;
buffer.size = 41;
buffer.posn = 0;
buffer.buffer = hex;
hex[41] = '\0';
- curl_easy_setopt(curl, CURLOPT_FILE, &buffer);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL);
- curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errorstr);
-
- url = xmalloc(strlen(base) + 6 + strlen(ref));
- strcpy(url, base);
- posn = url + strlen(base);
- strcpy(posn, "refs/");
- posn += 5;
- strcpy(posn, ref);
-
- curl_easy_setopt(curl, CURLOPT_URL, url);
-
- if (curl_easy_perform(curl))
- return error("Couldn't get %s for %s\n%s",
- url, ref, curl_errorstr);
+ url = quote_ref_url(base, ref);
+ slot = get_active_slot();
+ curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
+ curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
+ curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
+ curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+ if (start_active_slot(slot)) {
+ run_active_slot(slot);
+ if (slot->curl_result != CURLE_OK)
+ return error("Couldn't get %s for %s\n%s",
+ url, ref, curl_errorstr);
+ } else {
+ return error("Unable to start request");
+ }
hex[40] = '\0';
get_sha1_hex(hex, sha1);
@@ -631,6 +1176,7 @@ int main(int argc, char **argv)
char *commit_id;
char *url;
int arg = 1;
+ struct active_request_slot *slot;
while (arg < argc && argv[arg][0] == '-') {
if (argv[arg][1] == 't') {
@@ -660,44 +1206,74 @@ int main(int argc, char **argv)
curl_global_init(CURL_GLOBAL_ALL);
- curl = curl_easy_init();
- no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
- no_range_header = curl_slist_append(no_range_header, "Range:");
+#ifdef USE_CURL_MULTI
+ {
+ char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
+ if (http_max_requests != NULL)
+ max_requests = atoi(http_max_requests);
+ }
- curl_ssl_verify = getenv("GIT_SSL_NO_VERIFY") ? 0 : 1;
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify);
-#if LIBCURL_VERSION_NUM >= 0x070907
- curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
+ curlm = curl_multi_init();
+ if (curlm == NULL) {
+ fprintf(stderr, "Error creating curl multi handle.\n");
+ return 1;
+ }
#endif
- if ((ssl_cert = getenv("GIT_SSL_CERT")) != NULL) {
- curl_easy_setopt(curl, CURLOPT_SSLCERT, ssl_cert);
- }
+ if (getenv("GIT_SSL_NO_VERIFY"))
+ curl_ssl_verify = 0;
+
+ ssl_cert = getenv("GIT_SSL_CERT");
#if LIBCURL_VERSION_NUM >= 0x070902
- if ((ssl_key = getenv("GIT_SSL_KEY")) != NULL) {
- curl_easy_setopt(curl, CURLOPT_SSLKEY, ssl_key);
- }
+ ssl_key = getenv("GIT_SSL_KEY");
#endif
#if LIBCURL_VERSION_NUM >= 0x070908
- if ((ssl_capath = getenv("GIT_SSL_CAPATH")) != NULL) {
- curl_easy_setopt(curl, CURLOPT_CAPATH, ssl_capath);
- }
+ ssl_capath = getenv("GIT_SSL_CAPATH");
+#endif
+ ssl_cainfo = getenv("GIT_SSL_CAINFO");
+
+ git_config(http_options);
+
+ if (curl_ssl_verify == -1)
+ curl_ssl_verify = 1;
+
+#ifdef USE_CURL_MULTI
+ if (max_requests < 1)
+ max_requests = DEFAULT_MAX_REQUESTS;
+#endif
+
+ pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
+ no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
+ no_range_header = curl_slist_append(no_range_header, "Range:");
+
+#ifndef NO_CURL_EASY_DUPHANDLE
+ curl_default = get_curl_handle();
#endif
- if ((ssl_cainfo = getenv("GIT_SSL_CAINFO")) != NULL) {
- curl_easy_setopt(curl, CURLOPT_CAINFO, ssl_cainfo);
- }
alt = xmalloc(sizeof(*alt));
alt->base = url;
alt->got_indices = 0;
alt->packs = NULL;
alt->next = NULL;
- initial_base = url;
+ fetch_alternates(alt->base);
if (pull(commit_id))
return 1;
+ curl_slist_free_all(pragma_header);
curl_slist_free_all(no_pragma_header);
+ curl_slist_free_all(no_range_header);
+#ifndef NO_CURL_EASY_DUPHANDLE
+ curl_easy_cleanup(curl_default);
+#endif
+ slot = active_queue_head;
+ while (slot != NULL) {
+ curl_easy_cleanup(slot->curl);
+ slot = slot->next;
+ }
+#ifdef USE_CURL_MULTI
+ curl_multi_cleanup(curlm);
+#endif
curl_global_cleanup();
return 0;
}
diff --git a/ident.c b/ident.c
index 562f5f181..1bfbc6ff3 100644
--- a/ident.c
+++ b/ident.c
@@ -9,11 +9,8 @@
#include <pwd.h>
#include <time.h>
-#include <ctype.h>
-static char real_email[1000];
-static char real_name[1000];
-static char real_date[50];
+static char git_default_date[50];
static void copy_gecos(struct passwd *w, char *name, int sz)
{
@@ -58,22 +55,22 @@ int setup_ident(void)
die("You don't exist. Go away!");
/* Get the name ("gecos") */
- copy_gecos(pw, real_name, sizeof(real_name));
+ copy_gecos(pw, git_default_name, sizeof(git_default_name));
/* Make up a fake email address (name + '@' + hostname [+ '.' + domainname]) */
len = strlen(pw->pw_name);
- if (len > sizeof(real_email)/2)
+ if (len > sizeof(git_default_email)/2)
die("Your sysadmin must hate you!");
- memcpy(real_email, pw->pw_name, len);
- real_email[len++] = '@';
- gethostname(real_email + len, sizeof(real_email) - len);
- if (!strchr(real_email+len, '.')) {
- len = strlen(real_email);
- real_email[len++] = '.';
- getdomainname(real_email+len, sizeof(real_email)-len);
+ memcpy(git_default_email, pw->pw_name, len);
+ git_default_email[len++] = '@';
+ gethostname(git_default_email + len, sizeof(git_default_email) - len);
+ if (!strchr(git_default_email+len, '.')) {
+ len = strlen(git_default_email);
+ git_default_email[len++] = '.';
+ getdomainname(git_default_email+len, sizeof(git_default_email)-len);
}
/* And set the default date */
- datestamp(real_date, sizeof(real_date));
+ datestamp(git_default_date, sizeof(git_default_date));
return 0;
}
@@ -159,10 +156,10 @@ char *get_ident(const char *name, const char *email, const char *date_str)
int i;
if (!name)
- name = real_name;
+ name = git_default_name;
if (!email)
- email = real_email;
- strcpy(date, real_date);
+ email = git_default_email;
+ strcpy(date, git_default_date);
if (date_str)
parse_date(date_str, date, sizeof(date));
diff --git a/index-pack.c b/index-pack.c
new file mode 100644
index 000000000..785fe71a6
--- /dev/null
+++ b/index-pack.c
@@ -0,0 +1,462 @@
+#include "cache.h"
+#include "delta.h"
+#include "pack.h"
+#include "csum-file.h"
+
+static const char index_pack_usage[] =
+"git-index-pack [-o index-file] pack-file";
+
+struct object_entry
+{
+ unsigned long offset;
+ enum object_type type;
+ enum object_type real_type;
+ unsigned char sha1[20];
+};
+
+struct delta_entry
+{
+ struct object_entry *obj;
+ unsigned char base_sha1[20];
+};
+
+static const char *pack_name;
+static unsigned char *pack_base;
+static unsigned long pack_size;
+static struct object_entry *objects;
+static struct delta_entry *deltas;
+static int nr_objects;
+static int nr_deltas;
+
+static void open_pack_file(void)
+{
+ int fd;
+ struct stat st;
+
+ fd = open(pack_name, O_RDONLY);
+ if (fd < 0)
+ die("cannot open packfile '%s': %s", pack_name,
+ strerror(errno));
+ if (fstat(fd, &st)) {
+ int err = errno;
+ close(fd);
+ die("cannot fstat packfile '%s': %s", pack_name,
+ strerror(err));
+ }
+ pack_size = st.st_size;
+ pack_base = mmap(NULL, pack_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (pack_base == MAP_FAILED) {
+ int err = errno;
+ close(fd);
+ die("cannot mmap packfile '%s': %s", pack_name,
+ strerror(err));
+ }
+ close(fd);
+}
+
+static void parse_pack_header(void)
+{
+ const struct pack_header *hdr;
+ unsigned char sha1[20];
+ SHA_CTX ctx;
+
+ /* Ensure there are enough bytes for the header and final SHA1 */
+ if (pack_size < sizeof(struct pack_header) + 20)
+ die("packfile '%s' is too small", pack_name);
+
+ /* Header consistency check */
+ hdr = (void *)pack_base;
+ if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
+ die("packfile '%s' signature mismatch", pack_name);
+ if (hdr->hdr_version != htonl(PACK_VERSION))
+ die("packfile '%s' version %d different from ours %d",
+ pack_name, ntohl(hdr->hdr_version), PACK_VERSION);
+
+ nr_objects = ntohl(hdr->hdr_entries);
+
+ /* Check packfile integrity */
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, pack_base, pack_size - 20);
+ SHA1_Final(sha1, &ctx);
+ if (memcmp(sha1, pack_base + pack_size - 20, 20))
+ die("packfile '%s' SHA1 mismatch", pack_name);
+}
+
+static void bad_object(unsigned long offset, const char *format,
+ ...) NORETURN __attribute__((format (printf, 2, 3)));
+
+static void bad_object(unsigned long offset, const char *format, ...)
+{
+ va_list params;
+ char buf[1024];
+
+ va_start(params, format);
+ vsnprintf(buf, sizeof(buf), format, params);
+ va_end(params);
+ die("packfile '%s': bad object at offset %lu: %s",
+ pack_name, offset, buf);
+}
+
+static void *unpack_entry_data(unsigned long offset,
+ unsigned long *current_pos, unsigned long size)
+{
+ unsigned long pack_limit = pack_size - 20;
+ unsigned long pos = *current_pos;
+ z_stream stream;
+ void *buf = xmalloc(size);
+
+ memset(&stream, 0, sizeof(stream));
+ stream.next_out = buf;
+ stream.avail_out = size;
+ stream.next_in = pack_base + pos;
+ stream.avail_in = pack_limit - pos;
+ inflateInit(&stream);
+
+ for (;;) {
+ int ret = inflate(&stream, 0);
+ if (ret == Z_STREAM_END)
+ break;
+ if (ret != Z_OK)
+ bad_object(offset, "inflate returned %d", ret);
+ }
+ inflateEnd(&stream);
+ if (stream.total_out != size)
+ bad_object(offset, "size mismatch (expected %lu, got %lu)",
+ size, stream.total_out);
+ *current_pos = pack_limit - stream.avail_in;
+ return buf;
+}
+
+static void *unpack_raw_entry(unsigned long offset,
+ enum object_type *obj_type,
+ unsigned long *obj_size,
+ unsigned char *delta_base,
+ unsigned long *next_obj_offset)
+{
+ unsigned long pack_limit = pack_size - 20;
+ unsigned long pos = offset;
+ unsigned char c;
+ unsigned long size;
+ unsigned shift;
+ enum object_type type;
+ void *data;
+
+ c = pack_base[pos++];
+ type = (c >> 4) & 7;
+ size = (c & 15);
+ shift = 4;
+ while (c & 0x80) {
+ if (pos >= pack_limit)
+ bad_object(offset, "object extends past end of pack");
+ c = pack_base[pos++];
+ size += (c & 0x7fUL) << shift;
+ shift += 7;
+ }
+
+ switch (type) {
+ case OBJ_DELTA:
+ if (pos + 20 >= pack_limit)
+ bad_object(offset, "object extends past end of pack");
+ memcpy(delta_base, pack_base + pos, 20);
+ pos += 20;
+ /* fallthru */
+ case OBJ_COMMIT:
+ case OBJ_TREE:
+ case OBJ_BLOB:
+ case OBJ_TAG:
+ data = unpack_entry_data(offset, &pos, size);
+ break;
+ default:
+ bad_object(offset, "bad object type %d", type);
+ }
+
+ *obj_type = type;
+ *obj_size = size;
+ *next_obj_offset = pos;
+ return data;
+}
+
+static int find_delta(const unsigned char *base_sha1)
+{
+ int first = 0, last = nr_deltas;
+
+ while (first < last) {
+ int next = (first + last) / 2;
+ struct delta_entry *delta = &deltas[next];
+ int cmp;
+
+ cmp = memcmp(base_sha1, delta->base_sha1, 20);
+ if (!cmp)
+ return next;
+ if (cmp < 0) {
+ last = next;
+ continue;
+ }
+ first = next+1;
+ }
+ return -first-1;
+}
+
+static int find_deltas_based_on_sha1(const unsigned char *base_sha1,
+ int *first_index, int *last_index)
+{
+ int first = find_delta(base_sha1);
+ int last = first;
+ int end = nr_deltas - 1;
+
+ if (first < 0)
+ return -1;
+ while (first > 0 && !memcmp(deltas[first-1].base_sha1, base_sha1, 20))
+ --first;
+ while (last < end && !memcmp(deltas[last+1].base_sha1, base_sha1, 20))
+ ++last;
+ *first_index = first;
+ *last_index = last;
+ return 0;
+}
+
+static void sha1_object(const void *data, unsigned long size,
+ enum object_type type, unsigned char *sha1)
+{
+ SHA_CTX ctx;
+ char header[50];
+ int header_size;
+ const char *type_str;
+
+ switch (type) {
+ case OBJ_COMMIT: type_str = "commit"; break;
+ case OBJ_TREE: type_str = "tree"; break;
+ case OBJ_BLOB: type_str = "blob"; break;
+ case OBJ_TAG: type_str = "tag"; break;
+ default:
+ die("bad type %d", type);
+ }
+
+ header_size = sprintf(header, "%s %lu", type_str, size) + 1;
+
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, header, header_size);
+ SHA1_Update(&ctx, data, size);
+ SHA1_Final(sha1, &ctx);
+}
+
+static void resolve_delta(struct delta_entry *delta, void *base_data,
+ unsigned long base_size, enum object_type type)
+{
+ struct object_entry *obj = delta->obj;
+ void *delta_data;
+ unsigned long delta_size;
+ void *result;
+ unsigned long result_size;
+ enum object_type delta_type;
+ unsigned char base_sha1[20];
+ unsigned long next_obj_offset;
+ int j, first, last;
+
+ obj->real_type = type;
+ delta_data = unpack_raw_entry(obj->offset, &delta_type,
+ &delta_size, base_sha1,
+ &next_obj_offset);
+ result = patch_delta(base_data, base_size, delta_data, delta_size,
+ &result_size);
+ free(delta_data);
+ if (!result)
+ bad_object(obj->offset, "failed to apply delta");
+ sha1_object(result, result_size, type, obj->sha1);
+ if (!find_deltas_based_on_sha1(obj->sha1, &first, &last)) {
+ for (j = first; j <= last; j++)
+ resolve_delta(&deltas[j], result, result_size, type);
+ }
+ free(result);
+}
+
+static int compare_delta_entry(const void *a, const void *b)
+{
+ const struct delta_entry *delta_a = a;
+ const struct delta_entry *delta_b = b;
+ return memcmp(delta_a->base_sha1, delta_b->base_sha1, 20);
+}
+
+static void parse_pack_objects(void)
+{
+ int i;
+ unsigned long offset = sizeof(struct pack_header);
+ unsigned char base_sha1[20];
+ void *data;
+ unsigned long data_size;
+
+ /*
+ * First pass:
+ * - find locations of all objects;
+ * - calculate SHA1 of all non-delta objects;
+ * - remember base SHA1 for all deltas.
+ */
+ for (i = 0; i < nr_objects; i++) {
+ struct object_entry *obj = &objects[i];
+ obj->offset = offset;
+ data = unpack_raw_entry(offset, &obj->type, &data_size,
+ base_sha1, &offset);
+ obj->real_type = obj->type;
+ if (obj->type == OBJ_DELTA) {
+ struct delta_entry *delta = &deltas[nr_deltas++];
+ delta->obj = obj;
+ memcpy(delta->base_sha1, base_sha1, 20);
+ } else
+ sha1_object(data, data_size, obj->type, obj->sha1);
+ free(data);
+ }
+ if (offset != pack_size - 20)
+ die("packfile '%s' has junk at the end", pack_name);
+
+ /* Sort deltas by base SHA1 for fast searching */
+ qsort(deltas, nr_deltas, sizeof(struct delta_entry),
+ compare_delta_entry);
+
+ /*
+ * Second pass:
+ * - for all non-delta objects, look if it is used as a base for
+ * deltas;
+ * - if used as a base, uncompress the object and apply all deltas,
+ * recursively checking if the resulting object is used as a base
+ * for some more deltas.
+ */
+ for (i = 0; i < nr_objects; i++) {
+ struct object_entry *obj = &objects[i];
+ int j, first, last;
+
+ if (obj->type == OBJ_DELTA)
+ continue;
+ if (find_deltas_based_on_sha1(obj->sha1, &first, &last))
+ continue;
+ data = unpack_raw_entry(obj->offset, &obj->type, &data_size,
+ base_sha1, &offset);
+ for (j = first; j <= last; j++)
+ resolve_delta(&deltas[j], data, data_size, obj->type);
+ free(data);
+ }
+
+ /* Check for unresolved deltas */
+ for (i = 0; i < nr_deltas; i++) {
+ if (deltas[i].obj->real_type == OBJ_DELTA)
+ die("packfile '%s' has unresolved deltas", pack_name);
+ }
+}
+
+static int sha1_compare(const void *_a, const void *_b)
+{
+ struct object_entry *a = *(struct object_entry **)_a;
+ struct object_entry *b = *(struct object_entry **)_b;
+ return memcmp(a->sha1, b->sha1, 20);
+}
+
+static void write_index_file(const char *index_name, unsigned char *sha1)
+{
+ struct sha1file *f;
+ struct object_entry **sorted_by_sha =
+ xcalloc(nr_objects, sizeof(struct object_entry *));
+ struct object_entry **list = sorted_by_sha;
+ struct object_entry **last = sorted_by_sha + nr_objects;
+ unsigned int array[256];
+ int i;
+ SHA_CTX ctx;
+
+ for (i = 0; i < nr_objects; ++i)
+ sorted_by_sha[i] = &objects[i];
+ qsort(sorted_by_sha, nr_objects, sizeof(sorted_by_sha[0]),
+ sha1_compare);
+
+ unlink(index_name);
+ f = sha1create("%s", index_name);
+
+ /*
+ * Write the first-level table (the list is sorted,
+ * but we use a 256-entry lookup to be able to avoid
+ * having to do eight extra binary search iterations).
+ */
+ for (i = 0; i < 256; i++) {
+ struct object_entry **next = list;
+ while (next < last) {
+ struct object_entry *obj = *next;
+ if (obj->sha1[0] != i)
+ break;
+ next++;
+ }
+ array[i] = htonl(next - sorted_by_sha);
+ list = next;
+ }
+ sha1write(f, array, 256 * sizeof(int));
+
+ /* recompute the SHA1 hash of sorted object names.
+ * currently pack-objects does not do this, but that
+ * can be fixed.
+ */
+ SHA1_Init(&ctx);
+ /*
+ * Write the actual SHA1 entries..
+ */
+ list = sorted_by_sha;
+ for (i = 0; i < nr_objects; i++) {
+ struct object_entry *obj = *list++;
+ unsigned int offset = htonl(obj->offset);
+ sha1write(f, &offset, 4);
+ sha1write(f, obj->sha1, 20);
+ SHA1_Update(&ctx, obj->sha1, 20);
+ }
+ sha1write(f, pack_base + pack_size - 20, 20);
+ sha1close(f, NULL, 1);
+ free(sorted_by_sha);
+ SHA1_Final(sha1, &ctx);
+}
+
+int main(int argc, char **argv)
+{
+ int i;
+ char *index_name = NULL;
+ char *index_name_buf = NULL;
+ unsigned char sha1[20];
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+
+ if (*arg == '-') {
+ if (!strcmp(arg, "-o")) {
+ if (index_name || (i+1) >= argc)
+ usage(index_pack_usage);
+ index_name = argv[++i];
+ } else
+ usage(index_pack_usage);
+ continue;
+ }
+
+ if (pack_name)
+ usage(index_pack_usage);
+ pack_name = arg;
+ }
+
+ if (!pack_name)
+ usage(index_pack_usage);
+ if (!index_name) {
+ int len = strlen(pack_name);
+ if (len < 5 || strcmp(pack_name + len - 5, ".pack"))
+ die("packfile name '%s' does not end with '.pack'",
+ pack_name);
+ index_name_buf = xmalloc(len - 1);
+ memcpy(index_name_buf, pack_name, len - 5);
+ strcpy(index_name_buf + len - 5, ".idx");
+ index_name = index_name_buf;
+ }
+
+ open_pack_file();
+ parse_pack_header();
+ objects = xcalloc(nr_objects, sizeof(struct object_entry));
+ deltas = xcalloc(nr_objects, sizeof(struct delta_entry));
+ parse_pack_objects();
+ free(deltas);
+ write_index_file(index_name, sha1);
+ free(objects);
+ free(index_name_buf);
+
+ printf("%s\n", sha1_to_hex(sha1));
+
+ return 0;
+}
diff --git a/index.c b/index.c
index bdde65f75..ad0eafedc 100644
--- a/index.c
+++ b/index.c
@@ -22,14 +22,16 @@ static void remove_lock_file_on_signal(int signo)
int hold_index_file_for_update(struct cache_file *cf, const char *path)
{
+ int fd;
sprintf(cf->lockfile, "%s.lock", path);
- cf->next = cache_file_list;
- cache_file_list = cf;
- if (!cf->next) {
+ fd = open(cf->lockfile, O_RDWR | O_CREAT | O_EXCL, 0666);
+ if (fd >=0 && !cf->next) {
+ cf->next = cache_file_list;
+ cache_file_list = cf;
signal(SIGINT, remove_lock_file_on_signal);
atexit(remove_lock_file);
}
- return open(cf->lockfile, O_RDWR | O_CREAT | O_EXCL, 0666);
+ return fd;
}
int commit_index_file(struct cache_file *cf)
diff --git a/local-fetch.c b/local-fetch.c
index a57386ca6..87a93de02 100644
--- a/local-fetch.c
+++ b/local-fetch.c
@@ -166,7 +166,10 @@ static int fetch_file(const unsigned char *sha1)
int fetch(unsigned char *sha1)
{
- return fetch_file(sha1) && fetch_pack(sha1);
+ if (has_sha1_file(sha1))
+ return 0;
+ else
+ return fetch_file(sha1) && fetch_pack(sha1);
}
int fetch_ref(char *ref, unsigned char *sha1)
diff --git a/ls-tree.c b/ls-tree.c
index e198a20cb..dd642e0bf 100644
--- a/ls-tree.c
+++ b/ls-tree.c
@@ -54,11 +54,13 @@ static int prepare_children(struct tree_entry_list *elem)
return 0;
}
-static struct tree_entry_list *find_entry(const char *path)
+static struct tree_entry_list *find_entry(const char *path, char *pathbuf)
{
const char *next, *slash;
int len;
- struct tree_entry_list *elem = &root_entry;
+ struct tree_entry_list *elem = &root_entry, *oldelem = NULL;
+
+ *(pathbuf) = '\0';
/* Find tree element, descending from root, that
* corresponds to the named path, lazily expanding
@@ -86,6 +88,10 @@ static struct tree_entry_list *find_entry(const char *path)
len = slash - path;
}
if (len) {
+ if (oldelem) {
+ pathbuf += sprintf(pathbuf, "%s/", oldelem->name);
+ }
+
/* (len == 0) if the original path was "drivers/char/"
* and we have run already two rounds, having elem
* pointing at the drivers/char directory.
@@ -101,6 +107,8 @@ static struct tree_entry_list *find_entry(const char *path)
}
if (!elem)
return NULL;
+
+ oldelem = elem;
}
path = next;
}
@@ -108,19 +116,6 @@ static struct tree_entry_list *find_entry(const char *path)
return elem;
}
-static void show_entry_name(struct tree_entry_list *e)
-{
- /* This is yucky. The root level is there for
- * our convenience but we really want to do a
- * forest.
- */
- if (e->parent && e->parent != &root_entry) {
- show_entry_name(e->parent);
- putchar('/');
- }
- printf("%s", e->name);
-}
-
static const char *entry_type(struct tree_entry_list *e)
{
return (e->directory ? "tree" : "blob");
@@ -134,28 +129,35 @@ static const char *entry_hex(struct tree_entry_list *e)
}
/* forward declaration for mutually recursive routines */
-static int show_entry(struct tree_entry_list *, int);
+static int show_entry(struct tree_entry_list *, int, char *pathbuf);
-static int show_children(struct tree_entry_list *e, int level)
+static int show_children(struct tree_entry_list *e, int level, char *pathbuf)
{
+ int oldlen = strlen(pathbuf);
+
+ if (e != &root_entry)
+ sprintf(pathbuf + oldlen, "%s/", e->name);
+
if (prepare_children(e))
die("internal error: ls-tree show_children called with non tree");
e = e->item.tree->entries;
while (e) {
- show_entry(e, level);
+ show_entry(e, level, pathbuf);
e = e->next;
}
+
+ pathbuf[oldlen] = '\0';
+
return 0;
}
-static int show_entry(struct tree_entry_list *e, int level)
+static int show_entry(struct tree_entry_list *e, int level, char *pathbuf)
{
int err = 0;
if (e != &root_entry) {
- printf("%06o %s %s ", e->mode, entry_type(e),
- entry_hex(e));
- show_entry_name(e);
+ printf("%06o %s %s %s%s", e->mode, entry_type(e),
+ entry_hex(e), pathbuf, e->name);
putchar(line_termination);
}
@@ -176,10 +178,10 @@ static int show_entry(struct tree_entry_list *e, int level)
*/
if (level == 0 && !(ls_options & LS_TREE_ONLY))
/* case (1)-a and (1)-b */
- err = err | show_children(e, level+1);
+ err = err | show_children(e, level+1, pathbuf);
else if (level && ls_options & LS_RECURSIVE)
/* case (2)-b */
- err = err | show_children(e, level+1);
+ err = err | show_children(e, level+1, pathbuf);
}
return err;
}
@@ -187,7 +189,8 @@ static int show_entry(struct tree_entry_list *e, int level)
static int list_one(const char *path)
{
int err = 0;
- struct tree_entry_list *e = find_entry(path);
+ char pathbuf[MAXPATHLEN + 1];
+ struct tree_entry_list *e = find_entry(path, pathbuf);
if (!e) {
/* traditionally ls-tree does not complain about
* missing path. We may change this later to match
@@ -195,7 +198,7 @@ static int list_one(const char *path)
*/
return err;
}
- err = err | show_entry(e, 0);
+ err = err | show_entry(e, 0, pathbuf);
return err;
}
diff --git a/mailinfo.c b/mailinfo.c
index df470bb9c..cb853df99 100644
--- a/mailinfo.c
+++ b/mailinfo.c
@@ -9,6 +9,10 @@
#include <ctype.h>
#include <iconv.h>
+#ifdef NO_STRCASESTR
+extern char *gitstrcasestr(const char *haystack, const char *needle);
+#endif
+
static FILE *cmitmsg, *patchfile;
static int keep_subject = 0;
diff --git a/mailsplit.c b/mailsplit.c
index 7afea1aac..189f4ed72 100644
--- a/mailsplit.c
+++ b/mailsplit.c
@@ -9,30 +9,13 @@
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
-#include <ctype.h>
#include <assert.h>
+#include "cache.h"
-static int usage(void)
-{
- fprintf(stderr, "mailsplit <mbox> <directory>\n");
- exit(1);
-}
-
-static int linelen(const char *map, unsigned long size)
-{
- int len = 0, c;
-
- do {
- c = *map;
- map++;
- size--;
- len++;
- } while (size && c != '\n');
- return len;
-}
+static const char git_mailsplit_usage[] =
+"git-mailsplit [-d<prec>] [<mbox>] <directory>";
static int is_from_line(const char *line, int len)
{
@@ -65,81 +48,110 @@ static int is_from_line(const char *line, int len)
return 1;
}
-static int parse_email(const void *map, unsigned long size)
+/* Could be as small as 64, enough to hold a Unix "From " line. */
+static char buf[4096];
+
+/* Called with the first line (potentially partial)
+ * already in buf[] -- normally that should begin with
+ * the Unix "From " line. Write it into the specified
+ * file.
+ */
+static int split_one(FILE *mbox, const char *name)
{
- unsigned long offset;
+ FILE *output = NULL;
+ int len = strlen(buf);
+ int fd;
+ int status = 0;
- if (size < 6 || memcmp("From ", map, 5))
+ if (!is_from_line(buf, len))
goto corrupt;
- /* Make sure we don't trigger on this first line */
- map++; size--; offset=1;
+ fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
+ if (fd < 0)
+ die("cannot open output file %s", name);
+ output = fdopen(fd, "w");
- /*
- * Search for a line beginning with "From ", and
- * having something that looks like a date format.
+ /* Copy it out, while searching for a line that begins with
+ * "From " and having something that looks like a date format.
*/
- do {
- int len = linelen(map, size);
- if (is_from_line(map, len))
- return offset;
- map += len;
- size -= len;
- offset += len;
- } while (size);
- return offset;
-
-corrupt:
+ for (;;) {
+ int is_partial = (buf[len-1] != '\n');
+
+ if (fputs(buf, output) == EOF)
+ die("cannot write output");
+
+ if (fgets(buf, sizeof(buf), mbox) == NULL) {
+ if (feof(mbox)) {
+ status = 1;
+ break;
+ }
+ die("cannot read mbox");
+ }
+ len = strlen(buf);
+ if (!is_partial && is_from_line(buf, len))
+ break; /* done with one message */
+ }
+ fclose(output);
+ return status;
+
+ corrupt:
+ if (output)
+ fclose(output);
+ unlink(name);
fprintf(stderr, "corrupt mailbox\n");
exit(1);
}
-int main(int argc, char **argv)
+int main(int argc, const char **argv)
{
- int fd, nr;
- struct stat st;
- unsigned long size;
- void *map;
-
- if (argc != 3)
- usage();
- fd = open(argv[1], O_RDONLY);
- if (fd < 0) {
- perror(argv[1]);
- exit(1);
- }
- if (chdir(argv[2]) < 0)
- usage();
- if (fstat(fd, &st) < 0) {
- perror("stat");
- exit(1);
+ int i, nr, nr_prec = 4;
+ FILE *mbox = NULL;
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+
+ if (arg[0] != '-')
+ break;
+ /* do flags here */
+ if (!strncmp(arg, "-d", 2)) {
+ nr_prec = strtol(arg + 2, NULL, 10);
+ if (nr_prec < 3 || 10 <= nr_prec)
+ usage(git_mailsplit_usage);
+ continue;
+ }
}
- size = st.st_size;
- map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (map == MAP_FAILED) {
- perror("mmap");
- close(fd);
- exit(1);
+
+ /* Either one remaining arg (dir), or two (mbox and dir) */
+ switch (argc - i) {
+ case 1:
+ mbox = stdin;
+ break;
+ case 2:
+ if ((mbox = fopen(argv[i], "r")) == NULL)
+ die("cannot open mbox %s for reading", argv[i]);
+ break;
+ default:
+ usage(git_mailsplit_usage);
}
- close(fd);
+ if (chdir(argv[argc - 1]) < 0)
+ usage(git_mailsplit_usage);
+
nr = 0;
- do {
+ if (fgets(buf, sizeof(buf), mbox) == NULL)
+ die("cannot read mbox");
+
+ for (;;) {
char name[10];
- unsigned long len = parse_email(map, size);
- assert(len <= size);
- sprintf(name, "%04d", ++nr);
- fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
- if (fd < 0) {
- perror(name);
- exit(1);
- }
- if (write(fd, map, len) != len) {
- perror("write");
+
+ sprintf(name, "%0*d", nr_prec, ++nr);
+ switch (split_one(mbox, name)) {
+ case 0:
+ break;
+ case 1:
+ printf("%d\n", nr);
+ return 0;
+ default:
exit(1);
}
- close(fd);
- map += len;
- size -= len;
- } while (size > 0);
- return 0;
+ }
}
diff --git a/pack-objects.c b/pack-objects.c
index 3d622787c..b3e615203 100644
--- a/pack-objects.c
+++ b/pack-objects.c
@@ -1,11 +1,10 @@
-#include <ctype.h>
#include "cache.h"
#include "object.h"
#include "delta.h"
#include "pack.h"
#include "csum-file.h"
-static const char pack_usage[] = "git-pack-objects [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list";
+static const char pack_usage[] = "git-pack-objects [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list";
struct object_entry {
unsigned char sha1[20];
@@ -20,6 +19,7 @@ struct object_entry {
static unsigned char object_list_sha1[20];
static int non_empty = 0;
+static int local = 0;
static int incremental = 0;
static struct object_entry **sorted_by_sha, **sorted_by_type;
static struct object_entry *objects = NULL;
@@ -195,8 +195,20 @@ static int add_object_entry(unsigned char *sha1, unsigned int hash)
unsigned int idx = nr_objects;
struct object_entry *entry;
- if (incremental && has_sha1_pack(sha1))
- return 0;
+ if (incremental || local) {
+ struct packed_git *p;
+
+ for (p = packed_git; p; p = p->next) {
+ struct pack_entry e;
+
+ if (find_pack_entry_one(sha1, &e, p)) {
+ if (incremental)
+ return 0;
+ if (local && !p->pack_local)
+ return 0;
+ }
+ }
+ }
if (idx >= nr_alloc) {
unsigned int needed = (idx + 1024) * 3 / 2;
@@ -393,6 +405,7 @@ int main(int argc, char **argv)
SHA_CTX ctx;
char line[PATH_MAX + 20];
int window = 10, depth = 10, pack_to_stdout = 0;
+ struct object_entry **list;
int i;
for (i = 1; i < argc; i++) {
@@ -403,6 +416,10 @@ int main(int argc, char **argv)
non_empty = 1;
continue;
}
+ if (!strcmp("--local", arg)) {
+ local = 1;
+ continue;
+ }
if (!strcmp("--incremental", arg)) {
incremental = 1;
continue;
@@ -435,7 +452,7 @@ int main(int argc, char **argv)
if (pack_to_stdout != !base_name)
usage(pack_usage);
- SHA1_Init(&ctx);
+ prepare_packed_git();
while (fgets(line, sizeof(line), stdin) != NULL) {
unsigned int hash;
char *p;
@@ -451,10 +468,8 @@ int main(int argc, char **argv)
continue;
hash = hash * 11 + c;
}
- if (add_object_entry(sha1, hash))
- SHA1_Update(&ctx, sha1, 20);
+ add_object_entry(sha1, hash);
}
- SHA1_Final(object_list_sha1, &ctx);
if (non_empty && !nr_objects)
return 0;
get_object_details();
@@ -462,6 +477,14 @@ int main(int argc, char **argv)
fprintf(stderr, "Packing %d objects\n", nr_objects);
sorted_by_sha = create_sorted_list(sha1_sort);
+ SHA1_Init(&ctx);
+ list = sorted_by_sha;
+ for (i = 0; i < nr_objects; i++) {
+ struct object_entry *entry = *list++;
+ SHA1_Update(&ctx, entry->sha1, 20);
+ }
+ SHA1_Final(object_list_sha1, &ctx);
+
sorted_by_type = create_sorted_list(type_size_sort);
if (window && depth)
find_deltas(sorted_by_type, window+1, depth);
diff --git a/patch-id.c b/patch-id.c
index 5a8dc75d0..edbc4aa3e 100644
--- a/patch-id.c
+++ b/patch-id.c
@@ -1,4 +1,3 @@
-#include <ctype.h>
#include "cache.h"
static void flush_current_id(int patchlen, unsigned char *id, SHA_CTX *c)
@@ -55,6 +54,10 @@ static void generate_id_list(void)
if (!patchlen && memcmp(line, "diff ", 5))
continue;
+ /* Ignore git-diff index header */
+ if (!memcmp(line, "index ", 6))
+ continue;
+
/* Ignore line numbers when computing the SHA1 of the patch */
if (!memcmp(line, "@@ -", 4))
continue;
diff --git a/peek-remote.c b/peek-remote.c
index 4b1d0d5ba..ee49bf3b7 100644
--- a/peek-remote.c
+++ b/peek-remote.c
@@ -11,7 +11,7 @@ static int peek_remote(int fd[2])
{
struct ref *ref;
- get_remote_heads(fd[0], &ref, 0, NULL);
+ get_remote_heads(fd[0], &ref, 0, NULL, 0);
packet_flush(fd[1]);
while (ref) {
diff --git a/prune-packed.c b/prune-packed.c
index 5306e8e5e..73f0f3a46 100644
--- a/prune-packed.c
+++ b/prune-packed.c
@@ -26,6 +26,9 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len)
else if (unlink(pathname) < 0)
error("unable to unlink %s", pathname);
}
+ pathname[len] = 0;
+ if (rmdir(pathname))
+ mkdir(pathname, 0777);
}
static void prune_packed_objects(void)
@@ -46,7 +49,7 @@ static void prune_packed_objects(void)
sprintf(pathname + len, "%02x/", i);
d = opendir(pathname);
if (!d)
- die("unable to open %s", pathname);
+ continue;
prune_dir(i, d, pathname, len + 3);
closedir(d);
}
diff --git a/quote.c b/quote.c
index 5e6fda311..9d5d0bcb0 100644
--- a/quote.c
+++ b/quote.c
@@ -2,40 +2,52 @@
#include "quote.h"
/* Help to copy the thing properly quoted for the shell safety.
- * any single quote is replaced with '\'', and the caller is
- * expected to enclose the result within a single quote pair.
+ * any single quote is replaced with '\'', any exclamation point
+ * is replaced with '\!', and the whole thing is enclosed in a
*
* E.g.
* original sq_quote result
* name ==> name ==> 'name'
* a b ==> a b ==> 'a b'
* a'b ==> a'\''b ==> 'a'\''b'
+ * a!b ==> a'\!'b ==> 'a'\!'b'
*/
-char *sq_quote(const char *src)
-{
- static char *buf = NULL;
- int cnt, c;
- const char *cp;
- char *bp;
+#define EMIT(x) ( (++len < n) && (*bp++ = (x)) )
- /* count bytes needed to store the quoted string. */
- for (cnt = 3, cp = src; *cp; cnt++, cp++)
- if (*cp == '\'')
- cnt += 3;
+size_t sq_quote_buf(char *dst, size_t n, const char *src)
+{
+ char c;
+ char *bp = dst;
+ size_t len = 0;
- buf = xmalloc(cnt);
- bp = buf;
- *bp++ = '\'';
+ EMIT('\'');
while ((c = *src++)) {
- if (c != '\'')
- *bp++ = c;
- else {
- bp = strcpy(bp, "'\\''");
- bp += 4;
+ if (c == '\'' || c == '!') {
+ EMIT('\'');
+ EMIT('\\');
+ EMIT(c);
+ EMIT('\'');
+ } else {
+ EMIT(c);
}
}
- *bp++ = '\'';
- *bp = 0;
+ EMIT('\'');
+
+ if ( n )
+ *bp = 0;
+
+ return len;
+}
+
+char *sq_quote(const char *src)
+{
+ char *buf;
+ size_t cnt;
+
+ cnt = sq_quote_buf(NULL, 0, src) + 1;
+ buf = xmalloc(cnt);
+ sq_quote_buf(buf, cnt, src);
+
return buf;
}
diff --git a/quote.h b/quote.h
index c8cfb3a12..50ce1df97 100644
--- a/quote.h
+++ b/quote.h
@@ -1,10 +1,12 @@
#ifndef QUOTE_H
#define QUOTE_H
+#include <stddef.h>
/* Help to copy the thing properly quoted for the shell safety.
- * any single quote is replaced with '\'', and the whole thing
- * is enclosed in a single quote pair.
+ * any single quote is replaced with '\'', any exclamation point
+ * is replaced with '\!', and the whole thing is enclosed in a
+ * single quote pair.
*
* For example, if you are passing the result to system() as an
* argument:
@@ -19,8 +21,13 @@
*
* Note that the above examples leak memory! Remember to free result from
* sq_quote() in a real application.
+ *
+ * sq_quote_buf() writes to an existing buffer of specified size; it
+ * will return the number of characters that would have been written
+ * excluding the final null regardless of the buffer size.
*/
char *sq_quote(const char *src);
+size_t sq_quote_buf(char *dst, size_t n, const char *src);
#endif
diff --git a/read-cache.c b/read-cache.c
index d2aebdd6b..693273620 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -35,8 +35,11 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st)
switch (ntohl(ce->ce_mode) & S_IFMT) {
case S_IFREG:
changed |= !S_ISREG(st->st_mode) ? TYPE_CHANGED : 0;
- /* We consider only the owner x bit to be relevant for "mode changes" */
- if (0100 & (ntohl(ce->ce_mode) ^ st->st_mode))
+ /* We consider only the owner x bit to be relevant for
+ * "mode changes"
+ */
+ if (trust_executable_bit &&
+ (0100 & (ntohl(ce->ce_mode) ^ st->st_mode)))
changed |= MODE_CHANGED;
break;
case S_IFLNK:
@@ -392,7 +395,7 @@ int add_cache_entry(struct cache_entry *ce, int option)
int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
pos = cache_name_pos(ce->name, ntohs(ce->ce_flags));
- /* existing match? Just replace it */
+ /* existing match? Just replace it. */
if (pos >= 0) {
active_cache_changed = 1;
active_cache[pos] = ce;
@@ -415,7 +418,8 @@ int add_cache_entry(struct cache_entry *ce, int option)
if (!ok_to_add)
return -1;
- if (!skip_df_check && check_file_directory_conflict(ce, pos, ok_to_replace)) {
+ if (!skip_df_check &&
+ check_file_directory_conflict(ce, pos, ok_to_replace)) {
if (!ok_to_replace)
return -1;
pos = cache_name_pos(ce->name, ntohs(ce->ce_flags));
diff --git a/read-tree.c b/read-tree.c
index 5fdf58d24..6a456ae61 100644
--- a/read-tree.c
+++ b/read-tree.c
@@ -237,6 +237,35 @@ static void reject_merge(struct cache_entry *ce)
ce->name);
}
+/* Unlink the last component and attempt to remove leading
+ * directories, in case this unlink is the removal of the
+ * last entry in the directory -- empty directories are removed.
+ */
+static void unlink_entry(char *name)
+{
+ char *cp, *prev;
+
+ if (unlink(name))
+ return;
+ prev = NULL;
+ while (1) {
+ int status;
+ cp = strrchr(name, '/');
+ if (prev)
+ *prev = '/';
+ if (!cp)
+ break;
+
+ *cp = 0;
+ status = rmdir(name);
+ if (status) {
+ *cp = '/';
+ break;
+ }
+ prev = cp;
+ }
+}
+
static void check_updates(struct cache_entry **src, int nr)
{
static struct checkout state = {
@@ -250,7 +279,7 @@ static void check_updates(struct cache_entry **src, int nr)
struct cache_entry *ce = *src++;
if (!ce->ce_mode) {
if (update)
- unlink(ce->name);
+ unlink_entry(ce->name);
continue;
}
if (ce->ce_flags & mask) {
diff --git a/receive-pack.c b/receive-pack.c
index 06857eb77..8f157bc3f 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -95,6 +95,10 @@ static int update(const char *name,
char new_hex[60], *old_hex, *lock_name;
int newfd, namelen, written;
+ if (!strncmp(name, "refs/", 5) && check_ref_format(name + 5))
+ return error("refusing to create funny ref '%s' locally",
+ name);
+
namelen = strlen(name);
lock_name = xmalloc(namelen + 10);
memcpy(lock_name, name, namelen);
diff --git a/refs.c b/refs.c
index 2aac90ca5..97506a4eb 100644
--- a/refs.c
+++ b/refs.c
@@ -2,7 +2,6 @@
#include "cache.h"
#include <errno.h>
-#include <ctype.h>
/* We allow "recursive" symbolic refs. Only within reason, though */
#define MAXDEPTH 5
@@ -46,7 +45,7 @@ int validate_symref(const char *path)
len -= 4;
while (len && isspace(*buf))
buf++, len--;
- if (len >= 5 && !memcmp("refs/", buffer, 5))
+ if (len >= 5 && !memcmp("refs/", buf, 5))
return 0;
return -1;
}
@@ -335,17 +334,54 @@ int write_ref_sha1(const char *ref, int fd, const unsigned char *sha1)
return retval;
}
+/*
+ * Make sure "ref" is something reasonable to have under ".git/refs/";
+ * We do not like it if:
+ *
+ * - any path component of it begins with ".", or
+ * - it has double dots "..", or
+ * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
+ * - it ends with a "/".
+ */
+
+static inline int bad_ref_char(int ch)
+{
+ return (((unsigned) ch) <= ' ' ||
+ ch == '~' || ch == '^' || ch == ':');
+}
+
int check_ref_format(const char *ref)
{
- char *middle;
- if (ref[0] == '.' || ref[0] == '/')
- return -1;
- middle = strchr(ref, '/');
- if (!middle || !middle[1])
- return -1;
- if (strchr(middle + 1, '/'))
- return -1;
- return 0;
+ int ch, level;
+ const char *cp = ref;
+
+ level = 0;
+ while (1) {
+ while ((ch = *cp++) == '/')
+ ; /* tolerate duplicated slashes */
+ if (!ch)
+ return -1; /* should not end with slashes */
+
+ /* we are at the beginning of the path component */
+ if (ch == '.' || bad_ref_char(ch))
+ return -1;
+
+ /* scan the rest of the path component */
+ while ((ch = *cp++) != 0) {
+ if (bad_ref_char(ch))
+ return -1;
+ if (ch == '/')
+ break;
+ if (ch == '.' && *cp == '.')
+ return -1;
+ }
+ level++;
+ if (!ch) {
+ if (level < 2)
+ return -1; /* at least of form "heads/blah" */
+ return 0;
+ }
+ }
}
int write_ref_sha1_unlocked(const char *ref, const unsigned char *sha1)
diff --git a/rev-list.c b/rev-list.c
index 5ec9ccb60..c60aa7295 100644
--- a/rev-list.c
+++ b/rev-list.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "refs.h"
#include "tag.h"
#include "commit.h"
#include "tree.h"
@@ -489,6 +490,22 @@ static void handle_one_commit(struct commit *com, struct commit_list **lst)
commit_list_insert(com, lst);
}
+/* for_each_ref() callback does not allow user data -- Yuck. */
+static struct commit_list **global_lst;
+
+static int include_one_commit(const char *path, const unsigned char *sha1)
+{
+ struct commit *com = get_commit_reference(path, 0);
+ handle_one_commit(com, global_lst);
+ return 0;
+}
+
+static void handle_all(struct commit_list **lst)
+{
+ global_lst = lst;
+ for_each_ref(include_one_commit);
+ global_lst = NULL;
+}
int main(int argc, char **argv)
{
@@ -542,6 +559,10 @@ int main(int argc, char **argv)
bisect_list = 1;
continue;
}
+ if (!strcmp(arg, "--all")) {
+ handle_all(&list);
+ continue;
+ }
if (!strcmp(arg, "--objects")) {
tag_objects = 1;
tree_objects = 1;
diff --git a/rev-parse.c b/rev-parse.c
index 507b531dc..41b9dae42 100644
--- a/rev-parse.c
+++ b/rev-parse.c
@@ -32,6 +32,7 @@ static int revs_count = 0;
static int is_rev_argument(const char *arg)
{
static const char *rev_args[] = {
+ "--all",
"--bisect",
"--header",
"--max-age=",
diff --git a/rsh.c b/rsh.c
index bad5cff2c..d66526941 100644
--- a/rsh.c
+++ b/rsh.c
@@ -1,52 +1,16 @@
-#include "rsh.h"
-
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include "rsh.h"
+#include "quote.h"
#include "cache.h"
#define COMMAND_SIZE 4096
/*
- * Write a shell-quoted version of a string into a buffer, and
- * return bytes that ought to be output excluding final null.
- */
-static int shell_quote(char *buf, int nmax, const char *str)
-{
- char ch;
- int nq;
- int oc = 0;
-
- while ( (ch = *str++) ) {
- nq = 0;
- if ( strchr(" !\"#$%&\'()*;<=>?[\\]^`{|}", ch) )
- nq = 1;
-
- if ( nq ) {
- if ( nmax > 1 ) {
- *buf++ = '\\';
- nmax--;
- }
- oc++;
- }
-
- if ( nmax > 1 ) {
- *buf++ = ch;
- nmax--;
- }
- oc++;
- }
-
- if ( nmax )
- *buf = '\0';
-
- return oc;
-}
-
-/*
- * Append a string to a string buffer, with or without quoting. Return true
- * if the buffer overflowed.
+ * Append a string to a string buffer, with or without shell quoting.
+ * Return true if the buffer overflowed.
*/
static int add_to_string(char **ptrp, int *sizep, const char *str, int quote)
{
@@ -56,7 +20,7 @@ static int add_to_string(char **ptrp, int *sizep, const char *str, int quote)
int err = 0;
if ( quote ) {
- oc = shell_quote(p, size, str);
+ oc = sq_quote_buf(p, size, str);
} else {
oc = strlen(str);
memcpy(p, str, (oc >= size) ? size-1 : oc);
@@ -104,13 +68,12 @@ int setup_connection(int *fd_in, int *fd_out, const char *remote_prog,
if (!path) {
return error("Bad URL: %s", url);
}
- /* $GIT_RSH <host> "env GIR_DIR=<path> <remote_prog> <args...>" */
+ /* $GIT_RSH <host> "env GIT_DIR=<path> <remote_prog> <args...>" */
sizen = COMMAND_SIZE;
posn = command;
of = 0;
of |= add_to_string(&posn, &sizen, "env ", 0);
- of |= add_to_string(&posn, &sizen, GIT_DIR_ENVIRONMENT, 0);
- of |= add_to_string(&posn, &sizen, "=", 0);
+ of |= add_to_string(&posn, &sizen, GIT_DIR_ENVIRONMENT "=", 0);
of |= add_to_string(&posn, &sizen, path, 1);
of |= add_to_string(&posn, &sizen, " ", 0);
of |= add_to_string(&posn, &sizen, remote_prog, 1);
diff --git a/send-pack.c b/send-pack.c
index 55d8ff7e1..9f9a6e70b 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -181,7 +181,7 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
int new_refs;
/* No funny business with the matcher */
- remote_tail = get_remote_heads(in, &remote_refs, 0, NULL);
+ remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, 1);
get_local_heads();
/* match them up */
diff --git a/server-info.c b/server-info.c
index a9e5607f2..ba5359108 100644
--- a/server-info.c
+++ b/server-info.c
@@ -9,7 +9,14 @@ static FILE *info_ref_fp;
static int add_info_ref(const char *path, const unsigned char *sha1)
{
+ struct object *o = parse_object(sha1);
+
fprintf(info_ref_fp, "%s %s\n", sha1_to_hex(sha1), path);
+ if (o->type == tag_type) {
+ o = deref_tag(o);
+ fprintf(info_ref_fp, "%s %s^{}\n",
+ sha1_to_hex(o->sha1), path);
+ }
return 0;
}
@@ -59,6 +66,16 @@ static struct object *parse_object_cheap(const unsigned char *sha1)
struct commit *commit = (struct commit *)o;
free(commit->buffer);
commit->buffer = NULL;
+ } else if (o->type == tree_type) {
+ struct tree *tree = (struct tree *)o;
+ struct tree_entry_list *e, *n;
+ for (e = tree->entries; e; e = n) {
+ free(e->name);
+ e->name = NULL;
+ n = e->next;
+ free(e);
+ }
+ tree->entries = NULL;
}
return o;
}
diff --git a/sha1_file.c b/sha1_file.c
index 895c1fab6..e45679975 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -48,65 +48,6 @@ int get_sha1_hex(const char *hex, unsigned char *sha1)
return 0;
}
-static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir,
- *git_graft_file;
-static void setup_git_env(void)
-{
- git_dir = getenv(GIT_DIR_ENVIRONMENT);
- if (!git_dir)
- git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
- git_object_dir = getenv(DB_ENVIRONMENT);
- if (!git_object_dir) {
- git_object_dir = xmalloc(strlen(git_dir) + 9);
- sprintf(git_object_dir, "%s/objects", git_dir);
- }
- git_refs_dir = xmalloc(strlen(git_dir) + 6);
- sprintf(git_refs_dir, "%s/refs", git_dir);
- git_index_file = getenv(INDEX_ENVIRONMENT);
- if (!git_index_file) {
- git_index_file = xmalloc(strlen(git_dir) + 7);
- sprintf(git_index_file, "%s/index", git_dir);
- }
- git_graft_file = getenv(GRAFT_ENVIRONMENT);
- if (!git_graft_file)
- git_graft_file = strdup(git_path("info/grafts"));
-}
-
-char *get_git_dir(void)
-{
- if (!git_dir)
- setup_git_env();
- return git_dir;
-}
-
-char *get_object_directory(void)
-{
- if (!git_object_dir)
- setup_git_env();
- return git_object_dir;
-}
-
-char *get_refs_directory(void)
-{
- if (!git_refs_dir)
- setup_git_env();
- return git_refs_dir;
-}
-
-char *get_index_file(void)
-{
- if (!git_index_file)
- setup_git_env();
- return git_index_file;
-}
-
-char *get_graft_file(void)
-{
- if (!git_graft_file)
- setup_git_env();
- return git_graft_file;
-}
-
int safe_create_leading_directories(char *path)
{
char *pos = path;
@@ -475,7 +416,7 @@ int use_packed_git(struct packed_git *p)
return 0;
}
-struct packed_git *add_packed_git(char *path, int path_len)
+struct packed_git *add_packed_git(char *path, int path_len, int local)
{
struct stat st;
struct packed_git *p;
@@ -503,6 +444,7 @@ struct packed_git *add_packed_git(char *path, int path_len)
p->pack_base = NULL;
p->pack_last_used = 0;
p->pack_use_cnt = 0;
+ p->pack_local = local;
return p;
}
@@ -543,7 +485,7 @@ void install_packed_git(struct packed_git *pack)
packed_git = pack;
}
-static void prepare_packed_git_one(char *objdir)
+static void prepare_packed_git_one(char *objdir, int local)
{
char path[PATH_MAX];
int len;
@@ -565,7 +507,7 @@ static void prepare_packed_git_one(char *objdir)
/* we have .idx. Is it a file we can map? */
strcpy(path + len, de->d_name);
- p = add_packed_git(path, len + namelen);
+ p = add_packed_git(path, len + namelen, local);
if (!p)
continue;
p->next = packed_git;
@@ -581,11 +523,11 @@ void prepare_packed_git(void)
if (run_once)
return;
- prepare_packed_git_one(get_object_directory());
+ prepare_packed_git_one(get_object_directory(), 1);
prepare_alt_odb();
for (alt = alt_odb_list; alt; alt = alt->next) {
alt->name[0] = 0;
- prepare_packed_git_one(alt->base);
+ prepare_packed_git_one(alt->base, 0);
}
run_once = 1;
}
@@ -1248,6 +1190,73 @@ char *write_sha1_file_prepare(void *buf,
return sha1_file_name(sha1);
}
+/*
+ * Link the tempfile to the final place, possibly creating the
+ * last directory level as you do so.
+ *
+ * Returns the errno on failure, 0 on success.
+ */
+static int link_temp_to_file(const char *tmpfile, char *filename)
+{
+ int ret;
+
+ if (!link(tmpfile, filename))
+ return 0;
+
+ /*
+ * Try to mkdir the last path component if that failed
+ * with an ENOENT.
+ *
+ * Re-try the "link()" regardless of whether the mkdir
+ * succeeds, since a race might mean that somebody
+ * else succeeded.
+ */
+ ret = errno;
+ if (ret == ENOENT) {
+ char *dir = strrchr(filename, '/');
+ if (dir) {
+ *dir = 0;
+ mkdir(filename, 0777);
+ *dir = '/';
+ if (!link(tmpfile, filename))
+ return 0;
+ ret = errno;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Move the just written object into its final resting place
+ */
+int move_temp_to_file(const char *tmpfile, char *filename)
+{
+ int ret = link_temp_to_file(tmpfile, filename);
+ if (ret) {
+ /*
+ * Coda hack - coda doesn't like cross-directory links,
+ * so we fall back to a rename, which will mean that it
+ * won't be able to check collisions, but that's not a
+ * big deal.
+ *
+ * When this succeeds, we just return 0. We have nothing
+ * left to unlink.
+ */
+ if (ret == EXDEV && !rename(tmpfile, filename))
+ return 0;
+ }
+ unlink(tmpfile);
+ if (ret) {
+ if (ret != EEXIST) {
+ fprintf(stderr, "unable to write sha1 filename %s: %s", filename, strerror(ret));
+ return -1;
+ }
+ /* FIXME!!! Collision check here ? */
+ }
+
+ return 0;
+}
+
int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
{
int size;
@@ -1257,7 +1266,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
char *filename;
static char tmpfile[PATH_MAX];
unsigned char hdr[50];
- int fd, hdrlen, ret;
+ int fd, hdrlen;
/* Normally if we have it in the pack then we do not bother writing
* it out into .git/objects/??/?{38} file.
@@ -1320,32 +1329,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
close(fd);
free(compressed);
- ret = link(tmpfile, filename);
- if (ret < 0) {
- ret = errno;
-
- /*
- * Coda hack - coda doesn't like cross-directory links,
- * so we fall back to a rename, which will mean that it
- * won't be able to check collisions, but that's not a
- * big deal.
- *
- * When this succeeds, we just return 0. We have nothing
- * left to unlink.
- */
- if (ret == EXDEV && !rename(tmpfile, filename))
- return 0;
- }
- unlink(tmpfile);
- if (ret) {
- if (ret != EEXIST) {
- fprintf(stderr, "unable to write sha1 filename %s: %s", filename, strerror(ret));
- return -1;
- }
- /* FIXME!!! Collision check here ? */
- }
-
- return 0;
+ return move_temp_to_file(tmpfile, filename);
}
int write_sha1_to_fd(int fd, const unsigned char *sha1)
@@ -1420,8 +1404,7 @@ int write_sha1_to_fd(int fd, const unsigned char *sha1)
int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
size_t bufsize, size_t *bufposn)
{
- char *filename = sha1_file_name(sha1);
-
+ char tmpfile[PATH_MAX];
int local;
z_stream stream;
unsigned char real_sha1[20];
@@ -1429,10 +1412,11 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
int ret;
SHA_CTX c;
- local = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
+ snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory());
+ local = mkstemp(tmpfile);
if (local < 0)
- return error("Couldn't open %s\n", filename);
+ return error("Couldn't open %s for %s\n", tmpfile, sha1_to_hex(sha1));
memset(&stream, 0, sizeof(stream));
@@ -1462,7 +1446,7 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
size = read(fd, buffer + *bufposn, bufsize - *bufposn);
if (size <= 0) {
close(local);
- unlink(filename);
+ unlink(tmpfile);
if (!size)
return error("Connection closed?");
perror("Reading from connection");
@@ -1475,15 +1459,15 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
close(local);
SHA1_Final(real_sha1, &c);
if (ret != Z_STREAM_END) {
- unlink(filename);
+ unlink(tmpfile);
return error("File %s corrupted", sha1_to_hex(sha1));
}
if (memcmp(sha1, real_sha1, 20)) {
- unlink(filename);
+ unlink(tmpfile);
return error("File %s has bad hash\n", sha1_to_hex(sha1));
}
-
- return 0;
+
+ return move_temp_to_file(tmpfile, sha1_file_name(sha1));
}
int has_pack_index(const unsigned char *sha1)
@@ -1545,3 +1529,42 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con
munmap(buf, size);
return ret;
}
+
+int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object)
+{
+ int fd;
+ char *target;
+
+ switch (st->st_mode & S_IFMT) {
+ case S_IFREG:
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return error("open(\"%s\"): %s", path,
+ strerror(errno));
+ if (index_fd(sha1, fd, st, write_object, NULL) < 0)
+ return error("%s: failed to insert into database",
+ path);
+ break;
+ case S_IFLNK:
+ target = xmalloc(st->st_size+1);
+ if (readlink(path, target, st->st_size+1) != st->st_size) {
+ char *errstr = strerror(errno);
+ free(target);
+ return error("readlink(\"%s\"): %s", path,
+ errstr);
+ }
+ if (!write_object) {
+ unsigned char hdr[50];
+ int hdrlen;
+ write_sha1_file_prepare(target, st->st_size, "blob",
+ sha1, hdr, &hdrlen);
+ } else if (write_sha1_file(target, st->st_size, "blob", sha1))
+ return error("%s: failed to insert into database",
+ path);
+ free(target);
+ break;
+ default:
+ return error("%s: unsupported file type", path);
+ }
+ return 0;
+}
diff --git a/sha1_name.c b/sha1_name.c
index f64755fbc..75c688ecf 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -1,5 +1,8 @@
#include "cache.h"
+#include "tag.h"
#include "commit.h"
+#include "tree.h"
+#include "blob.h"
static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
{
@@ -119,6 +122,9 @@ static int find_short_packed_object(int len, const unsigned char *match, unsigne
return found;
}
+#define SHORT_NAME_NOT_FOUND (-1)
+#define SHORT_NAME_AMBIGUOUS (-2)
+
static int find_unique_short_object(int len, char *canonical,
unsigned char *res, unsigned char *sha1)
{
@@ -128,23 +134,24 @@ static int find_unique_short_object(int len, char *canonical,
has_unpacked = find_short_object_filename(len, canonical, unpacked_sha1);
has_packed = find_short_packed_object(len, res, packed_sha1);
if (!has_unpacked && !has_packed)
- return -1;
+ return SHORT_NAME_NOT_FOUND;
if (1 < has_unpacked || 1 < has_packed)
- return error("short SHA1 %.*s is ambiguous.", len, canonical);
+ return SHORT_NAME_AMBIGUOUS;
if (has_unpacked != has_packed) {
memcpy(sha1, (has_packed ? packed_sha1 : unpacked_sha1), 20);
return 0;
}
/* Both have unique ones -- do they match? */
if (memcmp(packed_sha1, unpacked_sha1, 20))
- return error("short SHA1 %.*s is ambiguous.", len, canonical);
+ return -2;
memcpy(sha1, packed_sha1, 20);
return 0;
}
-static int get_short_sha1(const char *name, int len, unsigned char *sha1)
+static int get_short_sha1(const char *name, int len, unsigned char *sha1,
+ int quietly)
{
- int i;
+ int i, status;
char canonical[40];
unsigned char res[20];
@@ -171,7 +178,29 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1)
res[i >> 1] |= val;
}
- return find_unique_short_object(i, canonical, res, sha1);
+ status = find_unique_short_object(i, canonical, res, sha1);
+ if (!quietly && (status == SHORT_NAME_AMBIGUOUS))
+ return error("short SHA1 %.*s is ambiguous.", len, canonical);
+ return status;
+}
+
+const char *find_unique_abbrev(const unsigned char *sha1, int len)
+{
+ int status;
+ static char hex[41];
+ memcpy(hex, sha1_to_hex(sha1), 40);
+ while (len < 40) {
+ unsigned char sha1_ret[20];
+ status = get_short_sha1(hex, len, sha1_ret, 1);
+ if (!status) {
+ hex[len] = 0;
+ return hex;
+ }
+ if (status != SHORT_NAME_AMBIGUOUS)
+ return NULL;
+ len++;
+ }
+ return NULL;
}
static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
@@ -248,6 +277,82 @@ static int get_nth_ancestor(const char *name, int len,
return 0;
}
+static int peel_onion(const char *name, int len, unsigned char *sha1)
+{
+ unsigned char outer[20];
+ const char *sp;
+ const char *type_string = NULL;
+ struct object *o;
+
+ /*
+ * "ref^{type}" dereferences ref repeatedly until you cannot
+ * dereference anymore, or you get an object of given type,
+ * whichever comes first. "ref^{}" means just dereference
+ * tags until you get a non-tag. "ref^0" is a shorthand for
+ * "ref^{commit}". "commit^{tree}" could be used to find the
+ * top-level tree of the given commit.
+ */
+ if (len < 4 || name[len-1] != '}')
+ return -1;
+
+ for (sp = name + len - 1; name <= sp; sp--) {
+ int ch = *sp;
+ if (ch == '{' && name < sp && sp[-1] == '^')
+ break;
+ }
+ if (sp <= name)
+ return -1;
+
+ sp++; /* beginning of type name, or closing brace for empty */
+ if (!strncmp(commit_type, sp, 6) && sp[6] == '}')
+ type_string = commit_type;
+ else if (!strncmp(tree_type, sp, 4) && sp[4] == '}')
+ type_string = tree_type;
+ else if (!strncmp(blob_type, sp, 4) && sp[4] == '}')
+ type_string = blob_type;
+ else if (sp[0] == '}')
+ type_string = NULL;
+ else
+ return -1;
+
+ if (get_sha1_1(name, sp - name - 2, outer))
+ return -1;
+
+ o = parse_object(outer);
+ if (!o)
+ return -1;
+ if (!type_string) {
+ o = deref_tag(o);
+ memcpy(sha1, o->sha1, 20);
+ }
+ else {
+ /* At this point, the syntax look correct, so
+ * if we do not get the needed object, we should
+ * barf.
+ */
+
+ while (1) {
+ if (!o)
+ return -1;
+ if (o->type == type_string) {
+ memcpy(sha1, o->sha1, 20);
+ return 0;
+ }
+ if (o->type == tag_type)
+ o = ((struct tag*) o)->tagged;
+ else if (o->type == commit_type)
+ o = &(((struct commit *) o)->tree->object);
+ else
+ return error("%.*s: expected %s type, but the object dereferences to %s type",
+ len, name, type_string,
+ o->type);
+ if (!o->parsed)
+ parse_object(o->sha1);
+ }
+ }
+ return 0;
+}
+
static int get_sha1_1(const char *name, int len, unsigned char *sha1)
{
int parent, ret;
@@ -289,10 +394,14 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
return get_nth_ancestor(name, len1, sha1, parent);
}
+ ret = peel_onion(name, len, sha1);
+ if (!ret)
+ return 0;
+
ret = get_sha1_basic(name, len, sha1);
if (!ret)
return 0;
- return get_short_sha1(name, len, sha1);
+ return get_short_sha1(name, len, sha1, 0);
}
/*
diff --git a/show-branch.c b/show-branch.c
index 8429c171c..bb14c8677 100644
--- a/show-branch.c
+++ b/show-branch.c
@@ -133,25 +133,28 @@ static void name_commits(struct commit_list *list,
nth = 0;
while (parents) {
struct commit *p = parents->item;
- char newname[1000];
+ char newname[1000], *en;
parents = parents->next;
nth++;
if (p->object.util)
continue;
+ en = newname;
switch (n->generation) {
case 0:
- sprintf(newname, "%s^%d",
- n->head_name, nth);
+ en += sprintf(en, "%s", n->head_name);
break;
case 1:
- sprintf(newname, "%s^^%d",
- n->head_name, nth);
+ en += sprintf(en, "%s^", n->head_name);
break;
default:
- sprintf(newname, "%s~%d^%d",
- n->head_name, n->generation,
- nth);
+ en += sprintf(en, "%s~%d",
+ n->head_name, n->generation);
+ break;
}
+ if (nth == 1)
+ en += sprintf(en, "^");
+ else
+ en += sprintf(en, "^%d", nth);
name_commit(p, strdup(newname), 0);
i++;
name_first_parent_chain(p);
@@ -205,7 +208,7 @@ static void join_revs(struct commit_list **list_p,
}
}
-static void show_one_commit(struct commit *commit)
+static void show_one_commit(struct commit *commit, int no_name)
{
char pretty[128], *cp;
struct commit_name *name = commit->object.util;
@@ -218,11 +221,21 @@ static void show_one_commit(struct commit *commit)
cp = pretty + 8;
else
cp = pretty;
- if (name && name->head_name) {
- printf("[%s", name->head_name);
- if (name->generation)
- printf("~%d", name->generation);
- printf("] ");
+
+ if (!no_name) {
+ if (name && name->head_name) {
+ printf("[%s", name->head_name);
+ if (name->generation) {
+ if (name->generation == 1)
+ printf("^");
+ else
+ printf("~%d", name->generation);
+ }
+ printf("] ");
+ }
+ else
+ printf("[%s] ",
+ find_unique_abbrev(commit->object.sha1, 7));
}
puts(cp);
}
@@ -354,7 +367,8 @@ int main(int ac, char **av)
unsigned char head_sha1[20];
int merge_base = 0;
int independent = 0;
- char **label;
+ int no_name = 0;
+ int sha1_name = 0;
setup_git_directory();
@@ -370,6 +384,10 @@ int main(int ac, char **av)
extra = 1;
else if (!strcmp(arg, "--list"))
extra = -1;
+ else if (!strcmp(arg, "--no-name"))
+ no_name = 1;
+ else if (!strcmp(arg, "--sha1-name"))
+ sha1_name = 1;
else if (!strncmp(arg, "--more=", 7))
extra = atoi(arg + 7);
else if (!strcmp(arg, "--merge-base"))
@@ -465,7 +483,8 @@ int main(int ac, char **av)
printf("%c [%s] ",
is_head ? '*' : '!', ref_name[i]);
}
- show_one_commit(rev[i]);
+ /* header lines never need name */
+ show_one_commit(rev[i], 1);
}
if (0 <= extra) {
for (i = 0; i < num_rev; i++)
@@ -480,7 +499,8 @@ int main(int ac, char **av)
sort_in_topological_order(&seen);
/* Give names to commits */
- name_commits(seen, rev, ref_name, num_rev);
+ if (!sha1_name && !no_name)
+ name_commits(seen, rev, ref_name, num_rev);
all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
@@ -490,7 +510,6 @@ int main(int ac, char **av)
struct commit *commit = pop_one_commit(&seen);
int this_flag = commit->object.flags;
int is_merge_point = (this_flag & all_revs) == all_revs;
- static char *obvious[] = { "" };
if (is_merge_point)
shown_merge_point = 1;
@@ -501,9 +520,7 @@ int main(int ac, char **av)
? '+' : ' ');
putchar(' ');
}
- show_one_commit(commit);
- if (num_rev == 1)
- label = obvious;
+ show_one_commit(commit, no_name);
if (shown_merge_point && is_merge_point)
if (--extra < 0)
break;
diff --git a/t/Makefile b/t/Makefile
index e71da7782..5c76afff8 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -7,10 +7,16 @@
SHELL_PATH ?= $(SHELL)
TAR ?= $(TAR)
+# Shell quote;
+# Result of this needs to be placed inside ''
+shq = $(subst ','\'',$(1))
+# This has surrounding ''
+shellquote = '$(call shq,$(1))'
+
T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
all:
- @$(foreach t,$T,echo "*** $t ***"; $(SHELL_PATH) $t $(GIT_TEST_OPTS) || exit; )
+ @$(foreach t,$T,echo "*** $t ***"; $(call shellquote,$(SHELL_PATH)) $t $(GIT_TEST_OPTS) || exit; )
@rm -fr trash
clean:
diff --git a/t/diff-lib.sh b/t/diff-lib.sh
index a912f435a..745a1b031 100755
--- a/t/diff-lib.sh
+++ b/t/diff-lib.sh
@@ -29,7 +29,13 @@ compare_diff_raw_z () {
compare_diff_patch () {
# When heuristics are improved, the score numbers would change.
# Ignore them while comparing.
- sed -e '/^[dis]*imilarity index [0-9]*%$/d' <"$1" >.tmp-1
- sed -e '/^[dis]*imilarity index [0-9]*%$/d' <"$2" >.tmp-2
+ sed -e '
+ /^[dis]*imilarity index [0-9]*%$/d
+ /^index [0-9a-f]*\.\.[0-9a-f]/d
+ ' <"$1" >.tmp-1
+ sed -e '
+ /^[dis]*imilarity index [0-9]*%$/d
+ /^index [0-9a-f]*\.\.[0-9a-f]/d
+ ' <"$2" >.tmp-2
diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
}
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index bd940bd09..5c5f85485 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -28,7 +28,7 @@ test_expect_success \
'.git/objects should be empty after git-init-db in an empty repo.' \
'cmp -s /dev/null should-be-empty'
-# also it should have 258 subdirectories; 256 fan-out, pack, and info.
+# also it should have 258 subdirectories; 256 fan-out anymore, pack, and info.
# 259 is counting "objects" itself
find .git/objects -type d -print >full-of-directories
test_expect_success \
diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh
new file mode 100644
index 000000000..35db799ed
--- /dev/null
+++ b/t/t1200-tutorial.sh
@@ -0,0 +1,160 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Johannes Schindelin
+#
+
+test_description='Test git-rev-parse with different parent options'
+
+. ./test-lib.sh
+
+echo "Hello World" > hello
+echo "Silly example" > example
+
+git-update-index --add hello example
+
+test_expect_success 'blob' "test blob = \"$(git-cat-file -t 557db03)\""
+
+test_expect_success 'blob 557db03' "test \"Hello World\" = \"$(git-cat-file blob 557db03)\""
+
+echo "It's a new day for git" >>hello
+cat > diff.expect << EOF
+diff --git a/hello b/hello
+index 557db03..263414f 100644
+--- a/hello
++++ b/hello
+@@ -1 +1,2 @@
+ Hello World
++It's a new day for git
+EOF
+git-diff-files -p > diff.output
+test_expect_success 'git-diff-files -p' 'cmp diff.expect diff.output'
+git diff > diff.output
+test_expect_success 'git diff' 'cmp diff.expect diff.output'
+
+tree=$(git-write-tree 2>/dev/null)
+
+test_expect_success 'tree' "test 8988da15d077d4829fc51d8544c097def6644dbb = $tree"
+
+output="$(echo "Initial commit" | git-commit-tree $(git-write-tree) 2>&1 > .git/refs/heads/master)"
+
+test_expect_success 'commit' "test 'Committing initial tree 8988da15d077d4829fc51d8544c097def6644dbb' = \"$output\""
+
+git-diff-index -p HEAD > diff.output
+test_expect_success 'git-diff-index -p HEAD' 'cmp diff.expect diff.output'
+
+git diff HEAD > diff.output
+test_expect_success 'git diff HEAD' 'cmp diff.expect diff.output'
+
+#rm hello
+#test_expect_success 'git-read-tree --reset HEAD' "git-read-tree --reset HEAD ; test \"hello: needs update\" = \"$(git-update-index --refresh)\""
+
+cat > whatchanged.expect << EOF
+diff-tree VARIABLE (from root)
+Author: VARIABLE
+Date: VARIABLE
+
+ Initial commit
+
+diff --git a/example b/example
+new file mode 100644
+index 0000000..f24c74a
+--- /dev/null
++++ b/example
+@@ -0,0 +1 @@
++Silly example
+diff --git a/hello b/hello
+new file mode 100644
+index 0000000..557db03
+--- /dev/null
++++ b/hello
+@@ -0,0 +1 @@
++Hello World
+EOF
+
+git-whatchanged -p --root | \
+ sed -e "1s/^\(.\{10\}\).\{40\}/\1VARIABLE/" \
+ -e "2,3s/^\(.\{8\}\).*$/\1VARIABLE/" \
+> whatchanged.output
+test_expect_success 'git-whatchanged -p --root' 'cmp whatchanged.expect whatchanged.output'
+
+git tag my-first-tag
+test_expect_success 'git tag my-first-tag' 'cmp .git/refs/heads/master .git/refs/tags/my-first-tag'
+
+# TODO: test git-clone
+
+git checkout -b mybranch
+test_expect_success 'git checkout -b mybranch' 'cmp .git/refs/heads/master .git/refs/heads/mybranch'
+
+cat > branch.expect <<EOF
+ master
+* mybranch
+EOF
+
+git branch > branch.output
+test_expect_success 'git branch' 'cmp branch.expect branch.output'
+
+git checkout mybranch
+echo "Work, work, work" >>hello
+git commit -m 'Some work.' hello
+
+git checkout master
+
+echo "Play, play, play" >>hello
+echo "Lots of fun" >>example
+git commit -m 'Some fun.' hello example
+
+test_expect_failure 'git resolve now fails' 'git resolve HEAD mybranch "Merge work in mybranch"'
+
+cat > hello << EOF
+Hello World
+It's a new day for git
+Play, play, play
+Work, work, work
+EOF
+
+git commit -m 'Merged "mybranch" changes.' hello
+
+cat > show-branch.expect << EOF
+* [master] Merged "mybranch" changes.
+ ! [mybranch] Some work.
+--
++ [master] Merged "mybranch" changes.
+++ [mybranch] Some work.
+EOF
+
+git show-branch master mybranch > show-branch.output
+test_expect_success 'git show-branch' 'cmp show-branch.expect show-branch.output'
+
+git checkout mybranch
+
+cat > resolve.expect << EOF
+Updating from VARIABLE to VARIABLE.
+ example | 1 +
+ hello | 1 +
+ 2 files changed, 2 insertions(+), 0 deletions(-)
+EOF
+
+git resolve HEAD master "Merge upstream changes." | \
+ sed -e "1s/[0-9a-f]\{40\}/VARIABLE/g" > resolve.output
+test_expect_success 'git resolve' 'cmp resolve.expect resolve.output'
+
+cat > show-branch2.expect << EOF
+! [master] Merged "mybranch" changes.
+ * [mybranch] Merged "mybranch" changes.
+--
+++ [master] Merged "mybranch" changes.
+EOF
+
+git show-branch master mybranch > show-branch2.output
+test_expect_success 'git show-branch' 'cmp show-branch2.expect show-branch2.output'
+
+# TODO: test git fetch
+
+# TODO: test git push
+
+test_expect_success 'git repack' 'git repack'
+test_expect_success 'git prune-packed' 'git prune-packed'
+test_expect_failure '-> only packed objects' 'find -type f .git/objects/[0-9a-f][0-9a-f]'
+
+test_done
+
diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh
new file mode 100644
index 000000000..541036834
--- /dev/null
+++ b/t/t3101-ls-tree-dirname.sh
@@ -0,0 +1,160 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+# Copyright (c) 2005 Robert Fitzsimons
+#
+
+test_description='git-ls-tree directory and filenames handling.
+
+This test runs git-ls-tree with the following in a tree.
+
+ 1.txt - a file
+ 2.txt - a file
+ path0/a/b/c/1.txt - a file in a directory
+ path1/b/c/1.txt - a file in a directory
+ path2/1.txt - a file in a directory
+ path3/1.txt - a file in a directory
+ path3/2.txt - a file in a directory
+
+Test the handling of mulitple directories which have matching file
+entries. Also test odd filename and missing entries handling.
+'
+. ./test-lib.sh
+
+test_expect_success \
+ 'setup' \
+ 'echo 111 >1.txt &&
+ echo 222 >2.txt &&
+ mkdir path0 path0/a path0/a/b path0/a/b/c &&
+ echo 111 >path0/a/b/c/1.txt &&
+ mkdir path1 path1/b path1/b/c &&
+ echo 111 >path1/b/c/1.txt &&
+ mkdir path2 &&
+ echo 111 >path2/1.txt &&
+ mkdir path3 &&
+ echo 111 >path3/1.txt &&
+ echo 222 >path3/2.txt &&
+ find *.txt path* \( -type f -o -type l \) -print |
+ xargs git-update-index --add &&
+ tree=`git-write-tree` &&
+ echo $tree'
+
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+test_output () {
+ sed -e "s/ $_x40 / X /" <current >check
+ diff -u expected check
+}
+
+test_expect_success \
+ 'ls-tree plain' \
+ 'git-ls-tree $tree >current &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+100644 blob X 2.txt
+040000 tree X path0
+040000 tree X path1
+040000 tree X path2
+040000 tree X path3
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree recursive' \
+ 'git-ls-tree -r $tree >current &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+100644 blob X 2.txt
+040000 tree X path0
+040000 tree X path0/a
+040000 tree X path0/a/b
+040000 tree X path0/a/b/c
+100644 blob X path0/a/b/c/1.txt
+040000 tree X path1
+040000 tree X path1/b
+040000 tree X path1/b/c
+100644 blob X path1/b/c/1.txt
+040000 tree X path2
+100644 blob X path2/1.txt
+040000 tree X path3
+100644 blob X path3/1.txt
+100644 blob X path3/2.txt
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filter 1.txt' \
+ 'git-ls-tree $tree 1.txt >current &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filter path1/b/c/1.txt' \
+ 'git-ls-tree $tree path1/b/c/1.txt >current &&
+ cat >expected <<\EOF &&
+100644 blob X path1/b/c/1.txt
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filter all 1.txt files' \
+ 'git-ls-tree $tree 1.txt path0/a/b/c/1.txt path1/b/c/1.txt path2/1.txt path3/1.txt >current &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+100644 blob X path0/a/b/c/1.txt
+100644 blob X path1/b/c/1.txt
+100644 blob X path2/1.txt
+100644 blob X path3/1.txt
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filter directories' \
+ 'git-ls-tree $tree path3 path2 path0/a/b/c path1/b/c path0/a >current &&
+ cat >expected <<\EOF &&
+040000 tree X path3
+100644 blob X path3/1.txt
+100644 blob X path3/2.txt
+040000 tree X path2
+100644 blob X path2/1.txt
+040000 tree X path0/a/b/c
+100644 blob X path0/a/b/c/1.txt
+040000 tree X path1/b/c
+100644 blob X path1/b/c/1.txt
+040000 tree X path0/a
+040000 tree X path0/a/b
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filter odd names' \
+ 'git-ls-tree $tree 1.txt /1.txt //1.txt path3/1.txt /path3/1.txt //path3//1.txt path3 /path3/ path3// >current &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+100644 blob X 1.txt
+100644 blob X 1.txt
+100644 blob X path3/1.txt
+100644 blob X path3/1.txt
+100644 blob X path3/1.txt
+040000 tree X path3
+100644 blob X path3/1.txt
+100644 blob X path3/2.txt
+040000 tree X path3
+100644 blob X path3/1.txt
+100644 blob X path3/2.txt
+040000 tree X path3
+100644 blob X path3/1.txt
+100644 blob X path3/2.txt
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filter missing files and extra slashes' \
+ 'git-ls-tree $tree 1.txt/ abc.txt path3//23.txt path3/2.txt/// >current &&
+ cat >expected <<\EOF &&
+EOF
+ test_output'
+
+test_done
diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh
index f3b6330a9..beb6d8f48 100755
--- a/t/t4000-diff-format.sh
+++ b/t/t4000-diff-format.sh
@@ -7,6 +7,7 @@ test_description='Test built-in diff output engine.
'
. ./test-lib.sh
+. ../diff-lib.sh
echo >path0 'Line 1
Line 2
@@ -48,6 +49,6 @@ EOF
test_expect_success \
'validate git-diff-files -p output.' \
- 'cmp -s current expected'
+ 'compare_diff_patch current expected'
test_done
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index be4748568..2e3c20d6b 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -7,6 +7,7 @@ test_description='Test rename detection in diff engine.
'
. ./test-lib.sh
+. ../diff-lib.sh
echo >path0 'Line 1
Line 2
@@ -61,6 +62,6 @@ EOF
test_expect_success \
'validate the output.' \
- 'diff -I "similarity.*" >/dev/null current expected'
+ 'compare_diff_patch current expected'
test_done
diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh
index f59614ae2..a23aaa0a9 100755
--- a/t/t4004-diff-rename-symlink.sh
+++ b/t/t4004-diff-rename-symlink.sh
@@ -10,6 +10,7 @@ copy of symbolic links, but should not produce rename/copy followed
by an edit for them.
'
. ./test-lib.sh
+. ../diff-lib.sh
test_expect_success \
'prepare reference tree' \
@@ -61,6 +62,6 @@ EOF
test_expect_success \
'validate diff output' \
- 'diff -u current expected'
+ 'compare_diff_patch current expected'
test_done
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 5dffb8e1e..4db1bb114 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -41,8 +41,8 @@ test_expect_success \
find a -type l | xargs git-update-index --add &&
treeid=`git-write-tree` &&
echo $treeid >treeid &&
- TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \
- git-commit-tree $treeid </dev/null >.git/HEAD'
+ git-update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \
+ git-commit-tree $treeid </dev/null)'
test_expect_success \
'git-tar-tree' \
@@ -59,7 +59,7 @@ test_expect_success \
test_expect_success \
'git-get-tar-commit-id' \
'git-get-tar-commit-id <b.tar >b.commitid &&
- diff .git/HEAD b.commitid'
+ diff .git/$(git-symbolic-ref HEAD) b.commitid'
test_expect_success \
'extract tar archive' \
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index bb62336f2..96db98b65 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -165,4 +165,22 @@ test_expect_success \
:'
+test_expect_success \
+ 'build pack index for an existing pack' \
+ 'cp test-1-${packname_1}.pack test-3.pack &&
+ git-index-pack -o tmp.idx test-3.pack &&
+ cmp tmp.idx test-1-${packname_1}.idx &&
+
+ git-index-pack test-3.pack &&
+ cmp test-3.idx test-1-${packname_1}.idx &&
+
+ cp test-2-${packname_2}.pack test-3.pack &&
+ git-index-pack -o tmp.idx test-2-${packname_2}.pack &&
+ cmp tmp.idx test-2-${packname_2}.idx &&
+
+ git-index-pack test-3.pack &&
+ cmp test-3.idx test-2-${packname_2}.idx &&
+
+ :'
+
test_done
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 1a4d2f2f1..7fc3bd7d3 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -8,6 +8,9 @@ test_description='See why rewinding head breaks send-pack
'
. ./test-lib.sh
+touch cpio-test
+test_expect_success 'working cpio' 'echo cpio-test | cpio -o > /dev/null'
+
cnt='1'
test_expect_success setup '
tree=$(git-write-tree) &&
diff --git a/t/t6001-rev-list-merge-order.sh b/t/t6001-rev-list-merge-order.sh
index 010124238..8ec9ebb98 100755
--- a/t/t6001-rev-list-merge-order.sh
+++ b/t/t6001-rev-list-merge-order.sh
@@ -108,7 +108,7 @@ save_tag h2 unique_commit g4 tree -p g2
save_tag g3 unique_commit g5 tree -p g2
save_tag g4 unique_commit g6 tree -p g3 -p h2
-tag l5 > .git/HEAD
+git-update-ref HEAD $(tag l5)
test_expect_success 'rev-list has correct number of entries' 'git-rev-list HEAD | wc -l | tr -s " "' <<EOF
19
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index 42fcbc60c..693de9b32 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -58,7 +58,7 @@ on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3
on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4
on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3
on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4
-tag l5 > .git/HEAD
+git-update-ref HEAD $(tag l5)
# E
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index 88d14ee1a..3c4c44c24 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -77,7 +77,7 @@ save_tag h2 unique_commit g4 tree -p g2
save_tag g3 unique_commit g5 tree -p g2
save_tag g4 unique_commit g6 tree -p g3 -p h2
-tag l5 > .git/HEAD
+git-update-ref HEAD $(tag l5)
test_expect_success 'rev-list has correct number of entries' 'git-rev-list HEAD | wc -l | tr -s " "' <<EOF
19
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 1523d2ebb..a8f239df8 100755
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -5,9 +5,10 @@
# For repeatability, reset the environment to known value.
LANG=C
+LC_ALL=C
PAGER=cat
TZ=UTC
-export LANG PAGER TZ
+export LANG LC_ALL PAGER TZ
unset AUTHOR_DATE
unset AUTHOR_EMAIL
unset AUTHOR_NAME
@@ -163,4 +164,8 @@ test=trash
rm -fr "$test"
mkdir "$test"
cd "$test"
-git-init-db 2>/dev/null || error "cannot run git-init-db"
+git-init-db --template=../../templates/blt/ 2>/dev/null ||
+error "cannot run git-init-db"
+
+mv .git/hooks .git/hooks-disabled
+
diff --git a/templates/.gitignore b/templates/.gitignore
index ca680c5b9..6759ecbf9 100644
--- a/templates/.gitignore
+++ b/templates/.gitignore
@@ -1 +1,2 @@
blt
+boilerplates.made
diff --git a/templates/Makefile b/templates/Makefile
index 221a08606..07e928e56 100644
--- a/templates/Makefile
+++ b/templates/Makefile
@@ -6,13 +6,21 @@ prefix ?= $(HOME)
template_dir ?= $(prefix)/share/git-core/templates/
# DESTDIR=
-all: boilerplates custom
+# Shell quote;
+# Result of this needs to be placed inside ''
+shq = $(subst ','\'',$(1))
+# This has surrounding ''
+shellquote = '$(call shq,$(1))'
+
+all: boilerplates.made custom
find blt
# Put templates that can be copied straight from the source
# in a file direc--tory--file in the source. They will be
# just copied to the destination.
-boilerplates:
+
+bpsrc = $(filter-out %~,$(wildcard *--*))
+boilerplates.made : $(bpsrc)
ls *--* 2>/dev/null | \
while read boilerplate; \
do \
@@ -25,6 +33,7 @@ boilerplates:
*) cp $$boilerplate blt/$$dst ;; \
esac || exit; \
done || exit
+ date >$@
# If you need build-tailored templates, build them into blt/
# directory yourself here.
@@ -32,9 +41,9 @@ custom:
: no custom templates yet
clean:
- rm -rf blt
+ rm -rf blt boilerplates.made
install: all
- $(INSTALL) -d -m755 $(DESTDIR)$(template_dir)
+ $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(template_dir))
(cd blt && $(TAR) cf - .) | \
- (cd $(DESTDIR)$(template_dir) && $(TAR) xf -)
+ (cd $(call shellquote,$(DESTDIR)$(template_dir)) && $(TAR) xf -)
diff --git a/update-index.c b/update-index.c
index b825a11d2..1eeb45dbb 100644
--- a/update-index.c
+++ b/update-index.c
@@ -37,8 +37,6 @@ static int add_file_to_cache(const char *path)
int size, namelen, option, status;
struct cache_entry *ce;
struct stat st;
- int fd;
- char *target;
status = lstat(path, &st);
if (status < 0 || S_ISDIR(st.st_mode)) {
@@ -69,42 +67,27 @@ static int add_file_to_cache(const char *path)
return error("lstat(\"%s\"): %s", path,
strerror(errno));
}
+
namelen = strlen(path);
size = cache_entry_size(namelen);
ce = xmalloc(size);
memset(ce, 0, size);
memcpy(ce->name, path, namelen);
fill_stat_cache_info(ce, &st);
+
ce->ce_mode = create_ce_mode(st.st_mode);
- ce->ce_flags = htons(namelen);
- switch (st.st_mode & S_IFMT) {
- case S_IFREG:
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return error("open(\"%s\"): %s", path, strerror(errno));
- if (index_fd(ce->sha1, fd, &st, !info_only, NULL) < 0)
- return error("%s: failed to insert into database", path);
- break;
- case S_IFLNK:
- target = xmalloc(st.st_size+1);
- if (readlink(path, target, st.st_size+1) != st.st_size) {
- char *errstr = strerror(errno);
- free(target);
- return error("readlink(\"%s\"): %s", path,
- errstr);
- }
- if (info_only) {
- unsigned char hdr[50];
- int hdrlen;
- write_sha1_file_prepare(target, st.st_size, "blob",
- ce->sha1, hdr, &hdrlen);
- } else if (write_sha1_file(target, st.st_size, "blob", ce->sha1))
- return error("%s: failed to insert into database", path);
- free(target);
- break;
- default:
- return error("%s: unsupported file type", path);
+ if (!trust_executable_bit) {
+ /* If there is an existing entry, pick the mode bits
+ * from it.
+ */
+ int pos = cache_name_pos(path, namelen);
+ if (0 <= pos)
+ ce->ce_mode = active_cache[pos]->ce_mode;
}
+ ce->ce_flags = htons(namelen);
+
+ if (index_path(ce->sha1, path, &st, !info_only))
+ return -1;
option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
if (add_cache_entry(ce, option))
@@ -280,8 +263,32 @@ static int add_cacheinfo(const char *arg1, const char *arg2, const char *arg3)
return add_cache_entry(ce, option);
}
-static struct cache_file cache_file;
+static int chmod_path(int flip, const char *path)
+{
+ int pos;
+ struct cache_entry *ce;
+ unsigned int mode;
+ pos = cache_name_pos(path, strlen(path));
+ if (pos < 0)
+ return -1;
+ ce = active_cache[pos];
+ mode = ntohl(ce->ce_mode);
+ if (!S_ISREG(mode))
+ return -1;
+ switch (flip) {
+ case '+':
+ ce->ce_mode |= htonl(0111); break;
+ case '-':
+ ce->ce_mode &= htonl(~0111); break;
+ default:
+ return -1;
+ }
+ active_cache_changed = 1;
+ return 0;
+}
+
+static struct cache_file cache_file;
static void update_one(const char *path, const char *prefix, int prefix_length)
{
@@ -299,6 +306,54 @@ static void update_one(const char *path, const char *prefix, int prefix_length)
die("Unable to process file %s", path);
}
+static void read_index_info(int line_termination)
+{
+ struct strbuf buf;
+ strbuf_init(&buf);
+ while (1) {
+ char *ptr;
+ unsigned char sha1[20];
+ unsigned int mode;
+
+ read_line(&buf, stdin, line_termination);
+ if (buf.eof)
+ break;
+
+ mode = strtoul(buf.buf, &ptr, 8);
+ if (ptr == buf.buf || *ptr != ' ' ||
+ get_sha1_hex(ptr + 1, sha1) ||
+ ptr[41] != '\t')
+ goto bad_line;
+
+ ptr += 42;
+ if (!verify_path(ptr)) {
+ fprintf(stderr, "Ignoring path %s\n", ptr);
+ continue;
+ }
+
+ if (!mode) {
+ /* mode == 0 means there is no such path -- remove */
+ if (remove_file_from_cache(ptr))
+ die("git-update-index: unable to remove %s",
+ ptr);
+ }
+ else {
+ /* mode ' ' sha1 '\t' name
+ * ptr[-1] points at tab,
+ * ptr[-41] is at the beginning of sha1
+ */
+ ptr[-42] = ptr[-1] = 0;
+ if (add_cacheinfo(buf.buf, ptr-41, ptr))
+ die("git-update-index: unable to update %s",
+ ptr);
+ }
+ continue;
+
+ bad_line:
+ die("malformed index info %s", buf.buf);
+ }
+}
+
int main(int argc, const char **argv)
{
int i, newfd, entries, has_errors = 0, line_termination = '\n';
@@ -307,6 +362,8 @@ int main(int argc, const char **argv)
const char *prefix = setup_git_directory();
int prefix_length = prefix ? strlen(prefix) : 0;
+ git_config(git_default_config);
+
newfd = hold_index_file_for_update(&cache_file, get_index_file());
if (newfd < 0)
die("unable to create new cachefile");
@@ -355,6 +412,14 @@ int main(int argc, const char **argv)
i += 3;
continue;
}
+ if (!strcmp(path, "--chmod=-x") ||
+ !strcmp(path, "--chmod=+x")) {
+ if (argc <= i+1)
+ die("git-update-index: %s <path>", path);
+ if (chmod_path(path[8], argv[++i]))
+ die("git-update-index: %s cannot chmod %s", path, argv[i]);
+ continue;
+ }
if (!strcmp(path, "--info-only")) {
info_only = 1;
continue;
@@ -373,6 +438,11 @@ int main(int argc, const char **argv)
read_from_stdin = 1;
break;
}
+ if (!strcmp(path, "--index-info")) {
+ allow_add = allow_replace = allow_remove = 1;
+ read_index_info(line_termination);
+ continue;
+ }
if (!strcmp(path, "--ignore-missing")) {
not_new = 1;
continue;
diff --git a/update-ref.c b/update-ref.c
index 4a1704c1a..65dc3d638 100644
--- a/update-ref.c
+++ b/update-ref.c
@@ -1,6 +1,5 @@
#include "cache.h"
#include "refs.h"
-#include <ctype.h>
static const char git_update_ref_usage[] = "git-update-ref <refname> <value> [<oldval>]";
diff --git a/upload-pack.c b/upload-pack.c
index da10742c4..21b4b8b75 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -1,6 +1,8 @@
#include "cache.h"
#include "refs.h"
#include "pkt-line.h"
+#include "tag.h"
+#include "object.h"
static const char upload_pack_usage[] = "git-upload-pack <dir>";
@@ -30,10 +32,18 @@ static void create_pack_file(void)
if (!pid) {
int i;
- int args = nr_has + nr_needs + 5;
- char **argv = xmalloc(args * sizeof(char *));
- char *buf = xmalloc(args * 45);
- char **p = argv;
+ int args;
+ char **argv;
+ char *buf;
+ char **p;
+
+ if (MAX_NEEDS <= nr_needs)
+ args = nr_has + 10;
+ else
+ args = nr_has + nr_needs + 5;
+ argv = xmalloc(args * sizeof(char *));
+ buf = xmalloc(args * 45);
+ p = argv;
dup2(fd[1], 1);
close(0);
@@ -41,10 +51,14 @@ static void create_pack_file(void)
close(fd[1]);
*p++ = "git-rev-list";
*p++ = "--objects";
- for (i = 0; i < nr_needs; i++) {
- *p++ = buf;
- memcpy(buf, sha1_to_hex(needs_sha1[i]), 41);
- buf += 41;
+ if (MAX_NEEDS <= nr_needs)
+ *p++ = "--all";
+ else {
+ for (i = 0; i < nr_needs; i++) {
+ *p++ = buf;
+ memcpy(buf, sha1_to_hex(needs_sha1[i]), 41);
+ buf += 41;
+ }
}
for (i = 0; i < nr_has; i++) {
*p++ = buf;
@@ -129,25 +143,37 @@ static int receive_needs(void)
needs = 0;
for (;;) {
+ unsigned char dummy[20], *sha1_buf;
len = packet_read_line(0, line, sizeof(line));
if (!len)
return needs;
- /*
- * This is purely theoretical right now: git-fetch-pack only
- * ever asks for a single HEAD
- */
- if (needs >= MAX_NEEDS)
- die("I'm only doing a max of %d requests", MAX_NEEDS);
- if (strncmp("want ", line, 5) || get_sha1_hex(line+5, needs_sha1[needs]))
- die("git-upload-pack: protocol error, expected to get sha, not '%s'", line);
+ sha1_buf = dummy;
+ if (needs == MAX_NEEDS) {
+ fprintf(stderr,
+ "warning: supporting only a max of %d requests. "
+ "sending everything instead.\n",
+ MAX_NEEDS);
+ }
+ else if (needs < MAX_NEEDS)
+ sha1_buf = needs_sha1[needs];
+
+ if (strncmp("want ", line, 5) || get_sha1_hex(line+5, sha1_buf))
+ die("git-upload-pack: protocol error, "
+ "expected to get sha, not '%s'", line);
needs++;
}
}
static int send_ref(const char *refname, const unsigned char *sha1)
{
+ struct object *o = parse_object(sha1);
+
packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname);
+ if (o->type == tag_type) {
+ o = deref_tag(o);
+ packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
+ }
return 0;
}
diff --git a/var.c b/var.c
index 3f13126cb..51cf86a58 100644
--- a/var.c
+++ b/var.c
@@ -42,6 +42,15 @@ static const char *read_var(const char *var)
return val;
}
+static int show_config(const char *var, const char *value)
+{
+ if (value)
+ printf("%s=%s\n", var, value);
+ else
+ printf("%s\n", var);
+ return git_default_config(var, value);
+}
+
int main(int argc, char **argv)
{
const char *val;
@@ -52,9 +61,11 @@ int main(int argc, char **argv)
val = NULL;
if (strcmp(argv[1], "-l") == 0) {
+ git_config(show_config);
list_vars();
return 0;
}
+ git_config(git_default_config);
val = read_var(argv[1]);
if (!val)
usage(var_usage);
diff --git a/verify-pack.c b/verify-pack.c
index 80b60a6b7..c99db9dd7 100644
--- a/verify-pack.c
+++ b/verify-pack.c
@@ -15,12 +15,12 @@ static int verify_one_pack(char *arg, int verbose)
len--;
}
/* Should name foo.idx now */
- if ((g = add_packed_git(arg, len)))
+ if ((g = add_packed_git(arg, len, 1)))
break;
/* No? did you name just foo? */
strcpy(arg + len, ".idx");
len += 4;
- if ((g = add_packed_git(arg, len)))
+ if ((g = add_packed_git(arg, len, 1)))
break;
return error("packfile %s not found.", arg);
}