Merge branch 'jc/fpl'
authorJunio C Hamano <junkio@cox.net>
Mon, 26 Mar 2007 00:47:07 +0000 (17:47 -0700)
committerJunio C Hamano <junkio@cox.net>
Mon, 26 Mar 2007 00:47:07 +0000 (17:47 -0700)
* jc/fpl:
git-log --first-parent: show only the first parent log

105 files changed:
Documentation/Makefile
Documentation/RelNotes-1.5.0.5.txt [new file with mode: 0644]
Documentation/RelNotes-1.5.1.txt
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-am.txt
Documentation/git-bisect.txt
Documentation/git-mergetool.txt
Documentation/git-send-email.txt
Documentation/git.txt
Documentation/glossary.txt
Documentation/howto/use-git-daemon.txt [new file with mode: 0644]
Documentation/sort_glossary.pl [deleted file]
Documentation/technical/pack-format.txt
Documentation/technical/shallow.txt [new file with mode: 0644]
Documentation/user-manual.txt
GIT-VERSION-GEN
Makefile
README
builtin-apply.c
builtin-blame.c
builtin-branch.c
builtin-bundle.c
builtin-diff-files.c
builtin-diff-index.c
builtin-diff-tree.c
builtin-diff.c
builtin-fsck.c
builtin-gc.c [new file with mode: 0644]
builtin-grep.c
builtin-log.c
builtin-pack-objects.c
builtin-prune.c
builtin-push.c
builtin-read-tree.c
builtin-reflog.c
builtin-rev-list.c
builtin-revert.c
builtin.h
cache.h
commit.c
config.c
connect.c
contrib/continuous/cidaemon [new file with mode: 0644]
contrib/continuous/post-receive-cinotify [new file with mode: 0644]
contrib/emacs/git.el
contrib/examples/git-gc.sh [new file with mode: 0755]
diff-lib.c
diff.c
diff.h
environment.c
fast-import.c
fetch.c
git-am.sh
git-applymbox.sh
git-bisect.sh
git-checkout.sh
git-clone.sh
git-cvsserver.perl
git-fetch.sh
git-gc.sh [deleted file]
git-merge-ours.sh
git-merge.sh
git-mergetool.sh
git-parse-remote.sh
git-rebase.sh
git-remote.perl
git-send-email.perl
git.c
gitk
gitweb/INSTALL [new file with mode: 0644]
gitweb/gitweb.css
gitweb/gitweb.perl
http-push.c
index-pack.c
list-objects.c
local-fetch.c
merge-index.c
merge-tree.c
object.c
pack-check.c
pack-redundant.c
pack.h
reachable.c
receive-pack.c
refs.c
revision.c
run-command.c
run-command.h
send-pack.c
sha1_file.c
t/t3200-branch.sh
t/t4017-diff-retval.sh [new file with mode: 0755]
t/t4017-quiet.sh [new file with mode: 0755]
t/t4118-apply-empty-context.sh
t/t5300-pack-object.sh
t/t5520-pull.sh
t/t6030-bisect-run.sh [new file with mode: 0755]
templates/hooks--update
tree-diff.c
tree-walk.c
tree-walk.h
tree.c
unpack-trees.c
xdiff/xutils.c
index 7c1c9e1918829b90aeb4e47d10aa0f5951204b0c..7db3fb992f2dccc09ee36281c6f4726ab322cb40 100644 (file)
@@ -16,8 +16,9 @@ ARTICLES += repository-layout
 ARTICLES += hooks
 ARTICLES += everyday
 ARTICLES += git-tools
+ARTICLES += glossary
 # with their own formatting rules.
-SP_ARTICLES = glossary howto/revert-branch-rebase user-manual
+SP_ARTICLES = howto/revert-branch-rebase user-manual
 
 DOC_HTML += $(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES))
 
@@ -106,16 +107,11 @@ user-manual.xml: user-manual.txt user-manual.conf
        $(ASCIIDOC) -b docbook -d book $<
 
 XSLT = http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl
-XSLTOPTS = --nonet --xinclude --stringparam html.stylesheet docbook-xsl.css
+XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
 
 user-manual.html: user-manual.xml
        xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
 
-glossary.html : glossary.txt sort_glossary.pl
-       cat $< | \
-       perl sort_glossary.pl | \
-       $(ASCIIDOC) -b xhtml11 - > glossary.html
-
 howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
        rm -f $@+ $@
        sh ./howto-index.sh $(wildcard howto/*.txt) >$@+
diff --git a/Documentation/RelNotes-1.5.0.5.txt b/Documentation/RelNotes-1.5.0.5.txt
new file mode 100644 (file)
index 0000000..aa86149
--- /dev/null
@@ -0,0 +1,28 @@
+GIT v1.5.0.5 Release Notes
+==========================
+
+Fixes since v1.5.0.3
+--------------------
+
+* Bugfixes
+
+  - git-merge (hence git-pull) did not refuse fast-forwarding
+    when the working tree had local changes that would have
+    conflicted with it.
+
+  - git.el does not add duplicate sign-off lines.
+
+  - git-commit shows the full stat of the resulting commit, not
+    just about the files in the current directory, when run from
+    a subdirectory.
+
+  - "git-checkout -m '@{8 hours ago}'" had a funny failure from
+    eval; fixed.
+
+  - git-gui updates.
+
+* Documentation updates
+
+* User manual updates
+
+
index f374e1c2c75a597c15a9f0bbb2ff2fec6ec190df..f78cf56bc8563681ab8851e88dd6158eabe98a9d 100644 (file)
@@ -25,6 +25,21 @@ Updates since v1.5.0
   - "git diff --pretty=format:<string>" to allow more flexible
     custom log output.
 
+  - "git diff --no-index" can read from '-' (standard input).
+
+  - "git diff" also learned --exit-code to exit with non-zero
+    status when it found differences.  In the future we might
+    want to make this the default but that would be a rather big
+    backward incompatible change; it will stay as an option for
+    now.
+
+  - "git branch --track" can be used to set up configuration
+    variables to help it easier to base your work on branches
+    you track from a remote site.
+
+  - "git format-patch --attach" now emits attachments.  Use
+    --inline to get an inlined multipart/mixed.
+
   - "git name-rev" learned --refs=<pattern>, to limit the tags
     used for naming the given revisions only to the ones
     matching the given pattern.
@@ -39,6 +54,9 @@ Updates since v1.5.0
   - "git bundle" can help sneaker-netting your changes between
     repositories.
 
+  - "git mergetool" can help 3-way file-level conflict
+    resolution with your favorite graphical merge tools.
+
   - A new configuration "core.symlinks" can be used to disable
     symlinks on filesystems that do not support them; they are
     checked out as regular files instead.
@@ -46,6 +64,11 @@ Updates since v1.5.0
 
 * Updated behaviour of existing commands.
 
+  - "git fsck" does not barf on corrupt loose objects.
+
+  - "git archimport" allows remapping when coming up with git
+    branch names from arch names.
+
   - git-svn got almost a rewrite.
 
   - core.autocrlf configuration, when set to 'true', makes git
@@ -99,6 +122,25 @@ Updates since v1.5.0
   - "git fetch" (hence "git clone" and "git pull") are less
     noisy when the output does not go to tty.
 
+  - "git fetch" between repositories with many refs were slow
+    even when there are not many changes that needed
+    transferring.  This has been sped up by partially rewriting
+    the heaviest parts in C.
+
+  - "git mailinfo" which splits an e-mail into a patch and the
+    metainformation was rewritten, thanks to Don Zickus.  It
+    handles nested multipart better.
+
+  - send-email learned configurable bcc and chain-reply-to.
+
+  - Using objects from packs is now seriouly optimized by clever
+    use of a cache.  This should be most noticeable in git-log
+    family of commands that involve reading many tree objects.
+    In addition, traversing revisions while filtering changes
+    with pathspecs is made faster by terminating the comparison
+    between the trees as early as possible.
+
+
 * Hooks
 
   - The sample update hook to show how to send out notification
@@ -106,9 +148,15 @@ Updates since v1.5.0
     the repository.  Earlier, it showed new commits that appeared
     on the branch.
 
+
+* Others
+
+  - git-revert, git-gc and git-cherry-pick are now built-ins.
+
+
 --
 exec >/var/tmp/1
-O=v1.5.0.3-268-g3ddad98
+O=v1.5.0.5-446-g5d86501
 echo O=`git describe master`
 git shortlog --no-merges $O..master ^maint
 
index aaae9ac3052b5a4de6ec53817e79797626f7da31..cf1e040381a99ed458dc061fe103e3c2d2c9474c 100644 (file)
@@ -240,6 +240,19 @@ the largest projects.  You probably do not need to adjust this value.
 +
 Common unit suffixes of 'k', 'm', or 'g' are supported.
 
+core.deltaBaseCacheLimit::
+       Maximum number of bytes to reserve for caching base objects
+       that multiple deltafied objects reference.  By storing the
+       entire decompressed base objects in a cache Git is able
+       to avoid unpacking and decompressing frequently used base
+       objects multiple times.
++
+Default is 16 MiB on all platforms.  This should be reasonable
+for all users/operating systems, except on the largest projects.
+You probably do not need to adjust this value.
++
+Common unit suffixes of 'k', 'm', or 'g' are supported.
+
 alias.*::
        Command aliases for the gitlink:git[1] command wrapper - e.g.
        after defining "alias.last = cat-file commit HEAD", the invocation
@@ -272,6 +285,10 @@ branch.<name>.merge::
        `git fetch`) to lookup the default branch for merging. Without
        this option, `git pull` defaults to merge the first refspec fetched.
        Specify multiple values to get an octopus merge.
+       If you wish to setup `git pull` so that it merges into <name> from
+       another branch in the local repository, you can point
+       branch.<name>.merge to the desired branch, and use the special setting
+       `.` (a period) for branch.<name>.remote.
 
 color.branch::
        A boolean to enable/disable color in the output of
@@ -456,7 +473,7 @@ merge.summary::
 merge.tool::
        Controls which merge resolution program is used by
        gitlink:git-mergetool[l].  Valid values are: "kdiff3", "tkdiff",
-       "meld", "xxdiff", "emerge"
+       "meld", "xxdiff", "emerge", "vimdiff"
 
 merge.verbosity::
        Controls the amount of output shown by the recursive merge
index d8696b7b36ff81f47214da9d7c4ec53142b97653..1689c748171a8ff48395cf432aabe0130fc6b8d2 100644 (file)
 -w::
        Shorthand for "--ignore-all-space".
 
+--exit-code::
+       Make the program exit with codes similar to diff(1).
+       That is, it exits with 1 if there were differences and
+       0 means no differences.
+
+--quiet::
+       Disable all output of the program. Implies --exit-code.
+
 For more detailed explanation on these common options, see also
 link:diffcore.html[diffcore documentation].
index 4fb1d844133ba8350361ee67d074935805a24156..148ce405681cd4f5bd38575b6a427a7e5745a109 100644 (file)
@@ -70,7 +70,7 @@ default.   You could use `--no-utf8` to override this.
        the patch.
 
 -C<n>, -p<n>::
-       These flag are passed to the `git-apply` program that applies
+       These flags are passed to the `git-apply` program that applies
        the patch.
 
 --interactive::
@@ -87,6 +87,33 @@ default.   You could use `--no-utf8` to override this.
 DISCUSSION
 ----------
 
+The commit author name is taken from the "From: " line of the
+message, and commit author time is taken from the "Date: " line
+of the message.  The "Subject: " line is used as the title of
+the commit, after stripping common prefix "[PATCH <anything>]".
+It is supposed to describe what the commit is about concisely as
+a one line text.
+
+The body of the message (iow, after a blank line that terminates
+RFC2822 headers) can begin with "Subject: " and "From: " lines
+that are different from those of the mail header, to override
+the values of these fields.
+
+The commit message is formed by the title taken from the
+"Subject: ", a blank line and the body of the message up to
+where the patch begins.  Excess whitespaces at the end of the
+lines are automatically stripped.
+
+The patch is expected to be inline, directly following the
+message.  Any line that is of form:
+
+* three-dashes and end-of-line, or
+* a line that begins with "diff -", or
+* a line that begins with "Index: "
+
+is taken as the beginning of a patch, and the commit log message
+is terminated before the first occurrence of such a line.
+
 When initially invoking it, you give it names of the mailboxes
 to crunch.  Upon seeing the first patch that does not apply, it
 aborts in the middle, just like 'git-applymbox' does.  You can
index 16ec7269b29c995fe051f74a37ba9d015d972006..b2bc58d8513b0c064333d8b0aa357ebcea3ba28f 100644 (file)
@@ -12,8 +12,8 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-The command takes various subcommands, and different options
-depending on the subcommand:
+The command takes various subcommands, and different options depending
+on the subcommand:
 
  git bisect start [<paths>...]
  git bisect bad <rev>
@@ -22,30 +22,34 @@ depending on the subcommand:
  git bisect visualize
  git bisect replay <logfile>
  git bisect log
+ git bisect run <cmd>...
 
-This command uses 'git-rev-list --bisect' option to help drive
-the binary search process to find which change introduced a bug,
-given an old "good" commit object name and a later "bad" commit
-object name.
+This command uses 'git-rev-list --bisect' option to help drive the
+binary search process to find which change introduced a bug, given an
+old "good" commit object name and a later "bad" commit object name.
+
+Basic bisect commands: start, bad, good
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The way you use it is:
 
 ------------------------------------------------
 $ git bisect start
-$ git bisect bad                       # Current version is bad
-$ git bisect good v2.6.13-rc2          # v2.6.13-rc2 was the last version
-                                       # tested that was good
+$ git bisect bad                 # Current version is bad
+$ git bisect good v2.6.13-rc2    # v2.6.13-rc2 was the last version
+                                 # tested that was good
 ------------------------------------------------
 
-When you give at least one bad and one good versions, it will
-bisect the revision tree and say something like:
+When you give at least one bad and one good versions, it will bisect
+the revision tree and say something like:
 
 ------------------------------------------------
 Bisecting: 675 revisions left to test after this
 ------------------------------------------------
 
-and check out the state in the middle. Now, compile that kernel, and boot
-it. Now, let's say that this booted kernel works fine, then just do
+and check out the state in the middle. Now, compile that kernel, and
+boot it. Now, let's say that this booted kernel works fine, then just
+do
 
 ------------------------------------------------
 $ git bisect good                      # this one is good
@@ -57,12 +61,15 @@ which will now say
 Bisecting: 337 revisions left to test after this
 ------------------------------------------------
 
-and you continue along, compiling that one, testing it, and depending on
-whether it is good or bad, you say "git bisect good" or "git bisect bad",
-and ask for the next bisection.
+and you continue along, compiling that one, testing it, and depending
+on whether it is good or bad, you say "git bisect good" or "git bisect
+bad", and ask for the next bisection.
+
+Until you have no more left, and you'll have been left with the first
+bad kernel rev in "refs/bisect/bad".
 
-Until you have no more left, and you'll have been left with the first bad
-kernel rev in "refs/bisect/bad".
+Bisect reset
+~~~~~~~~~~~~
 
 Oh, and then after you want to reset to the original head, do a
 
@@ -70,10 +77,13 @@ Oh, and then after you want to reset to the original head, do a
 $ git bisect reset
 ------------------------------------------------
 
-to get back to the master branch, instead of being in one of the bisection
-branches ("git bisect start" will do that for you too, actually: it will
-reset the bisection state, and before it does that it checks that you're
-not using some old bisection branch).
+to get back to the master branch, instead of being in one of the
+bisection branches ("git bisect start" will do that for you too,
+actually: it will reset the bisection state, and before it does that
+it checks that you're not using some old bisection branch).
+
+Bisect visualize
+~~~~~~~~~~~~~~~~
 
 During the bisection process, you can say
 
@@ -83,9 +93,17 @@ $ git bisect visualize
 
 to see the currently remaining suspects in `gitk`.
 
-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
+Bisect log and bisect replay
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+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
 
 ------------
 $ git bisect replay that-file
@@ -94,12 +112,16 @@ $ git bisect replay that-file
 if you find later you made a mistake telling good/bad about a
 revision.
 
-If in a middle of bisect session, you know what the bisect
-suggested to try next is not a good one to test (e.g. the change
-the commit introduces is known not to work in your environment
-and you know it does not have anything to do with the bug you
-are chasing), you may want to find a near-by commit and try that
-instead.  It goes something like this:
+Avoiding to test a commit
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If in a middle of bisect session, you know what the bisect suggested
+to try next is not a good one to test (e.g. the change the commit
+introduces is known not to work in your environment and you know it
+does not have anything to do with the bug you are chasing), you may
+want to find a near-by commit and try that instead.
+
+It goes something like this:
 
 ------------
 $ git bisect good/bad                  # previous round was good/bad.
@@ -109,18 +131,52 @@ $ git reset --hard HEAD~3         # try 3 revs before what
                                        # was suggested
 ------------
 
-Then compile and test the one you chose to try.  After that,
-tell bisect what the result was as usual.
+Then compile and test the one you chose to try. After that, tell
+bisect what the result was as usual.
 
-You can further cut down the number of trials if you know what
-part of the tree is involved in the problem you are tracking
-down, by giving paths parameters when you say `bisect start`,
-like this:
+Cutting down bisection by giving path parameter to bisect start
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can further cut down the number of trials if you know what part of
+the tree is involved in the problem you are tracking down, by giving
+paths parameters when you say `bisect start`, like this:
 
 ------------
 $ git bisect start arch/i386 include/asm-i386
 ------------
 
+Bisect run
+~~~~~~~~~~
+
+If you have a script that can tell if the current source code is good
+or bad, you can automatically bisect using:
+
+------------
+$ git bisect run my_script
+------------
+
+Note that the "run" script (`my_script` in the above example) should
+exit with code 0 in case the current source code is good and with a
+code between 1 and 127 (included) in case the current source code is
+bad.
+
+Any other exit code will abort the automatic bisect process. (A
+program that does "exit(-1)" leaves $? = 255, see exit(3) manual page,
+the value is chopped with "& 0377".)
+
+You may often find that during bisect you want to have near-constant
+tweaks (e.g., s/#define DEBUG 0/#define DEBUG 1/ in a header file, or
+"revision that does not have this commit needs this patch applied to
+work around other problem this bisection is not interested in")
+applied to the revision being tested.
+
+To cope with such a situation, after the inner git-bisect finds the
+next revision to test, with the "run" script, you can apply that tweak
+before compiling, run the real test, and after the test decides if the
+revision (possibly with the needed tweaks) passed the test, rewind the
+tree to the pristine state.  Finally the "run" script can exit with
+the status of the real test to let "git bisect run" command loop to
+know the outcome.
 
 Author
 ------
index ae69a0eb83ea705b2564c5ed8d3f2ff1313ae8fd..5baaaca0b5433ae279b3961d72d0cffa8ca046e3 100644 (file)
@@ -25,7 +25,7 @@ OPTIONS
 -t or --tool=<tool>::
        Use the merge resolution program specified by <tool>.
        Valid merge tools are:
-       kdiff3, tkdiff, meld, xxdiff, and emerge.
+       kdiff3, tkdiff, meld, xxdiff, emerge, and vimdiff.
 
        If a merge resolution program is not specified, 'git mergetool'
        will use the configuration variable merge.tool.  If the
index 9b3aabb6fe5590f41319298337e8f9ea6d9fff4e..682313e95dc9b96d3cc14f33554ad82bcede9a56 100644 (file)
@@ -60,7 +60,8 @@ The --cc option must be repeated for each user you want on the cc list.
        is not set, this will be prompted for.
 
 --no-signed-off-by-cc::
-       Do not add emails found in Signed-off-by: lines to the cc list.
+       Do not add emails found in Signed-off-by: or Cc: lines to the
+       cc list.
 
 --quiet::
        Make git-send-email less verbose.  One line per email should be
index e875e8318d24fa046c7cddfc3e583b4c242d9302..31397dc5390d79343dc5d6fa5501cd493be0ea21 100644 (file)
@@ -35,7 +35,9 @@ ifdef::stalenotes[]
 You are reading the documentation for the latest version of git.
 Documentation for older releases are available here:
 
-* link:v1.5.0.3/git.html[documentation for release 1.5.0.3]
+* link:v1.5.0.5/git.html[documentation for release 1.5.0.5]
+
+* link:v1.5.0.5/RelNotes-1.5.0.5.txt[release notes for 1.5.0.5]
 
 * link:v1.5.0.3/RelNotes-1.5.0.3.txt[release notes for 1.5.0.3]
 
index 9f446241e2dabee4b93f5c1625351f2260e73717..2465514e461a4a6c4e61f04ced18d6229eae0f51 100644 (file)
-alternate object database::
-       Via the alternates mechanism, a repository can inherit part of its
-       object database from another object database, which is called
-       "alternate".
-
-bare repository::
-       A bare repository is normally an appropriately named
-       directory with a `.git` suffix that does not have a
-       locally checked-out copy of any of the files under revision
-       control.  That is, all of the `git` administrative and
-       control files that would normally be present in the
-       hidden `.git` sub-directory are directly present in
-       the `repository.git` directory instead, and no other files
-       are present and checked out.  Usually publishers of public
-       repositories make bare repositories available.
-
-blob object::
-       Untyped object, e.g. the contents of a file.
-
-branch::
-       A non-cyclical graph of revisions, i.e. the complete history of
-       a particular revision, which is called the branch head. The
-       branch heads are stored in `$GIT_DIR/refs/heads/`.
-
-cache::
-       Obsolete for: index.
-
-chain::
-       A list of objects, where each object in the list contains a
-       reference to its successor (for example, the successor of a commit
-       could be one of its parents).
-
-changeset::
-       BitKeeper/cvsps speak for "commit". Since git does not store
-       changes, but states, it really does not make sense to use
-       the term "changesets" with git.
-
-checkout::
-       The action of updating the working tree to a revision which was
-       stored in the object database.
-
-cherry-picking::
-       In SCM jargon, "cherry pick" means to choose a subset of
-       changes out of a series of changes (typically commits)
-       and record them as a new series of changes on top of
-       different codebase.  In GIT, this is performed by
-       "git cherry-pick" command to extract the change
-       introduced by an existing commit and to record it based
-       on the tip of the current branch as a new commit.
-
-clean::
-       A working tree is clean, if it corresponds to the revision
-       referenced by the current head.  Also see "dirty".
-
-commit::
-       As a verb: The action of storing the current state of the index in the
-       object database. The result is a revision.
-       As a noun: Short hand for commit object.
-
-commit object::
-       An object which contains the information about a particular
-       revision, such as parents, committer, author, date and the
-       tree object which corresponds to the top directory of the
-       stored revision.
-
-core git::
-       Fundamental data structures and utilities of git. Exposes only
-       limited source code management tools.
-
-DAG::
-       Directed acyclic graph. The commit objects form a directed acyclic
-       graph, because they have parents (directed), and the graph of commit
-       objects is acyclic (there is no chain which begins and ends with the
-       same object).
-
-dangling object::
-       An unreachable object which is not reachable even from other
-       unreachable objects; a dangling object has no references to it
-       from any reference or object in the repository.
-
-dircache::
+GIT Glossary
+============
+
+[[def_alternate_object_database]]alternate object database::
+       Via the alternates mechanism, a <<def_repository,repository>> can
+       inherit part of its <<def_object_database,object database>> from another
+       <<def_object_database,object database>>, which is called "alternate".
+
+[[def_bare_repository]]bare repository::
+       A <<def_bare_repository,bare repository>> is normally an appropriately
+       named <<def_directory,directory>> with a `.git` suffix that does not
+       have a locally checked-out copy of any of the files under
+       <<def_revision,revision>> control. That is, all of the `git`
+       administrative and control files that would normally be present in the
+       hidden `.git` sub-directory are directly present in the
+       `repository.git` directory instead,
+       and no other files are present and checked out. Usually publishers of
+       public repositories make bare repositories available.
+
+[[def_blob_object]]blob object::
+       Untyped <<def_object,object>>, e.g. the contents of a file.
+
+[[def_branch]]branch::
+       A non-cyclical graph of revisions, i.e. the complete history of a
+       particular <<def_revision,revision>>, which is called the
+       branch <<def_head,head>>. The heads
+       are stored in `$GIT_DIR/refs/heads/`.
+
+[[def_cache]]cache::
+       Obsolete for: <<def_index,index>>.
+
+[[def_chain]]chain::
+       A list of objects, where each <<def_object,object>> in the list contains
+       a reference to its successor (for example, the successor of a
+       <<def_commit,commit>> could be one of its parents).
+
+[[def_changeset]]changeset::
+       BitKeeper/cvsps speak for "<<def_commit,commit>>". Since git does not
+       store changes, but states, it really does not make sense to use the term
+       "changesets" with git.
+
+[[def_checkout]]checkout::
+       The action of updating the <<def_working_tree,working tree>> to a
+       <<def_revision,revision>> which was stored in the
+       <<def_object_database,object database>>.
+
+[[def_cherry-picking]]cherry-picking::
+       In <<def_SCM,SCM>> jargon, "cherry pick" means to choose a subset of
+       changes out of a series of changes (typically commits) and record them
+       as a new series of changes on top of different codebase. In GIT, this is
+       performed by "git cherry-pick" command to extract the change introduced
+       by an existing <<def_commit,commit>> and to record it based on the tip
+       of the current <<def_branch,branch>> as a new <<def_commit,commit>>.
+
+[[def_clean]]clean::
+       A <<def_working_tree,working tree>> is <<def_clean,clean>>, if it
+       corresponds to the <<def_revision,revision>> referenced by the current
+       <<def_head,head>>. Also see "<<def_dirty,dirty>>".
+
+[[def_commit]]commit::
+       As a verb: The action of storing the current state of the
+       <<def_index,index>> in the <<def_object_database,object database>>. The
+       result is a <<def_revision,revision>>. As a noun: Short hand for
+       <<def_commit_object,commit object>>.
+
+[[def_commit_object]]commit object::
+       An <<def_object,object>> which contains the information about a
+       particular <<def_revision,revision>>, such as parents, committer,
+       author, date and the <<def_tree_object,tree object>> which corresponds
+       to the top <<def_directory,directory>> of the stored
+       <<def_revision,revision>>.
+
+[[def_core_git]]core git::
+       Fundamental data structures and utilities of git. Exposes only limited
+       source code management tools.
+
+[[def_DAG]]DAG::
+       Directed acyclic graph. The <<def_commit,commit>> objects form a
+       directed acyclic graph, because they have parents (directed), and the
+       graph of <<def_commit,commit>> objects is acyclic (there is no
+       <<def_chain,chain>> which begins and ends with the same
+       <<def_object,object>>).
+
+[[def_dangling_object]]dangling object::
+       An <<def_unreachable_object,unreachable object>> which is not
+       <<def_reachable,reachable>> even from other unreachable objects; a
+       <<def_dangling_object,dangling object>> has no references to it from any
+       reference or <<def_object,object>> in the <<def_repository,repository>>.
+
+[[def_dircache]]dircache::
        You are *waaaaay* behind.
 
-dirty::
-       A working tree is said to be dirty if it contains modifications
-       which have not been committed to the current branch.
-
-directory::
+[[def_directory]]directory::
        The list you get with "ls" :-)
 
-ent::
-       Favorite synonym to "tree-ish" by some total geeks. See
+[[def_dirty]]dirty::
+       A <<def_working_tree,working tree>> is said to be <<def_dirty,dirty>> if
+       it contains modifications which have not been committed to the current
+       <<def_branch,branch>>.
+
+[[def_ent]]ent::
+       Favorite synonym to "<<def_tree-ish,tree-ish>>" by some total geeks. See
        `http://en.wikipedia.org/wiki/Ent_(Middle-earth)` for an in-depth
-       explanation.  Avoid this term, not to confuse people.
-
-fast forward::
-       A fast-forward is a special type of merge where you have
-       a revision and you are "merging" another branch's changes
-       that happen to be a descendant of what you have.
-       In such these cases, you do not make a new merge commit but
-       instead just update to his revision. This will happen
-       frequently on a tracking branch of a remote repository.
-
-fetch::
-       Fetching a branch means to get the branch's head ref from a
-       remote repository, to find out which objects are missing from
-       the local object database, and to get them, too.
-
-file system::
-       Linus Torvalds originally designed git to be a user space file
-       system, i.e. the infrastructure to hold files and directories.
-       That ensured the efficiency and speed of git.
-
-git archive::
-       Synonym for repository (for arch people).
-
-grafts::
-       Grafts enables two otherwise different lines of development to be
-       joined together by recording fake ancestry information for commits.
-       This way you can make git pretend the set of parents a commit
-       has is different from what was recorded when the commit was created.
-       Configured via the `.git/info/grafts` file.
-
-hash::
-       In git's context, synonym to object name.
-
-head::
-       The top of a branch. It contains a ref to the corresponding
-       commit object.
-
-head ref::
-       A ref pointing to a head. Often, this is abbreviated to "head".
-       Head refs are stored in `$GIT_DIR/refs/heads/`.
-
-hook::
-       During the normal execution of several git commands,
-       call-outs are made to optional scripts that allow
-       a developer to add functionality or checking.
-       Typically, the hooks allow for a command to be pre-verified
-       and potentially aborted, and allow for a post-notification
-       after the operation is done.
-       The hook scripts are found in the `$GIT_DIR/hooks/` directory,
-       and are enabled by simply making them executable.
-
-index::
-       A collection of files with stat information, whose contents are
-       stored as objects. The index is a stored version of your working
-       tree. Truth be told, it can also contain a second, and even a third
-       version of a working tree, which are used when merging.
-
-index entry::
-       The information regarding a particular file, stored in the index.
-       An index entry can be unmerged, if a merge was started, but not
-       yet finished (i.e. if the index contains multiple versions of
-       that file).
-
-master::
-       The default development branch. Whenever you create a git
-       repository, a branch named "master" is created, and becomes
-       the active branch. In most cases, this contains the local
+       explanation. Avoid this term, not to confuse people.
+
+[[def_fast_forward]]fast forward::
+       A fast-forward is a special type of <<def_merge,merge>> where you have a
+       <<def_revision,revision>> and you are "merging" another
+       <<def_branch,branch>>'s changes that happen to be a descendant of what
+       you have. In such these cases, you do not make a new <<def_merge,merge>>
+       <<def_commit,commit>> but instead just update to his
+       <<def_revision,revision>>. This will happen frequently on a
+       <<def_tracking_branch,tracking branch>> of a remote
+       <<def_repository,repository>>.
+
+[[def_fetch]]fetch::
+       Fetching a <<def_branch,branch>> means to get the
+       <<def_branch,branch>>'s <<def_head_ref,head ref>> from a remote
+       <<def_repository,repository>>, to find out which objects are missing
+       from the local <<def_object_database,object database>>, and to get them,
+       too.
+
+[[def_file_system]]file system::
+       Linus Torvalds originally designed git to be a user space file system,
+       i.e. the infrastructure to hold files and directories. That ensured the
+       efficiency and speed of git.
+
+[[def_git_archive]]git archive::
+       Synonym for <<def_repository,repository>> (for arch people).
+
+[[def_grafts]]grafts::
+       Grafts enables two otherwise different lines of development to be joined
+       together by recording fake ancestry information for commits. This way
+       you can make git pretend the set of parents a <<def_commit,commit>> has
+       is different from what was recorded when the <<def_commit,commit>> was
+       created. Configured via the `.git/info/grafts` file.
+
+[[def_hash]]hash::
+       In git's context, synonym to <<def_object_name,object name>>.
+
+[[def_head]]head::
+       The top of a <<def_branch,branch>>. It contains a <<def_ref,ref>> to the
+       corresponding <<def_commit_object,commit object>>.
+
+[[def_head_ref]]head ref::
+       A <<def_ref,ref>> pointing to a <<def_head,head>>. Often, this is
+       abbreviated to "<<def_head,head>>". Head refs are stored in
+       `$GIT_DIR/refs/heads/`.
+
+[[def_hook]]hook::
+       During the normal execution of several git commands, call-outs are made
+       to optional scripts that allow a developer to add functionality or
+       checking. Typically, the hooks allow for a command to be pre-verified
+       and potentially aborted, and allow for a post-notification after the
+       operation is done. The <<def_hook,hook>> scripts are found in the
+       `$GIT_DIR/hooks/` <<def_directory,directory>>, and are enabled by simply
+       making them executable.
+
+[[def_index]]index::
+       A collection of files with stat information, whose contents are stored
+       as objects. The <<def_index,index>> is a stored version of your working
+       <<def_tree,tree>>. Truth be told, it can also contain a second, and even
+       a third version of a <<def_working_tree,working tree>>, which are used
+       when merging.
+
+[[def_index_entry]]index entry::
+       The information regarding a particular file, stored in the
+       <<def_index,index>>. An <<def_index_entry,index entry>> can be unmerged,
+       if a <<def_merge,merge>> was started, but not yet finished (i.e. if the
+       <<def_index,index>> contains multiple versions of that file).
+
+[[def_master]]master::
+       The default development <<def_branch,branch>>. Whenever you create a git
+       <<def_repository,repository>>, a <<def_branch,branch>> named
+       "<<def_master,master>>" is created, and becomes the active
+       <<def_branch,branch>>. In most cases, this contains the local
        development, though that is purely conventional and not required.
 
-merge::
-       To merge branches means to try to accumulate the changes since a
-       common ancestor and apply them to the first branch. An automatic
-       merge uses heuristics to accomplish that. Evidently, an automatic
-       merge can fail.
-
-object::
-       The unit of storage in git. It is uniquely identified by
-       the SHA1 of its contents. Consequently, an object can not
-       be changed.
-
-object database::
-       Stores a set of "objects", and an individual object is identified
-       by its object name. The objects usually live in `$GIT_DIR/objects/`.
-
-object identifier::
-       Synonym for object name.
-
-object name::
-       The unique identifier of an object. The hash of the object's contents
-       using the Secure Hash Algorithm 1 and usually represented by the 40
-       character hexadecimal encoding of the hash of the object (possibly
-       followed by a white space).
-
-object type::
-       One of the identifiers "commit","tree","tag" and "blob" describing
-       the type of an object.
-
-octopus::
-       To merge more than two branches. Also denotes an intelligent
-       predator.
-
-origin::
-       The default upstream repository. Most projects have at
-       least one upstream project which they track. By default
-       'origin' is used for that purpose.  New upstream updates
+[[def_merge]]merge::
+       To <<def_merge,merge>> branches means to try to accumulate the changes
+       since a common ancestor and apply them to the first
+       <<def_branch,branch>>. An automatic <<def_merge,merge>> uses heuristics
+       to accomplish that. Evidently, an automatic <<def_merge,merge>> can
+       fail.
+
+[[def_object]]object::
+       The unit of storage in git. It is uniquely identified by the
+       <<def_SHA1,SHA1>> of its contents. Consequently, an
+       <<def_object,object>> can not be changed.
+
+[[def_object_database]]object database::
+       Stores a set of "objects", and an individual <<def_object,object>> is
+       identified by its <<def_object_name,object name>>. The objects usually
+       live in `$GIT_DIR/objects/`.
+
+[[def_object_identifier]]object identifier::
+       Synonym for <<def_object_name,object name>>.
+
+[[def_object_name]]object name::
+       The unique identifier of an <<def_object,object>>. The <<def_hash,hash>>
+       of the <<def_object,object>>'s contents using the Secure Hash Algorithm
+       1 and usually represented by the 40 character hexadecimal encoding of
+       the <<def_hash,hash>> of the <<def_object,object>> (possibly followed by
+       a white space).
+
+[[def_object_type]]object type::
+       One of the identifiers
+       "<<def_commit,commit>>","<<def_tree,tree>>","<<def_tag,tag>>" or "<<def_blob_object,blob>>"
+       describing the type of an <<def_object,object>>.
+
+[[def_octopus]]octopus::
+       To <<def_merge,merge>> more than two branches. Also denotes an
+       intelligent predator.
+
+[[def_origin]]origin::
+       The default upstream <<def_repository,repository>>. Most projects have
+       at least one upstream project which they track. By default
+       '<<def_origin,origin>>' is used for that purpose. New upstream updates
        will be fetched into remote tracking branches named
        origin/name-of-upstream-branch, which you can see using
-       "git branch -r".
+       "git <<def_branch,branch>> -r".
 
-pack::
-       A set of objects which have been compressed into one file (to save
-       space or to transmit them efficiently).
+[[def_pack]]pack::
+       A set of objects which have been compressed into one file (to save space
+       or to transmit them efficiently).
 
-pack index::
+[[def_pack_index]]pack index::
        The list of identifiers, and other information, of the objects in a
-       pack, to assist in efficiently accessing the contents of a pack.
-
-parent::
-       A commit object contains a (possibly empty) list of the logical
-       predecessor(s) in the line of development, i.e. its parents.
-
-pickaxe::
-       The term pickaxe refers to an option to the diffcore routines
-       that help select changes that add or delete a given text string.
-       With the --pickaxe-all option, it can be used to view the
-       full changeset that introduced or removed, say, a particular
-       line of text.  See gitlink:git-diff[1].
-
-plumbing::
-       Cute name for core git.
-
-porcelain::
-       Cute name for programs and program suites depending on core git,
-       presenting a high level access to core git. Porcelains expose
-       more of a SCM interface than the plumbing.
-
-pull::
-       Pulling a branch means to fetch it and merge it.
-
-push::
-       Pushing a branch means to get the branch's head ref from a remote
-       repository, find out if it is an ancestor to the branch's local
-       head ref is a direct, and in that case, putting all objects, which
-       are reachable from the local head ref, and which are missing from
-       the remote repository, into the remote object database, and updating
-       the remote head ref. If the remote head is not an ancestor to the
-       local head, the push fails.
-
-reachable::
-       All of the ancestors of a given commit are said to be reachable from
-       that commit.  More generally, one object is reachable from another if
-       we can reach the one from the other by a chain that follows tags to
-       whatever they tag, commits to their parents or trees, and trees to the
-       trees or blobs that they contain.
-
-rebase::
-       To clean a branch by starting from the head of the main line of
-       development ("master"), and reapply the (possibly cherry-picked)
-       changes from that branch.
-
-ref::
-       A 40-byte hex representation of a SHA1 or a name that denotes
-       a particular object. These may be stored in `$GIT_DIR/refs/`.
-
-refspec::
-       A refspec is used by fetch and push to describe the mapping
-       between remote ref and local ref.  They are combined with
-       a colon in the format <src>:<dst>, preceded by an optional
-       plus sign, +.  For example:
-       `git fetch $URL refs/heads/master:refs/heads/origin`
-       means "grab the master branch head from the $URL and store
-       it as my origin branch head".
-       And `git push $URL refs/heads/master:refs/heads/to-upstream`
-       means "publish my master branch head as to-upstream branch
-       at $URL".   See also gitlink:git-push[1]
-
-repository::
-       A collection of refs together with an object database containing
-       all objects, which are reachable from the refs, possibly accompanied
-       by meta data from one or more porcelains. A repository can
-       share an object database with other repositories.
-
-resolve::
-       The action of fixing up manually what a failed automatic merge
-       left behind.
-
-revision::
-       A particular state of files and directories which was stored in
-       the object database. It is referenced by a commit object.
-
-rewind::
-       To throw away part of the development, i.e. to assign the head to
-       an earlier revision.
-
-SCM::
+       <<def_pack,pack>>, to assist in efficiently accessing the contents of a
+       <<def_pack,pack>>.
+
+[[def_parent]]parent::
+       A <<def_commit_object,commit object>> contains a (possibly empty) list
+       of the logical predecessor(s) in the line of development, i.e. its
+       parents.
+
+[[def_pickaxe]]pickaxe::
+       The term <<def_pickaxe,pickaxe>> refers to an option to the diffcore
+       routines that help select changes that add or delete a given text
+       string. With the --pickaxe-all option, it can be used to view the full
+       <<def_changeset,changeset>> that introduced or removed, say, a
+       particular line of text. See gitlink:git-diff[1].
+
+[[def_plumbing]]plumbing::
+       Cute name for <<def_core_git,core git>>.
+
+[[def_porcelain]]porcelain::
+       Cute name for programs and program suites depending on
+       <<def_core_git,core git>>, presenting a high level access to
+       <<def_core_git,core git>>. Porcelains expose more of a <<def_SCM,SCM>>
+       interface than the <<def_plumbing,plumbing>>.
+
+[[def_pull]]pull::
+       Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
+       <<def_merge,merge>> it.
+
+[[def_push]]push::
+       Pushing a <<def_branch,branch>> means to get the <<def_branch,branch>>'s
+       <<def_head_ref,head ref>> from a remote <<def_repository,repository>>,
+       find out if it is an ancestor to the <<def_branch,branch>>'s local
+       <<def_head_ref,head ref>> is a direct, and in that case, putting all
+       objects, which are <<def_reachable,reachable>> from the local
+       <<def_head_ref,head ref>>, and which are missing from the remote
+       <<def_repository,repository>>, into the remote
+       <<def_object_database,object database>>, and updating the remote
+       <<def_head_ref,head ref>>. If the remote <<def_head,head>> is not an
+       ancestor to the local <<def_head,head>>, the <<def_push,push>> fails.
+
+[[def_reachable]]reachable::
+       All of the ancestors of a given <<def_commit,commit>> are said to be
+       <<def_reachable,reachable>> from that <<def_commit,commit>>. More
+       generally, one <<def_object,object>> is <<def_reachable,reachable>> from
+       another if we can reach the one from the other by a <<def_chain,chain>>
+       that follows <<def_tag,tags>> to whatever they tag,
+       <<def_commit_object,commits>> to their parents or trees, and
+       <<def_tree_object,trees>> to the trees or <<def_blob_object,blobs>>
+       that they contain.
+
+[[def_rebase]]rebase::
+       To reapply a series of changes from a <<def_branch,branch>> to a
+       different base, and reset the <<def_head,head>> of that branch
+       to the result.
+
+[[def_ref]]ref::
+       A 40-byte hex representation of a <<def_SHA1,SHA1>> or a name that
+       denotes a particular <<def_object,object>>. These may be stored in
+       `$GIT_DIR/refs/`.
+
+[[def_refspec]]refspec::
+       A <<def_refspec,refspec>> is used by <<def_fetch,fetch>> and
+       <<def_push,push>> to describe the mapping between remote <<def_ref,ref>>
+       and local <<def_ref,ref>>. They are combined with a colon in the format
+       <src>:<dst>, preceded by an optional plus sign, +. For example: `git
+       fetch $URL refs/heads/master:refs/heads/origin` means
+       "grab the master <<def_branch,branch>> <<def_head,head>>
+       from the $URL and store it as my origin
+       <<def_branch,branch>> <<def_head,head>>". And `git <<def_push,push>>
+       $URL refs/heads/master:refs/heads/to-upstream` means
+       "publish my master <<def_branch,branch>>
+       <<def_head,head>> as to-upstream <<def_branch,branch>> at $URL". See
+       also gitlink:git-push[1]
+
+[[def_repository]]repository::
+       A collection of refs together with an <<def_object_database,object
+       database>> containing all objects which are <<def_reachable,reachable>>
+       from the refs, possibly accompanied by meta data from one or more
+       porcelains. A <<def_repository,repository>> can share an
+       <<def_object_database,object database>> with other repositories.
+
+[[def_resolve]]resolve::
+       The action of fixing up manually what a failed automatic
+       <<def_merge,merge>> left behind.
+
+[[def_revision]]revision::
+       A particular state of files and directories which was stored in the
+       <<def_object_database,object database>>. It is referenced by a
+       <<def_commit_object,commit object>>.
+
+[[def_rewind]]rewind::
+       To throw away part of the development, i.e. to assign the
+       <<def_head,head>> to an earlier <<def_revision,revision>>.
+
+[[def_SCM]]SCM::
        Source code management (tool).
 
-SHA1::
-       Synonym for object name.
-
-shallow repository::
-       A shallow repository has an incomplete history some of
-       whose commits have parents cauterized away (in other
-       words, git is told to pretend that these commits do not
-       have the parents, even though they are recorded in the
-       commit object).  This is sometimes useful when you are
-       interested only in the recent history of a project even
-       though the real history recorded in the upstream is
-       much larger.  A shallow repository is created by giving
-       `--depth` option to gitlink:git-clone[1], and its
-       history can be later deepened with gitlink:git-fetch[1].
-
-symref::
-       Symbolic reference: instead of containing the SHA1 id itself, it
-       is of the format 'ref: refs/some/thing' and when referenced, it
-       recursively dereferences to this reference. 'HEAD' is a prime
-       example of a symref. Symbolic references are manipulated with
-       the gitlink:git-symbolic-ref[1] command.
-
-topic branch::
-       A regular git branch that is used by a developer to
-       identify a conceptual line of development.  Since branches
-       are very easy and inexpensive, it is often desirable to
-       have several small branches that each contain very well
-       defined concepts or small incremental yet related changes.
-
-tracking branch::
-       A regular git branch that is used to follow changes from
-       another repository.  A tracking branch should not contain
-       direct modifications or have local commits made to it.
-       A tracking branch can usually be identified as the
-       right-hand-side ref in a Pull: refspec.
-
-tree object::
-       An object containing a list of file names and modes along with refs
-       to the associated blob and/or tree objects. A tree is equivalent
-       to a directory.
-
-tree::
-       Either a working tree, or a tree object together with the
-       dependent blob and tree objects (i.e. a stored representation
-       of a working tree).
-
-tree-ish::
-       A ref pointing to either a commit object, a tree object, or a
-       tag object pointing to a tag or commit or tree object.
-
-tag object::
-       An object containing a ref pointing to another object, which can
-       contain a message just like a commit object. It can also
-       contain a (PGP) signature, in which case it is called a "signed
-       tag object".
-
-tag::
-       A ref pointing to a tag or commit object. In contrast to a head,
-       a tag is not changed by a commit. Tags (not tag objects) are
-       stored in `$GIT_DIR/refs/tags/`. A git tag has nothing to do with
-       a Lisp tag (which is called object type in git's context).
-       A tag is most typically used to mark a particular point in the
-       commit ancestry chain.
-
-unmerged index::
-       An index which contains unmerged index entries.
-
-unreachable object::
-       An object which is not reachable from a branch, tag, or any
-       other reference.
-
-working tree::
-       The set of files and directories currently being worked on,
-       i.e. you can work in your working tree without using git at all.
-
+[[def_SHA1]]SHA1::
+       Synonym for <<def_object_name,object name>>.
+
+[[def_shallow_repository]]shallow repository::
+       A <<def_shallow_repository,shallow repository>> has an incomplete
+       history some of whose commits have parents cauterized away (in other
+       words, git is told to pretend that these commits do not have the
+       parents, even though they are recorded in the <<def_commit_object,commit
+       object>>). This is sometimes useful when you are interested only in the
+       recent history of a project even though the real history recorded in the
+       upstream is much larger. A <<def_shallow_repository,shallow repository>>
+       is created by giving the `--depth` option to gitlink:git-clone[1], and
+       its history can be later deepened with gitlink:git-fetch[1].
+
+[[def_symref]]symref::
+       Symbolic reference: instead of containing the <<def_SHA1,SHA1>> id
+       itself, it is of the format 'ref: refs/some/thing' and when
+       referenced, it recursively dereferences to this reference. 'HEAD' is a
+       prime example of a <<def_symref,symref>>. Symbolic references are
+       manipulated with the gitlink:git-symbolic-ref[1] command.
+
+[[def_tag]]tag::
+       A <<def_ref,ref>> pointing to a <<def_tag,tag>> or
+       <<def_commit_object,commit object>>. In contrast to a <<def_head,head>>,
+       a tag is not changed by a <<def_commit,commit>>. Tags (not
+       <<def_tag_object,tag objects>>) are stored in `$GIT_DIR/refs/tags/`. A
+       git tag has nothing to do with a Lisp tag (which would be
+       called an <<def_object_type,object type>> in git's context). A
+       tag is most typically used to mark a particular point in the
+       <<def_commit,commit>> ancestry <<def_chain,chain>>.
+
+[[def_tag_object]]tag object::
+       An <<def_object,object>> containing a <<def_ref,ref>> pointing to
+       another <<def_object,object>>, which can contain a message just like a
+       <<def_commit_object,commit object>>. It can also contain a (PGP)
+       signature, in which case it is called a "signed <<def_tag_object,tag
+       object>>".
+
+[[def_topic_branch]]topic branch::
+       A regular git <<def_branch,branch>> that is used by a developer to
+       identify a conceptual line of development. Since branches are very easy
+       and inexpensive, it is often desirable to have several small branches
+       that each contain very well defined concepts or small incremental yet
+       related changes.
+
+[[def_tracking_branch]]tracking branch::
+       A regular git <<def_branch,branch>> that is used to follow changes from
+       another <<def_repository,repository>>. A <<def_tracking_branch,tracking
+       branch>> should not contain direct modifications or have local commits
+       made to it. A <<def_tracking_branch,tracking branch>> can usually be
+       identified as the right-hand-side <<def_ref,ref>> in a Pull:
+       <<def_refspec,refspec>>.
+
+[[def_tree]]tree::
+       Either a <<def_working_tree,working tree>>, or a <<def_tree_object,tree
+       object>> together with the dependent blob and <<def_tree,tree>> objects
+       (i.e. a stored representation of a <<def_working_tree,working tree>>).
+
+[[def_tree_object]]tree object::
+       An <<def_object,object>> containing a list of file names and modes along
+       with refs to the associated blob and/or tree objects. A
+       <<def_tree,tree>> is equivalent to a <<def_directory,directory>>.
+
+[[def_tree-ish]]tree-ish::
+       A <<def_ref,ref>> pointing to either a <<def_commit_object,commit
+       object>>, a <<def_tree_object,tree object>>, or a <<def_tag_object,tag
+       object>> pointing to a <<def_tag,tag>> or <<def_commit,commit>> or
+       <<def_tree_object,tree object>>.
+
+[[def_unmerged_index]]unmerged index::
+       An <<def_index,index>> which contains unmerged
+       <<def_index_entry,index entries>>.
+
+[[def_unreachable_object]]unreachable object::
+       An <<def_object,object>> which is not <<def_reachable,reachable>> from a
+       <<def_branch,branch>>, <<def_tag,tag>>, or any other reference.
+
+[[def_working_tree]]working tree::
+       The set of files and directories currently being worked on, i.e. you can
+       work in your <<def_working_tree,working tree>> without using git at all.
diff --git a/Documentation/howto/use-git-daemon.txt b/Documentation/howto/use-git-daemon.txt
new file mode 100644 (file)
index 0000000..1a1eb24
--- /dev/null
@@ -0,0 +1,52 @@
+How to use git-daemon
+
+Git can be run in inetd mode and in stand alone mode. But all you want is
+let a coworker pull from you, and therefore need to set up a git server
+real quick, right?
+
+Note that git-daemon is not really chatty at the moment, especially when
+things do not go according to plan (e.g. a socket could not be bound).
+
+Another word of warning: if you run
+
+       $ git ls-remote git://127.0.0.1/rule-the-world.git
+
+and you see a message like
+
+       fatal: The remote end hung up unexpectedly
+
+it only means that _something_ went wrong. To find out _what_ went wrong,
+you have to ask the server. (Git refuses to be more precise for your
+security only. Take off your shoes now. You have any coins in your pockets?
+Sorry, not allowed -- who knows what you planned to do with them?)
+
+With these two caveats, let's see an example:
+
+       $ git daemon --reuseaddr --verbose --base-path=/home/gitte/git \
+         --export-all -- /home/gitte/git/rule-the-world.git
+
+(Of course, unless your user name is `gitte` _and_ your repository is in
+~/rule-the-world.git, you have to adjust the paths. If your repository is
+not bare, be aware that you have to type the path to the .git directory!)
+
+This invocation tries to reuse the address if it is already taken
+(this can save you some debugging, because otherwise killing and restarting
+git-daemon could just silently fail to bind to a socket).
+
+Also, it is (relatively) verbose when somebody actually connects to it.
+It also sets the base path, which means that all the projects which can be
+accessed using this daemon have to reside in or under that path.
+
+The option `--export-all` just means that you _don't_ have to create a
+file named `git-daemon-export-ok` in each exported repository. (Otherwise,
+git-daemon would complain loudly, and refuse to cooperate.)
+
+Last of all, the repository which should be exported is specified. It is
+a good practice to put the paths after a "--" separator.
+
+Now, test your daemon with
+
+       $ git ls-remote git://127.0.0.1/rule-the-world.git
+
+If this does not work, find out why, and submit a patch to this document.
+
diff --git a/Documentation/sort_glossary.pl b/Documentation/sort_glossary.pl
deleted file mode 100644 (file)
index 05dc7b2..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/usr/bin/perl
-
-%terms=();
-
-while(<>) {
-       if(/^(\S.*)::$/) {
-               my $term=$1;
-               if(defined($terms{$term})) {
-                       die "$1 defined twice\n";
-               }
-               $terms{$term}="";
-               LOOP: while(<>) {
-                       if(/^$/) {
-                               last LOOP;
-                       }
-                       if(/^   \S/) {
-                               $terms{$term}.=$_;
-                       } else {
-                               die "Error 1: $_";
-                       }
-               }
-       }
-}
-
-sub format_tab_80 ($) {
-       my $text=$_[0];
-       my $result="";
-       $text=~s/\s+/ /g;
-       $text=~s/^\s+//;
-       while($text=~/^(.{1,72})(|\s+(\S.*)?)$/) {
-               $result.="      ".$1."\n";
-               $text=$3;
-       }
-       return $result;
-}
-
-sub no_spaces ($) {
-       my $result=$_[0];
-       $result=~tr/ /_/;
-       return $result;
-}
-
-print 'GIT Glossary
-============
-
-This list is sorted alphabetically:
-
-';
-
-@keys=sort {uc($a) cmp uc($b)} keys %terms;
-$pattern='(\b(?<!link:git-)'.join('\b|\b(?<!-)',reverse @keys).'\b)';
-foreach $key (@keys) {
-       $terms{$key}=~s/$pattern/sprintf "<<ref_".no_spaces($1).",$1>>";/eg;
-       print '[[ref_'.no_spaces($key).']]'.$key."::\n"
-               .format_tab_80($terms{$key})."\n";
-}
-
-print '
-
-Author
-------
-Written by Johannes Schindelin <Johannes.Schindelin@gmx.de> and
-the git-list <git@vger.kernel.org>.
-
-GIT
----
-Part of the link:git.html[git] suite
-';
-
index 0e1ffb24276027aa54c6b04fe404367f267cc07d..9ce3c473ae1a9b5fd0b3a909fb264d8e0fff27a5 100644 (file)
@@ -21,11 +21,11 @@ GIT pack format
      which looks like this:
 
      (undeltified representation)
-     n-byte type and length (4-bit type, (n-1)*7+4-bit length)
+     n-byte type and length (3-bit type, (n-1)*7+4-bit length)
      compressed data
 
      (deltified representation)
-     n-byte type and length (4-bit type, (n-1)*7+4-bit length)
+     n-byte type and length (3-bit type, (n-1)*7+4-bit length)
      20-byte base object name
      compressed delta data
 
@@ -102,11 +102,13 @@ trailer     | | packfile checksum              |
 Pack file entry: <+
 
      packed object header:
-       1-byte type (upper 4-bit)
+       1-byte size extension bit (MSB)
+              type (next 3 bit)
               size0 (lower 4-bit) 
         n-byte sizeN (as long as MSB is set, each 7-bit)
                size0..sizeN form 4+7+7+..+7 bit integer, size0
-               is the most significant part.
+               is the least significant part, and sizeN is the
+               most significant part.
      packed object data:
         If it is not DELTA, then deflated bytes (the size above
                is the size before compression).
diff --git a/Documentation/technical/shallow.txt b/Documentation/technical/shallow.txt
new file mode 100644 (file)
index 0000000..559263a
--- /dev/null
@@ -0,0 +1,49 @@
+Def.: Shallow commits do have parents, but not in the shallow
+repo, and therefore grafts are introduced pretending that
+these commits have no parents.
+
+The basic idea is to write the SHA1s of shallow commits into
+$GIT_DIR/shallow, and handle its contents like the contents
+of $GIT_DIR/info/grafts (with the difference that shallow
+cannot contain parent information).
+
+This information is stored in a new file instead of grafts, or
+even the config, since the user should not touch that file
+at all (even throughout development of the shallow clone, it
+was never manually edited!).
+
+Each line contains exactly one SHA1. When read, a commit_graft
+will be constructed, which has nr_parent < 0 to make it easier
+to discern from user provided grafts.
+
+Since fsck-objects relies on the library to read the objects,
+it honours shallow commits automatically.
+
+There are some unfinished ends of the whole shallow business:
+
+- maybe we have to force non-thin packs when fetching into a
+  shallow repo (ATM they are forced non-thin).
+
+- A special handling of a shallow upstream is needed. At some
+  stage, upload-pack has to check if it sends a shallow commit,
+  and it should send that information early (or fail, if the
+  client does not support shallow repositories). There is no
+  support at all for this in this patch series.
+
+- Instead of locking $GIT_DIR/shallow at the start, just
+  the timestamp of it is noted, and when it comes to writing it,
+  a check is performed if the mtime is still the same, dying if
+  it is not.
+
+- It is unclear how "push into/from a shallow repo" should behave.
+
+- If you deepen a history, you'd want to get the tags of the
+  newly stored (but older!) commits. This does not work right now.
+
+To make a shallow clone, you can call "git-clone --depth 20 repo".
+The result contains only commit chains with a length of at most 20.
+It also writes an appropriate $GIT_DIR/shallow.
+
+You can deepen a shallow repository with "git-fetch --depth 20
+repo branch", which will fetch branch from repo, but stop at depth
+20, updating $GIT_DIR/shallow.
index d7b227e6471f6571129477c57a25d383f6b8c008..1c49e6995b42e42f1aba4e7b24d073e50c3b5234 100644 (file)
@@ -288,21 +288,22 @@ collection of files.  It stores the history as a compressed
 collection of interrelated snapshots (versions) of the project's
 contents.
 
-A single git repository may contain multiple branches.  Each branch
-is a bookmark referencing a particular point in the project history.
-The gitlink:git-branch[1] command shows you the list of branches:
+A single git repository may contain multiple branches.  It keeps track
+of them by keeping a list of <<def_head,heads>> which reference the
+latest version on each branch; the gitlink:git-branch[1] command shows
+you the list of branch heads:
 
 ------------------------------------------------
 $ git branch
 * master
 ------------------------------------------------
 
-A freshly cloned repository contains a single branch, named "master",
-and the working directory contains the version of the project
-referred to by the master branch.
+A freshly cloned repository contains a single branch head, named
+"master", and working directory is initialized to the state of
+the project referred to by "master".
 
-Most projects also use tags.  Tags, like branches, are references
-into the project's history, and can be listed using the
+Most projects also use <<def_tag,tags>>.  Tags, like heads, are
+references into the project's history, and can be listed using the
 gitlink:git-tag[1] command:
 
 ------------------------------------------------
@@ -320,9 +321,9 @@ v2.6.13
 ------------------------------------------------
 
 Tags are expected to always point at the same version of a project,
-while branches are expected to advance as development progresses.
+while heads are expected to advance as development progresses.
 
-Create a new branch pointing to one of these versions and check it
+Create a new branch head pointing to one of these versions and check it
 out using gitlink:git-checkout[1]:
 
 ------------------------------------------------
@@ -346,10 +347,10 @@ the current branch to point at v2.6.17 instead, with
 $ git reset --hard v2.6.17
 ------------------------------------------------
 
-Note that if the current branch was your only reference to a
+Note that if the current branch head was your only reference to a
 particular point in history, then resetting that branch may leave you
-with no way to find the history it used to point to; so use this
-command carefully.
+with no way to find the history it used to point to; so use this command
+carefully.
 
 Understanding History: Commits
 ------------------------------
@@ -452,17 +453,15 @@ be replaced with another letter or number.
 Understanding history: What is a branch?
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Though we've been using the word "branch" to mean a kind of reference
-to a particular commit, the word branch is also commonly used to
-refer to the line of commits leading up to that point.  In the
-example above, git may think of the branch named "A" as just a
-pointer to one particular commit, but we may refer informally to the
-line of three commits leading up to that point as all being part of
+When we need to be precise, we will use the word "branch" to mean a line
+of development, and "branch head" (or just "head") to mean a reference
+to the most recent commit on a branch.  In the example above, the branch
+head named "A" is a pointer to one particular commit, but we refer to
+the line of three commits leading up to that point as all being part of
 "branch A".
 
-If we need to make it clear that we're just talking about the most
-recent commit on the branch, we may refer to that commit as the
-"head" of the branch.
+However, when no confusion will result, we often just use the term
+"branch" both for branches and for branch heads.
 
 Manipulating branches
 ---------------------
@@ -1698,7 +1697,7 @@ If you and maintainer both have accounts on the same machine, then
 then you can just pull changes from each other's repositories
 directly; note that all of the commands (gitlink:git-clone[1],
 git-fetch[1], git-pull[1], etc.) that accept a URL as an argument
-will also accept a local file patch; so, for example, you can
+will also accept a local directory name; so, for example, you can
 use
 
 -------------------------------------------------
@@ -3013,9 +3012,6 @@ confusing and scary messages, but it won't actually do anything bad. In
 contrast, running "git prune" while somebody is actively changing the 
 repository is a *BAD* idea).
 
-Glossary of git terms
-=====================
-
 include::glossary.txt[]
 
 Notes and todo list for this manual
index 6abde8d7b36865c0c8f43b4489ce0074a27f24c8..39ba8d135cbdfe6d53500bffebed39ef864bcc54 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.5.0.GIT
+DEF_VER=v1.5.1-rc1.GIT
 
 LF='
 '
index dc024d45c143359e2e647990f37d927a59267cc0..51c1fed7116b3816398f3ec4535f027beb71e933 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -177,7 +177,7 @@ BASIC_LDFLAGS =
 SCRIPT_SH = \
        git-bisect.sh git-checkout.sh \
        git-clean.sh git-clone.sh git-commit.sh \
-       git-fetch.sh git-gc.sh \
+       git-fetch.sh \
        git-ls-remote.sh \
        git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
        git-pull.sh git-rebase.sh \
@@ -297,6 +297,7 @@ BUILTIN_OBJS = \
        builtin-fmt-merge-msg.o \
        builtin-for-each-ref.o \
        builtin-fsck.o \
+       builtin-gc.o \
        builtin-grep.o \
        builtin-init-db.o \
        builtin-log.o \
diff --git a/README b/README
index 441167cb8ad864b14c984ce9b58bf574c2b95357..548142c327a6790ff8821d67c2ee1eff7a656b52 100644 (file)
--- a/README
+++ b/README
@@ -38,3 +38,9 @@ requests, comments and patches to git@vger.kernel.org. To subscribe
 to the list, send an email with just "subscribe git" in the body to
 majordomo@vger.kernel.org. The mailing list archives are available at
 http://marc.theaimsgroup.com/?l=git and other archival sites.
+
+The messages titled "A note from the maintainer", "What's in
+git.git (stable)" and "What's cooking in git.git (topics)" and
+the discussion following them on the mailing list give a good
+reference for project status, development direction and
+remaining tasks.
index dfa17167963d1318298208e880be1cae47d06ea9..27a182bfaa826711b2e6c4c775fbd6e83b243f7e 100644 (file)
@@ -2355,7 +2355,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
 
 static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
 {
-       int fd;
+       int fd, converted;
        char *nbuf;
        unsigned long nsize;
 
@@ -2364,17 +2364,18 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
                 * terminated.
                 */
                return symlink(buf, path);
+
+       fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666);
+       if (fd < 0)
+               return -1;
+
        nsize = size;
        nbuf = (char *) buf;
-       if (convert_to_working_tree(path, &nbuf, &nsize)) {
-               free((char *) buf);
+       converted = convert_to_working_tree(path, &nbuf, &nsize);
+       if (converted) {
                buf = nbuf;
                size = nsize;
        }
-
-       fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666);
-       if (fd < 0)
-               return -1;
        while (size) {
                int written = xwrite(fd, buf, size);
                if (written < 0)
@@ -2386,6 +2387,8 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
        }
        if (close(fd) < 0)
                die("closing file %s: %s", path, strerror(errno));
+       if (converted)
+               free(nbuf);
        return 0;
 }
 
index b51cdc71faeca467cd3a965db0ff84d1f087076d..60ec5354f11c61c49829d41e8c07d22573f16bc7 100644 (file)
@@ -180,16 +180,15 @@ struct scoreboard {
        int *lineno;
 };
 
-static int cmp_suspect(struct origin *a, struct origin *b)
+static inline int same_suspect(struct origin *a, struct origin *b)
 {
-       int cmp = hashcmp(a->commit->object.sha1, b->commit->object.sha1);
-       if (cmp)
-               return cmp;
-       return strcmp(a->path, b->path);
+       if (a == b)
+               return 1;
+       if (a->commit != b->commit)
+               return 0;
+       return !strcmp(a->path, b->path);
 }
 
-#define cmp_suspect(a, b) ( ((a)==(b)) ? 0 : cmp_suspect(a,b) )
-
 static void sanity_check_refcnt(struct scoreboard *);
 
 /*
@@ -202,7 +201,7 @@ static void coalesce(struct scoreboard *sb)
        struct blame_entry *ent, *next;
 
        for (ent = sb->ent; ent && (next = ent->next); ent = next) {
-               if (!cmp_suspect(ent->suspect, next->suspect) &&
+               if (same_suspect(ent->suspect, next->suspect) &&
                    ent->guilty == next->guilty &&
                    ent->s_lno + ent->num_lines == next->s_lno) {
                        ent->num_lines += next->num_lines;
@@ -775,7 +774,7 @@ static int find_last_in_target(struct scoreboard *sb, struct origin *target)
        int last_in_target = -1;
 
        for (e = sb->ent; e; e = e->next) {
-               if (e->guilty || cmp_suspect(e->suspect, target))
+               if (e->guilty || !same_suspect(e->suspect, target))
                        continue;
                if (last_in_target < e->s_lno + e->num_lines)
                        last_in_target = e->s_lno + e->num_lines;
@@ -795,7 +794,7 @@ static void blame_chunk(struct scoreboard *sb,
        struct blame_entry *e;
 
        for (e = sb->ent; e; e = e->next) {
-               if (e->guilty || cmp_suspect(e->suspect, target))
+               if (e->guilty || !same_suspect(e->suspect, target))
                        continue;
                if (same <= e->s_lno)
                        continue;
@@ -970,7 +969,7 @@ static int find_move_in_parent(struct scoreboard *sb,
        while (made_progress) {
                made_progress = 0;
                for (e = sb->ent; e; e = e->next) {
-                       if (e->guilty || cmp_suspect(e->suspect, target))
+                       if (e->guilty || !same_suspect(e->suspect, target))
                                continue;
                        find_copy_in_blob(sb, e, parent, split, &file_p);
                        if (split[1].suspect &&
@@ -1002,12 +1001,12 @@ static struct blame_list *setup_blame_list(struct scoreboard *sb,
        struct blame_list *blame_list = NULL;
 
        for (e = sb->ent, num_ents = 0; e; e = e->next)
-               if (!e->guilty && !cmp_suspect(e->suspect, target))
+               if (!e->guilty && same_suspect(e->suspect, target))
                        num_ents++;
        if (num_ents) {
                blame_list = xcalloc(num_ents, sizeof(struct blame_list));
                for (e = sb->ent, i = 0; e; e = e->next)
-                       if (!e->guilty && !cmp_suspect(e->suspect, target))
+                       if (!e->guilty && same_suspect(e->suspect, target))
                                blame_list[i++].ent = e;
        }
        *num_ents_p = num_ents;
@@ -1137,7 +1136,7 @@ static void pass_whole_blame(struct scoreboard *sb,
                origin->file.ptr = NULL;
        }
        for (e = sb->ent; e; e = e->next) {
-               if (cmp_suspect(e->suspect, origin))
+               if (!same_suspect(e->suspect, origin))
                        continue;
                origin_incref(porigin);
                origin_decref(e->suspect);
@@ -1443,7 +1442,7 @@ static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)
 
                /* Take responsibility for the remaining entries */
                for (ent = sb->ent; ent; ent = ent->next)
-                       if (!cmp_suspect(ent->suspect, suspect))
+                       if (same_suspect(ent->suspect, suspect))
                                found_guilty_entry(ent);
                origin_decref(suspect);
 
index 42b1ff129e7d5bbea47a4d1af955395014f200f9..a4494ee337d70a400664e529e84f90d475a03b92 100644 (file)
@@ -372,9 +372,26 @@ static int get_remote_config(const char *key, const char *value)
        return 0;
 }
 
-static void set_branch_defaults(const char *name, const char *real_ref)
+static void set_branch_merge(const char *name, const char *config_remote,
+                            const char *config_repo)
 {
        char key[1024];
+       if (sizeof(key) <=
+           snprintf(key, sizeof(key), "branch.%s.remote", name))
+               die("what a long branch name you have!");
+       git_config_set(key, config_remote);
+
+       /*
+        * We do not have to check if we have enough space for
+        * the 'merge' key, since it's shorter than the
+        * previous 'remote' key, which we already checked.
+        */
+       snprintf(key, sizeof(key), "branch.%s.merge", name);
+       git_config_set(key, config_repo);
+}
+
+static void set_branch_defaults(const char *name, const char *real_ref)
+{
        const char *slash = strrchr(real_ref, '/');
 
        if (!slash)
@@ -384,21 +401,15 @@ static void set_branch_defaults(const char *name, const char *real_ref)
        start_len = strlen(real_ref);
        base_len = slash - real_ref;
        git_config(get_remote_config);
+       if (!config_repo && !config_remote &&
+           !prefixcmp(real_ref, "refs/heads/")) {
+               set_branch_merge(name, ".", real_ref);
+               printf("Branch %s set up to track local branch %s.\n",
+                      name, real_ref);
+       }
 
        if (config_repo && config_remote) {
-               if (sizeof(key) <=
-                   snprintf(key, sizeof(key), "branch.%s.remote", name))
-                       die("what a long branch name you have!");
-               git_config_set(key, config_remote);
-
-               /*
-                * We do not have to check if we have enough space for
-                * the 'merge' key, since it's shorter than the
-                * previous 'remote' key, which we already checked.
-                */
-               snprintf(key, sizeof(key), "branch.%s.merge", name);
-               git_config_set(key, config_repo);
-
+               set_branch_merge(name, config_remote, config_repo);
                printf("Branch %s set up to track remote branch %s.\n",
                       name, real_ref);
        }
index 786808081b8d5fe233ecd6533a4f5e8beb7d126c..0a9b73867f86a1ff33f7e2b2f458b02aacdd332d 100644 (file)
@@ -4,7 +4,7 @@
 #include "diff.h"
 #include "revision.h"
 #include "list-objects.h"
-#include "exec_cmd.h"
+#include "run-command.h"
 
 /*
  * Basic handler for bundle files to connect repositories via sneakernet.
@@ -99,67 +99,6 @@ static int read_header(const char *path, struct bundle_header *header) {
        return fd;
 }
 
-/* if in && *in >= 0, take that as input file descriptor instead */
-static int fork_with_pipe(const char **argv, int *in, int *out)
-{
-       int needs_in, needs_out;
-       int fdin[2], fdout[2], pid;
-
-       needs_in = in && *in < 0;
-       if (needs_in) {
-               if (pipe(fdin) < 0)
-                       return error("could not setup pipe");
-               *in = fdin[1];
-       }
-
-       needs_out = out && *out < 0;
-       if (needs_out) {
-               if (pipe(fdout) < 0)
-                       return error("could not setup pipe");
-               *out = fdout[0];
-       }
-
-       if ((pid = fork()) < 0) {
-               if (needs_in) {
-                       close(fdin[0]);
-                       close(fdin[1]);
-               }
-               if (needs_out) {
-                       close(fdout[0]);
-                       close(fdout[1]);
-               }
-               return error("could not fork");
-       }
-       if (!pid) {
-               if (needs_in) {
-                       dup2(fdin[0], 0);
-                       close(fdin[0]);
-                       close(fdin[1]);
-               } else if (in) {
-                       dup2(*in, 0);
-                       close(*in);
-               }
-               if (needs_out) {
-                       dup2(fdout[1], 1);
-                       close(fdout[0]);
-                       close(fdout[1]);
-               } else if (out) {
-                       dup2(*out, 1);
-                       close(*out);
-               }
-               exit(execv_git_cmd(argv));
-       }
-       if (needs_in)
-               close(fdin[0]);
-       else if (in)
-               close(*in);
-       if (needs_out)
-               close(fdout[1]);
-       else if (out)
-               close(*out);
-       return pid;
-}
-
 static int list_refs(struct ref_list *r, int argc, const char **argv)
 {
        int i;
@@ -263,9 +202,10 @@ static int create_bundle(struct bundle_header *header, const char *path,
        int bundle_fd = -1;
        const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *));
        const char **argv_pack = xmalloc(5 * sizeof(const char *));
-       int pid, in, out, i, status, ref_count = 0;
+       int i, ref_count = 0;
        char buffer[1024];
        struct rev_info revs;
+       struct child_process rls;
 
        bundle_fd = (!strcmp(path, "-") ? 1 :
                        open(path, O_CREAT | O_EXCL | O_WRONLY, 0666));
@@ -285,11 +225,13 @@ static int create_bundle(struct bundle_header *header, const char *path,
        argv_boundary[1] = "--boundary";
        argv_boundary[2] = "--pretty=oneline";
        argv_boundary[argc + 2] = NULL;
-       out = -1;
-       pid = fork_with_pipe(argv_boundary, NULL, &out);
-       if (pid < 0)
+       memset(&rls, 0, sizeof(rls));
+       rls.argv = argv_boundary;
+       rls.out = -1;
+       rls.git_cmd = 1;
+       if (start_command(&rls))
                return -1;
-       while ((i = read_string(out, buffer, sizeof(buffer))) > 0) {
+       while ((i = read_string(rls.out, buffer, sizeof(buffer))) > 0) {
                unsigned char sha1[20];
                if (buffer[0] == '-') {
                        write_or_die(bundle_fd, buffer, i);
@@ -303,11 +245,8 @@ static int create_bundle(struct bundle_header *header, const char *path,
                        object->flags |= SHOWN;
                }
        }
-       while ((i = waitpid(pid, &status, 0)) < 0)
-               if (errno != EINTR)
-                       return error("rev-list died");
-       if (!WIFEXITED(status) || WEXITSTATUS(status))
-               return error("rev-list died %d", WEXITSTATUS(status));
+       if (finish_command(&rls))
+               return error("rev-list died");
 
        /* write references */
        argc = setup_revisions(argc, argv, &revs, NULL);
@@ -352,26 +291,23 @@ static int create_bundle(struct bundle_header *header, const char *path,
        argv_pack[2] = "--stdout";
        argv_pack[3] = "--thin";
        argv_pack[4] = NULL;
-       in = -1;
-       out = bundle_fd;
-       pid = fork_with_pipe(argv_pack, &in, &out);
-       if (pid < 0)
+       memset(&rls, 0, sizeof(rls));
+       rls.argv = argv_pack;
+       rls.in = -1;
+       rls.out = bundle_fd;
+       rls.git_cmd = 1;
+       if (start_command(&rls))
                return error("Could not spawn pack-objects");
        for (i = 0; i < revs.pending.nr; i++) {
                struct object *object = revs.pending.objects[i].item;
                if (object->flags & UNINTERESTING)
-                       write(in, "^", 1);
-               write(in, sha1_to_hex(object->sha1), 40);
-               write(in, "\n", 1);
+                       write(rls.in, "^", 1);
+               write(rls.in, sha1_to_hex(object->sha1), 40);
+               write(rls.in, "\n", 1);
        }
-       close(in);
-       while (waitpid(pid, &status, 0) < 0)
-               if (errno != EINTR)
-                       return -1;
-       if (!WIFEXITED(status) || WEXITSTATUS(status))
+       if (finish_command(&rls))
                return error ("pack-objects died");
-
-       return status;
+       return 0;
 }
 
 static int unbundle(struct bundle_header *header, int bundle_fd,
@@ -379,22 +315,17 @@ static int unbundle(struct bundle_header *header, int bundle_fd,
 {
        const char *argv_index_pack[] = {"index-pack",
                "--fix-thin", "--stdin", NULL};
-       int pid, status, dev_null;
+       struct child_process ip;
 
        if (verify_bundle(header, 0))
                return -1;
-       dev_null = open("/dev/null", O_WRONLY);
-       if (dev_null < 0)
-               return error("Could not open /dev/null");
-       pid = fork_with_pipe(argv_index_pack, &bundle_fd, &dev_null);
-       if (pid < 0)
-               return error("Could not spawn index-pack");
-       while (waitpid(pid, &status, 0) < 0)
-               if (errno != EINTR)
-                       return error("index-pack died");
-       if (!WIFEXITED(status) || WEXITSTATUS(status))
-               return error("index-pack exited with status %d",
-                               WEXITSTATUS(status));
+       memset(&ip, 0, sizeof(ip));
+       ip.argv = argv_index_pack;
+       ip.in = bundle_fd;
+       ip.no_stdout = 1;
+       ip.git_cmd = 1;
+       if (run_command(&ip))
+               return error("index-pack died");
        return list_heads(header, argc, argv);
 }
 
index aec83384298042fc4509605299f82e82745ae675..6ba5077a2be6619f110280622cf46a7f4cfb8b01 100644 (file)
@@ -17,6 +17,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
        int nongit = 0;
+       int result;
 
        prefix = setup_git_directory_gently(&nongit);
        init_revisions(&rev, prefix);
@@ -29,5 +30,6 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
                argc = setup_revisions(argc, argv, &rev, NULL);
        if (!rev.diffopt.output_format)
                rev.diffopt.output_format = DIFF_FORMAT_RAW;
-       return run_diff_files_cmd(&rev, argc, argv);
+       result = run_diff_files_cmd(&rev, argc, argv);
+       return rev.diffopt.exit_with_status ? rev.diffopt.has_changes: result;
 }
index 083599d5c4c174cfab7c148428630534e4cd8174..d90eba95a6be17bd0486e0311c2657b1f69ab7d9 100644 (file)
@@ -14,6 +14,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
        struct rev_info rev;
        int cached = 0;
        int i;
+       int result;
 
        init_revisions(&rev, prefix);
        git_config(git_default_config); /* no "diff" UI options */
@@ -42,5 +43,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
                perror("read_cache");
                return -1;
        }
-       return run_diff_index(&rev, cached);
+       result = run_diff_index(&rev, cached);
+       return rev.diffopt.exit_with_status ? rev.diffopt.has_changes: result;
 }
index 24cb2d7f84064d7833fa04f33aeca6eafc8b931d..0b591c87169ff4b8c2173bedb26d6ed1a8a84b68 100644 (file)
@@ -118,7 +118,8 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
        }
 
        if (!read_stdin)
-               return 0;
+               return opt->diffopt.exit_with_status ?
+                   opt->diffopt.has_changes: 0;
 
        if (opt->diffopt.detect_rename)
                opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
@@ -133,5 +134,5 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
                else
                        diff_tree_stdin(line);
        }
-       return 0;
+       return opt->diffopt.exit_with_status ? opt->diffopt.has_changes: 0;
 }
index 4efbb8237bd49e8717a42833b2d9b2db064b45ac..21d13f0b30359295b8385754fccb4bb71f995dba 100644 (file)
@@ -190,6 +190,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        const char *path = NULL;
        struct blobinfo blob[2];
        int nongit = 0;
+       int result = 0;
 
        /*
         * We could get N tree-ish in the rev.pending_objects list.
@@ -292,17 +293,17 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        if (!ents) {
                switch (blobs) {
                case 0:
-                       return run_diff_files_cmd(&rev, argc, argv);
+                       result = run_diff_files_cmd(&rev, argc, argv);
                        break;
                case 1:
                        if (paths != 1)
                                usage(builtin_diff_usage);
-                       return builtin_diff_b_f(&rev, argc, argv, blob, path);
+                       result = builtin_diff_b_f(&rev, argc, argv, blob, path);
                        break;
                case 2:
                        if (paths)
                                usage(builtin_diff_usage);
-                       return builtin_diff_blobs(&rev, argc, argv, blob);
+                       result = builtin_diff_blobs(&rev, argc, argv, blob);
                        break;
                default:
                        usage(builtin_diff_usage);
@@ -311,19 +312,21 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        else if (blobs)
                usage(builtin_diff_usage);
        else if (ents == 1)
-               return builtin_diff_index(&rev, argc, argv);
+               result = builtin_diff_index(&rev, argc, argv);
        else if (ents == 2)
-               return builtin_diff_tree(&rev, argc, argv, ent);
+               result = builtin_diff_tree(&rev, argc, argv, ent);
        else if ((ents == 3) && (ent[0].item->flags & UNINTERESTING)) {
                /* diff A...B where there is one sane merge base between
                 * A and B.  We have ent[0] == merge-base, ent[1] == A,
                 * and ent[2] == B.  Show diff between the base and B.
                 */
                ent[1] = ent[2];
-               return builtin_diff_tree(&rev, argc, argv, ent);
+               result = builtin_diff_tree(&rev, argc, argv, ent);
        }
        else
-               return builtin_diff_combined(&rev, argc, argv,
+               result = builtin_diff_combined(&rev, argc, argv,
                                             ent, ents);
-       usage(builtin_diff_usage);
+       if (rev.diffopt.exit_with_status)
+               result = rev.diffopt.has_changes;
+       return result;
 }
index b8e71b640b66ebd95a436a5c1c7f3fce0647be0c..21f1f9e91d5a1ba8e8c3fd968b56d5c860b2aeac 100644 (file)
@@ -227,8 +227,7 @@ static int fsck_tree(struct tree *item)
        const char *o_name;
        const unsigned char *o_sha1;
 
-       desc.buf = item->buffer;
-       desc.size = item->size;
+       init_tree_desc(&desc, item->buffer, item->size);
 
        o_mode = 0;
        o_name = NULL;
@@ -242,7 +241,7 @@ static int fsck_tree(struct tree *item)
 
                if (strchr(name, '/'))
                        has_full_path = 1;
-               has_zero_pad |= *(char *)desc.buf == '0';
+               has_zero_pad |= *(char *)desc.buffer == '0';
                update_tree_entry(&desc);
 
                switch (mode) {
diff --git a/builtin-gc.c b/builtin-gc.c
new file mode 100644 (file)
index 0000000..3b1f8c2
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * git gc builtin command
+ *
+ * Cleanup unreachable files and optimize the repository.
+ *
+ * Copyright (c) 2007 James Bowes
+ *
+ * Based on git-gc.sh, which is
+ *
+ * Copyright (c) 2006 Shawn O. Pearce
+ */
+
+#include "cache.h"
+#include "run-command.h"
+
+#define FAILED_RUN "failed to run %s"
+
+static const char builtin_gc_usage[] = "git-gc [--prune]";
+
+static int pack_refs = -1;
+
+static const char *argv_pack_refs[] = {"pack-refs", "--prune", NULL};
+static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
+static const char *argv_repack[] = {"repack", "-a", "-d", "-l", NULL};
+static const char *argv_prune[] = {"prune", NULL};
+static const char *argv_rerere[] = {"rerere", "gc", NULL};
+
+static int gc_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "gc.packrefs")) {
+               if (!strcmp(value, "notbare"))
+                       pack_refs = -1;
+               else
+                       pack_refs = git_config_bool(var, value);
+               return 0;
+       }
+       return git_default_config(var, value);
+}
+
+int cmd_gc(int argc, const char **argv, const char *prefix)
+{
+       int i;
+       int prune = 0;
+
+       git_config(gc_config);
+
+       if (pack_refs < 0)
+               pack_refs = !is_bare_repository();
+
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (!strcmp(arg, "--prune")) {
+                       prune = 1;
+                       continue;
+               }
+               /* perhaps other parameters later... */
+               break;
+       }
+       if (i != argc)
+               usage(builtin_gc_usage);
+
+       if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
+               return error(FAILED_RUN, argv_pack_refs[0]);
+
+       if (run_command_v_opt(argv_reflog, RUN_GIT_CMD))
+               return error(FAILED_RUN, argv_reflog[0]);
+
+       if (run_command_v_opt(argv_repack, RUN_GIT_CMD))
+               return error(FAILED_RUN, argv_repack[0]);
+
+       if (prune && run_command_v_opt(argv_prune, RUN_GIT_CMD))
+               return error(FAILED_RUN, argv_prune[0]);
+
+       if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
+               return error(FAILED_RUN, argv_rerere[0]);
+
+       return 0;
+}
index 4510d353247355a28979fbf63b15523c2e705591..981f3d4d8eb079f5985beaaf94014d5745c5aacc 100644 (file)
@@ -378,7 +378,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
                         * decide if we want to descend into "abc"
                         * directory.
                         */
-                       strcpy(path_buf + len + entry.pathlen, "/");
+                       strcpy(path_buf + len + tree_entry_len(entry.path, entry.sha1), "/");
 
                if (!pathspec_matches(paths, down))
                        ;
@@ -388,11 +388,13 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
                        enum object_type type;
                        struct tree_desc sub;
                        void *data;
-                       data = read_sha1_file(entry.sha1, &type, &sub.size);
+                       unsigned long size;
+
+                       data = read_sha1_file(entry.sha1, &type, &size);
                        if (!data)
                                die("unable to read tree (%s)",
                                    sha1_to_hex(entry.sha1));
-                       sub.buf = data;
+                       init_tree_desc(&sub, data, size);
                        hit |= grep_tree(opt, paths, &sub, tree_name, down);
                        free(data);
                }
@@ -408,12 +410,13 @@ static int grep_object(struct grep_opt *opt, const char **paths,
        if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
                struct tree_desc tree;
                void *data;
+               unsigned long size;
                int hit;
                data = read_object_with_reference(obj->sha1, tree_type,
-                                                 &tree.size, NULL);
+                                                 &size, NULL);
                if (!data)
                        die("unable to read tree (%s)", sha1_to_hex(obj->sha1));
-               tree.buf = data;
+               init_tree_desc(&tree, data, size);
                hit = grep_tree(opt, paths, &tree, name, "");
                free(data);
                return hit;
index 865832c85e8e860b328a5f5741e871b486fd89b6..71df957eaa0b85bd841fe3758b6efbfd51243881 100644 (file)
@@ -35,7 +35,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
                if (!prefixcmp(arg, "--encoding=")) {
                        arg += 11;
                        if (strcmp(arg, "none"))
-                               git_log_output_encoding = strdup(arg);
+                               git_log_output_encoding = xstrdup(arg);
                        else
                                git_log_output_encoding = "";
                }
index f8ebad0b2f2006d619dc06814acb6024ee674eec..b5f9648e809a3ef7f381239470c031208ade4a34 100644 (file)
@@ -166,11 +166,12 @@ static void prepare_pack_revindex(struct pack_revindex *rix)
        struct packed_git *p = rix->p;
        int num_ent = num_packed_objects(p);
        int i;
-       void *index = p->index_base + 256;
+       const char *index = p->index_data;
 
+       index += 4 * 256;
        rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1));
        for (i = 0; i < num_ent; i++) {
-               unsigned int hl = *((unsigned int *)((char *) index + 24*i));
+               uint32_t hl = *((uint32_t *)(index + 24 * i));
                rix->revindex[i].offset = ntohl(hl);
                rix->revindex[i].nr = i;
        }
@@ -217,11 +218,11 @@ static off_t find_packed_object_size(struct packed_git *p, off_t ofs)
        return entry[1].offset - ofs;
 }
 
-static unsigned char *find_packed_object_name(struct packed_git *p,
-                                             off_t ofs)
+static const unsigned char *find_packed_object_name(struct packed_git *p,
+                                                   off_t ofs)
 {
        struct revindex_entry *entry = find_packed_object(p, ofs);
-       return (unsigned char *)(p->index_base + 256) + 24 * entry->nr + 4;
+       return ((unsigned char *)p->index_data) + 4 * 256 + 24 * entry->nr + 4;
 }
 
 static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
@@ -853,7 +854,7 @@ static void add_pbase_object(struct tree_desc *tree,
                unsigned long size;
                enum object_type type;
 
-               if (entry.pathlen != cmplen ||
+               if (tree_entry_len(entry.path, entry.sha1) != cmplen ||
                    memcmp(entry.path, name, cmplen) ||
                    !has_sha1_file(entry.sha1) ||
                    (type = sha1_object_info(entry.sha1, &size)) < 0)
@@ -872,8 +873,7 @@ static void add_pbase_object(struct tree_desc *tree,
                        tree = pbase_tree_get(entry.sha1);
                        if (!tree)
                                return;
-                       sub.buf = tree->tree_data;
-                       sub.size = tree->tree_size;
+                       init_tree_desc(&sub, tree->tree_data, tree->tree_size);
 
                        add_pbase_object(&sub, down, downlen, fullname);
                        pbase_tree_put(tree);
@@ -936,8 +936,7 @@ static void add_preferred_base_object(const char *name, unsigned hash)
                }
                else {
                        struct tree_desc tree;
-                       tree.buf = it->pcache.tree_data;
-                       tree.size = it->pcache.tree_size;
+                       init_tree_desc(&tree, it->pcache.tree_data, it->pcache.tree_size);
                        add_pbase_object(&tree, name, cmplen, name);
                }
        }
@@ -996,7 +995,8 @@ static void check_object(struct object_entry *entry)
                 * delta.
                 */
                if (!no_reuse_delta) {
-                       unsigned char c, *base_name;
+                       unsigned char c;
+                       const unsigned char *base_name;
                        off_t ofs;
                        unsigned long used_0;
                        /* there is at least 20 bytes left in the pack */
index 09864b7a6d52bfb0855735418486e046438ecee4..44df59e4a70f84cdebac94f2591765ada8d4b92d 100644 (file)
@@ -14,10 +14,8 @@ static int prune_object(char *path, const char *filename, const unsigned char *s
                enum object_type type = sha1_object_info(sha1, NULL);
                printf("%s %s\n", sha1_to_hex(sha1),
                       (type > 0) ? typename(type) : "unknown");
-               return 0;
-       }
-       unlink(mkpath("%s/%s", path, filename));
-       rmdir(path);
+       } else
+               unlink(mkpath("%s/%s", path, filename));
        return 0;
 }
 
@@ -60,6 +58,8 @@ static int prune_dir(int i, char *path)
                }
                fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
        }
+       if (!show_only)
+               rmdir(path);
        closedir(dir);
        return 0;
 }
index 6ab9a28e8c106858a1002971438e46778ff843d8..70b1168fa677fc889543e87b2a3f964b175375c6 100644 (file)
@@ -323,10 +323,10 @@ static int do_push(const char *repo)
                int dest_refspec_nr = refspec_nr;
                const char **dest_refspec = refspec;
                const char *dest = uri[i];
-               const char *sender = "git-send-pack";
+               const char *sender = "send-pack";
                if (!prefixcmp(dest, "http://") ||
                    !prefixcmp(dest, "https://"))
-                       sender = "git-http-push";
+                       sender = "http-push";
                else if (thin)
                        argv[dest_argc++] = "--thin";
                argv[0] = sender;
@@ -336,7 +336,7 @@ static int do_push(const char *repo)
                argv[dest_argc] = NULL;
                if (verbose)
                        fprintf(stderr, "Pushing to %s\n", dest);
-               err = run_command_v_opt(argv, 0);
+               err = run_command_v_opt(argv, RUN_GIT_CMD);
                if (!err)
                        continue;
                switch (err) {
index e47715538bd1ff81d1a91e0a43cd9bdbe1cb0d3f..82df94101a20a9ea9c06cd6d6ce8db56d8a7de18 100644 (file)
@@ -55,8 +55,7 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
        int cnt;
 
        hashcpy(it->sha1, tree->object.sha1);
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
+       init_tree_desc(&desc, tree->buffer, tree->size);
        cnt = 0;
        while (tree_entry(&desc, &entry)) {
                if (!S_ISDIR(entry.mode))
index 186aabce042a1d6e5d83141495a854db09223bf8..4c39f1da98e5e690f28f5145a1ab5dba790da68d 100644 (file)
@@ -52,18 +52,18 @@ static int tree_is_complete(const unsigned char *sha1)
        if (tree->object.flags & INCOMPLETE)
                return 0;
 
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
-       if (!desc.buf) {
+       if (!tree->buffer) {
                enum object_type type;
-               void *data = read_sha1_file(sha1, &type, &desc.size);
+               unsigned long size;
+               void *data = read_sha1_file(sha1, &type, &size);
                if (!data) {
                        tree->object.flags |= INCOMPLETE;
                        return 0;
                }
-               desc.buf = data;
                tree->buffer = data;
+               tree->size = size;
        }
+       init_tree_desc(&desc, tree->buffer, tree->size);
        complete = 1;
        while (tree_entry(&desc, &entry)) {
                if (!has_sha1_file(entry.sha1) ||
index c2db5a5b037babf9020353d9b11dc348915b6c1b..51858e3233a74a2a5cc7e96e7dc5d9786fecc326 100644 (file)
@@ -180,7 +180,7 @@ static struct commit_list *find_bisection(struct commit_list *list)
                        nr++;
                p = p->next;
        }
-       closest = 0;
+       closest = -1;
        best = list;
 
        for (p = list; p; p = p->next) {
index 652eece5ad71fbfc19c7132d9fe256c0b5218036..4ba0ee63ab4ca5e77aa350d1956f1b3312945f5d 100644 (file)
@@ -235,8 +235,8 @@ static int revert_or_cherry_pick(int argc, const char **argv)
        unsigned char head[20];
        struct commit *base, *next;
        int i;
-       char *oneline, *encoding, *reencoded_message = NULL;
-       const char *message;
+       char *oneline, *reencoded_message = NULL;
+       const char *message, *encoding;
 
        git_config(git_default_config);
        me = action == REVERT ? "revert" : "cherry-pick";
@@ -294,13 +294,13 @@ static int revert_or_cherry_pick(int argc, const char **argv)
        oneline = get_oneline(message);
 
        if (action == REVERT) {
+               char *oneline_body = strchr(oneline, ' ');
+
                base = commit;
                next = commit->parents->item;
-               add_to_msg("Revert ");
-               add_to_msg(find_unique_abbrev(commit->object.sha1,
-                                       DEFAULT_ABBREV));
-               add_to_msg(oneline);
-               add_to_msg("\nThis reverts commit ");
+               add_to_msg("Revert \"");
+               add_to_msg(oneline_body + 1);
+               add_to_msg("\"\n\nThis reverts commit ");
                add_to_msg(sha1_to_hex(commit->object.sha1));
                add_to_msg(".\n");
        } else {
index 1cb64b7ecd60f71ca0fd0d4d47e93c5f3d05d9ce..af203e9e367b1dc1abb012234b5a8ae5e09f1629 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -37,6 +37,7 @@ extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
 extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
 extern int cmd_fsck(int argc, const char **argv, const char *prefix);
+extern int cmd_gc(int argc, const char **argv, const char *prefix);
 extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
 extern int cmd_grep(int argc, const char **argv, const char *prefix);
 extern int cmd_help(int argc, const char **argv, const char *prefix);
diff --git a/cache.h b/cache.h
index a4762eda5c3dbf34fb1742b1c645395f092d8d6b..384b260227dfb2fa30ecc175681b9b8d73a3577a 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -228,6 +228,7 @@ extern const char *apply_default_whitespace;
 extern int zlib_compression_level;
 extern size_t packed_git_window_size;
 extern size_t packed_git_limit;
+extern size_t delta_base_cache_limit;
 extern int auto_crlf;
 
 #define GIT_REPO_VERSION 0
@@ -282,7 +283,7 @@ char *enter_repo(char *path, int strict);
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 extern int sha1_object_info(const unsigned char *, unsigned long *);
 extern void * read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size);
-extern int hash_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *sha1);
+extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
 extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 
@@ -371,10 +372,11 @@ struct pack_window {
 extern struct packed_git {
        struct packed_git *next;
        struct pack_window *windows;
-       uint32_t *index_base;
-       time_t mtime;
+       const void *index_data;
        off_t index_size;
        off_t pack_size;
+       time_t mtime;
+       int index_version;
        int pack_fd;
        int pack_local;
        unsigned char sha1[20];
@@ -412,7 +414,7 @@ extern int server_supports(const char *feature);
 
 extern struct packed_git *parse_pack_index(unsigned char *sha1);
 extern struct packed_git *parse_pack_index_file(const unsigned char *sha1,
-                                               char *idx_path);
+                                               const char *idx_path);
 
 extern void prepare_packed_git(void);
 extern void reprepare_packed_git(void);
@@ -424,7 +426,7 @@ extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
 extern void pack_report(void);
 extern unsigned char* use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
 extern void unuse_pack(struct pack_window **);
-extern struct packed_git *add_packed_git(char *, int, int);
+extern struct packed_git *add_packed_git(const char *, int, int);
 extern uint32_t num_packed_objects(const struct packed_git *p);
 extern int nth_packed_object_sha1(const struct packed_git *, uint32_t, unsigned char*);
 extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
@@ -450,7 +452,7 @@ extern int check_repository_format_version(const char *var, const char *value);
 extern char git_default_email[MAX_GITNAME];
 extern char git_default_name[MAX_GITNAME];
 
-extern char *git_commit_encoding;
+extern const char *git_commit_encoding;
 extern const char *git_log_output_encoding;
 
 extern int copy_fd(int ifd, int ofd);
index 5b9234e12e8d1ef46d0d53b15cea850dfbde14c2..718e568855a37586f99f20bcfafd44cf3aa2b657 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -706,7 +706,7 @@ static char *logmsg_reencode(const struct commit *commit,
        encoding = get_header(commit, "encoding");
        use_encoding = encoding ? encoding : utf8;
        if (!strcmp(use_encoding, output_encoding))
-               out = strdup(commit->buffer);
+               out = xstrdup(commit->buffer);
        else
                out = reencode_string(commit->buffer,
                                      output_encoding, use_encoding);
index a3c7b772bce1d302e60bbf02212bb4ad4da0ee7b..6479855723d6dc94fa7c440868724a794bb59901 100644 (file)
--- a/config.c
+++ b/config.c
@@ -331,6 +331,11 @@ int git_default_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.deltabasecachelimit")) {
+               delta_base_cache_limit = git_config_int(var, value);
+               return 0;
+       }
+
        if (!strcmp(var, "core.autocrlf")) {
                if (value && !strcasecmp(value, "input")) {
                        auto_crlf = -1;
@@ -351,12 +356,12 @@ int git_default_config(const char *var, const char *value)
        }
 
        if (!strcmp(var, "i18n.commitencoding")) {
-               git_commit_encoding = strdup(value);
+               git_commit_encoding = xstrdup(value);
                return 0;
        }
 
        if (!strcmp(var, "i18n.logoutputencoding")) {
-               git_log_output_encoding = strdup(value);
+               git_log_output_encoding = xstrdup(value);
                return 0;
        }
 
index 8a8a13bb72b33f335a5a10642f0461ef673ef168..5048653639b3eea4c68eaaa4382363b2bc468e06 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -3,6 +3,7 @@
 #include "pkt-line.h"
 #include "quote.h"
 #include "refs.h"
+#include "run-command.h"
 
 static char *server_capabilities;
 
@@ -598,8 +599,8 @@ static void git_proxy_connect(int fd[2], char *host)
 {
        const char *port = STR(DEFAULT_GIT_PORT);
        char *colon, *end;
-       int pipefd[2][2];
-       pid_t pid;
+       const char *argv[4];
+       struct child_process proxy;
 
        if (host[0] == '[') {
                end = strchr(host + 1, ']');
@@ -618,25 +619,18 @@ static void git_proxy_connect(int fd[2], char *host)
                port = colon + 1;
        }
 
-       if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0)
-               die("unable to create pipe pair for communication");
-       pid = fork();
-       if (!pid) {
-               dup2(pipefd[1][0], 0);
-               dup2(pipefd[0][1], 1);
-               close(pipefd[0][0]);
-               close(pipefd[0][1]);
-               close(pipefd[1][0]);
-               close(pipefd[1][1]);
-               execlp(git_proxy_command, git_proxy_command, host, port, NULL);
-               die("exec failed");
-       }
-       if (pid < 0)
-               die("fork failed");
-       fd[0] = pipefd[0][0];
-       fd[1] = pipefd[1][1];
-       close(pipefd[0][1]);
-       close(pipefd[1][0]);
+       argv[0] = git_proxy_command;
+       argv[1] = host;
+       argv[2] = port;
+       argv[3] = NULL;
+       memset(&proxy, 0, sizeof(proxy));
+       proxy.argv = argv;
+       proxy.in = -1;
+       proxy.out = -1;
+       if (start_command(&proxy))
+               die("cannot start proxy %s", argv[0]);
+       fd[0] = proxy.out; /* read from proxy stdout */
+       fd[1] = proxy.in;  /* write to proxy stdin */
 }
 
 #define MAX_CMD_LEN 1024
diff --git a/contrib/continuous/cidaemon b/contrib/continuous/cidaemon
new file mode 100644 (file)
index 0000000..4009a15
--- /dev/null
@@ -0,0 +1,503 @@
+#!/usr/bin/perl
+#
+# A daemon that waits for update events sent by its companion
+# post-receive-cinotify hook, checks out a new copy of source,
+# compiles it, and emails the guilty parties if the compile
+# (and optionally test suite) fails.
+#
+# To use this daemon, configure it and run it.  It will disconnect
+# from your terminal and fork into the background.  The daemon must
+# have local filesystem access to the source repositories, as it
+# uses objects/info/alternates to avoid copying objects.
+#
+# Add its companion post-receive-cinotify hook as the post-receive
+# hook to each repository that the daemon should monitor.  Yes, a
+# single daemon can monitor more than one repository.
+#
+# To use multiple daemons on the same system, give them each a
+# unique queue file and tmpdir.
+#
+# Global Config
+# -------------
+# Reads from a Git style configuration file.  This will be
+# ~/.gitconfig by default but can be overridden by setting
+# the GIT_CONFIG_FILE environment variable before starting.
+#
+# cidaemon.smtpHost
+#   Hostname of the SMTP server the daemon will send email
+#   through.  Defaults to 'localhost'.
+#
+# cidaemon.smtpUser
+#   Username to authenticate to the SMTP server as.  This
+#   variable is optional; if it is not supplied then no
+#   authentication will be performed.
+#
+# cidaemon.smtpPassword
+#   Password to authenticate to the SMTP server as.  This
+#   variable is optional.  If not supplied but smtpUser was,
+#   the daemon prompts for the password before forking into
+#   the background.
+#
+# cidaemon.smtpAuth
+#   Type of authentication to perform with the SMTP server.
+#   If set to 'login' and smtpUser was defined, this will
+#   use the AUTH LOGIN command, which is suitable for use
+#   with at least one version of Microsoft Exchange Server.
+#   If not set the daemon will use whatever auth methods
+#   are supported by your version of Net::SMTP.
+#
+# cidaemon.email
+#   Email address that daemon generated emails will be sent
+#   from.  This should be a useful email address within your
+#   organization.  Required.
+#
+# cidaemon.name
+#   Human friendly name that the daemon will send emails as.
+#   Defaults to 'cidaemon'.
+#
+# cidaemon.scanDelay
+#   Number of seconds to sleep between polls of the queue file.
+#   Defaults to 60.
+#
+# cidaemon.recentCache
+#   Number of recent commit SHA-1s per repository to cache and
+#   skip building if they appear again.  This is useful to avoid
+#   rebuilding the same commit multiple times just because it was
+#   pushed into more than one branch.  Defaults to 100.
+#
+# cidaemon.tmpdir
+#   Scratch directory to create the builds within.  The daemon
+#   makes a new subdirectory for each build, then deletes it when
+#   the build has finished.  The pid file is also placed here.
+#   Defaults to '/tmp'.
+#
+# cidaemon.queue
+#   Path to the queue file that the post-receive-cinotify hook
+#   appends events to.  This file is polled by the daemon.  It
+#   must not be on an NFS mount (uses flock).  Required.
+#
+# cidaemon.nocc
+#   Perl regex patterns to match against author and committer
+#   lines.  If a pattern matches, that author or committer will
+#   not be notified of a build failure.
+#
+# Per Repository Config
+# ----------------------
+# Read from the source repository's config file.
+#
+# builder.command
+#   Shell command to execute the build.  This command must
+#   return 0 on "success" and non-zero on failure.  If you
+#   also want to run a test suite, make sure your command
+#   does that too.  Required.
+#
+# builder.queue
+#   Queue file to notify the cidaemon through.  Should match
+#   cidaemon.queue.  If not set the hook will not notify the
+#   cidaemon.
+#
+# builder.skip
+#   Perl regex patterns of refs that should not be sent to
+#   cidaemon.  Updates of these refs will be ignored.
+#
+# builder.newBranchBase
+#   Glob patterns of refs that should be used to form the
+#   'old' revions of a newly created ref.  This should set
+#   to be globs that match your 'mainline' branches.  This
+#   way a build failure of a brand new topic branch does not
+#   attempt to email everyone since the beginning of time;
+#   instead it only emails those authors of commits not in
+#   these 'mainline' branches.
+
+local $ENV{PATH} = join ':', qw(
+       /opt/git/bin
+       /usr/bin
+       /bin
+       );
+
+use strict;
+use warnings;
+use FindBin qw($RealBin);
+use File::Spec;
+use lib File::Spec->catfile($RealBin, '..', 'perl5');
+use Storable qw(retrieve nstore);
+use Fcntl ':flock';
+use POSIX qw(strftime);
+use Getopt::Long qw(:config no_auto_abbrev auto_help);
+
+sub git_config ($;$)
+{
+       my $var = shift;
+       my $required = shift || 0;
+       local *GIT;
+       open GIT, '-|','git','config','--get',$var;
+       my $r = <GIT>;
+       chop $r if $r;
+       close GIT;
+       die "error: $var not set.\n" if ($required && !$r);
+       return $r;
+}
+
+package EXCHANGE_NET_SMTP;
+
+# Microsoft Exchange Server requires an 'AUTH LOGIN'
+# style of authentication.  This is different from
+# the default supported by Net::SMTP so we subclass
+# and override the auth method to support that.
+
+use Net::SMTP;
+use Net::Cmd;
+use MIME::Base64 qw(encode_base64);
+our @ISA = qw(Net::SMTP);
+our $auth_type = ::git_config 'cidaemon.smtpAuth';
+
+sub new
+{
+       my $self = shift;
+       my $type = ref($self) || $self;
+       $type->SUPER::new(@_);
+}
+
+sub auth
+{
+       my $self = shift;
+       return $self->SUPER::auth(@_) unless $auth_type eq 'login';
+
+       my $user = encode_base64 shift, '';
+       my $pass = encode_base64 shift, '';
+       return 0 unless CMD_MORE == $self->command("AUTH LOGIN")->response;
+       return 0 unless CMD_MORE == $self->command($user)->response;
+       CMD_OK == $self->command($pass)->response;
+}
+
+package main;
+
+my ($debug_flag, %recent);
+
+my $ex_host = git_config('cidaemon.smtpHost') || 'localhost';
+my $ex_user = git_config('cidaemon.smtpUser');
+my $ex_pass = git_config('cidaemon.smtpPassword');
+
+my $ex_from_addr = git_config('cidaemon.email', 1);
+my $ex_from_name = git_config('cidaemon.name') || 'cidaemon';
+
+my $scan_delay = git_config('cidaemon.scanDelay') || 60;
+my $recent_size = git_config('cidaemon.recentCache') || 100;
+my $tmpdir = git_config('cidaemon.tmpdir') || '/tmp';
+my $queue_name = git_config('cidaemon.queue', 1);
+my $queue_lock = "$queue_name.lock";
+
+my @nocc_list;
+open GIT,'git config --get-all cidaemon.nocc|';
+while (<GIT>) {
+       chop;
+       push @nocc_list, $_;
+}
+close GIT;
+
+sub nocc_author ($)
+{
+       local $_ = shift;
+       foreach my $pat (@nocc_list) {
+               return 1 if /$pat/;
+       }
+       0;
+}
+
+sub input_echo ($)
+{
+       my $prompt = shift;
+
+       local $| = 1;
+       print $prompt;
+       my $input = <STDIN>;
+       chop $input;
+       return $input;
+}
+
+sub input_noecho ($)
+{
+       my $prompt = shift;
+
+       my $end = sub {system('stty','echo');print "\n";exit};
+       local $SIG{TERM} = $end;
+       local $SIG{INT} = $end;
+       system('stty','-echo');
+
+       local $| = 1;
+       print $prompt;
+       my $input = <STDIN>;
+       system('stty','echo');
+       print "\n";
+       chop $input;
+       return $input;
+}
+
+sub rfc2822_date ()
+{
+        strftime("%a, %d %b %Y %H:%M:%S %Z", localtime);
+}
+
+sub send_email ($$$)
+{
+       my ($subj, $body, $to) = @_;
+       my $now = rfc2822_date;
+       my $to_str = '';
+       my @rcpt_to;
+       foreach (@$to) {
+               my $s = $_;
+               $s =~ s/^/"/;
+               $s =~ s/(\s+<)/"$1/;
+               $to_str .= ', ' if $to_str;
+               $to_str .= $s;
+               push @rcpt_to, $1 if $s =~ /<(.*)>/;
+       }
+       die "Nobody to send to.\n" unless @rcpt_to;
+       my $msg = <<EOF;
+From: "$ex_from_name" <$ex_from_addr>
+To: $to_str
+Date: $now
+Subject: $subj
+
+$body
+EOF
+
+       my $smtp = EXCHANGE_NET_SMTP->new(Host => $ex_host)
+               or die "Cannot connect to $ex_host: $!\n";
+       if ($ex_user && $ex_pass) {
+               $smtp->auth($ex_user,$ex_pass)
+                       or die "$ex_host rejected $ex_user\n";
+       }
+       $smtp->mail($ex_from_addr)
+               or die "$ex_host rejected $ex_from_addr\n";
+       scalar($smtp->recipient(@rcpt_to, { SkipBad => 1 }))
+               or die "$ex_host did not accept any addresses.\n";
+       $smtp->data($msg)
+               or die "$ex_host rejected message data\n";
+       $smtp->quit;
+}
+
+sub pop_queue ()
+{
+       open LOCK, ">$queue_lock" or die "Can't open $queue_lock: $!";
+       flock LOCK, LOCK_EX;
+
+       my $queue = -f $queue_name ? retrieve $queue_name : [];
+       my $ent = shift @$queue;
+       nstore $queue, $queue_name;
+
+       flock LOCK, LOCK_UN;
+       close LOCK;
+       $ent;
+}
+
+sub git_exec (@)
+{
+       system('git',@_) == 0 or die "Cannot git " . join(' ', @_) . "\n";
+}
+
+sub git_val (@)
+{
+       open(C, '-|','git',@_);
+       my $r = <C>;
+       chop $r if $r;
+       close C;
+       $r;
+}
+
+sub do_build ($$)
+{
+       my ($git_dir, $new) = @_;
+
+       my $tmp = File::Spec->catfile($tmpdir, "builder$$");
+       system('rm','-rf',$tmp) == 0 or die "Cannot clear $tmp\n";
+       die "Cannot clear $tmp.\n" if -e $tmp;
+
+       my $result = 1;
+       eval {
+               my $command;
+               {
+                       local $ENV{GIT_DIR} = $git_dir;
+                       $command = git_val 'config','builder.command';
+               }
+               die "No builder.command for $git_dir.\n" unless $command;
+
+               git_exec 'clone','-n','-l','-s',$git_dir,$tmp;
+               chmod 0700, $tmp or die "Cannot lock $tmp\n";
+               chdir $tmp or die "Cannot enter $tmp\n";
+
+               git_exec 'update-ref','HEAD',$new;
+               git_exec 'read-tree','-m','-u','HEAD','HEAD';
+               system $command;
+               if ($? == -1) {
+                       print STDERR "failed to execute '$command': $!\n";
+                       $result = 1;
+               } elsif ($? & 127) {
+                       my $sig = $? & 127;
+                       print STDERR "'$command' died from signal $sig\n";
+                       $result = 1;
+               } else {
+                       my $r = $? >> 8;
+                       print STDERR "'$command' exited with $r\n" if $r;
+                       $result = $r;
+               }
+       };
+       if ($@) {
+               $result = 2;
+               print STDERR "$@\n";
+       }
+
+       chdir '/';
+       system('rm','-rf',$tmp);
+       rmdir $tmp;
+       $result;
+}
+
+sub build_failed ($$$$$)
+{
+       my ($git_dir, $ref, $old, $new, $msg) = @_;
+
+       $git_dir =~ m,/([^/]+)$,;
+       my $repo_name = $1;
+       $ref =~ s,^refs/(heads|tags)/,,;
+
+       my %authors;
+       my $shortlog;
+       my $revstr;
+       {
+               local $ENV{GIT_DIR} = $git_dir;
+               my @revs = ($new);
+               push @revs, '--not', @$old if @$old;
+               open LOG,'-|','git','rev-list','--pretty=raw',@revs;
+               while (<LOG>) {
+                       if (s/^(author|committer) //) {
+                               chomp;
+                               s/>.*$/>/;
+                               $authors{$_} = 1 unless nocc_author $_;
+                       }
+               }
+               close LOG;
+               open LOG,'-|','git','shortlog',@revs;
+               $shortlog .= $_ while <LOG>;
+               close LOG;
+               $revstr = join(' ', @revs);
+       }
+
+       my @to = sort keys %authors;
+       unless (@to) {
+               print STDERR "error: No authors in $revstr\n";
+               return;
+       }
+
+       my $subject = "[$repo_name] $ref : Build Failed";
+       my $body = <<EOF;
+Project: $git_dir
+Branch:  $ref
+Commits: $revstr
+
+$shortlog
+Build Output:
+--------------------------------------------------------------
+$msg
+EOF
+       send_email($subject, $body, \@to);
+}
+
+sub run_build ($$$$)
+{
+       my ($git_dir, $ref, $old, $new) = @_;
+
+       if ($debug_flag) {
+               my @revs = ($new);
+               push @revs, '--not', @$old if @$old;
+               print "BUILDING $git_dir\n";
+               print "  BRANCH: $ref\n";
+               print "  COMMITS: ", join(' ', @revs), "\n";
+       }
+
+       local(*R, *W);
+       pipe R, W or die "cannot pipe builder: $!";
+
+       my $builder = fork();
+       if (!defined $builder) {
+               die "cannot fork builder: $!";
+       } elsif (0 == $builder) {
+               close R;
+               close STDIN;open(STDIN, '/dev/null');
+               open(STDOUT, '>&W');
+               open(STDERR, '>&W');
+               exit do_build $git_dir, $new;
+       } else {
+               close W;
+               my $out = '';
+               $out .= $_ while <R>;
+               close R;
+               waitpid $builder, 0;
+               build_failed $git_dir, $ref, $old, $new, $out if $?;
+       }
+
+       print "DONE\n\n" if $debug_flag;
+}
+
+sub daemon_loop ()
+{
+       my $run = 1;
+       my $stop_sub = sub {$run = 0};
+       $SIG{HUP} = $stop_sub;
+       $SIG{INT} = $stop_sub;
+       $SIG{TERM} = $stop_sub;
+
+       mkdir $tmpdir, 0755;
+       my $pidfile = File::Spec->catfile($tmpdir, "cidaemon.pid");
+       open(O, ">$pidfile"); print O "$$\n"; close O;
+
+       while ($run) {
+               my $ent = pop_queue;
+               if ($ent) {
+                       my ($git_dir, $ref, $old, $new) = @$ent;
+
+                       $ent = $recent{$git_dir};
+                       $recent{$git_dir} = $ent = [[], {}] unless $ent;
+                       my ($rec_arr, $rec_hash) = @$ent;
+                       next if $rec_hash->{$new}++;
+                       while (@$rec_arr >= $recent_size) {
+                               my $to_kill = shift @$rec_arr;
+                               delete $rec_hash->{$to_kill};
+                       }
+                       push @$rec_arr, $new;
+
+                       run_build $git_dir, $ref, $old, $new;
+               } else {
+                       sleep $scan_delay;
+               }
+       }
+
+       unlink $pidfile;
+}
+
+$debug_flag = 0;
+GetOptions(
+       'debug|d' => \$debug_flag,
+       'smtp-user=s' => \$ex_user,
+) or die "usage: $0 [--debug] [--smtp-user=user]\n";
+
+$ex_pass = input_noecho("$ex_user SMTP password: ")
+       if ($ex_user && !$ex_pass);
+
+if ($debug_flag) {
+       daemon_loop;
+       exit 0;
+}
+
+my $daemon = fork();
+if (!defined $daemon) {
+       die "cannot fork daemon: $!";
+} elsif (0 == $daemon) {
+       close STDIN;open(STDIN, '/dev/null');
+       close STDOUT;open(STDOUT, '>/dev/null');
+       close STDERR;open(STDERR, '>/dev/null');
+       daemon_loop;
+       exit 0;
+} else {
+       print "Daemon $daemon running in the background.\n";
+}
diff --git a/contrib/continuous/post-receive-cinotify b/contrib/continuous/post-receive-cinotify
new file mode 100644 (file)
index 0000000..b8f5a60
--- /dev/null
@@ -0,0 +1,104 @@
+#!/usr/bin/perl
+#
+# A hook that notifies its companion cidaemon through a simple
+# queue file that a ref has been updated via a push (actually
+# by a receive-pack running on the server).
+#
+# See cidaemon for per-repository configuration details.
+#
+# To use this hook, add it as the post-receive hook, make it
+# executable, and set its configuration options.
+#
+
+local $ENV{PATH} = '/opt/git/bin';
+
+use strict;
+use warnings;
+use File::Spec;
+use Storable qw(retrieve nstore);
+use Fcntl ':flock';
+
+my $git_dir = File::Spec->rel2abs($ENV{GIT_DIR});
+my $queue_name = `git config --get builder.queue`;chop $queue_name;
+$queue_name =~ m,^([^\s]+)$,; $queue_name = $1; # untaint
+unless ($queue_name) {
+       1 while <STDIN>;
+       print STDERR "\nerror: builder.queue not set.  Not enqueing.\n\n";
+       exit;
+}
+my $queue_lock = "$queue_name.lock";
+
+my @skip;
+open S, "git config --get-all builder.skip|";
+while (<S>) {
+       chop;
+       push @skip, $_;
+}
+close S;
+
+my @new_branch_base;
+open S, "git config --get-all builder.newBranchBase|";
+while (<S>) {
+       chop;
+       push @new_branch_base, $_;
+}
+close S;
+
+sub skip ($)
+{
+       local $_ = shift;
+       foreach my $p (@skip) {
+               return 1 if /^$p/;
+       }
+       0;
+}
+
+open LOCK, ">$queue_lock" or die "Can't open $queue_lock: $!";
+flock LOCK, LOCK_EX;
+
+my $queue = -f $queue_name ? retrieve $queue_name : [];
+my %existing;
+foreach my $r (@$queue) {
+       my ($gd, $ref) = @$r;
+       $existing{$gd}{$ref} = $r;
+}
+
+my @new_branch_commits;
+my $loaded_new_branch_commits = 0;
+
+while (<STDIN>) {
+       chop;
+       my ($old, $new, $ref) = split / /, $_, 3;
+
+       next if $old eq $new;
+       next if $new =~ /^0{40}$/;
+       next if skip $ref;
+
+       my $r = $existing{$git_dir}{$ref};
+       if ($r) {
+               $r->[3] = $new;
+       } else {
+               if ($old =~ /^0{40}$/) {
+                       if (!$loaded_new_branch_commits && @new_branch_base) {
+                               open M,'-|','git','show-ref',@new_branch_base;
+                               while (<M>) {
+                                       ($_) = split / /, $_;
+                                       push @new_branch_commits, $_;
+                               }
+                               close M;
+                               $loaded_new_branch_commits = 1;
+                       }
+                       $old = [@new_branch_commits];
+               } else {
+                       $old = [$old];
+               }
+
+               $r = [$git_dir, $ref, $old, $new];
+               $existing{$git_dir}{$ref} = $r;
+               push @$queue, $r;
+       }
+}
+nstore $queue, $queue_name;
+
+flock LOCK, LOCK_UN;
+close LOCK;
index db87a37895f952a4e9b90de4b426e5c2ca7c0c0d..5f22dec5f71bfe2cb983dd9eabbb1b848b252eba 100644 (file)
@@ -1,6 +1,6 @@
 ;;; git.el --- A user interface for git
 
-;; Copyright (C) 2005, 2006 Alexandre Julliard <julliard@winehq.org>
+;; Copyright (C) 2005, 2006, 2007 Alexandre Julliard <julliard@winehq.org>
 
 ;; Version: 1.0
 
@@ -213,6 +213,23 @@ and returns the process output as a string."
     (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))
   (message "Running git %s...done" (car args)))
 
+(defun git-run-hook (hook env &rest args)
+  "Run a git hook and display its output if any."
+  (let ((dir default-directory)
+        (hook-name (expand-file-name (concat ".git/hooks/" hook))))
+    (or (not (file-executable-p hook-name))
+        (let (status (buffer (get-buffer-create "*Git Hook Output*")))
+          (with-current-buffer buffer
+            (erase-buffer)
+            (cd dir)
+            (setq status
+                  (if env
+                      (apply #'call-process "env" nil (list buffer t) nil
+                             (append (git-get-env-strings env) (list hook-name) args))
+                    (apply #'call-process hook-name nil (list buffer t) nil args))))
+          (display-message-or-buffer buffer)
+          (eq 0 status)))))
+
 (defun git-get-string-sha1 (string)
   "Read a SHA1 from the specified string."
   (and string
@@ -590,6 +607,20 @@ and returns the process output as a string."
     (when modified
       (apply #'git-run-command nil env "update-index" "--" (git-get-filenames modified)))))
 
+(defun git-run-pre-commit-hook ()
+  "Run the pre-commit hook if any."
+  (unless git-status (error "Not in git-status buffer."))
+  (let ((files (git-marked-files-state 'added 'deleted 'modified)))
+    (or (not files)
+        (not (file-executable-p ".git/hooks/pre-commit"))
+        (let ((index-file (make-temp-file "gitidx")))
+          (unwind-protect
+            (let ((head-tree (unless (git-empty-db-p) (git-rev-parse "HEAD^{tree}"))))
+              (git-read-tree head-tree index-file)
+              (git-update-index index-file files)
+              (git-run-hook "pre-commit" `(("GIT_INDEX_FILE" . ,index-file))))
+          (delete-file index-file))))))
+
 (defun git-do-commit ()
   "Perform the actual commit using the current buffer as log message."
   (interactive)
@@ -622,7 +653,8 @@ and returns the process output as a string."
                               (git-run-command nil nil "rerere"))
                             (git-refresh-files)
                             (git-refresh-ewoc-hf git-status)
-                            (message "Committed %s." commit))
+                            (message "Committed %s." commit)
+                            (git-run-hook "post-commit" nil))
                         (message "Commit aborted."))))
                 (message "No files to commit.")))
           (delete-file index-file))))))
@@ -944,28 +976,29 @@ and returns the process output as a string."
   "Commit the marked file(s), asking for a commit message."
   (interactive)
   (unless git-status (error "Not in git-status buffer."))
-  (let ((buffer (get-buffer-create "*git-commit*"))
-        (coding-system (git-get-commits-coding-system))
-        author-name author-email subject date)
-    (when (eq 0 (buffer-size buffer))
-      (when (file-readable-p ".dotest/info")
-        (with-temp-buffer
-          (insert-file-contents ".dotest/info")
-          (goto-char (point-min))
-          (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t)
-            (setq author-name (match-string 1))
-            (setq author-email (match-string 2)))
-          (goto-char (point-min))
-          (when (re-search-forward "^Subject: \\(.*\\)$" nil t)
-            (setq subject (match-string 1)))
-          (goto-char (point-min))
-          (when (re-search-forward "^Date: \\(.*\\)$" nil t)
-            (setq date (match-string 1)))))
-      (git-setup-log-buffer buffer author-name author-email subject date))
-    (log-edit #'git-do-commit nil #'git-log-edit-files buffer)
-    (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))
-    (setq buffer-file-coding-system coding-system)
-    (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t)))
+  (when (git-run-pre-commit-hook)
+    (let ((buffer (get-buffer-create "*git-commit*"))
+          (coding-system (git-get-commits-coding-system))
+          author-name author-email subject date)
+      (when (eq 0 (buffer-size buffer))
+        (when (file-readable-p ".dotest/info")
+          (with-temp-buffer
+            (insert-file-contents ".dotest/info")
+            (goto-char (point-min))
+            (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t)
+              (setq author-name (match-string 1))
+              (setq author-email (match-string 2)))
+            (goto-char (point-min))
+            (when (re-search-forward "^Subject: \\(.*\\)$" nil t)
+              (setq subject (match-string 1)))
+            (goto-char (point-min))
+            (when (re-search-forward "^Date: \\(.*\\)$" nil t)
+              (setq date (match-string 1)))))
+        (git-setup-log-buffer buffer author-name author-email subject date))
+      (log-edit #'git-do-commit nil #'git-log-edit-files buffer)
+      (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))
+      (setq buffer-file-coding-system coding-system)
+      (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))))
 
 (defun git-find-file ()
   "Visit the current file in its own buffer."
diff --git a/contrib/examples/git-gc.sh b/contrib/examples/git-gc.sh
new file mode 100755 (executable)
index 0000000..436d7ca
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# Copyright (c) 2006, Shawn O. Pearce
+#
+# Cleanup unreachable files and optimize the repository.
+
+USAGE='[--prune]'
+SUBDIRECTORY_OK=Yes
+. git-sh-setup
+
+no_prune=:
+while case $# in 0) break ;; esac
+do
+       case "$1" in
+       --prune)
+               no_prune=
+               ;;
+       --)
+               usage
+               ;;
+       esac
+       shift
+done
+
+case "$(git config --get gc.packrefs)" in
+notbare|"")
+       test $(is_bare_repository) = true || pack_refs=true;;
+*)
+       pack_refs=$(git config --bool --get gc.packrefs)
+esac
+
+test "true" != "$pack_refs" ||
+git-pack-refs --prune &&
+git-reflog expire --all &&
+git-repack -a -d -l &&
+$no_prune git-prune &&
+git-rerere gc || exit
index 6abb981534bf032d50e28eb3a14033c1b6546762..5c5b05bfe32bc90484b5bd6a9c171e0f9b04fbd6 100644 (file)
@@ -170,8 +170,10 @@ static int handle_diff_files_args(struct rev_info *revs,
                else if (!strcmp(argv[1], "--theirs"))
                        revs->max_count = 3;
                else if (!strcmp(argv[1], "-n") ||
-                               !strcmp(argv[1], "--no-index"))
+                               !strcmp(argv[1], "--no-index")) {
                        revs->max_count = -2;
+                       revs->diffopt.exit_with_status = 1;
+               }
                else if (!strcmp(argv[1], "-q"))
                        *silent = 1;
                else
@@ -237,6 +239,7 @@ int setup_diff_no_index(struct rev_info *revs,
                        break;
                } else if (i < argc - 3 && !strcmp(argv[i], "--no-index")) {
                        i = argc - 3;
+                       revs->diffopt.exit_with_status = 1;
                        break;
                }
        if (argc != i + 2 || (!is_outside_repo(argv[i + 1], nongit, prefix) &&
@@ -321,6 +324,9 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
                struct cache_entry *ce = active_cache[i];
                int changed;
 
+               if (revs->diffopt.quiet && revs->diffopt.has_changes)
+                       break;
+
                if (!ce_path_match(ce, revs->prune_data))
                        continue;
 
@@ -562,6 +568,9 @@ static int diff_cache(struct rev_info *revs,
                struct cache_entry *ce = *ac;
                int same = (entries > 1) && ce_same_name(ce, ac[1]);
 
+               if (revs->diffopt.quiet && revs->diffopt.has_changes)
+                       break;
+
                if (!ce_path_match(ce, pathspec))
                        goto skip_entry;
 
diff --git a/diff.c b/diff.c
index 954ca83e0b0c95f55c0287d2deeb4cab76153fe0..d8f9242ea8fe2ee92884623939a85b6587a3ad9d 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1958,6 +1958,23 @@ int diff_setup_done(struct diff_options *options)
        if (options->abbrev <= 0 || 40 < options->abbrev)
                options->abbrev = 40; /* full */
 
+       /*
+        * It does not make sense to show the first hit we happened
+        * to have found.  It does not make sense not to return with
+        * exit code in such a case either.
+        */
+       if (options->quiet) {
+               options->output_format = DIFF_FORMAT_NO_OUTPUT;
+               options->exit_with_status = 1;
+       }
+
+       /*
+        * If we postprocess in diffcore, we cannot simply return
+        * upon the first hit.  We need to run diff as usual.
+        */
+       if (options->pickaxe || options->filter)
+               options->quiet = 0;
+
        return 0;
 }
 
@@ -2134,6 +2151,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->color_diff = options->color_diff_words = 1;
        else if (!strcmp(arg, "--no-renames"))
                options->detect_rename = 0;
+       else if (!strcmp(arg, "--exit-code"))
+               options->exit_with_status = 1;
+       else if (!strcmp(arg, "--quiet"))
+               options->quiet = 1;
        else
                return 0;
        return 1;
@@ -2898,6 +2919,8 @@ static void diffcore_apply_filter(const char *filter)
 
 void diffcore_std(struct diff_options *options)
 {
+       if (options->quiet)
+               return;
        if (options->break_opt != -1)
                diffcore_break(options->break_opt);
        if (options->detect_rename)
@@ -2910,18 +2933,11 @@ void diffcore_std(struct diff_options *options)
                diffcore_order(options->orderfile);
        diff_resolve_rename_copy();
        diffcore_apply_filter(options->filter);
-}
 
-
-void diffcore_std_no_resolve(struct diff_options *options)
-{
-       if (options->pickaxe)
-               diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
-       if (options->orderfile)
-               diffcore_order(options->orderfile);
-       diffcore_apply_filter(options->filter);
+       options->has_changes = !!diff_queued_diff.nr;
 }
 
+
 void diff_addremove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
@@ -2957,6 +2973,7 @@ void diff_addremove(struct diff_options *options,
                fill_filespec(two, sha1, mode);
 
        diff_queue(&diff_queued_diff, one, two);
+       options->has_changes = 1;
 }
 
 void diff_change(struct diff_options *options,
@@ -2982,6 +2999,7 @@ void diff_change(struct diff_options *options,
        fill_filespec(two, new_sha1, new_mode);
 
        diff_queue(&diff_queued_diff, one, two);
+       options->has_changes = 1;
 }
 
 void diff_unmerge(struct diff_options *options,
diff --git a/diff.h b/diff.h
index 4b435e8b1933222da02f9e4cf2c28d2fef44d292..a0d2ce13994c1a8751bf7b207671e95c5bc5db97 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -56,7 +56,10 @@ struct diff_options {
                 silent_on_remove:1,
                 find_copies_harder:1,
                 color_diff:1,
-                color_diff_words:1;
+                color_diff_words:1,
+                has_changes:1,
+                quiet:1,
+                exit_with_status:1;
        int context;
        int break_opt;
        int detect_rename;
@@ -170,8 +173,6 @@ extern int diff_setup_done(struct diff_options *);
 
 extern void diffcore_std(struct diff_options *);
 
-extern void diffcore_std_no_resolve(struct diff_options *);
-
 #define COMMON_DIFF_OPTIONS_HELP \
 "\ncommon diff options:\n" \
 "  -z            output diff-raw with lines terminated with NUL.\n" \
index 0151ad07227d20a7f8d4a5c390d77b2aa85ef739..22316597df60648c850001e068938eaf39d57f6d 100644 (file)
@@ -20,13 +20,14 @@ int is_bare_repository_cfg = -1; /* unspecified */
 int log_all_ref_updates = -1; /* unspecified */
 int warn_ambiguous_refs = 1;
 int repository_format_version;
-char *git_commit_encoding;
+const char *git_commit_encoding;
 const char *git_log_output_encoding;
 int shared_repository = PERM_UMASK;
 const char *apply_default_whitespace;
 int zlib_compression_level = Z_DEFAULT_COMPRESSION;
 size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
 size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
+size_t delta_base_cache_limit = 16 * 1024 * 1024;
 int pager_in_use;
 int pager_use_color = 1;
 int auto_crlf = 0;     /* 1: both ways, -1: only when adding git objects */
index 55ffae4fa61ee3a476acddab3f11714535134f7d..bea12151c29af7f4242baaf80e12fd26eb550a45 100644 (file)
@@ -630,7 +630,7 @@ static void start_packfile(void)
        int pack_fd;
 
        snprintf(tmpfile, sizeof(tmpfile),
-               "%s/pack_XXXXXX", get_object_directory());
+               "%s/tmp_pack_XXXXXX", get_object_directory());
        pack_fd = mkstemp(tmpfile);
        if (pack_fd < 0)
                die("Can't create %s: %s", tmpfile, strerror(errno));
@@ -730,7 +730,7 @@ static char *create_index(void)
        }
 
        snprintf(tmpfile, sizeof(tmpfile),
-               "%s/index_XXXXXX", get_object_directory());
+               "%s/tmp_idx_XXXXXX", get_object_directory());
        idx_fd = mkstemp(tmpfile);
        if (idx_fd < 0)
                die("Can't create %s: %s", tmpfile, strerror(errno));
diff --git a/fetch.c b/fetch.c
index f69be82f10d287d71f6184c4b9203bdab3ce81fc..8e29d313f817981a29ebb65f2f579c9f1bd8e9b6 100644 (file)
--- a/fetch.c
+++ b/fetch.c
@@ -42,8 +42,7 @@ static int process_tree(struct tree *tree)
        if (parse_tree(tree))
                return -1;
 
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
+       init_tree_desc(&desc, tree->buffer, tree->size);
        while (tree_entry(&desc, &entry)) {
                struct object *obj = NULL;
 
index 88af8dd256ef871e8a35ec47df925db9b13b95f7..e69ecbfdb1a817b477aff8618f284a4c921e00e5 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -408,12 +408,10 @@ do
                # trust what the user has in the index file and the
                # working tree.
                resolved=
-               changed="$(git-diff-index --cached --name-only HEAD)"
-               if test '' = "$changed"
-               then
+               git-diff-index --quiet --cached HEAD && {
                        echo "No changes - did you forget to use 'git add'?"
                        stop_here_user_resolve $this
-               fi
+               }
                unmerged=$(git-ls-files -u)
                if test -n "$unmerged"
                then
@@ -435,13 +433,11 @@ do
                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 HEAD)"
-                   if test '' = "$changed"
-                   then
-                           echo No changes -- Patch already applied.
-                           go_next
-                           continue
-                   fi
+                   git-diff-index --quiet --cached HEAD && {
+                       echo No changes -- Patch already applied.
+                       go_next
+                       continue
+                   }
                    # clear apply_status -- we have successfully merged.
                    apply_status=0
                fi
index 2cbdc7eb3cc1c9843bd8877b39a194e677272260..3efd6a746407bdc38ba9d251332427c508e5ee40 100755 (executable)
@@ -77,9 +77,9 @@ do
     *)
            git-mailinfo $keep_subject $utf8 \
                .dotest/msg .dotest/patch <$i >.dotest/info || exit 1
-           test -s $dotest/patch || {
+           test -s .dotest/patch || {
                echo "Patch is empty.  Was is split wrong?"
-               stop_here $this
+               exit 1
            }
            git-stripspace < .dotest/msg > .dotest/msg-clean
            ;;
index b1c3a6b1c1af9815db473595f4eb3e9593425f10..936b4a4b835e51410ccfbb37f4205accbade4422 100755 (executable)
@@ -1,14 +1,15 @@
 #!/bin/sh
 
-USAGE='[start|bad|good|next|reset|visualize|replay|log]'
+USAGE='[start|bad|good|next|reset|visualize|replay|log|run]'
 LONG_USAGE='git bisect start [<pathspec>]      reset bisect state and start bisection.
 git bisect bad [<rev>]         mark <rev> a known-bad revision.
 git bisect good [<rev>...]     mark <rev>... known-good revisions.
 git bisect next                        find next bisection to test and check it out.
 git bisect reset [<branch>]    finish bisection search and go back to branch.
 git bisect visualize            show bisect status in gitk.
-git bisect replay <logfile>    replay bisection log
-git bisect log                 show bisect log.'
+git bisect replay <logfile>    replay bisection log.
+git bisect log                 show bisect log.
+git bisect run <cmd>...        use <cmd>... to automatically bisect.'
 
 . git-sh-setup
 require_work_tree
@@ -49,7 +50,7 @@ bisect_start() {
        head=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD) ||
        die "Bad HEAD - I need a symbolic ref"
        case "$head" in
-       refs/heads/bisect*)
+       refs/heads/bisect)
                if [ -s "$GIT_DIR/head-name" ]; then
                    branch=`cat "$GIT_DIR/head-name"`
                else
@@ -85,7 +86,7 @@ bisect_bad() {
        0)
                rev=$(git-rev-parse --verify HEAD) ;;
        1)
-               rev=$(git-rev-parse --verify "$1") ;;
+               rev=$(git-rev-parse --verify "$1^{commit}") ;;
        *)
                usage ;;
        esac || exit
@@ -104,7 +105,7 @@ bisect_good() {
        esac
        for rev in $revs
        do
-               rev=$(git-rev-parse --verify "$rev") || exit
+               rev=$(git-rev-parse --verify "$rev^{commit}") || exit
                echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
                echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
                echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
@@ -140,7 +141,7 @@ bisect_next() {
        bad=$(git-rev-parse --verify refs/bisect/bad) &&
        good=$(git-rev-parse --sq --revs-only --not \
                $(cd "$GIT_DIR" && ls refs/bisect/good-*)) &&
-       rev=$(eval "git-rev-list --bisect $good $bad -- $(cat $GIT_DIR/BISECT_NAMES)") || exit
+       rev=$(eval "git-rev-list --bisect $good $bad -- $(cat "$GIT_DIR/BISECT_NAMES")") || exit
        if [ -z "$rev" ]; then
            echo "$bad was both good and bad"
            exit 1
@@ -185,6 +186,7 @@ bisect_reset() {
                rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
                rm -f "$GIT_DIR/BISECT_LOG"
                rm -f "$GIT_DIR/BISECT_NAMES"
+               rm -f "$GIT_DIR/BISECT_RUN"
        fi
 }
 
@@ -220,6 +222,50 @@ bisect_replay () {
        bisect_auto_next
 }
 
+bisect_run () {
+    while true
+    do
+      echo "running $@"
+      "$@"
+      res=$?
+
+      # Check for really bad run error.
+      if [ $res -lt 0 -o $res -ge 128 ]; then
+         echo >&2 "bisect run failed:"
+         echo >&2 "exit code $res from '$@' is < 0 or >= 128"
+         exit $res
+      fi
+
+      # Use "bisect_good" or "bisect_bad"
+      # depending on run success or failure.
+      if [ $res -gt 0 ]; then
+         next_bisect='bisect_bad'
+      else
+         next_bisect='bisect_good'
+      fi
+
+      # We have to use a subshell because bisect_good or
+      # bisect_bad functions can exit.
+      ( $next_bisect > "$GIT_DIR/BISECT_RUN" )
+      res=$?
+
+      cat "$GIT_DIR/BISECT_RUN"
+
+      if [ $res -ne 0 ]; then
+         echo >&2 "bisect run failed:"
+         echo >&2 "$next_bisect exited with error code $res"
+         exit $res
+      fi
+
+      if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
+         echo "bisect run success"
+         exit 0;
+      fi
+
+    done
+}
+
+
 case "$#" in
 0)
     usage ;;
@@ -244,6 +290,8 @@ case "$#" in
        bisect_replay "$@" ;;
     log)
        cat "$GIT_DIR/BISECT_LOG" ;;
+    run)
+        bisect_run "$@" ;;
     *)
         usage ;;
     esac
index fcadf200ee990f195ce3f5c72306cbf0206277cd..a7390e808c76dd5c8dab04396974ee5a709497fd 100755 (executable)
@@ -89,7 +89,7 @@ while [ "$#" != "0" ]; do
     esac
 done
 
-case "$new_branch,$track" in
+case "$newbranch,$track" in
 ,--*)
        die "git checkout: --track and --no-track require -b"
 esac
@@ -163,6 +163,13 @@ cd_to_toplevel
 detached=
 detach_warn=
 
+describe_detached_head () {
+       test -n "$quiet" || {
+               printf >&2 "$1 "
+               GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2"
+       }
+}
+
 if test -z "$branch$newbranch" && test "$new" != "$old"
 then
        detached="$new"
@@ -173,9 +180,9 @@ If you want to create a new branch from this checkout, you may do so
 (now or later) by using -b with the checkout command again. Example:
   git checkout -b <new_branch_name>"
        fi
-elif test -z "$oldbranch" && test -z "$quiet"
+elif test -z "$oldbranch"
 then
-       echo >&2 "Previous HEAD position was $old"
+       describe_detached_head 'Previous HEAD position was' "$old"
 fi
 
 if [ "X$old" = X ]
@@ -250,8 +257,13 @@ if [ "$?" -eq 0 ]; then
        if test -n "$branch"
        then
                GIT_DIR="$GIT_DIR" git-symbolic-ref -m "checkout: moving to $branch" HEAD "refs/heads/$branch"
-               if test -z "$quiet"
+               if test -n "$quiet"
+               then
+                       true    # nothing
+               elif test "refs/heads/$branch" = "$oldbranch"
                then
+                       echo >&2 "Already on branch \"$branch\""
+               else
                        echo >&2 "Switched to${newbranch:+ a new} branch \"$branch\""
                fi
        elif test -n "$detached"
@@ -270,6 +282,7 @@ if [ "$?" -eq 0 ]; then
                then
                        echo >&2 "$detach_warn"
                fi
+               describe_detached_head 'HEAD is now at' HEAD
        fi
        rm -f "$GIT_DIR/MERGE_HEAD"
 else
index de51983584bb0fd015ed75704b72bec8fdb55430..6ba477d1f4ee16c20e3fee7dd881f1645bcb80f1 100755 (executable)
@@ -42,6 +42,7 @@ clone_dumb_http () {
        http_fetch "$1/info/refs" "$clone_tmp/refs" ||
                die "Cannot get remote repository information.
 Perhaps git-update-server-info needs to be run there?"
+       test "z$quiet" = z && v=-v || v=
        while read sha1 refname
        do
                name=`expr "z$refname" : 'zrefs/\(.*\)'` &&
@@ -59,7 +60,7 @@ Perhaps git-update-server-info needs to be run there?"
                else
                        tname=$name
                fi
-               git-http-fetch -v -a -w "$tname" "$name" "$1/" || exit 1
+               git-http-fetch $v -a -w "$tname" "$name" "$1/" || exit 1
        done <"$clone_tmp/refs"
        rm -fr "$clone_tmp"
        http_fetch "$1/HEAD" "$GIT_DIR/REMOTE_HEAD" ||
index 65fcc840497fb44fd0bd843b6eedaab258d8b88f..68aa75255e21ff8231fe2f54e5772f8cbb026fce 100755 (executable)
@@ -947,6 +947,7 @@ sub req_update
 
             # we need to merge with the local changes ( M=successful merge, C=conflict merge )
             $log->info("Merging $file_local, $file_old, $file_new");
+            print "M Merging differences between 1.$oldmeta->{revision} and 1.$meta->{revision} into $filename\n";
 
             $log->debug("Temporary directory for merge is $dir");
 
@@ -973,6 +974,7 @@ sub req_update
             elsif ( $return == 1 )
             {
                 $log->info("Merged with conflicts");
+                print "E cvs update: conflicts found in $filename\n";
                 print "M C $filename\n";
 
                 # Don't want to actually _DO_ the update if -n specified
@@ -1067,6 +1069,7 @@ sub req_ci
     $log->info("Created index '$file_index' with for head $state->{module} - exit status $?");
 
     my @committedfiles = ();
+    my %oldmeta;
 
     # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
@@ -1077,6 +1080,7 @@ sub req_ci
         next unless ( exists $state->{entries}{$filename}{modified_filename} or not $state->{entries}{$filename}{unchanged} );
 
         my $meta = $updater->getmeta($filename);
+       $oldmeta{$filename} = $meta;
 
         my $wrev = revparse($filename);
 
@@ -1205,11 +1209,18 @@ sub req_ci
 
         $log->debug("Checked-in $dirpart : $filename");
 
+       print "M $state->{CVSROOT}/$state->{module}/$filename,v  <--  $dirpart$filepart\n";
         if ( defined $meta->{filehash} && $meta->{filehash} eq "deleted" )
         {
+            print "M new revision: delete; previous revision: 1.$oldmeta{$filename}{revision}\n";
             print "Remove-entry $dirpart\n";
             print "$filename\n";
         } else {
+            if ($meta->{revision} == 1) {
+               print "M initial revision: 1.1\n";
+            } else {
+               print "M new revision: 1.$meta->{revision}; previous revision: 1.$oldmeta{$filename}{revision}\n";
+            }
             print "Checked-in $dirpart\n";
             print "$filename\n";
             my $kopts = kopts_from_path($filepart);
@@ -1295,7 +1306,7 @@ sub req_status
         }
         if ( defined($meta->{revision}) )
         {
-            print "M Repository revision:\t1." . $meta->{revision} . "\t$state->{repository}/$filename,v\n";
+            print "M Repository revision:\t1." . $meta->{revision} . "\t$state->{CVSROOT}/$state->{module}/$filename,v\n";
             print "M Sticky Tag:\t\t(none)\n";
             print "M Sticky Date:\t\t(none)\n";
             print "M Sticky Options:\t\t(none)\n";
index 9d45dd266a8110a2d39fe29ffeb84aacb6053623..93349330d45ad43c5c7e146a942d76b78c0e2ee9 100755 (executable)
@@ -110,8 +110,8 @@ ls_remote_result=$(git ls-remote $exec "$remote") ||
 
 append_fetch_head () {
        flags=
-       test -n "$verbose" && flags="$flags -v"
-       test -n "$force" && flags="$flags -f"
+       test -n "$verbose" && flags="$flags$LF-v"
+       test -n "$force$single_force" && flags="$flags$LF-f"
        GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \
                git-fetch--tool $flags append-fetch-head "$@"
 }
@@ -157,7 +157,7 @@ then
        fi
 fi
 
-fetch_native () {
+fetch_all_at_once () {
 
   eval=$(echo "$1" | git-fetch--tool parse-reflist "-")
   eval "$eval"
@@ -165,7 +165,9 @@ fetch_native () {
     ( : subshell because we muck with IFS
       IFS="    $LF"
       (
-       if test -f "$remote" ; then
+       if test "$remote" = . ; then
+           git-show-ref $rref || echo failed "$remote"
+       elif test -f "$remote" ; then
            test -n "$shallow_depth" &&
                die "shallow clone with bundle is not supported"
            git-bundle unbundle "$remote" $rref ||
@@ -188,7 +190,7 @@ fetch_native () {
 
 }
 
-fetch_dumb () {
+fetch_per_ref () {
   reflist="$1"
   refs=
   rref=
@@ -292,10 +294,10 @@ fetch_dumb () {
 fetch_main () {
        case "$remote" in
        http://* | https://* | ftp://* | rsync://* )
-               fetch_dumb "$@"
+               fetch_per_ref "$@"
                ;;
        *)
-               fetch_native "$@"
+               fetch_all_at_once "$@"
                ;;
        esac
 }
diff --git a/git-gc.sh b/git-gc.sh
deleted file mode 100755 (executable)
index 436d7ca..0000000
--- a/git-gc.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2006, Shawn O. Pearce
-#
-# Cleanup unreachable files and optimize the repository.
-
-USAGE='[--prune]'
-SUBDIRECTORY_OK=Yes
-. git-sh-setup
-
-no_prune=:
-while case $# in 0) break ;; esac
-do
-       case "$1" in
-       --prune)
-               no_prune=
-               ;;
-       --)
-               usage
-               ;;
-       esac
-       shift
-done
-
-case "$(git config --get gc.packrefs)" in
-notbare|"")
-       test $(is_bare_repository) = true || pack_refs=true;;
-*)
-       pack_refs=$(git config --bool --get gc.packrefs)
-esac
-
-test "true" != "$pack_refs" ||
-git-pack-refs --prune &&
-git-reflog expire --all &&
-git-repack -a -d -l &&
-$no_prune git-prune &&
-git-rerere gc || exit
index 4f3d053889de4a5ba8e6e5d519c014a51220accd..2b6a5c0d104b09b2eb471be9ec86e215ac003b0a 100755 (executable)
@@ -9,6 +9,6 @@
 # because the current index is what we will be committing as the
 # merge result.
 
-test "$(git-diff-index --cached --name-status HEAD)" = "" || exit 2
+git-diff-index --quiet --cached HEAD || exit 2
 
 exit 0
index 6ce62c860a3732e1239e216fb7492a126216be67..fa4589173f426d6172883c47479c52b8700cafa8 100755 (executable)
@@ -108,6 +108,10 @@ merge_name () {
                git-show-ref -q --verify "refs/heads/$truname" 2>/dev/null
        then
                echo "$rh               branch '$truname' (early part) of ."
+       elif test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD"
+       then
+               sed -e 's/      not-for-merge   /               /' -e 1q \
+                       "$GIT_DIR/FETCH_HEAD"
        else
                echo "$rh               commit '$remote'"
        fi
@@ -292,13 +296,13 @@ f,*)
        # Again the most common case of merging one remote.
        echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $1)"
        git-update-index --refresh 2>/dev/null
-       new_head=$(git-rev-parse --verify "$1^0") &&
-       git-read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" &&
        msg="Fast forward"
        if test -n "$have_message"
        then
                msg="$msg (no commit created; -m option ignored)"
        fi
+       new_head=$(git-rev-parse --verify "$1^0") &&
+       git-read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" &&
        finish "$new_head" "$msg" || exit
        dropsave
        exit 0
index 52386a5443ac19f1d8cbf7dc32c1237b2665b861..7942fd0b6490760b09248b961b38adb89ede1adb 100755 (executable)
@@ -185,9 +185,9 @@ merge_file () {
                mv -- "$BACKUP" "$path.orig"
            fi
            ;;
-       meld)
+       meld|vimdiff)
            touch "$BACKUP"
-           meld -- "$LOCAL" "$path" "$REMOTE"
+           $merge_tool -- "$LOCAL" "$path" "$REMOTE"
            if test "$path" -nt "$BACKUP" ; then
                status=0;
            else
@@ -288,10 +288,15 @@ done
 
 if test -z "$merge_tool"; then
     merge_tool=`git-config merge.tool`
-    if test $merge_tool = kdiff3 -o $merge_tool = tkdiff -o \
-       $merge_tool = xxdiff -o $merge_tool = meld ; then
-       unset merge_tool
-    fi
+    case "$merge_tool" in
+       kdiff3 | tkdiff | xxdiff | meld | emerge | vimdiff)
+           ;; # happy
+       *)
+           echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
+           echo >&2 "Resetting to default..."
+           unset merge_tool
+           ;;
+    esac
 fi
 
 if test -z "$merge_tool" ; then
@@ -305,6 +310,8 @@ if test -z "$merge_tool" ; then
        merge_tool=meld
     elif type emacs >/dev/null 2>&1; then
        merge_tool=emerge
+    elif type vimdiff >/dev/null 2>&1; then
+       merge_tool=vimdiff
     else
        echo "No available merge resolution programs available."
        exit 1
@@ -312,7 +319,7 @@ if test -z "$merge_tool" ; then
 fi
 
 case "$merge_tool" in
-    kdiff3|tkdiff|meld|xxdiff)
+    kdiff3|tkdiff|meld|xxdiff|vimdiff)
        if ! type "$merge_tool" > /dev/null 2>&1; then
            echo "The merge tool $merge_tool is not available"
            exit 1
index c46131f6d6619aa07ddb72265583b1de0832ff3e..437b0c3b1bc522c4b5a4a3a656b97cb2009c43a0 100755 (executable)
@@ -9,6 +9,9 @@ get_data_source () {
        */*)
                echo ''
                ;;
+       .)
+               echo self
+               ;;
        *)
                if test "$(git-config --get "remote.$1.url")"
                then
@@ -31,6 +34,9 @@ get_remote_url () {
        '')
                echo "$1"
                ;;
+       self)
+               echo "$1"
+               ;;
        config)
                git-config --get "remote.$1.url"
                ;;
@@ -57,7 +63,7 @@ get_default_remote () {
 get_remote_default_refs_for_push () {
        data_source=$(get_data_source "$1")
        case "$data_source" in
-       '' | branches)
+       '' | branches | self)
                ;; # no default push mapping, just send matching refs.
        config)
                git-config --get-all "remote.$1.push" ;;
@@ -163,6 +169,10 @@ get_remote_default_refs_for_fetch () {
        case "$data_source" in
        '')
                echo "HEAD:" ;;
+       self)
+               canon_refs_list_for_fetch -d "$1" \
+                       $(git-for-each-ref --format='%(refname):')
+               ;;
        config)
                canon_refs_list_for_fetch -d "$1" \
                        $(git-config --get-all "remote.$1.fetch") ;;
@@ -177,7 +187,7 @@ get_remote_default_refs_for_fetch () {
                                        }' "$GIT_DIR/remotes/$1")
                ;;
        *)
-               die "internal error: get-remote-default-ref-for-push $1" ;;
+               die "internal error: get-remote-default-ref-for-fetch $1" ;;
        esac
 }
 
index b51d19d12e6d5a108f13c4ec60c5f5a6e560ef47..1d96f32685cdc51c89c63516519e8b544ab818af 100755 (executable)
@@ -59,7 +59,7 @@ continue_merge () {
                die "$RESOLVEMSG"
        fi
 
-       if test -n "`git-diff-index HEAD`"
+       if ! git-diff-index --quiet HEAD
        then
                if ! git-commit -C "`cat $dotest/current`"
                then
@@ -124,13 +124,11 @@ while case "$#" in 0) break ;; esac
 do
        case "$1" in
        --continue)
-               diff=$(git-diff-files)
-               case "$diff" in
-               ?*)     echo "You must edit all merge conflicts and then"
+               git-diff-files --quiet || {
+                       echo "You must edit all merge conflicts and then"
                        echo "mark them as resolved using git update-index"
                        exit 1
-                       ;;
-               esac
+               }
                if test -d "$dotest"
                then
                        prev_head="`cat $dotest/prev_head`"
@@ -265,6 +263,10 @@ upstream_name="$1"
 upstream=`git rev-parse --verify "${upstream_name}^0"` ||
     die "invalid upstream $upstream_name"
 
+# Make sure the branch to rebase onto is valid.
+onto_name=${newbase-"$upstream_name"}
+onto=$(git-rev-parse --verify "${onto_name}^0") || exit
+
 # If a hook exists, give it a chance to interrupt
 if test -x "$GIT_DIR/hooks/pre-rebase"
 then
@@ -291,10 +293,6 @@ case "$#" in
 esac
 branch=$(git-rev-parse --verify "${branch_name}^0") || exit
 
-# Make sure the branch to rebase onto is valid.
-onto_name=${newbase-"$upstream_name"}
-onto=$(git-rev-parse --verify "${onto_name}^0") || exit
-
 # Now we are rebasing commits $upstream..$branch on top of $onto
 
 # Check if we are already based on $onto, but this should be
index bd70bf1ddd663db7d0af49b8513fdf234767e422..52013fe76dba73e19244fadf63f91677ce8e6a40 100755 (executable)
@@ -15,6 +15,10 @@ sub add_remote_config {
                $hash->{$name}{'FETCH'} ||= [];
                push @{$hash->{$name}{'FETCH'}}, $value;
        }
+       elsif ($what eq 'push') {
+               $hash->{$name}{'PUSH'} ||= [];
+               push @{$hash->{$name}{'PUSH'}}, $value;
+       }
        if (!exists $hash->{$name}{'SOURCE'}) {
                $hash->{$name}{'SOURCE'} = 'config';
        }
@@ -44,7 +48,8 @@ sub add_remote_remotes {
                        }
                }
                elsif (/^Push:\s*(.*)$/) {
-                       ; # later
+                       $it->{'PUSH'} ||= [];
+                       push @{$it->{'PUSH'}}, $1;
                }
                elsif (/^Pull:\s*(.*)$/) {
                        $it->{'FETCH'} ||= [];
@@ -250,6 +255,15 @@ sub show_remote {
        if ($info->{'LS_REMOTE'}) {
                show_mapping($name, $info);
        }
+       if ($info->{'PUSH'}) {
+               my @pushed = map {
+                       s|^refs/heads/||;
+                       s|:refs/heads/|:|;
+                       $_;
+               } @{$info->{'PUSH'}};
+               print "  Local branch(es) pushed with 'git push'\n";
+               print "    @pushed\n";
+       }
 }
 
 sub add_remote {
index 6989c0260fcafe29397c89cfcc61a8ef9a49d58e..ae50990d081c3709c0a498422be382b9a632f5ec 100755 (executable)
@@ -65,8 +65,8 @@ sub usage {
                   Defaults to on.
 
    --no-signed-off-cc Suppress the automatic addition of email addresses
-                 that appear in a Signed-off-by: line, to the cc: list.
-                Note: Using this option is not recommended.
+                 that appear in Signed-off-by: or Cc: lines to the cc:
+                 list.  Note: Using this option is not recommended.
 
    --smtp-server  If set, specifies the outgoing SMTP server to use.
                   Defaults to localhost.
@@ -572,8 +572,8 @@ sub send_message
                        }
                } else {
                        $message .=  $_;
-                       if (/^Signed-off-by: (.*)$/i && !$no_signed_off_cc) {
-                               my $c = $1;
+                       if (/^(Signed-off-by|Cc): (.*)$/i && !$no_signed_off_cc) {
+                               my $c = $2;
                                chomp $c;
                                push @cc, $c;
                                printf("(sob) Adding cc: %s from line '%s'\n",
diff --git a/git.c b/git.c
index dde4d07e8fbb830791417d422e4b9204373935e4..ed1c65e3095fbbbeab88976bbae358a9d76ad937 100644 (file)
--- a/git.c
+++ b/git.c
@@ -249,6 +249,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "format-patch", cmd_format_patch, RUN_SETUP },
                { "fsck", cmd_fsck, RUN_SETUP },
                { "fsck-objects", cmd_fsck, RUN_SETUP },
+               { "gc", cmd_gc, RUN_SETUP },
                { "get-tar-commit-id", cmd_get_tar_commit_id },
                { "grep", cmd_grep, RUN_SETUP | USE_PAGER },
                { "help", cmd_help },
diff --git a/gitk b/gitk
index 9ddff3e7f7b011564c56fb619f57bed66d875f7e..db28d745dc005722ff3d7c071aeb37f9fd4fdc21 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -720,6 +720,7 @@ proc makewindow {} {
     bindkey <Key-Return> {findnext 0}
     bindkey ? findprev
     bindkey f nextfile
+    bindkey <F5> updatecommits
     bind . <Control-q> doquit
     bind . <Control-f> dofind
     bind . <Control-g> {findnext 0}
@@ -985,6 +986,7 @@ f           Scroll diff view to next file
 <Ctrl-plus>    Increase font size
 <Ctrl-KP->     Decrease font size
 <Ctrl-minus>   Decrease font size
+<F5>           Update
 } \
            -justify left -bg white -border 2 -relief sunken
     pack $w.m -side top -fill both
@@ -1904,7 +1906,7 @@ proc do_file_hl {serial} {
     } else {
        set gdtargs [list "-S$highlight_files"]
     }
-    set cmd [concat | git-diff-tree -r -s --stdin $gdtargs]
+    set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
     set filehighlight [open $cmd r+]
     fconfigure $filehighlight -blocking 0
     fileevent $filehighlight readable readfhighlight
@@ -1956,7 +1958,7 @@ proc readfhighlight {} {
     }
     if {[eof $filehighlight]} {
        # strange...
-       puts "oops, git-diff-tree died"
+       puts "oops, git diff-tree died"
        catch {close $filehighlight}
        unset filehighlight
     }
diff --git a/gitweb/INSTALL b/gitweb/INSTALL
new file mode 100644 (file)
index 0000000..371407d
--- /dev/null
@@ -0,0 +1,184 @@
+GIT web Interface (gitweb) Installation
+=======================================
+
+First you have to generate gitweb.cgi from gitweb.perl using
+"make gitweb/gitweb.cgi", then copy appropriate files (gitweb.cgi,
+gitweb.css, git-logo.png and git-favicon.png) to their destination.
+For example if git was (or is) installed with /usr prefix, you can do
+
+       $ make prefix=/usr gitweb/gitweb.cgi  ;# as yourself
+       # cp gitweb/git* /var/www/cgi-bin/    ;# as root
+
+Alternatively you can use autoconf generated ./configure script to
+set up path to git binaries (via config.mak.autogen), so you can write
+instead
+
+       $ make configure                     ;# as yourself
+       $ ./configure --prefix=/usr          ;# as yourself
+       $ make gitweb/gitweb.cgi             ;# as yourself
+       # cp gitweb/git* /var/www/cgi-bin/   ;# as root
+
+The above example assumes that your web server is configured to run
+[executable] files in /var/www/cgi-bin/ as server scripts (as CGI
+scripts).
+
+
+Build time configuration
+------------------------
+
+See also "How to configure gitweb for your local system" in README
+file for gitweb (in gitweb/README).
+
+- There are many configuration variables which affects building of
+  gitweb.cgi; see "default configuration for gitweb" section in main
+  (top dir) Makefile, and instructions for building gitweb/gitweb.cgi
+  target.
+
+  One of most important is where to find git wrapper binary. Gitweb
+  tries to find git wrapper at $(bindir)/git, so you have to set $bindir
+  when building gitweb.cgi, or $prefix from which $bindir is derived. If
+  you build and install gitweb together with the rest of git suite,
+  there should be no problems. Otherwise, if git was for example
+  installed from a binary package, you have to set $prefix (or $bindir)
+  accordingly.
+
+- Another important issue is where are git repositories you want to make
+  available to gitweb. By default gitweb search for repositories under
+  /pub/git; if you want to have projects somewhere else, like /home/git,
+  use GITWEB_PROJECTROOT build configuration variable.
+
+  By default all git repositories under projectroot are visible and
+  available to gitweb. List of projects is generated by default by
+  scanning the projectroot directory for git repositories. This can be
+  changed (configured) as described in "Gitweb repositories" section
+  below.
+
+  Note that gitweb deals directly with object database, and does not
+  need working directory; the name of the project is the name of its
+  repository object database, usually projectname.git for bare
+  repositories. If you want to provide gitweb access to non-bare (live)
+  repository, you can make projectname.git symbolic link under
+  projectroot linking to projectname/.git (but it is just
+  a suggestion).
+
+- You can control where gitweb tries to find its main CSS style file,
+  its favicon and logo with GITWEB_CSS, GITWEB_FAVICON and GITWEB_LOGO
+  build configuration variables. By default gitweb tries to find them
+  in the same directory as gitweb.cgi script.
+
+Build example
+~~~~~~~~~~~~~
+
+- To install gitweb to /var/www/cgi-bin/gitweb/ when git wrapper
+  is installed at /usr/local/bin/git and the repositories (projects)
+  we want to display are under /home/local/scm, you can do
+
+       make GITWEB_PROJECTROOT="/home/local/scm" \
+            GITWEB_CSS="/gitweb/gitweb.css" \
+            GITWEB_LOGO="/gitweb/git-logo.png" \
+            GITWEB_FAVICON="/gitweb/git-favicon.png" \
+            bindir=/usr/local/bin \
+            gitweb/gitweb.cgi
+
+       cp -fv ~/git/gitweb/gitweb.{cgi,css} \
+              ~/git/gitweb/git-{favicon,logo}.png \
+            /var/www/cgi-bin/gitweb/
+
+
+Gitweb config file
+------------------
+
+See also "Runtime gitweb configuration" section in README file
+for gitweb (in gitweb/README).
+
+- You can configure gitweb further using gitweb configuration file;
+  by default it is file named gitweb_config.perl in the same place as
+  gitweb.cgi script. You can control default place for config file
+  using GITWEB_CONFIG build configuration variable, and you can set it
+  using GITWEB_CONFIG environmental variable.
+
+- Gitweb config file is [fragment] of perl code. You can set variables
+  using "our $variable = value"; text from "#" character until the end
+  of a line is ignored. See perlsyn(1) for details.
+
+  See the top of gitweb.perl file for examples of customizable options.
+
+
+Gitweb repositories:
+--------------------
+
+- By default all git repositories under projectroot are visible and
+  available to gitweb. List of projects is generated by default by
+  scanning the projectroot directory for git repositories (for object
+  databases to be more exact).
+
+  You can provide pre-generated list of [visible] repositories,
+  together with information about their owners (the project ownership
+  is taken from owner of repository directory otherwise), by setting
+  GITWEB_LIST build configuration variable (or $projects_list variable
+  in gitweb config file) to point to a plain file.
+
+  Each line of projects list file should consist of url-encoded path
+  to project repository database (relative to projectroot) separated
+  by space from url-encoded project owner; spaces in both project path
+  and project owner have to be encoded as either '%20' or '+'.
+
+  You can generate projects list index file using project_index action
+  (the 'TXT' link on projects list page) directly from gitweb.
+
+- By default even if project is not visible on projects list page, you
+  can view it nevertheless by hand-crafting gitweb URL. You can set
+  GITWEB_STRICT_EXPORT build configuration variable (or $strict_export
+  variable in gitweb config file) to only allow viewing of
+  repositories also shown on the overview page.
+
+- Alternatively, you can configure gitweb to only list and allow
+  viewing of the explicitly exported repositories, via
+  GITWEB_EXPORT_OK build configuration variable (or $export_ok
+  variable in gitweb config file). If it evaluates to true, gitweb
+  show repository only if this file exists in its object database
+  (if directory has the magic file $export_ok).
+
+
+Requirements
+------------
+
+ - Core git tools
+ - Perl
+ - Perl modules: CGI, Encode, Fcntl, File::Find, File::Basename.
+ - web server
+
+
+Example web server configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+See also "Webserver configuration" section in README file for gitweb
+(in gitweb/README).
+
+
+- Apache2, gitweb installed as CGI script,
+  under /var/www/cgi-bin/
+
+       ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
+
+       <Directory "/var/www/cgi-bin">
+           Options Indexes FollowSymlinks ExecCGI
+           AllowOverride None
+           Order allow,deny
+           Allow from all
+       </Directory>
+
+- Apache2, gitweb installed as mod_perl legacy script,
+  under /var/www/perl/
+
+       Alias /perl "/var/www/perl"
+
+       <Directory "/var/www/perl">
+           SetHandler perl-script
+           PerlResponseHandler ModPerl::Registry
+           PerlOptions +ParseHeaders
+           Options Indexes FollowSymlinks +ExecCGI
+           AllowOverride None
+           Order allow,deny
+           Allow from all
+       </Directory>
index 7177c6e86b8e8c3dc1e5d0db4bce4122cbe2430a..5e402924040d55aff5fb831a737d108d1e68a0bb 100644 (file)
@@ -107,7 +107,7 @@ span.age {
        font-style: italic;
 }
 
-div.page_body span.signoff {
+span.signoff {
        color: #888888;
 }
 
index 27b5970bcadf21f2f0e294c8c34d9595f6288503..5214050a882650db098f152c5cc032fdcc823232 100755 (executable)
@@ -3719,7 +3719,7 @@ sub git_commit {
                $formats_nav .=
                        '(merge: ' .
                        join(' ', map {
-                               $cgi->a({-href => href(action=>"commitdiff",
+                               $cgi->a({-href => href(action=>"commit",
                                                       hash=>$_)},
                                        esc_html(substr($_, 0, 7)));
                        } @$parents ) .
index cbb02d3bc1eef3d5a088be320a3a8bcb87a4a685..724720c562ab6da2b02e91b69f5365bc7bfee4f4 100644 (file)
@@ -1750,8 +1750,7 @@ static struct object_list **process_tree(struct tree *tree,
        me.elem = name;
        me.elem_len = strlen(name);
 
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
+       init_tree_desc(&desc, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry)) {
                if (S_ISDIR(entry.mode))
index b405864be954941dedc4b0cc0c7f7aefcf65dd2d..6284fe3760465cceccfa7fb1580a8652761f35b6 100644 (file)
@@ -139,7 +139,7 @@ static const char *open_pack_file(const char *pack_name)
                if (!pack_name) {
                        static char tmpfile[PATH_MAX];
                        snprintf(tmpfile, sizeof(tmpfile),
-                                "%s/pack_XXXXXX", get_object_directory());
+                                "%s/tmp_pack_XXXXXX", get_object_directory());
                        output_fd = mkstemp(tmpfile);
                        pack_name = xstrdup(tmpfile);
                } else
@@ -347,26 +347,18 @@ static int find_delta_children(const union delta_base *base,
 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_type; break;
-       case OBJ_TREE:   type_str = tree_type; break;
-       case OBJ_BLOB:   type_str = blob_type; break;
-       case OBJ_TAG:    type_str = tag_type; break;
-       default:
-               die("bad type %d", type);
+       hash_sha1_file(data, size, typename(type), sha1);
+       if (has_sha1_file(sha1)) {
+               void *has_data;
+               enum object_type has_type;
+               unsigned long has_size;
+               has_data = read_sha1_file(sha1, &has_type, &has_size);
+               if (!has_data)
+                       die("cannot read existing object %s", sha1_to_hex(sha1));
+               if (size != has_size || type != has_type ||
+                   memcmp(data, has_data, size) != 0)
+                       die("SHA1 COLLISION FOUND WITH %s !", sha1_to_hex(sha1));
        }
-
-       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 object_entry *delta_obj, void *base_data,
@@ -547,7 +539,7 @@ static int write_compressed(int fd, void *in, unsigned int size)
        return size;
 }
 
-static void append_obj_to_pack(void *buf,
+static void append_obj_to_pack(const unsigned char *sha1, void *buf,
                               unsigned long size, enum object_type type)
 {
        struct object_entry *obj = &objects[nr_objects++];
@@ -565,7 +557,7 @@ static void append_obj_to_pack(void *buf,
        write_or_die(output_fd, header, n);
        obj[1].offset = obj[0].offset + n;
        obj[1].offset += write_compressed(output_fd, buf, size);
-       sha1_object(buf, size, type, obj->sha1);
+       hashcpy(obj->sha1, sha1);
 }
 
 static int delta_pos_compare(const void *_a, const void *_b)
@@ -618,7 +610,9 @@ static void fix_unresolved_deltas(int nr_unresolved)
                                resolve_delta(child, data, size, type);
                }
 
-               append_obj_to_pack(data, size, type);
+               if (check_sha1_signature(d->base.sha1, data, size, typename(type)))
+                       die("local object %s is corrupt", sha1_to_hex(d->base.sha1));
+               append_obj_to_pack(d->base.sha1, data, size, type);
                free(data);
                if (verbose)
                        percent = display_progress(nr_resolved_deltas,
@@ -696,7 +690,7 @@ static const char *write_index_file(const char *index_name, unsigned char *sha1)
        if (!index_name) {
                static char tmpfile[PATH_MAX];
                snprintf(tmpfile, sizeof(tmpfile),
-                        "%s/index_XXXXXX", get_object_directory());
+                        "%s/tmp_idx_XXXXXX", get_object_directory());
                fd = mkstemp(tmpfile);
                index_name = xstrdup(tmpfile);
        } else {
index f1fa21c3978f32882b6aac68c32d33ead6f8f1ff..2ba2c958e0aac63f0d5b092019e71f6905edb052 100644 (file)
@@ -49,8 +49,7 @@ static void process_tree(struct rev_info *revs,
        me.elem = name;
        me.elem_len = strlen(name);
 
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
+       init_tree_desc(&desc, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry)) {
                if (S_ISDIR(entry.mode))
index 7cfe8b3587c10bf0791c066a72482771e7daae77..4b650efa8b19424a61566b7ec87e7808874a6721 100644 (file)
@@ -64,9 +64,9 @@ static int copy_file(const char *source, char *dest, const char *hex,
                }
                /* If we got ENOENT there is no point continuing. */
                if (errno == ENOENT) {
-                       if (warn_if_not_exists)
-                               fprintf(stderr, "does not exist %s\n", source);
-                       return -1;
+                       if (!warn_if_not_exists)
+                               return -1;
+                       return error("does not exist %s", source);
                }
        }
        if (use_symlink) {
@@ -74,9 +74,8 @@ static int copy_file(const char *source, char *dest, const char *hex,
                if (stat(source, &st)) {
                        if (!warn_if_not_exists && errno == ENOENT)
                                return -1;
-                       fprintf(stderr, "cannot stat %s: %s\n", source,
-                               strerror(errno));
-                       return -1;
+                       return error("cannot stat %s: %s", source,
+                                    strerror(errno));
                }
                if (!symlink(source, dest)) {
                        pull_say("symlink %s\n", hex);
@@ -90,25 +89,21 @@ static int copy_file(const char *source, char *dest, const char *hex,
                if (ifd < 0) {
                        if (!warn_if_not_exists && errno == ENOENT)
                                return -1;
-                       fprintf(stderr, "cannot open %s\n", source);
-                       return -1;
+                       return error("cannot open %s", source);
                }
                ofd = open(dest, O_WRONLY | O_CREAT | O_EXCL, 0666);
                if (ofd < 0) {
-                       fprintf(stderr, "cannot open %s\n", dest);
                        close(ifd);
-                       return -1;
+                       return error("cannot open %s", dest);
                }
                status = copy_fd(ifd, ofd);
                close(ofd);
                if (status)
-                       fprintf(stderr, "cannot write %s\n", dest);
-               else
-                       pull_say("copy %s\n", hex);
-               return status;
+                       return error("cannot write %s", dest);
+               pull_say("copy %s\n", hex);
+               return 0;
        }
-       fprintf(stderr, "failed to copy %s with given copy methods.\n", hex);
-       return -1;
+       return error("failed to copy %s with given copy methods.", hex);
 }
 
 static int fetch_pack(const unsigned char *sha1)
@@ -181,13 +176,11 @@ int fetch_ref(char *ref, unsigned char *sha1)
        ifd = open(filename, O_RDONLY);
        if (ifd < 0) {
                close(ifd);
-               fprintf(stderr, "cannot open %s\n", filename);
-               return -1;
+               return error("cannot open %s", filename);
        }
        if (read_in_full(ifd, hex, 40) != 40 || get_sha1_hex(hex, sha1)) {
                close(ifd);
-               fprintf(stderr, "cannot read from %s\n", filename);
-               return -1;
+               return error("cannot read from %s", filename);
        }
        close(ifd);
        pull_say("ref %s\n", sha1_to_hex(sha1));
index 7027d7865971646f178690a150246d9bc4d674c0..5599fd321bb4c09c1c0ce4fc62dfb9ecf5531c2b 100644 (file)
@@ -1,30 +1,17 @@
 #include "cache.h"
+#include "run-command.h"
 
 static const char *pgm;
-static const char *arguments[8];
+static const char *arguments[9];
 static int one_shot, quiet;
 static int err;
 
 static void run_program(void)
 {
-       pid_t pid = fork();
-       int status;
-
-       if (pid < 0)
-               die("unable to fork");
-       if (!pid) {
-               execlp(pgm, arguments[0],
-                           arguments[1],
-                           arguments[2],
-                           arguments[3],
-                           arguments[4],
-                           arguments[5],
-                           arguments[6],
-                           arguments[7],
-                           NULL);
-               die("unable to execute '%s'", pgm);
-       }
-       if (waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) || WEXITSTATUS(status)) {
+       struct child_process child;
+       memset(&child, 0, sizeof(child));
+       child.argv = arguments;
+       if (run_command(&child)) {
                if (one_shot) {
                        err++;
                } else {
@@ -49,6 +36,7 @@ static int merge_entry(int pos, const char *path)
        arguments[5] = "";
        arguments[6] = "";
        arguments[7] = "";
+       arguments[8] = NULL;
        found = 0;
        do {
                static char hexbuf[4][60];
index b2867ba7226ea6ff69876f8f20da87d200fe5fca..3b8d9e6887ae051bf61cc0833f97d83bb47a0bae 100644 (file)
@@ -188,7 +188,7 @@ static void resolve(const char *base, struct name_entry *branch1, struct name_en
 
 static int unresolved_directory(const char *base, struct name_entry n[3])
 {
-       int baselen;
+       int baselen, pathlen;
        char *newbase;
        struct name_entry *p;
        struct tree_desc t[3];
@@ -205,10 +205,11 @@ static int unresolved_directory(const char *base, struct name_entry n[3])
        if (!S_ISDIR(p->mode))
                return 0;
        baselen = strlen(base);
-       newbase = xmalloc(baselen + p->pathlen + 2);
+       pathlen = tree_entry_len(p->path, p->sha1);
+       newbase = xmalloc(baselen + pathlen + 2);
        memcpy(newbase, base, baselen);
-       memcpy(newbase + baselen, p->path, p->pathlen);
-       memcpy(newbase + baselen + p->pathlen, "/", 2);
+       memcpy(newbase + baselen, p->path, pathlen);
+       memcpy(newbase + baselen + pathlen, "/", 2);
 
        buf0 = fill_tree_descriptor(t+0, n[0].sha1);
        buf1 = fill_tree_descriptor(t+1, n[1].sha1);
index 5b468893421794c50741ce9085c12bc41fb1985f..78a44a6ef4e4823487861c9173f3db4a3fb76e3a 100644 (file)
--- a/object.c
+++ b/object.c
@@ -184,8 +184,10 @@ struct object *parse_object(const unsigned char *sha1)
 
        if (buffer) {
                struct object *obj;
-               if (check_sha1_signature(sha1, buffer, size, typename(type)) < 0)
-                       printf("sha1 mismatch %s\n", sha1_to_hex(sha1));
+               if (check_sha1_signature(sha1, buffer, size, typename(type)) < 0) {
+                       error("sha1 mismatch %s\n", sha1_to_hex(sha1));
+                       return NULL;
+               }
 
                obj = parse_object_buffer(sha1, type, size, buffer, &eaten);
                if (!eaten)
index 299c514128b8b6330561a1a2b8ac30324a7ac479..d9883225eabf10ad9a3a169e7049c9ee25e5d9cd 100644 (file)
@@ -5,7 +5,7 @@ static int verify_packfile(struct packed_git *p,
                struct pack_window **w_curs)
 {
        off_t index_size = p->index_size;
-       void *index_base = p->index_base;
+       const unsigned char *index_base = p->index_data;
        SHA_CTX ctx;
        unsigned char sha1[20];
        off_t offset = 0, pack_sig = p->pack_size - 20;
@@ -31,7 +31,7 @@ static int verify_packfile(struct packed_git *p,
        if (hashcmp(sha1, use_pack(p, w_curs, pack_sig, NULL)))
                return error("Packfile %s SHA1 mismatch with itself",
                             p->pack_name);
-       if (hashcmp(sha1, (unsigned char *)index_base + index_size - 40))
+       if (hashcmp(sha1, index_base + index_size - 40))
                return error("Packfile %s SHA1 mismatch with idx",
                             p->pack_name);
        unuse_pack(w_curs);
@@ -127,7 +127,7 @@ static void show_pack_info(struct packed_git *p)
 int verify_pack(struct packed_git *p, int verbose)
 {
        off_t index_size = p->index_size;
-       void *index_base = p->index_base;
+       const unsigned char *index_base = p->index_data;
        SHA_CTX ctx;
        unsigned char sha1[20];
        int ret;
@@ -137,7 +137,7 @@ int verify_pack(struct packed_git *p, int verbose)
        SHA1_Init(&ctx);
        SHA1_Update(&ctx, index_base, (unsigned int)(index_size - 20));
        SHA1_Final(sha1, &ctx);
-       if (hashcmp(sha1, (unsigned char *)index_base + index_size - 20))
+       if (hashcmp(sha1, index_base + index_size - 20))
                ret = error("Packfile index for %s SHA1 mismatch",
                            p->pack_name);
 
index c8f7d9af7b502e12a3d36fc3b3e796e43b4f2bdb..40e579b2d9788bb0867b345c3596b1ad1539272a 100644 (file)
@@ -17,7 +17,7 @@ static int load_all_packs, verbose, alt_odb;
 
 struct llist_item {
        struct llist_item *next;
-       unsigned char *sha1;
+       const unsigned char *sha1;
 };
 static struct llist {
        struct llist_item *front;
@@ -104,9 +104,9 @@ static struct llist * llist_copy(struct llist *list)
        return ret;
 }
 
-static inline struct llist_item * llist_insert(struct llist *list,
-                                              struct llist_item *after,
-                                              unsigned char *sha1)
+static inline struct llist_item *llist_insert(struct llist *list,
+                                             struct llist_item *after,
+                                              const unsigned char *sha1)
 {
        struct llist_item *new = llist_item_get();
        new->sha1 = sha1;
@@ -128,12 +128,14 @@ static inline struct llist_item * llist_insert(struct llist *list,
        return new;
 }
 
-static inline struct llist_item *llist_insert_back(struct llist *list, unsigned char *sha1)
+static inline struct llist_item *llist_insert_back(struct llist *list,
+                                                  const unsigned char *sha1)
 {
        return llist_insert(list, list->back, sha1);
 }
 
-static inline struct llist_item *llist_insert_sorted_unique(struct llist *list, unsigned char *sha1, struct llist_item *hint)
+static inline struct llist_item *llist_insert_sorted_unique(struct llist *list,
+                       const unsigned char *sha1, struct llist_item *hint)
 {
        struct llist_item *prev = NULL, *l;
 
@@ -246,12 +248,12 @@ static struct pack_list * pack_list_difference(const struct pack_list *A,
 static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
 {
        int p1_off, p2_off;
-       unsigned char *p1_base, *p2_base;
+       const unsigned char *p1_base, *p2_base;
        struct llist_item *p1_hint = NULL, *p2_hint = NULL;
 
        p1_off = p2_off = 256 * 4 + 4;
-       p1_base = (unsigned char *) p1->pack->index_base;
-       p2_base = (unsigned char *) p2->pack->index_base;
+       p1_base = p1->pack->index_data;
+       p2_base = p2->pack->index_data;
 
        while (p1_off <= p1->pack->index_size - 3 * 20 &&
               p2_off <= p2->pack->index_size - 3 * 20)
@@ -351,11 +353,11 @@ static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
 {
        size_t ret = 0;
        int p1_off, p2_off;
-       unsigned char *p1_base, *p2_base;
+       const unsigned char *p1_base, *p2_base;
 
        p1_off = p2_off = 256 * 4 + 4;
-       p1_base = (unsigned char *)p1->index_base;
-       p2_base = (unsigned char *)p2->index_base;
+       p1_base = p1->index_data;
+       p2_base = p2->index_data;
 
        while (p1_off <= p1->index_size - 3 * 20 &&
               p2_off <= p2->index_size - 3 * 20)
@@ -534,7 +536,7 @@ static struct pack_list * add_pack(struct packed_git *p)
 {
        struct pack_list l;
        size_t off;
-       unsigned char *base;
+       const unsigned char *base;
 
        if (!p->pack_local && !(alt_odb || verbose))
                return NULL;
@@ -543,7 +545,7 @@ static struct pack_list * add_pack(struct packed_git *p)
        llist_init(&l.all_objects);
 
        off = 256 * 4 + 4;
-       base = (unsigned char *)p->index_base;
+       base = p->index_data;
        while (off <= p->index_size - 3 * 20) {
                llist_insert_back(l.all_objects, base + off);
                off += 24;
diff --git a/pack.h b/pack.h
index deb427edbe43710dd2b76a3af0dbd87750344103..d4d412ccbb403f1374d41a00791eec3c16ba64ef 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -16,24 +16,15 @@ struct pack_header {
 };
 
 /*
- * Packed object index header
- *
- * struct pack_idx_header {
- *     uint32_t idx_signature;
- *     uint32_t idx_version;
- * };
- *
- * Note: this header isn't active yet.  In future versions of git
- * we may change the index file format.  At that time we would start
- * the first four bytes of the new index format with this signature,
- * as all older git binaries would find this value illegal and abort
- * reading the file.
+ * The first four bytes of index formats later than version 1 should
+ * start with this signature, as all older git binaries would find this
+ * value illegal and abort reading the file.
  *
  * This is the case because the number of objects in a packfile
  * cannot exceed 1,431,660,000 as every object would need at least
- * 3 bytes of data and the overall packfile cannot exceed 4 GiB due
- * to the 32 bit offsets used by the index.  Clearly the signature
- * exceeds this maximum.
+ * 3 bytes of data and the overall packfile cannot exceed 4 GiB with
+ * version 1 of the index file due to the offsets limited to 32 bits.
+ * Clearly the signature exceeds this maximum.
  *
  * Very old git binaries will also compare the first 4 bytes to the
  * next 4 bytes in the index and abort with a "non-monotonic index"
@@ -43,6 +34,15 @@ struct pack_header {
  */
 #define PACK_IDX_SIGNATURE 0xff744f63  /* "\377tOc" */
 
+/*
+ * Packed object index header
+ */
+struct pack_idx_header {
+       uint32_t idx_signature;
+       uint32_t idx_version;
+};
+
+
 extern int verify_pack(struct packed_git *, int);
 
 #define PH_ERROR_EOF           (-1)
index 01760d70462927ad33c7976a50e31372863a45f9..ff3dd34962ec69320a67a4823b844755ebfe0e7d 100644 (file)
@@ -42,8 +42,7 @@ static void process_tree(struct tree *tree,
        me.elem = name;
        me.elem_len = strlen(name);
 
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
+       init_tree_desc(&desc, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry)) {
                if (S_ISDIR(entry.mode))
index 7cf58782e38b0d52c104921d938163f2ba2c343c..26aa26bcb5089adcf400d12b673519e328b49a23 100644 (file)
@@ -382,10 +382,10 @@ static const char *unpack(void)
                }
        } else {
                const char *keeper[6];
-               int fd[2], s, len, status;
-               pid_t pid;
+               int s, len, status;
                char keep_arg[256];
                char packname[46];
+               struct child_process ip;
 
                s = sprintf(keep_arg, "--keep=receive-pack %i on ", getpid());
                if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
@@ -397,20 +397,12 @@ static const char *unpack(void)
                keeper[3] = hdr_arg;
                keeper[4] = keep_arg;
                keeper[5] = NULL;
-
-               if (pipe(fd) < 0)
-                       return "index-pack pipe failed";
-               pid = fork();
-               if (pid < 0)
+               memset(&ip, 0, sizeof(ip));
+               ip.argv = keeper;
+               ip.out = -1;
+               ip.git_cmd = 1;
+               if (start_command(&ip))
                        return "index-pack fork failed";
-               if (!pid) {
-                       dup2(fd[1], 1);
-                       close(fd[1]);
-                       close(fd[0]);
-                       execv_git_cmd(keeper);
-                       die("execv of index-pack failed");
-               }
-               close(fd[1]);
 
                /*
                 * The first thing we expects from index-pack's output
@@ -420,9 +412,8 @@ static const char *unpack(void)
                 * later on.  If we don't get that then tough luck with it.
                 */
                for (len = 0;
-                    len < 46 && (s = xread(fd[0], packname+len, 46-len)) > 0;
+                    len < 46 && (s = xread(ip.out, packname+len, 46-len)) > 0;
                     len += s);
-               close(fd[0]);
                if (len == 46 && packname[45] == '\n' &&
                    memcmp(packname, "keep\t", 5) == 0) {
                        char path[PATH_MAX];
@@ -432,14 +423,8 @@ static const char *unpack(void)
                        pack_lockfile = xstrdup(path);
                }
 
-               /* Then wrap our index-pack process. */
-               while (waitpid(pid, &status, 0) < 0)
-                       if (errno != EINTR)
-                               return "waitpid failed";
-               if (WIFEXITED(status)) {
-                       int code = WEXITSTATUS(status);
-                       if (code)
-                               return "index-pack exited with error code";
+               status = finish_command(&ip);
+               if (!status) {
                        reprepare_packed_git();
                        return NULL;
                }
diff --git a/refs.c b/refs.c
index 9f1fb68d047b62622598379706fa08960f6995f8..f471152bfc6c500a2597068496ffff3f4d8f5961 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -980,6 +980,27 @@ int write_ref_sha1(struct ref_lock *lock,
                unlock_ref(lock);
                return -1;
        }
+       if (strcmp(lock->orig_ref_name, "HEAD") != 0) {
+               /*
+                * Special hack: If a branch is updated directly and HEAD
+                * points to it (may happen on the remote side of a push
+                * for example) then logically the HEAD reflog should be
+                * updated too.
+                * A generic solution implies reverse symref information,
+                * but finding all symrefs pointing to the given branch
+                * would be rather costly for this rare event (the direct
+                * update of a branch) to be worth it.  So let's cheat and
+                * check with HEAD only which should cover 99% of all usage
+                * scenarios (even 100% of the default ones).
+                */
+               unsigned char head_sha1[20];
+               int head_flag;
+               const char *head_ref;
+               head_ref = resolve_ref("HEAD", head_sha1, 1, &head_flag);
+               if (head_ref && (head_flag & REF_ISSYMREF) &&
+                   !strcmp(head_ref, lock->ref_name))
+                       log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
+       }
        if (commit_lock_file(lock->lk)) {
                error("Couldn't set %s", lock->ref_name);
                unlock_ref(lock);
index 8afc1968470c88827a2c3da05ece345520b2d336..f23c1d52269576917ac2d8347b322805daa1bac5 100644 (file)
@@ -62,8 +62,7 @@ void mark_tree_uninteresting(struct tree *tree)
        if (parse_tree(tree) < 0)
                die("bad tree %s", sha1_to_hex(obj->sha1));
 
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
+       init_tree_desc(&desc, tree->buffer, tree->size);
        while (tree_entry(&desc, &entry)) {
                if (S_ISDIR(entry.mode))
                        mark_tree_uninteresting(lookup_tree(entry.sha1));
@@ -213,6 +212,13 @@ static int everybody_uninteresting(struct commit_list *orig)
        return 1;
 }
 
+/*
+ * The goal is to get REV_TREE_NEW as the result only if the
+ * diff consists of all '+' (and no other changes), and
+ * REV_TREE_DIFFERENT otherwise (of course if the trees are
+ * the same we want REV_TREE_SAME).  That means that once we
+ * get to REV_TREE_DIFFERENT, we do not have to look any further.
+ */
 static int tree_difference = REV_TREE_SAME;
 
 static void file_add_remove(struct diff_options *options,
@@ -236,6 +242,8 @@ static void file_add_remove(struct diff_options *options,
                diff = REV_TREE_NEW;
        }
        tree_difference = diff;
+       if (tree_difference == REV_TREE_DIFFERENT)
+               options->has_changes = 1;
 }
 
 static void file_change(struct diff_options *options,
@@ -245,6 +253,7 @@ static void file_change(struct diff_options *options,
                 const char *base, const char *path)
 {
        tree_difference = REV_TREE_DIFFERENT;
+       options->has_changes = 1;
 }
 
 int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
@@ -254,6 +263,7 @@ int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
        if (!t2)
                return REV_TREE_DIFFERENT;
        tree_difference = REV_TREE_SAME;
+       revs->pruning.has_changes = 0;
        if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "",
                           &revs->pruning) < 0)
                return REV_TREE_DIFFERENT;
@@ -264,24 +274,24 @@ int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1)
 {
        int retval;
        void *tree;
+       unsigned long size;
        struct tree_desc empty, real;
 
        if (!t1)
                return 0;
 
-       tree = read_object_with_reference(t1->object.sha1, tree_type, &real.size, NULL);
+       tree = read_object_with_reference(t1->object.sha1, tree_type, &size, NULL);
        if (!tree)
                return 0;
-       real.buf = tree;
+       init_tree_desc(&real, tree, size);
+       init_tree_desc(&empty, "", 0);
 
-       empty.buf = "";
-       empty.size = 0;
-
-       tree_difference = 0;
+       tree_difference = REV_TREE_SAME;
+       revs->pruning.has_changes = 0;
        retval = diff_tree(&empty, &real, "", &revs->pruning);
        free(tree);
 
-       return retval >= 0 && !tree_difference;
+       return retval >= 0 && (tree_difference == REV_TREE_SAME);
 }
 
 static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
@@ -547,6 +557,7 @@ void init_revisions(struct rev_info *revs, const char *prefix)
        revs->ignore_merges = 1;
        revs->simplify_history = 1;
        revs->pruning.recursive = 1;
+       revs->pruning.quiet = 1;
        revs->pruning.add_remove = file_add_remove;
        revs->pruning.change = file_change;
        revs->lifo = 1;
@@ -1031,7 +1042,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                        if (!prefixcmp(arg, "--encoding=")) {
                                arg += 11;
                                if (strcmp(arg, "none"))
-                                       git_log_output_encoding = strdup(arg);
+                                       git_log_output_encoding = xstrdup(arg);
                                else
                                        git_log_output_encoding = "";
                                continue;
index bef9775e0807200e07c9780c69e3e1bda0c6c874..eff523e191b35385895f6b077fc76c7c21819012 100644 (file)
@@ -8,11 +8,19 @@ static inline void close_pair(int fd[2])
        close(fd[1]);
 }
 
+static inline void dup_devnull(int to)
+{
+       int fd = open("/dev/null", O_RDWR);
+       dup2(fd, to);
+       close(fd);
+}
+
 int start_command(struct child_process *cmd)
 {
-       int need_in = !cmd->no_stdin && cmd->in < 0;
-       int fdin[2];
+       int need_in, need_out;
+       int fdin[2], fdout[2];
 
+       need_in = !cmd->no_stdin && cmd->in < 0;
        if (need_in) {
                if (pipe(fdin) < 0)
                        return -ERR_RUN_COMMAND_PIPE;
@@ -20,19 +28,32 @@ int start_command(struct child_process *cmd)
                cmd->close_in = 1;
        }
 
+       need_out = !cmd->no_stdout
+               && !cmd->stdout_to_stderr
+               && cmd->out < 0;
+       if (need_out) {
+               if (pipe(fdout) < 0) {
+                       if (need_in)
+                               close_pair(fdin);
+                       return -ERR_RUN_COMMAND_PIPE;
+               }
+               cmd->out = fdout[0];
+               cmd->close_out = 1;
+       }
+
        cmd->pid = fork();
        if (cmd->pid < 0) {
                if (need_in)
                        close_pair(fdin);
+               if (need_out)
+                       close_pair(fdout);
                return -ERR_RUN_COMMAND_FORK;
        }
 
        if (!cmd->pid) {
-               if (cmd->no_stdin) {
-                       int fd = open("/dev/null", O_RDWR);
-                       dup2(fd, 0);
-                       close(fd);
-               } else if (need_in) {
+               if (cmd->no_stdin)
+                       dup_devnull(0);
+               else if (need_in) {
                        dup2(fdin[0], 0);
                        close_pair(fdin);
                } else if (cmd->in) {
@@ -40,8 +61,18 @@ int start_command(struct child_process *cmd)
                        close(cmd->in);
                }
 
-               if (cmd->stdout_to_stderr)
+               if (cmd->no_stdout)
+                       dup_devnull(1);
+               else if (cmd->stdout_to_stderr)
                        dup2(2, 1);
+               else if (need_out) {
+                       dup2(fdout[1], 1);
+                       close_pair(fdout);
+               } else if (cmd->out > 1) {
+                       dup2(cmd->out, 1);
+                       close(cmd->out);
+               }
+
                if (cmd->git_cmd) {
                        execv_git_cmd(cmd->argv);
                } else {
@@ -55,6 +86,11 @@ int start_command(struct child_process *cmd)
        else if (cmd->in)
                close(cmd->in);
 
+       if (need_out)
+               close(fdout[1]);
+       else if (cmd->out > 1)
+               close(cmd->out);
+
        return 0;
 }
 
@@ -62,6 +98,8 @@ int finish_command(struct child_process *cmd)
 {
        if (cmd->close_in)
                close(cmd->in);
+       if (cmd->close_out)
+               close(cmd->out);
 
        for (;;) {
                int status, code;
index ff090679a6fecd66bda4fba804a8ab6555571aa9..3680ef9d452490c67788b0ab027839a8383ed855 100644 (file)
@@ -15,8 +15,11 @@ struct child_process {
        const char **argv;
        pid_t pid;
        int in;
+       int out;
        unsigned close_in:1;
+       unsigned close_out:1;
        unsigned no_stdin:1;
+       unsigned no_stdout:1;
        unsigned git_cmd:1; /* if this is to be git sub-command */
        unsigned stdout_to_stderr:1;
 };
index 512b660e99f26e391df34d48e1aebc9c6c3250e7..d5b51628dfbcc44b22b5069df19eeac67dda1e57 100644 (file)
@@ -3,7 +3,7 @@
 #include "tag.h"
 #include "refs.h"
 #include "pkt-line.h"
-#include "exec_cmd.h"
+#include "run-command.h"
 
 static const char send_pack_usage[] =
 "git-send-pack [--all] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
@@ -19,46 +19,35 @@ static int use_thin_pack;
  */
 static int pack_objects(int fd, struct ref *refs)
 {
-       int pipe_fd[2];
-       pid_t pid;
-
-       if (pipe(pipe_fd) < 0)
-               return error("send-pack: pipe failed");
-       pid = fork();
-       if (pid < 0)
-               return error("send-pack: unable to fork git-pack-objects");
-       if (!pid) {
-               /*
-                * The child becomes pack-objects --revs; we feed
-                * the revision parameters to it via its stdin and
-                * let its stdout go back to the other end.
-                */
-               static const char *args[] = {
-                       "pack-objects",
-                       "--all-progress",
-                       "--revs",
-                       "--stdout",
-                       NULL,
-                       NULL,
-               };
-               if (use_thin_pack)
-                       args[4] = "--thin";
-               dup2(pipe_fd[0], 0);
-               dup2(fd, 1);
-               close(pipe_fd[0]);
-               close(pipe_fd[1]);
-               close(fd);
-               execv_git_cmd(args);
-               die("git-pack-objects exec failed (%s)", strerror(errno));
-       }
+       /*
+        * The child becomes pack-objects --revs; we feed
+        * the revision parameters to it via its stdin and
+        * let its stdout go back to the other end.
+        */
+       const char *args[] = {
+               "pack-objects",
+               "--all-progress",
+               "--revs",
+               "--stdout",
+               NULL,
+               NULL,
+       };
+       struct child_process po;
+
+       if (use_thin_pack)
+               args[4] = "--thin";
+       memset(&po, 0, sizeof(po));
+       po.argv = args;
+       po.in = -1;
+       po.out = fd;
+       po.git_cmd = 1;
+       if (start_command(&po))
+               die("git-pack-objects failed (%s)", strerror(errno));
 
        /*
         * We feed the pack-objects we just spawned with revision
         * parameters by writing to the pipe.
         */
-       close(pipe_fd[0]);
-       close(fd);
-
        while (refs) {
                char buf[42];
 
@@ -67,38 +56,23 @@ static int pack_objects(int fd, struct ref *refs)
                        memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
                        buf[0] = '^';
                        buf[41] = '\n';
-                       if (!write_or_whine(pipe_fd[1], buf, 42,
+                       if (!write_or_whine(po.in, buf, 42,
                                                "send-pack: send refs"))
                                break;
                }
                if (!is_null_sha1(refs->new_sha1)) {
                        memcpy(buf, sha1_to_hex(refs->new_sha1), 40);
                        buf[40] = '\n';
-                       if (!write_or_whine(pipe_fd[1], buf, 41,
+                       if (!write_or_whine(po.in, buf, 41,
                                                "send-pack: send refs"))
                                break;
                }
                refs = refs->next;
        }
-       close(pipe_fd[1]);
-
-       for (;;) {
-               int status, code;
-               pid_t waiting = waitpid(pid, &status, 0);
 
-               if (waiting < 0) {
-                       if (errno == EINTR)
-                               continue;
-                       return error("waitpid failed (%s)", strerror(errno));
-               }
-               if ((waiting != pid) || WIFSIGNALED(status) ||
-                   !WIFEXITED(status))
-                       return error("pack-objects died with strange error");
-               code = WEXITSTATUS(status);
-               if (code)
-                       return -code;
-               return 0;
-       }
+       if (finish_command(&po))
+               return error("pack-objects died with strange error");
+       return 0;
 }
 
 static void unmark_and_free(struct commit_list *list, unsigned int mark)
index 5691448d732ffabc16d854001659870e503f665a..0897b945e5dace8189adc35e6eea389cfc7606af 100644 (file)
@@ -432,16 +432,15 @@ void pack_report()
                pack_mapped, peak_pack_mapped);
 }
 
-static int check_packed_git_idx(const char *path,
-       unsigned long *idx_size_,
-       void **idx_map_)
+static int check_packed_git_idx(const char *path,  struct packed_git *p)
 {
        void *idx_map;
-       uint32_t *index;
+       struct pack_idx_header *hdr;
        size_t idx_size;
-       uint32_t nr, i;
+       uint32_t nr, i, *index;
        int fd = open(path, O_RDONLY);
        struct stat st;
+
        if (fd < 0)
                return -1;
        if (fstat(fd, &st)) {
@@ -456,15 +455,12 @@ static int check_packed_git_idx(const char *path,
        idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
        close(fd);
 
-       index = idx_map;
-       *idx_map_ = idx_map;
-       *idx_size_ = idx_size;
-
        /* a future index format would start with this, as older git
         * binaries would fail the non-monotonic index check below.
         * give a nicer warning to the user if we can.
         */
-       if (index[0] == htonl(PACK_IDX_SIGNATURE)) {
+       hdr = idx_map;
+       if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
                munmap(idx_map, idx_size);
                return error("index file %s is a newer version"
                        " and is not supported by this binary"
@@ -473,6 +469,7 @@ static int check_packed_git_idx(const char *path,
        }
 
        nr = 0;
+       index = idx_map;
        for (i = 0; i < 256; i++) {
                uint32_t n = ntohl(index[i]);
                if (n < nr) {
@@ -494,6 +491,9 @@ static int check_packed_git_idx(const char *path,
                return error("wrong index file size in %s", path);
        }
 
+       p->index_version = 1;
+       p->index_data = idx_map;
+       p->index_size = idx_size;
        return 0;
 }
 
@@ -614,7 +614,7 @@ static int open_packed_git_1(struct packed_git *p)
                return error("end of packfile %s is unavailable", p->pack_name);
        if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1))
                return error("packfile %s signature is unavailable", p->pack_name);
-       idx_sha1 = ((unsigned char *)p->index_base) + p->index_size - 40;
+       idx_sha1 = ((unsigned char *)p->index_data) + p->index_size - 40;
        if (hashcmp(sha1, idx_sha1))
                return error("packfile %s does not match index", p->pack_name);
        return 0;
@@ -710,38 +710,37 @@ unsigned char* use_pack(struct packed_git *p,
        return win->base + offset;
 }
 
-struct packed_git *add_packed_git(char *path, int path_len, int local)
+struct packed_git *add_packed_git(const char *path, int path_len, int local)
 {
        struct stat st;
-       struct packed_git *p;
-       unsigned long idx_size;
-       void *idx_map;
-       unsigned char sha1[20];
+       struct packed_git *p = xmalloc(sizeof(*p) + path_len + 2);
 
-       if (check_packed_git_idx(path, &idx_size, &idx_map))
+       /*
+        * Make sure a corresponding .pack file exists and that
+        * the index looks sane.
+        */
+       path_len -= strlen(".idx");
+       if (path_len < 1)
                return NULL;
-
-       /* do we have a corresponding .pack file? */
-       strcpy(path + path_len - 4, ".pack");
-       if (stat(path, &st) || !S_ISREG(st.st_mode)) {
-               munmap(idx_map, idx_size);
+       memcpy(p->pack_name, path, path_len);
+       strcpy(p->pack_name + path_len, ".pack");
+       if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode) ||
+           check_packed_git_idx(path, p)) {
+               free(p);
                return NULL;
        }
+
        /* ok, it looks sane as far as we can check without
         * actually mapping the pack file.
         */
-       p = xmalloc(sizeof(*p) + path_len + 2);
-       strcpy(p->pack_name, path);
-       p->index_size = idx_size;
        p->pack_size = st.st_size;
-       p->index_base = idx_map;
        p->next = NULL;
        p->windows = NULL;
        p->pack_fd = -1;
        p->pack_local = local;
        p->mtime = st.st_mtime;
-       if ((path_len > 44) && !get_sha1_hex(path + path_len - 44, sha1))
-               hashcpy(p->sha1, sha1);
+       if (path_len < 40 || get_sha1_hex(path + path_len - 40, p->sha1))
+               hashclr(p->sha1);
        return p;
 }
 
@@ -751,23 +750,19 @@ struct packed_git *parse_pack_index(unsigned char *sha1)
        return parse_pack_index_file(sha1, path);
 }
 
-struct packed_git *parse_pack_index_file(const unsigned char *sha1, char *idx_path)
+struct packed_git *parse_pack_index_file(const unsigned char *sha1,
+                                        const char *idx_path)
 {
-       struct packed_git *p;
-       unsigned long idx_size;
-       void *idx_map;
-       char *path;
+       const char *path = sha1_pack_name(sha1);
+       struct packed_git *p = xmalloc(sizeof(*p) + strlen(path) + 2);
 
-       if (check_packed_git_idx(idx_path, &idx_size, &idx_map))
+       if (check_packed_git_idx(idx_path, p)) {
+               free(p);
                return NULL;
+       }
 
-       path = sha1_pack_name(sha1);
-
-       p = xmalloc(sizeof(*p) + strlen(path) + 2);
        strcpy(p->pack_name, path);
-       p->index_size = idx_size;
        p->pack_size = 0;
-       p->index_base = idx_map;
        p->next = NULL;
        p->windows = NULL;
        p->pack_fd = -1;
@@ -1035,14 +1030,27 @@ static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size
                n = size;
        memcpy(buf, (char *) buffer + bytes, n);
        bytes = n;
-       if (bytes < size) {
+       if (bytes <= size) {
+               /*
+                * The above condition must be (bytes <= size), not
+                * (bytes < size).  In other words, even though we
+                * expect no more output and set avail_out to zer0,
+                * the input zlib stream may have bytes that express
+                * "this concludes the stream", and we *do* want to
+                * eat that input.
+                *
+                * Otherwise we would not be able to test that we
+                * consumed all the input to reach the expected size;
+                * we also want to check that zlib tells us that all
+                * went well with status == Z_STREAM_END at the end.
+                */
                stream->next_out = buf + bytes;
                stream->avail_out = size - bytes;
                while (status == Z_OK)
                        status = inflate(stream, Z_FINISH);
        }
        buf[size] = 0;
-       if ((status == Z_OK || status == Z_STREAM_END) && !stream->avail_in) {
+       if (status == Z_STREAM_END && !stream->avail_in) {
                inflateEnd(stream);
                return buf;
        }
@@ -1357,6 +1365,110 @@ static void *unpack_compressed_entry(struct packed_git *p,
        return buffer;
 }
 
+#define MAX_DELTA_CACHE (256)
+
+static size_t delta_base_cached;
+
+static struct delta_base_cache_lru_list {
+       struct delta_base_cache_lru_list *prev;
+       struct delta_base_cache_lru_list *next;
+} delta_base_cache_lru = { &delta_base_cache_lru, &delta_base_cache_lru };
+
+static struct delta_base_cache_entry {
+       struct delta_base_cache_lru_list lru;
+       void *data;
+       struct packed_git *p;
+       off_t base_offset;
+       unsigned long size;
+       enum object_type type;
+} delta_base_cache[MAX_DELTA_CACHE];
+
+static unsigned long pack_entry_hash(struct packed_git *p, off_t base_offset)
+{
+       unsigned long hash;
+
+       hash = (unsigned long)p + (unsigned long)base_offset;
+       hash += (hash >> 8) + (hash >> 16);
+       return hash % MAX_DELTA_CACHE;
+}
+
+static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
+       unsigned long *base_size, enum object_type *type, int keep_cache)
+{
+       void *ret;
+       unsigned long hash = pack_entry_hash(p, base_offset);
+       struct delta_base_cache_entry *ent = delta_base_cache + hash;
+
+       ret = ent->data;
+       if (ret && ent->p == p && ent->base_offset == base_offset)
+               goto found_cache_entry;
+       return unpack_entry(p, base_offset, type, base_size);
+
+found_cache_entry:
+       if (!keep_cache) {
+               ent->data = NULL;
+               ent->lru.next->prev = ent->lru.prev;
+               ent->lru.prev->next = ent->lru.next;
+               delta_base_cached -= ent->size;
+       }
+       else {
+               ret = xmalloc(ent->size + 1);
+               memcpy(ret, ent->data, ent->size);
+               ((char *)ret)[ent->size] = 0;
+       }
+       *type = ent->type;
+       *base_size = ent->size;
+       return ret;
+}
+
+static inline void release_delta_base_cache(struct delta_base_cache_entry *ent)
+{
+       if (ent->data) {
+               free(ent->data);
+               ent->data = NULL;
+               ent->lru.next->prev = ent->lru.prev;
+               ent->lru.prev->next = ent->lru.next;
+               delta_base_cached -= ent->size;
+       }
+}
+
+static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
+       void *base, unsigned long base_size, enum object_type type)
+{
+       unsigned long hash = pack_entry_hash(p, base_offset);
+       struct delta_base_cache_entry *ent = delta_base_cache + hash;
+       struct delta_base_cache_lru_list *lru;
+
+       release_delta_base_cache(ent);
+       delta_base_cached += base_size;
+
+       for (lru = delta_base_cache_lru.next;
+            delta_base_cached > delta_base_cache_limit
+            && lru != &delta_base_cache_lru;
+            lru = lru->next) {
+               struct delta_base_cache_entry *f = (void *)lru;
+               if (f->type == OBJ_BLOB)
+                       release_delta_base_cache(f);
+       }
+       for (lru = delta_base_cache_lru.next;
+            delta_base_cached > delta_base_cache_limit
+            && lru != &delta_base_cache_lru;
+            lru = lru->next) {
+               struct delta_base_cache_entry *f = (void *)lru;
+               release_delta_base_cache(f);
+       }
+
+       ent->p = p;
+       ent->base_offset = base_offset;
+       ent->type = type;
+       ent->data = base;
+       ent->size = base_size;
+       ent->lru.next = &delta_base_cache_lru;
+       ent->lru.prev = delta_base_cache_lru.prev;
+       delta_base_cache_lru.prev->next = &ent->lru;
+       delta_base_cache_lru.prev = &ent->lru;
+}
+
 static void *unpack_delta_entry(struct packed_git *p,
                                struct pack_window **w_curs,
                                off_t curpos,
@@ -1370,7 +1482,7 @@ static void *unpack_delta_entry(struct packed_git *p,
        off_t base_offset;
 
        base_offset = get_delta_base(p, w_curs, &curpos, *type, obj_offset);
-       base = unpack_entry(p, base_offset, type, &base_size);
+       base = cache_or_unpack_entry(p, base_offset, &base_size, type, 0);
        if (!base)
                die("failed to read delta base object"
                    " at %"PRIuMAX" from %s",
@@ -1383,7 +1495,7 @@ static void *unpack_delta_entry(struct packed_git *p,
        if (!result)
                die("failed to apply delta");
        free(delta_data);
-       free(base);
+       add_delta_base_cache(p, base_offset, base, base_size, *type);
        return result;
 }
 
@@ -1423,24 +1535,27 @@ uint32_t num_packed_objects(const struct packed_git *p)
 int nth_packed_object_sha1(const struct packed_git *p, uint32_t n,
                           unsigned char* sha1)
 {
-       void *index = p->index_base + 256;
+       const unsigned char *index = p->index_data;
+       index += 4 * 256;
        if (num_packed_objects(p) <= n)
                return -1;
-       hashcpy(sha1, (unsigned char *) index + (24 * n) + 4);
+       hashcpy(sha1, index + 24 * n + 4);
        return 0;
 }
 
 off_t find_pack_entry_one(const unsigned char *sha1,
                                  struct packed_git *p)
 {
-       uint32_t *level1_ofs = p->index_base;
+       const uint32_t *level1_ofs = p->index_data;
        int hi = ntohl(level1_ofs[*sha1]);
        int lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
-       void *index = p->index_base + 256;
+       const unsigned char *index = p->index_data;
+
+       index += 4 * 256;
 
        do {
                int mi = (lo + hi) / 2;
-               int cmp = hashcmp((unsigned char *)index + (24 * mi) + 4, sha1);
+               int cmp = hashcmp(index + 24 * mi + 4, sha1);
                if (!cmp)
                        return ntohl(*((uint32_t *)((char *)index + (24 * mi))));
                if (cmp > 0)
@@ -1564,7 +1679,7 @@ static void *read_packed_sha1(const unsigned char *sha1,
        if (!find_pack_entry(sha1, &e, NULL))
                return NULL;
        else
-               return unpack_entry(e.p, e.offset, type, size);
+               return cache_or_unpack_entry(e.p, e.offset, size, type, 1);
 }
 
 /*
@@ -1693,7 +1808,7 @@ void *read_object_with_reference(const unsigned char *sha1,
        }
 }
 
-static void write_sha1_file_prepare(void *buf, unsigned long len,
+static void write_sha1_file_prepare(const void *buf, unsigned long len,
                                     const char *type, unsigned char *sha1,
                                     char *hdr, int *hdrlen)
 {
@@ -1821,7 +1936,7 @@ static void setup_object_header(z_stream *stream, const char *type, unsigned lon
        stream->avail_out -= hdrlen;
 }
 
-int hash_sha1_file(void *buf, unsigned long len, const char *type,
+int hash_sha1_file(const void *buf, unsigned long len, const char *type,
                    unsigned char *sha1)
 {
        char hdr[32];
@@ -1832,7 +1947,7 @@ int hash_sha1_file(void *buf, unsigned long len, const char *type,
 
 int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
 {
-       int size;
+       int size, ret;
        unsigned char *compressed;
        z_stream stream;
        unsigned char sha1[20];
@@ -1864,7 +1979,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
                return error("sha1 file %s: %s\n", filename, strerror(errno));
        }
 
-       snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory());
+       snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory());
 
        fd = mkstemp(tmpfile);
        if (fd < 0) {
@@ -1892,9 +2007,14 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
        /* Then the data itself.. */
        stream.next_in = buf;
        stream.avail_in = len;
-       while (deflate(&stream, Z_FINISH) == Z_OK)
-               /* nothing */;
-       deflateEnd(&stream);
+       ret = deflate(&stream, Z_FINISH);
+       if (ret != Z_STREAM_END)
+               die("unable to deflate new object %s (%d)", sha1_to_hex(sha1), ret);
+
+       ret = deflateEnd(&stream);
+       if (ret != Z_OK)
+               die("deflateEnd on object %s failed (%d)", sha1_to_hex(sha1), ret);
+
        size = stream.total_out;
 
        if (write_buffer(fd, compressed, size) < 0)
@@ -1985,7 +2105,7 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
        int ret;
        SHA_CTX c;
 
-       snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory());
+       snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory());
 
        local = mkstemp(tmpfile);
        if (local < 0) {
@@ -2034,6 +2154,7 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
        } while (1);
        inflateEnd(&stream);
 
+       fchmod(local, 0444);
        close(local);
        SHA1_Final(real_sha1, &c);
        if (ret != Z_STREAM_END) {
index 75c000a968c4b3a371393062e14801aa90ee53f1..9558bdb6318cc2956c43153084e1f1cc8386973f 100755 (executable)
@@ -145,9 +145,15 @@ test_expect_success 'test overriding tracking setup via --no-track' \
      git-config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
      (git-show-ref -q refs/remotes/local/master || git-fetch local) &&
      git-branch --no-track my2 local/master &&
+     git-config branch.autosetupmerge false &&
      ! test $(git-config branch.my2.remote) = local &&
      ! test $(git-config branch.my2.merge) = refs/heads/master'
 
+test_expect_success 'test local tracking setup' \
+    'git branch --track my6 s &&
+     test $(git-config branch.my6.remote) = . &&
+     test $(git-config branch.my6.merge) = refs/heads/s'
+
 # Keep this test last, as it changes the current branch
 cat >expect <<EOF
 0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000     branch: Created from master
diff --git a/t/t4017-diff-retval.sh b/t/t4017-diff-retval.sh
new file mode 100755 (executable)
index 0000000..6873190
--- /dev/null
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+test_description='Return value of diffs'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       echo 1 >a &&
+       git add . &&
+       git commit -m first &&
+       echo 2 >b &&
+       git add . &&
+       git commit -a -m second
+'
+
+test_expect_success 'git diff-tree HEAD^ HEAD' '
+       git diff-tree --exit-code HEAD^ HEAD
+       test $? = 1
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
+       git diff-tree --exit-code HEAD^ HEAD -- a
+       test $? = 0
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
+       git diff-tree --exit-code HEAD^ HEAD -- b
+       test $? = 1
+'
+test_expect_success 'echo HEAD | git diff-tree --stdin' '
+       echo $(git rev-parse HEAD) | git diff-tree --exit-code --stdin
+       test $? = 1
+'
+test_expect_success 'git diff-tree HEAD HEAD' '
+       git diff-tree --exit-code HEAD HEAD
+       test $? = 0
+'
+test_expect_success 'git diff-files' '
+       git diff-files --exit-code
+       test $? = 0
+'
+test_expect_success 'git diff-index --cached HEAD' '
+       git diff-index --exit-code --cached HEAD
+       test $? = 0
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+       git diff-index --exit-code --cached HEAD^
+       test $? = 1
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+       echo text >>b &&
+       echo 3 >c &&
+       git add . && {
+               git diff-index --exit-code --cached HEAD^
+               test $? = 1
+       }
+'
+test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
+       git commit -m "text in b" && {
+               git diff-tree -p --exit-code -Stext HEAD^ HEAD -- b
+               test $? = 1
+       }
+'
+test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
+       git diff-tree -p --exit-code -Snot-found HEAD^ HEAD -- b
+       test $? = 0
+'
+test_expect_success 'git diff-files' '
+       echo 3 >>c && {
+               git diff-files --exit-code
+               test $? = 1
+       }
+'
+test_expect_success 'git diff-index --cached HEAD' '
+       git update-index c && {
+               git diff-index --exit-code --cached HEAD
+               test $? = 1
+       }
+'
+
+test_done
diff --git a/t/t4017-quiet.sh b/t/t4017-quiet.sh
new file mode 100755 (executable)
index 0000000..e747e84
--- /dev/null
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+test_description='Return value of diffs'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       echo 1 >a &&
+       git add . &&
+       git commit -m first &&
+       echo 2 >b &&
+       git add . &&
+       git commit -a -m second
+'
+
+test_expect_success 'git diff-tree HEAD^ HEAD' '
+       git diff-tree --quiet HEAD^ HEAD >cnt
+       test $? = 1 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
+       git diff-tree --quiet HEAD^ HEAD -- a >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
+       git diff-tree --quiet HEAD^ HEAD -- b >cnt
+       test $? = 1 && test $(wc -l <cnt) = 0
+'
+# this diff outputs one line: sha1 of the given head
+test_expect_success 'echo HEAD | git diff-tree --stdin' '
+       echo $(git rev-parse HEAD) | git diff-tree --quiet --stdin >cnt
+       test $? = 1 && test $(wc -l <cnt) = 1
+'
+test_expect_success 'git diff-tree HEAD HEAD' '
+       git diff-tree --quiet HEAD HEAD >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-files' '
+       git diff-files --quiet >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD' '
+       git diff-index --quiet --cached HEAD >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+       git diff-index --quiet --cached HEAD^ >cnt
+       test $? = 1 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+       echo text >>b &&
+       echo 3 >c &&
+       git add . && {
+               git diff-index --quiet --cached HEAD^ >cnt
+               test $? = 1 && test $(wc -l <cnt) = 0
+       }
+'
+test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
+       git commit -m "text in b" && {
+               git diff-tree --quiet -Stext HEAD^ HEAD -- b >cnt
+               test $? = 1 && test $(wc -l <cnt) = 0
+       }
+'
+test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
+       git diff-tree --quiet -Snot-found HEAD^ HEAD -- b >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-files' '
+       echo 3 >>c && {
+               git diff-files --quiet >cnt
+               test $? = 1 && test $(wc -l <cnt) = 0
+       }
+'
+test_expect_success 'git diff-index --cached HEAD' '
+       git update-index c && {
+               git diff-index --quiet --cached HEAD >cnt
+               test $? = 1 && test $(wc -l <cnt) = 0
+       }
+'
+
+test_done
index 690a1820032025056307de8926e8f56826c29367..27cc6f2b88974051cd521755800342e375d84af7 100755 (executable)
@@ -23,7 +23,8 @@ test_expect_success setup '
        cat file2 >file2.orig
        git add file1 file2 &&
        sed -e "/^B/d" <file1.orig >file1 &&
-       sed -e "/^B/d" <file2.orig >file2 &&
+       sed -e "/^[BQ]/d" <file2.orig >file2 &&
+       echo Q | tr -d "\\012" >>file2 &&
        cat file1 >file1.mods &&
        cat file2 >file2.mods &&
        git diff |
index f51154745586b246ecb30caee3f2150441911e1d..35e036a86465ce066b3e3a5f98c769fba6cd4276 100755 (executable)
@@ -64,7 +64,7 @@ test_expect_success \
 cd "$TRASH"
 
 test_expect_success \
-    'pack with delta' \
+    'pack with REF_DELTA' \
     'pwd &&
      packname_2=$(git-pack-objects test-2 <obj-list)'
 
@@ -72,7 +72,7 @@ rm -fr .git2
 mkdir .git2
 
 test_expect_success \
-    'unpack with delta' \
+    'unpack with REF_DELTA' \
     'GIT_OBJECT_DIRECTORY=.git2/objects &&
      export GIT_OBJECT_DIRECTORY &&
      git-init &&
@@ -82,7 +82,7 @@ test_expect_success \
 unset GIT_OBJECT_DIRECTORY
 cd "$TRASH/.git2"
 test_expect_success \
-    'check unpack with delta' \
+    'check unpack with REF_DELTA' \
     '(cd ../.git && find objects -type f -print) |
      while read path
      do
@@ -93,6 +93,42 @@ test_expect_success \
      done'
 cd "$TRASH"
 
+test_expect_success \
+    'pack with OFS_DELTA' \
+    'pwd &&
+     packname_3=$(git-pack-objects --delta-base-offset test-3 <obj-list)'
+
+rm -fr .git2
+mkdir .git2
+
+test_expect_success \
+    'unpack with OFS_DELTA' \
+    'GIT_OBJECT_DIRECTORY=.git2/objects &&
+     export GIT_OBJECT_DIRECTORY &&
+     git-init &&
+     git-unpack-objects -n <test-3-${packname_3}.pack &&
+     git-unpack-objects <test-3-${packname_3}.pack'
+
+unset GIT_OBJECT_DIRECTORY
+cd "$TRASH/.git2"
+test_expect_success \
+    'check unpack with OFS_DELTA' \
+    '(cd ../.git && find objects -type f -print) |
+     while read path
+     do
+         cmp $path ../.git/$path || {
+            echo $path differs.
+            return 1
+        }
+     done'
+cd "$TRASH"
+
+test_expect_success \
+    'compare delta flavors' \
+    'size_2=`stat -c "%s" test-2-${packname_2}.pack` &&
+     size_3=`stat -c "%s" test-3-${packname_3}.pack` &&
+     test $size_2 -gt $size_3'
+
 rm -fr .git2
 mkdir .git2
 
@@ -111,12 +147,11 @@ test_expect_success \
     } >current &&
     diff expect current'
 
-
 test_expect_success \
-    'use packed deltified objects' \
+    'use packed deltified (REF_DELTA) objects' \
     'GIT_OBJECT_DIRECTORY=.git2/objects &&
      export GIT_OBJECT_DIRECTORY &&
-     rm -f .git2/objects/pack/test-?.idx &&
+     rm .git2/objects/pack/test-* &&
      cp test-2-${packname_2}.pack test-2-${packname_2}.idx .git2/objects/pack && {
         git-diff-tree --root -p $commit &&
         while read object
@@ -127,11 +162,28 @@ test_expect_success \
     } >current &&
     diff expect current'
 
+test_expect_success \
+    'use packed deltified (OFS_DELTA) objects' \
+    'GIT_OBJECT_DIRECTORY=.git2/objects &&
+     export GIT_OBJECT_DIRECTORY &&
+     rm .git2/objects/pack/test-* &&
+     cp test-3-${packname_3}.pack test-3-${packname_3}.idx .git2/objects/pack && {
+        git-diff-tree --root -p $commit &&
+        while read object
+        do
+           t=`git-cat-file -t $object` &&
+           git-cat-file $t $object || return 1
+        done <obj-list
+    } >current &&
+    diff expect current'
+
 unset GIT_OBJECT_DIRECTORY
 
 test_expect_success \
     'verify pack' \
-    'git-verify-pack test-1-${packname_1}.idx test-2-${packname_2}.idx'
+    'git-verify-pack   test-1-${packname_1}.idx \
+                       test-2-${packname_2}.idx \
+                       test-3-${packname_3}.idx'
 
 test_expect_success \
     'corrupt a pack and see if verify catches' \
@@ -194,6 +246,23 @@ test_expect_success \
      git-index-pack test-3.pack &&
      cmp test-3.idx test-2-${packname_2}.idx &&
 
+     cp test-3-${packname_3}.pack test-3.pack &&
+     git-index-pack -o tmp.idx test-3-${packname_3}.pack &&
+     cmp tmp.idx test-3-${packname_3}.idx &&
+
+     git-index-pack test-3.pack &&
+     cmp test-3.idx test-3-${packname_3}.idx &&
+
      :'
 
+test_expect_success \
+    'fake a SHA1 hash collision' \
+    'test -f   .git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67 &&
+     cp -f     .git/objects/9d/235ed07cd19811a6ceb342de82f190e49c9f68 \
+               .git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67'
+
+test_expect_failure \
+    'make sure index-pack detects the SHA1 collision' \
+    'git-index-pack -o bad.idx test-3.pack'
+
 test_done
index 7eb37838bb788dfb68361bca95860fd532276deb..243212d3dac03c19db18e9c8fcfc3b9049137cdd 100755 (executable)
@@ -29,5 +29,29 @@ test_expect_success 'checking the results' '
        diff file cloned/file
 '
 
+test_expect_success 'test . as a remote' '
+
+       git branch copy master &&
+       git config branch.copy.remote . &&
+       git config branch.copy.merge refs/heads/master &&
+       echo updated >file &&
+       git commit -a -m updated &&
+       git checkout copy &&
+       test `cat file` = file &&
+       git pull &&
+       test `cat file` = updated
+'
+
+test_expect_success 'the default remote . should not break explicit pull' '
+       git checkout -b second master^ &&
+       echo modified >file &&
+       git commit -a -m modified &&
+       git checkout copy &&
+       git reset --hard HEAD^ &&
+       test `cat file` = file &&
+       git pull . second &&
+       test `cat file` = modified
+'
+
 test_done
 
diff --git a/t/t6030-bisect-run.sh b/t/t6030-bisect-run.sh
new file mode 100755 (executable)
index 0000000..39c7228
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Christian Couder
+#
+test_description='Tests git-bisect run functionality'
+
+. ./test-lib.sh
+
+add_line_into_file()
+{
+    _line=$1
+    _file=$2
+
+    if [ -f "$_file" ]; then
+        echo "$_line" >> $_file || return $?
+        MSG="Add <$_line> into <$_file>."
+    else
+        echo "$_line" > $_file || return $?
+        git add $_file || return $?
+        MSG="Create file <$_file> with <$_line> inside."
+    fi
+
+    git-commit -m "$MSG" $_file
+}
+
+HASH1=
+HASH3=
+HASH4=
+
+test_expect_success \
+    'set up basic repo with 1 file (hello) and 4 commits' \
+    'add_line_into_file "1: Hello World" hello &&
+     add_line_into_file "2: A new day for git" hello &&
+     add_line_into_file "3: Another new day for git" hello &&
+     add_line_into_file "4: Ciao for now" hello &&
+     HASH1=$(git rev-list HEAD | tail -1) &&
+     HASH3=$(git rev-list HEAD | head -2 | tail -1) &&
+     HASH4=$(git rev-list HEAD | head -1)'
+
+# We want to automatically find the commit that
+# introduced "Another" into hello.
+test_expect_success \
+    'git bisect run simple case' \
+    'echo "#!/bin/sh" > test_script.sh &&
+     echo "grep Another hello > /dev/null" >> test_script.sh &&
+     echo "test \$? -ne 0" >> test_script.sh &&
+     chmod +x test_script.sh &&
+     git bisect start &&
+     git bisect good $HASH1 &&
+     git bisect bad $HASH4 &&
+     git bisect run ./test_script.sh > my_bisect_log.txt &&
+     grep "$HASH3 is first bad commit" my_bisect_log.txt'
+
+#
+#
+test_done
+
index 8f6c4fea2429cef0da4748f8871ceaed94b432cc..0ff03309e69aed6b506e333e087f23099ad80290 100644 (file)
@@ -1,36 +1,16 @@
 #!/bin/sh
 #
-# An example hook script to mail out commit update information.
-# It can also blocks tags that aren't annotated.
+# An example hook script to blocks unannotated tags from entering.
 # Called by git-receive-pack with arguments: refname sha1-old sha1-new
 #
 # To enable this hook, make this file executable by "chmod +x update".
 #
 # Config
 # ------
-# hooks.mailinglist
-#   This is the list that all pushes will go to; leave it blank to not send
-#   emails frequently.  The log email will list every log entry in full between
-#   the old ref value and the new ref value.
-# hooks.announcelist
-#   This is the list that all pushes of annotated tags will go to.  Leave it
-#   blank to just use the mailinglist field.  The announce emails list the
-#   short log summary of the changes since the last annotated tag
 # hooks.allowunannotated
 #   This boolean sets whether unannotated tags will be allowed into the
 #   repository.  By default they won't be.
 #
-# Notes
-# -----
-# All emails have their subjects prefixed with "[SCM]" to aid filtering.
-# All emails include the headers "X-Git-Refname", "X-Git-Oldrev",
-# "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and info.
-
-# --- Constants
-EMAILPREFIX="[SCM] "
-LOGBEGIN="- Log -----------------------------------------------------------------"
-LOGEND="-----------------------------------------------------------------------"
-DATEFORMAT="%F %R %z"
 
 # --- Command line
 refname="$1"
@@ -51,235 +31,42 @@ if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
 fi
 
 # --- Config
-projectdesc=$(cat $GIT_DIR/description)
-recipients=$(git-repo-config hooks.mailinglist)
-announcerecipients=$(git-repo-config hooks.announcelist)
 allowunannotated=$(git-repo-config --bool hooks.allowunannotated)
 
+# check for no description
+if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb" ]; then
+       echo "*** Project description file hasn't been set" >&2
+       exit 1
+fi
+
 # --- Check types
 newrev_type=$(git-cat-file -t $newrev)
 
 case "$refname","$newrev_type" in
        refs/tags/*,commit)
                # un-annotated tag
-               refname_type="tag"
                short_refname=${refname##refs/tags/}
                if [ "$allowunannotated" != "true" ]; then
-                       echo "*** The un-annotated tag, $short_refname is not allowed in this repository" >&2
+                       echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
                        echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
                        exit 1
                fi
                ;;
        refs/tags/*,tag)
                # annotated tag
-               refname_type="annotated tag"
-               short_refname=${refname##refs/tags/}
-               # change recipients
-               if [ -n "$announcerecipients" ]; then
-                       recipients="$announcerecipients"
-               fi
                ;;
        refs/heads/*,commit)
                # branch
-               refname_type="branch"
-               short_refname=${refname##refs/heads/}
                ;;
        refs/remotes/*,commit)
                # tracking branch
-               refname_type="tracking branch"
-               short_refname=${refname##refs/remotes/}
-               # Should this even be allowed?
-               echo "*** Push-update of tracking branch, $refname.  No email generated." >&2
-               exit 0
                ;;
        *)
                # Anything else (is there anything else?)
-               echo "*** Update hook: unknown type of update, \"$newrev_type\", to ref $refname" >&2
+               echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
                exit 1
                ;;
 esac
 
-# Check if we've got anyone to send to
-if [ -z "$recipients" ]; then
-       # If the email isn't sent, then at least give the user some idea of what command
-       # would generate the email at a later date
-       echo "*** No recipients found - no email will be sent, but the push will continue" >&2
-       echo "*** for $0 $1 $2 $3" >&2
-       exit 0
-fi
-
-# --- Email parameters
-committer=$(git show --pretty=full -s $newrev | grep "^Commit: " | sed -e "s/^Commit: //")
-describe=$(git describe $newrev 2>/dev/null)
-if [ -z "$describe" ]; then
-       describe=$newrev
-fi
-
-# --- Email (all stdout will be the email)
-(
-# Generate header
-cat <<-EOF
-From: $committer
-To: $recipients
-Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname now at $describe
-X-Git-Refname: $refname
-X-Git-Reftype: $refname_type
-X-Git-Oldrev: $oldrev
-X-Git-Newrev: $newrev
-
-Hello,
-
-This is an automated email from the git hooks/update script, it was
-generated because a ref change was pushed to the repository.
-
-Updating $refname_type, $short_refname,
-EOF
-
-case "$refname_type" in
-       "tracking branch"|branch)
-               if expr "$oldrev" : '0*$' >/dev/null
-               then
-                       # If the old reference is "0000..0000" then this is a new branch
-                       # and so oldrev is not valid
-                       echo "  as a new  $refname_type"
-                   echo "        to  $newrev ($newrev_type)"
-                       echo ""
-                       echo $LOGBEGIN
-                       # This shows all log entries that are not already covered by
-                       # another ref - i.e. commits that are now accessible from this
-                       # ref that were previously not accessible
-                       git log $newrev --not --all
-                       echo $LOGEND
-               else
-                       # oldrev is valid
-                       oldrev_type=$(git-cat-file -t "$oldrev")
-
-                       # Now the problem is for cases like this:
-                       #   * --- * --- * --- * (oldrev)
-                       #          \
-                       #           * --- * --- * (newrev)
-                       # i.e. there is no guarantee that newrev is a strict subset
-                       # of oldrev - (would have required a force, but that's allowed).
-                       # So, we can't simply say rev-list $oldrev..$newrev.  Instead
-                       # we find the common base of the two revs and list from there
-                       baserev=$(git-merge-base $oldrev $newrev)
-
-                       # Commit with a parent
-                       for rev in $(git-rev-list $newrev --not $baserev --all)
-                       do
-                               revtype=$(git-cat-file -t "$rev")
-                               echo "       via  $rev ($revtype)"
-                       done
-                       if [ "$baserev" = "$oldrev" ]; then
-                               echo "      from  $oldrev ($oldrev_type)"
-                       else
-                               echo "  based on  $baserev"
-                               echo "      from  $oldrev ($oldrev_type)"
-                               echo ""
-                               echo "This ref update crossed a branch point; i.e. the old rev is not a strict subset"
-                               echo "of the new rev.  This occurs, when you --force push a change in a situation"
-                               echo "like this:"
-                               echo ""
-                               echo " * -- * -- B -- O -- O -- O ($oldrev)"
-                               echo "            \\"
-                               echo "             N -- N -- N ($newrev)"
-                               echo ""
-                               echo "Therefore, we assume that you've already had alert emails for all of the O"
-                               echo "revisions, and now give you all the revisions in the N branch from the common"
-                               echo "base, B ($baserev), up to the new revision."
-                       fi
-                       echo ""
-                       echo $LOGBEGIN
-                       git log $newrev --not $baserev --all
-                       echo $LOGEND
-                       echo ""
-                       echo "Diffstat:"
-                       git-diff-tree --no-color --stat -M -C --find-copies-harder $baserev..$newrev
-               fi
-               ;;
-       "annotated tag")
-               # Should we allow changes to annotated tags?
-               if expr "$oldrev" : '0*$' >/dev/null
-               then
-                       # If the old reference is "0000..0000" then this is a new atag
-                       # and so oldrev is not valid
-                       echo "        to  $newrev ($newrev_type)"
-               else
-                       echo "        to  $newrev ($newrev_type)"
-                       echo "      from  $oldrev"
-               fi
-
-               # If this tag succeeds another, then show which tag it replaces
-               prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null)
-               if [ -n "$prevtag" ]; then
-                       echo "  replaces  $prevtag"
-               fi
-
-               # Read the tag details
-               eval $(git cat-file tag $newrev | \
-                       sed -n '4s/tagger \([^>]*>\)[^0-9]*\([0-9]*\).*/tagger="\1" ts="\2"/p')
-               tagged=$(date --date="1970-01-01 00:00:00 +0000 $ts seconds" +"$DATEFORMAT")
-
-               echo " tagged by  $tagger"
-               echo "        on  $tagged"
-
-               echo ""
-               echo $LOGBEGIN
-               echo ""
-
-               if [ -n "$prevtag" ]; then
-                       git rev-list --pretty=short "$prevtag..$newrev" | git shortlog
-               else
-                       git rev-list --pretty=short $newrev | git shortlog
-               fi
-
-               echo $LOGEND
-               echo ""
-               ;;
-       *)
-               # By default, unannotated tags aren't allowed in; if
-               # they are though, it's debatable whether we would even want an
-               # email to be generated; however, I don't want to add another config
-               # option just for that.
-               #
-               # Unannotated tags are more about marking a point than releasing
-               # a version; therefore we don't do the shortlog summary that we
-               # do for annotated tags above - we simply show that the point has
-               # been marked, and print the log message for the marked point for
-               # reference purposes
-               #
-               # Note this section also catches any other reference type (although
-               # there aren't any) and deals with them in the same way.
-               if expr "$oldrev" : '0*$' >/dev/null
-               then
-                       # If the old reference is "0000..0000" then this is a new tag
-                       # and so oldrev is not valid
-                       echo "  as a new  $refname_type"
-                       echo "        to  $newrev ($newrev_type)"
-               else
-                       echo "        to  $newrev ($newrev_type)"
-                       echo "      from  $oldrev"
-               fi
-               echo ""
-               echo $LOGBEGIN
-               git-show --no-color --root -s $newrev
-               echo $LOGEND
-               echo ""
-               ;;
-esac
-
-# Footer
-cat <<-EOF
-
-hooks/update
----
-Git Source Code Management System
-$0 $1 \\
-  $2 \\
-  $3
-EOF
-#) | cat >&2
-) | /usr/sbin/sendmail -t
-
 # --- Finished
 exit 0
index c8275823d0eb976e95b256f2bce5497f45d5da77..852498eb49fe0cba5e1cd21661288e8321a4b20e 100644 (file)
@@ -5,9 +5,8 @@
 #include "diff.h"
 #include "tree.h"
 
-static char *malloc_base(const char *base, const char *path, int pathlen)
+static char *malloc_base(const char *base, int baselen, const char *path, int pathlen)
 {
-       int baselen = strlen(base);
        char *newbase = xmalloc(baselen + pathlen + 2);
        memcpy(newbase, base, baselen);
        memcpy(newbase + baselen, path, pathlen);
@@ -16,9 +15,9 @@ static char *malloc_base(const char *base, const char *path, int pathlen)
 }
 
 static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,
-                      const char *base);
+                      const char *base, int baselen);
 
-static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
+static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const char *base, int baselen, struct diff_options *opt)
 {
        unsigned mode1, mode2;
        const char *path1, *path2;
@@ -28,15 +27,15 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
        sha1 = tree_entry_extract(t1, &path1, &mode1);
        sha2 = tree_entry_extract(t2, &path2, &mode2);
 
-       pathlen1 = strlen(path1);
-       pathlen2 = strlen(path2);
+       pathlen1 = tree_entry_len(path1, sha1);
+       pathlen2 = tree_entry_len(path2, sha2);
        cmp = base_name_compare(path1, pathlen1, mode1, path2, pathlen2, mode2);
        if (cmp < 0) {
-               show_entry(opt, "-", t1, base);
+               show_entry(opt, "-", t1, base, baselen);
                return -1;
        }
        if (cmp > 0) {
-               show_entry(opt, "+", t2, base);
+               show_entry(opt, "+", t2, base, baselen);
                return 1;
        }
        if (!opt->find_copies_harder && !hashcmp(sha1, sha2) && mode1 == mode2)
@@ -47,14 +46,14 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
         * file, we need to consider it a remove and an add.
         */
        if (S_ISDIR(mode1) != S_ISDIR(mode2)) {
-               show_entry(opt, "-", t1, base);
-               show_entry(opt, "+", t2, base);
+               show_entry(opt, "-", t1, base, baselen);
+               show_entry(opt, "+", t2, base, baselen);
                return 0;
        }
 
        if (opt->recursive && S_ISDIR(mode1)) {
                int retval;
-               char *newbase = malloc_base(base, path1, pathlen1);
+               char *newbase = malloc_base(base, baselen, path1, pathlen1);
                if (opt->tree_in_recursive)
                        opt->change(opt, mode1, mode2,
                                    sha1, sha2, base, path1);
@@ -67,32 +66,46 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
        return 0;
 }
 
-static int interesting(struct tree_desc *desc, const char *base, struct diff_options *opt)
+/*
+ * Is a tree entry interesting given the pathspec we have?
+ *
+ * Return:
+ *  - 2 for "yes, and all subsequent entries will be"
+ *  - 1 for yes
+ *  - zero for no
+ *  - negative for "no, and no subsequent entries will be either"
+ */
+static int tree_entry_interesting(struct tree_desc *desc, const char *base, int baselen, struct diff_options *opt)
 {
        const char *path;
+       const unsigned char *sha1;
        unsigned mode;
        int i;
-       int baselen, pathlen;
+       int pathlen;
+       int never_interesting = -1;
 
        if (!opt->nr_paths)
                return 1;
 
-       (void)tree_entry_extract(desc, &path, &mode);
+       sha1 = tree_entry_extract(desc, &path, &mode);
 
-       pathlen = strlen(path);
-       baselen = strlen(base);
+       pathlen = tree_entry_len(path, sha1);
 
-       for (i=0; i < opt->nr_paths; i++) {
+       for (i = 0; i < opt->nr_paths; i++) {
                const char *match = opt->paths[i];
                int matchlen = opt->pathlens[i];
+               int m = -1; /* signals that we haven't called strncmp() */
 
                if (baselen >= matchlen) {
                        /* If it doesn't match, move along... */
                        if (strncmp(base, match, matchlen))
                                continue;
 
-                       /* The base is a subdirectory of a path which was specified. */
-                       return 1;
+                       /*
+                        * The base is a subdirectory of a path which
+                        * was specified, so all of them are interesting.
+                        */
+                       return 2;
                }
 
                /* Does the base match? */
@@ -102,6 +115,37 @@ static int interesting(struct tree_desc *desc, const char *base, struct diff_opt
                match += baselen;
                matchlen -= baselen;
 
+               if (never_interesting) {
+                       /*
+                        * We have not seen any match that sorts later
+                        * than the current path.
+                        */
+
+                       /*
+                        * Does match sort strictly earlier than path
+                        * with their common parts?
+                        */
+                       m = strncmp(match, path,
+                                   (matchlen < pathlen) ? matchlen : pathlen);
+                       if (m < 0)
+                               continue;
+
+                       /*
+                        * If we come here even once, that means there is at
+                        * least one pathspec that would sort equal to or
+                        * later than the path we are currently looking at.
+                        * In other words, if we have never reached this point
+                        * after iterating all pathspecs, it means all
+                        * pathspecs are either outside of base, or inside the
+                        * base but sorts strictly earlier than the current
+                        * one.  In either case, they will never match the
+                        * subsequent entries.  In such a case, we initialized
+                        * the variable to -1 and that is what will be
+                        * returned, allowing the caller to terminate early.
+                        */
+                       never_interesting = 0;
+               }
+
                if (pathlen > matchlen)
                        continue;
 
@@ -112,27 +156,50 @@ static int interesting(struct tree_desc *desc, const char *base, struct diff_opt
                                continue;
                }
 
-               if (strncmp(path, match, pathlen))
-                       continue;
-
-               return 1;
+               if (m == -1)
+                       /*
+                        * we cheated and did not do strncmp(), so we do
+                        * that here.
+                        */
+                       m = strncmp(match, path, pathlen);
+
+               /*
+                * If common part matched earlier then it is a hit,
+                * because we rejected the case where path is not a
+                * leading directory and is shorter than match.
+                */
+               if (!m)
+                       return 1;
        }
-       return 0; /* No matches */
+       return never_interesting; /* No matches */
 }
 
 /* A whole sub-tree went away or appeared */
-static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base)
+static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base, int baselen)
 {
+       int all_interesting = 0;
        while (desc->size) {
-               if (interesting(desc, base, opt))
-                       show_entry(opt, prefix, desc, base);
+               int show;
+
+               if (all_interesting)
+                       show = 1;
+               else {
+                       show = tree_entry_interesting(desc, base, baselen,
+                                                     opt);
+                       if (show == 2)
+                               all_interesting = 1;
+               }
+               if (show < 0)
+                       break;
+               if (show)
+                       show_entry(opt, prefix, desc, base, baselen);
                update_tree_entry(desc);
        }
 }
 
 /* A file entry went away or appeared */
 static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,
-                      const char *base)
+                      const char *base, int baselen)
 {
        unsigned mode;
        const char *path;
@@ -140,16 +207,18 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
 
        if (opt->recursive && S_ISDIR(mode)) {
                enum object_type type;
-               char *newbase = malloc_base(base, path, strlen(path));
+               int pathlen = tree_entry_len(path, sha1);
+               char *newbase = malloc_base(base, baselen, path, pathlen);
                struct tree_desc inner;
                void *tree;
+               unsigned long size;
 
-               tree = read_sha1_file(sha1, &type, &inner.size);
+               tree = read_sha1_file(sha1, &type, &size);
                if (!tree || type != OBJ_TREE)
                        die("corrupt tree sha %s", sha1_to_hex(sha1));
 
-               inner.buf = tree;
-               show_tree(opt, prefix, &inner, newbase);
+               init_tree_desc(&inner, tree, size);
+               show_tree(opt, prefix, &inner, newbase, baselen + 1 + pathlen);
 
                free(tree);
                free(newbase);
@@ -158,28 +227,54 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
        }
 }
 
-int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
+static void skip_uninteresting(struct tree_desc *t, const char *base, int baselen, struct diff_options *opt)
 {
-       while (t1->size | t2->size) {
-               if (opt->nr_paths && t1->size && !interesting(t1, base, opt)) {
-                       update_tree_entry(t1);
-                       continue;
+       int all_interesting = 0;
+       while (t->size) {
+               int show;
+
+               if (all_interesting)
+                       show = 1;
+               else {
+                       show = tree_entry_interesting(t, base, baselen, opt);
+                       if (show == 2)
+                               all_interesting = 1;
                }
-               if (opt->nr_paths && t2->size && !interesting(t2, base, opt)) {
-                       update_tree_entry(t2);
+               if (!show) {
+                       update_tree_entry(t);
                        continue;
                }
+               /* Skip it all? */
+               if (show < 0)
+                       t->size = 0;
+               return;
+       }
+}
+
+int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
+{
+       int baselen = strlen(base);
+
+       for (;;) {
+               if (opt->quiet && opt->has_changes)
+                       break;
+               if (opt->nr_paths) {
+                       skip_uninteresting(t1, base, baselen, opt);
+                       skip_uninteresting(t2, base, baselen, opt);
+               }
                if (!t1->size) {
-                       show_entry(opt, "+", t2, base);
+                       if (!t2->size)
+                               break;
+                       show_entry(opt, "+", t2, base, baselen);
                        update_tree_entry(t2);
                        continue;
                }
                if (!t2->size) {
-                       show_entry(opt, "-", t1, base);
+                       show_entry(opt, "-", t1, base, baselen);
                        update_tree_entry(t1);
                        continue;
                }
-               switch (compare_tree_entry(t1, t2, base, opt)) {
+               switch (compare_tree_entry(t1, t2, base, baselen, opt)) {
                case -1:
                        update_tree_entry(t1);
                        continue;
@@ -199,16 +294,17 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha
 {
        void *tree1, *tree2;
        struct tree_desc t1, t2;
+       unsigned long size1, size2;
        int retval;
 
-       tree1 = read_object_with_reference(old, tree_type, &t1.size, NULL);
+       tree1 = read_object_with_reference(old, tree_type, &size1, NULL);
        if (!tree1)
                die("unable to read source tree (%s)", sha1_to_hex(old));
-       tree2 = read_object_with_reference(new, tree_type, &t2.size, NULL);
+       tree2 = read_object_with_reference(new, tree_type, &size2, NULL);
        if (!tree2)
                die("unable to read destination tree (%s)", sha1_to_hex(new));
-       t1.buf = tree1;
-       t2.buf = tree2;
+       init_tree_desc(&t1, tree1, size1);
+       init_tree_desc(&t2, tree2, size2);
        retval = diff_tree(&t1, &t2, base, opt);
        free(tree1);
        free(tree2);
@@ -219,15 +315,15 @@ int diff_root_tree_sha1(const unsigned char *new, const char *base, struct diff_
 {
        int retval;
        void *tree;
+       unsigned long size;
        struct tree_desc empty, real;
 
-       tree = read_object_with_reference(new, tree_type, &real.size, NULL);
+       tree = read_object_with_reference(new, tree_type, &size, NULL);
        if (!tree)
                die("unable to read root tree (%s)", sha1_to_hex(new));
-       real.buf = tree;
+       init_tree_desc(&real, tree, size);
 
-       empty.size = 0;
-       empty.buf = "";
+       init_tree_desc(&empty, "", 0);
        retval = diff_tree(&empty, &real, base, opt);
        free(tree);
        return retval;
index 70f899957e8ce511f0f5a470e8b6927d58d1b63e..cbb24eb3f663cd0370f528d983a2266a55b91c4f 100644 (file)
@@ -2,6 +2,44 @@
 #include "tree-walk.h"
 #include "tree.h"
 
+static const char *get_mode(const char *str, unsigned int *modep)
+{
+       unsigned char c;
+       unsigned int mode = 0;
+
+       while ((c = *str++) != ' ') {
+               if (c < '0' || c > '7')
+                       return NULL;
+               mode = (mode << 3) + (c - '0');
+       }
+       *modep = mode;
+       return str;
+}
+
+static void decode_tree_entry(struct tree_desc *desc, const void *buf, unsigned long size)
+{
+       const char *path;
+       unsigned int mode, len;
+
+       path = get_mode(buf, &mode);
+       if (!path)
+               die("corrupt tree file");
+       len = strlen(path) + 1;
+
+       /* Initialize the descriptor entry */
+       desc->entry.path = path;
+       desc->entry.mode = mode;
+       desc->entry.sha1 = (const unsigned char *)(path + len);
+}
+
+void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
+{
+       desc->buffer = buffer;
+       desc->size = size;
+       if (size)
+               decode_tree_entry(desc, buffer, size);
+}
+
 void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1)
 {
        unsigned long size = 0;
@@ -12,16 +50,15 @@ void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1)
                if (!buf)
                        die("unable to read tree %s", sha1_to_hex(sha1));
        }
-       desc->size = size;
-       desc->buf = buf;
+       init_tree_desc(desc, buf, size);
        return buf;
 }
 
 static int entry_compare(struct name_entry *a, struct name_entry *b)
 {
        return base_name_compare(
-                       a->path, a->pathlen, a->mode,
-                       b->path, b->pathlen, b->mode);
+                       a->path, tree_entry_len(a->path, a->sha1), a->mode,
+                       b->path, tree_entry_len(b->path, b->sha1), b->mode);
 }
 
 static void entry_clear(struct name_entry *a)
@@ -31,80 +68,33 @@ static void entry_clear(struct name_entry *a)
 
 static void entry_extract(struct tree_desc *t, struct name_entry *a)
 {
-       a->sha1 = tree_entry_extract(t, &a->path, &a->mode);
-       a->pathlen = strlen(a->path);
+       *a = t->entry;
 }
 
 void update_tree_entry(struct tree_desc *desc)
 {
-       const void *buf = desc->buf;
+       const void *buf = desc->buffer;
+       const unsigned char *end = desc->entry.sha1 + 20;
        unsigned long size = desc->size;
-       int len = strlen(buf) + 1 + 20;
+       unsigned long len = end - (const unsigned char *)buf;
 
        if (size < len)
                die("corrupt tree file");
-       desc->buf = (char *) buf + len;
-       desc->size = size - len;
-}
-
-static const char *get_mode(const char *str, unsigned int *modep)
-{
-       unsigned char c;
-       unsigned int mode = 0;
-
-       while ((c = *str++) != ' ') {
-               if (c < '0' || c > '7')
-                       return NULL;
-               mode = (mode << 3) + (c - '0');
-       }
-       *modep = mode;
-       return str;
-}
-
-const unsigned char *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
-{
-       const void *tree = desc->buf;
-       unsigned long size = desc->size;
-       int len = strlen(tree)+1;
-       const unsigned char *sha1 = (unsigned char *) tree + len;
-       const char *path;
-       unsigned int mode;
-
-       path = get_mode(tree, &mode);
-       if (!path || size < len + 20)
-               die("corrupt tree file");
-       *pathp = path;
-       *modep = canon_mode(mode);
-       return sha1;
+       buf = end;
+       size -= len;
+       desc->buffer = buf;
+       desc->size = size;
+       if (size)
+               decode_tree_entry(desc, buf, size);
 }
 
 int tree_entry(struct tree_desc *desc, struct name_entry *entry)
 {
-       const void *tree = desc->buf;
-       const char *path;
-       unsigned long len, size = desc->size;
-
-       if (!size)
+       if (!desc->size)
                return 0;
 
-       path = get_mode(tree, &entry->mode);
-       if (!path)
-               die("corrupt tree file");
-
-       entry->path = path;
-       len = strlen(path);
-       entry->pathlen = len;
-
-       path += len + 1;
-       entry->sha1 = (const unsigned char *) path;
-
-       path += 20;
-       len = path - (char *) tree;
-       if (len > size)
-               die("corrupt tree file");
-
-       desc->buf = path;
-       desc->size = size - len;
+       *entry = desc->entry;
+       update_tree_entry(desc);
        return 1;
 }
 
@@ -169,7 +159,7 @@ static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char
 
                sha1 = tree_entry_extract(t, &entry, mode);
                update_tree_entry(t);
-               entrylen = strlen(entry);
+               entrylen = tree_entry_len(entry, sha1);
                if (entrylen > namelen)
                        continue;
                cmp = memcmp(name, entry, entrylen);
@@ -198,10 +188,11 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
 {
        int retval;
        void *tree;
+       unsigned long size;
        struct tree_desc t;
        unsigned char root[20];
 
-       tree = read_object_with_reference(tree_sha1, tree_type, &t.size, root);
+       tree = read_object_with_reference(tree_sha1, tree_type, &size, root);
        if (!tree)
                return -1;
 
@@ -210,7 +201,7 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
                return 0;
        }
 
-       t.buf = tree;
+       init_tree_desc(&t, tree, size);
        retval = find_tree_entry(&t, name, sha1, mode);
        free(tree);
        return retval;
index e57befa4dac0854906d5b792ce546dab0e6dcdc3..43458cf8ce3a115ee22bb3512749d456c93f783c 100644 (file)
@@ -1,19 +1,32 @@
 #ifndef TREE_WALK_H
 #define TREE_WALK_H
 
-struct tree_desc {
-       const void *buf;
-       unsigned long size;
-};
-
 struct name_entry {
        const unsigned char *sha1;
        const char *path;
        unsigned int mode;
-       int pathlen;
 };
 
+struct tree_desc {
+       const void *buffer;
+       struct name_entry entry;
+       unsigned int size;
+};
+
+static inline const unsigned char *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
+{
+       *pathp = desc->entry.path;
+       *modep = canon_mode(desc->entry.mode);
+       return desc->entry.sha1;
+}
+
+static inline int tree_entry_len(const char *name, const unsigned char *sha1)
+{
+       return (char *)sha1 - (char *)name - 1;
+}
+
 void update_tree_entry(struct tree_desc *);
+void init_tree_desc(struct tree_desc *desc, const void *buf, unsigned long size);
 const unsigned char *tree_entry_extract(struct tree_desc *, const char **, unsigned int *);
 
 /* Helper function that does both of the above and returns true for success */
diff --git a/tree.c b/tree.c
index 46923ee61bcce99e677abcc37e233359d39cc9fc..d188c0fbaee110a17ca7a0d16dcc979091f44ded 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -83,8 +83,7 @@ int read_tree_recursive(struct tree *tree,
        if (parse_tree(tree))
                return -1;
 
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
+       init_tree_desc(&desc, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry)) {
                if (!match_tree_entry(base, baselen, entry.path, entry.mode, match))
@@ -101,14 +100,15 @@ int read_tree_recursive(struct tree *tree,
                if (S_ISDIR(entry.mode)) {
                        int retval;
                        char *newbase;
+                       unsigned int pathlen = tree_entry_len(entry.path, entry.sha1);
 
-                       newbase = xmalloc(baselen + 1 + entry.pathlen);
+                       newbase = xmalloc(baselen + 1 + pathlen);
                        memcpy(newbase, base, baselen);
-                       memcpy(newbase + baselen, entry.path, entry.pathlen);
-                       newbase[baselen + entry.pathlen] = '/';
+                       memcpy(newbase + baselen, entry.path, pathlen);
+                       newbase[baselen + pathlen] = '/';
                        retval = read_tree_recursive(lookup_tree(entry.sha1),
                                                     newbase,
-                                                    baselen + entry.pathlen + 1,
+                                                    baselen + pathlen + 1,
                                                     stage, match, fn);
                        free(newbase);
                        if (retval)
@@ -151,18 +151,14 @@ static void track_tree_refs(struct tree *item)
        struct name_entry entry;
 
        /* Count how many entries there are.. */
-       desc.buf = item->buffer;
-       desc.size = item->size;
-       while (desc.size) {
+       init_tree_desc(&desc, item->buffer, item->size);
+       while (tree_entry(&desc, &entry))
                n_refs++;
-               update_tree_entry(&desc);
-       }
 
        /* Allocate object refs and walk it again.. */
        i = 0;
        refs = alloc_object_refs(n_refs);
-       desc.buf = item->buffer;
-       desc.size = item->size;
+       init_tree_desc(&desc, item->buffer, item->size);
        while (tree_entry(&desc, &entry)) {
                struct object *obj;
 
index 2e2232cbb07e61de3be74302fba67142a58a857b..ee10eea24cdd37d308066721c947b2d2449e786e 100644 (file)
@@ -27,8 +27,7 @@ static struct tree_entry_list *create_tree_entry_list(struct tree *tree)
        if (!tree->object.parsed)
                parse_tree(tree);
 
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
+       init_tree_desc(&desc, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &one)) {
                struct tree_entry_list *entry;
index 3653864e4bf704c024453df8d9abf26d89b4778d..bf91c0f73c658637d078ac93226148faa3bb0dc4 100644 (file)
@@ -236,12 +236,13 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
        return 0;
 }
 
-unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
+static unsigned long xdl_hash_record_with_whitespace(char const **data,
+               char const *top, long flags) {
        unsigned long ha = 5381;
        char const *ptr = *data;
 
        for (; ptr < top && *ptr != '\n'; ptr++) {
-               if (isspace(*ptr) && (flags & XDF_WHITESPACE_FLAGS)) {
+               if (isspace(*ptr)) {
                        const char *ptr2 = ptr;
                        while (ptr + 1 < top && isspace(ptr[1])
                                        && ptr[1] != '\n')
@@ -270,6 +271,23 @@ unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
 }
 
 
+unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
+       unsigned long ha = 5381;
+       char const *ptr = *data;
+
+       if (flags & XDF_WHITESPACE_FLAGS)
+               return xdl_hash_record_with_whitespace(data, top, flags);
+
+       for (; ptr < top && *ptr != '\n'; ptr++) {
+               ha += (ha << 5);
+               ha ^= (unsigned long) *ptr;
+       }
+       *data = ptr < top ? ptr + 1: ptr;
+
+       return ha;
+}
+
+
 unsigned int xdl_hashbits(unsigned int size) {
        unsigned int val = 1, bits = 0;