Merge branch 'sp/mmap'
authorJunio C Hamano <junkio@cox.net>
Sun, 7 Jan 2007 08:12:47 +0000 (00:12 -0800)
committerJunio C Hamano <junkio@cox.net>
Sun, 7 Jan 2007 08:12:47 +0000 (00:12 -0800)
* sp/mmap: (27 commits)
Spell default packedgitlimit slightly differently
Increase packedGit{Limit,WindowSize} on 64 bit systems.
Update packedGit config option documentation.
mmap: set FD_CLOEXEC for file descriptors we keep open for mmap()
pack-objects: fix use of use_pack().
Fix random segfaults in pack-objects.
Cleanup read_cache_from error handling.
Replace mmap with xmmap, better handling MAP_FAILED.
Release pack windows before reporting out of memory.
Default core.packdGitWindowSize to 1 MiB if NO_MMAP.
Test suite for sliding window mmap implementation.
Create pack_report() as a debugging aid.
Support unmapping windows on 'temporary' packfiles.
Improve error message when packfile mmap fails.
Ensure core.packedGitWindowSize cannot be less than 2 pages.
Load core configuration in git-verify-pack.
Fully activate the sliding window pack access.
Unmap individual windows rather than entire files.
Document why header parsing won't exceed a window.
Loop over pack_windows when inflating/accessing data.
...

Conflicts:

cache.h
pack-check.c

59 files changed:
Documentation/config.txt
Documentation/cvs-migration.txt
Documentation/fetch-options.txt
Documentation/git-branch.txt
Documentation/git-clone.txt
Documentation/git-pull.txt
Documentation/git-svn.txt
Documentation/git-svnimport.txt
Documentation/git-tag.txt
Documentation/glossary.txt
Documentation/hooks.txt
Documentation/pull-fetch-param.txt
Documentation/technical/send-pack-pipeline.txt [new file with mode: 0644]
Documentation/tutorial.txt
Documentation/urls.txt
Makefile
archive-tar.c
builtin-branch.c
builtin-prune.c
builtin-rerere.c
cache.h
commit.c
contrib/emacs/git.el
diff-lib.c
diff.c
diff.h
diffcore.h
fetch-pack.c
generate-cmdlist.sh
git-am.sh
git-clean.sh
git-commit.sh
git-fetch.sh
git-instaweb.sh
git-merge.sh
git-parse-remote.sh
git-pull.sh
git-remote.perl [new file with mode: 0755]
git-reset.sh
git-svn.perl
git-svnimport.perl
git-tag.sh
git-verify-tag.sh
gitweb/gitweb.perl
lockfile.c
merge-recursive.c
refs.c
send-pack.c
t/lib-git-svn.sh
t/t5401-update-hooks.sh [new file with mode: 0755]
t/t9100-git-svn-basic.sh
t/t9101-git-svn-props.sh
t/t9103-git-svn-graft-branches.sh
t/t9104-git-svn-follow-parent.sh
t/test-lib.sh
tree-walk.c
write_or_die.c
wt-status.c
wt-status.h
index b24d9dff6209d6cc101cee5568914938343c9880..b4aae0d0aeba86e995b37a5fe3ab7ec61e268687 100644 (file)
@@ -173,6 +173,21 @@ branch.<name>.merge::
        this option, `git pull` defaults to merge the first refspec fetched.
        Specify multiple values to get an octopus merge.
 
+color.branch::
+       A boolean to enable/disable color in the output of
+       gitlink:git-branch[1]. May be set to `true` (or `always`),
+       `false` (or `never`) or `auto`, in which case colors are used
+       only when the output is to a terminal. Defaults to false.
+
+color.branch.<slot>::
+       Use customized color for branch coloration. `<slot>` is one of
+       `current` (the current branch), `local` (a local branch),
+       `remote` (a tracking branch in refs/remotes/), `plain` (other
+       refs), or `reset` (the normal terminal color).  The value for
+       these configuration variables can be one of: `normal`, `bold`,
+       `dim`, `ul`, `blink`, `reverse`, `reset`, `black`, `red`,
+       `green`, `yellow`, `blue`, `magenta`, `cyan`, or `white`.
+
 color.diff::
        When true (or `always`), always use colors in patch.
        When false (or `never`), never.  When set to `auto`, use
@@ -183,11 +198,8 @@ color.diff.<slot>::
        specifies which part of the patch to use the specified
        color, and is one of `plain` (context text), `meta`
        (metainformation), `frag` (hunk header), `old` (removed
-       lines), or `new` (added lines).  The value for these
-       configuration variables can be one of: `normal`, `bold`,
-       `dim`, `ul`, `blink`, `reverse`, `reset`, `black`,
-       `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, or
-       `white`.
+       lines), or `new` (added lines).  The values of these
+       variables may be specified as in color.branch.<slot>.
 
 color.pager::
        A boolean to enable/disable colored output when the pager is in
@@ -205,7 +217,7 @@ color.status.<slot>::
        `added` or `updated` (files which are added but not committed),
        `changed` (files which are changed but not added in the index),
        or `untracked` (files which are not tracked by git). The values of
-       these variables may be specified as in color.diff.<slot>.
+       these variables may be specified as in color.branch.<slot>.
 
 diff.renameLimit::
        The number of files to consider when performing the copy/rename
index b657f4589f95257547b646ef22d3201fefbae6d6..8e09beaa799dbff462b258e5593fd5c79c91312a 100644 (file)
@@ -34,13 +34,10 @@ them first before running git pull.
 
 [NOTE]
 ================================
-The first `git clone` places the following in the
-`my-project/.git/remotes/origin` file, and that's why the previous step
-and the next step both work.
-------------
-URL: foo.com:/pub/project.git/
-Pull: refs/heads/master:refs/remotes/origin/master
-------------
+The `pull` command knows where to get updates from because of certain
+configuration variables that were set by the first `git clone`
+command; see `git repo-config -l` and the gitlink:git-repo-config[1] man
+page for details.
 ================================
 
 You can update the shared repository with your changes by first committing
index 13f34d3ca28a402d56420f3bf5615837cf529e0a..5b4d184a73d013b8da11db3adb08905c0101bf88 100644 (file)
 -u, \--update-head-ok::
        By default `git-fetch` refuses to update the head which
        corresponds to the current branch.  This flag disables the
-       check.  Note that fetching into the current branch will not
-       update the index and working directory, so use it with care.
+       check.  This is purely for the internal use for `git-pull`
+       to communicate with `git-fetch`, and unless you are
+       implementing your own Porcelain you are not supposed to
+       use it.
+
+\--depth=<depth>::
+       Deepen the history of a 'shallow' repository created by
+       `git clone` with `--depth=<depth>` option (see gitlink:git-clone[1])
+       by the specified number of commits.
 
index c464bd2fda2e4828d2e5a36ca05b4c66ad592f7c..e872fc89fc17caefc844bd5129f74040bada5cb9 100644 (file)
@@ -8,7 +8,7 @@ git-branch - List, create, or delete branches.
 SYNOPSIS
 --------
 [verse]
-'git-branch' [-r | -a] [-v [--abbrev=<length>]]
+'git-branch' [--color | --no-color] [-r | -a] [-v [--abbrev=<length>]]
 'git-branch' [-l] [-f] <branchname> [<start-point>]
 'git-branch' (-m | -M) [<oldbranch>] <newbranch>
 'git-branch' (-d | -D) [-r] <branchname>...
@@ -60,6 +60,13 @@ OPTIONS
 -M::
        Move/rename a branch even if the new branchname already exists.
 
+--color::
+       Color branches to highlight current, local, and remote branches.
+
+--no-color::
+       Turn off branch colors, even when the configuration file gives the
+       default to color output.
+
 -r::
        List or delete (if used with -d) the remote-tracking branches.
 
index 874934a33243106544181ffeae2fee8b03ff5e8d..a78207461db41172114b865a49b068fe4aac2a9a 100644 (file)
@@ -11,26 +11,27 @@ SYNOPSIS
 [verse]
 'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare]
          [-o <name>] [-u <upload-pack>] [--reference <repository>]
-         <repository> [<directory>]
+         [--depth=<depth>] <repository> [<directory>]
 
 DESCRIPTION
 -----------
 
 Clones a repository into a newly created directory, creates
 remote-tracking branches for each branch in the cloned repository
-(visible using `git branch -r`), and creates and checks out a master
-branch equal to the cloned repository's master branch.
+(visible using `git branch -r`), and creates and checks out an initial
+branch equal to the cloned repository's currently active branch.
 
 After the clone, a plain `git fetch` without arguments will update
 all the remote-tracking branches, and a `git pull` without
 arguments will in addition merge the remote master branch into the
-current branch.
+current master branch, if any.
 
 This default configuration is achieved by creating references to
 the remote branch heads under `$GIT_DIR/refs/remotes/origin` and
 by initializing `remote.origin.url` and `remote.origin.fetch`
 configuration variables.
 
+
 OPTIONS
 -------
 --local::
@@ -75,16 +76,13 @@ OPTIONS
        Also the branch heads at the remote are copied directly
        to corresponding local branch heads, without mapping
        them to `refs/remotes/origin/`.  When this option is
-       used, neither the `origin` branch nor the default
-       `remotes/origin` file is created.
+       used, neither remote-tracking branches nor the related
+       configuration variables are created.
 
 --origin <name>::
 -o <name>::
-       Instead of using the branch name 'origin' to keep track
-       of the upstream repository, use <name> instead.  Note
-       that the shorthand name stored in `remotes/origin` is
-       not affected, but the local branch name to pull the
-       remote `master` branch into is.
+       Instead of using the remote name 'origin' to keep track
+       of the upstream repository, use <name> instead.
 
 --upload-pack <upload-pack>::
 -u <upload-pack>::
@@ -98,6 +96,15 @@ OPTIONS
        if unset the templates are taken from the installation
        defined default, typically `/usr/share/git-core/templates`.
 
+--depth=<depth>::
+       Create a 'shallow' clone with a history truncated to the
+       specified number of revs.  A shallow repository has
+       number of limitations (you cannot clone or fetch from
+       it, nor push from nor into it), but is adequate if you
+       want to only look at near the tip of a large project
+       with a long history, and would want to send in a fixes
+       as patches.
+
 <repository>::
        The (possibly remote) repository to clone from.  It can
        be any URL git-fetch supports.
index 2a5aea73bafedd62f20cecb1fbf5069b515022bd..13be992006d8252ca737e37dd21ed8f2d1e13412 100644 (file)
@@ -37,17 +37,27 @@ EXAMPLES
 --------
 
 git pull, git pull origin::
-       Fetch the default head from the repository you cloned
-       from and merge it into your current branch.
-
-git pull -s ours . obsolete::
-       Merge local branch `obsolete` into the current branch,
-       using `ours` merge strategy.
+       Update the remote-tracking branches for the repository
+       you cloned from, then merge one of them into your
+       current branch.  Normally the branch merged in is
+       the HEAD of the remote repository, but the choice is
+       determined by the branch.<name>.remote and
+       branch.<name>.merge options; see gitlink:git-repo-config[1]
+       for details.
+
+git pull origin next::
+       Merge into the current branch the remote branch `next`;
+       leaves a copy of `next` temporarily in FETCH_HEAD, but
+       does not update any remote-tracking branches.
 
 git pull . fixes enhancements::
        Bundle local branch `fixes` and `enhancements` on top of
        the current branch, making an Octopus merge.
 
+git pull -s ours . obsolete::
+       Merge local branch `obsolete` into the current branch,
+       using `ours` merge strategy.
+
 git pull --no-commit . maint::
        Merge local branch `maint` into the current branch, but
        do not make a commit automatically.  This can be used
@@ -61,48 +71,19 @@ release/version name would be acceptable.
 Command line pull of multiple branches from one repository::
 +
 ------------------------------------------------
-$ cat .git/remotes/origin
-URL: git://git.kernel.org/pub/scm/git/git.git
-Pull: master:origin
-
 $ git checkout master
-$ git fetch origin master:origin +pu:pu maint:maint
-$ git pull . origin
+$ git fetch origin +pu:pu maint:tmp
+$ git pull . tmp
 ------------------------------------------------
 +
-Here, a typical `.git/remotes/origin` file from a
-`git-clone` operation is used in combination with
-command line options to `git-fetch` to first update
-multiple branches of the local repository and then
-to merge the remote `origin` branch into the local
-`master` branch.  The local `pu` branch is updated
-even if it does not result in a fast forward update.
-Here, the pull can obtain its objects from the local
-repository using `.`, as the previous `git-fetch` is
-known to have already obtained and made available
-all the necessary objects.
-
-
-Pull of multiple branches from one repository using `.git/remotes` file::
+This updates (or creates, as necessary) branches `pu` and `tmp`
+in the local repository by fetching from the branches
+(respectively) `pu` and `maint` from the remote repository.
 +
-------------------------------------------------
-$ cat .git/remotes/origin
-URL: git://git.kernel.org/pub/scm/git/git.git
-Pull: master:origin
-Pull: +pu:pu
-Pull: maint:maint
-
-$ git checkout master
-$ git pull origin
-------------------------------------------------
+The `pu` branch will be updated even if it is does not
+fast-forward; the others will not be.
 +
-Here, a typical `.git/remotes/origin` file from a
-`git-clone` operation has been hand-modified to include
-the branch-mapping of additional remote and local
-heads directly.  A single `git-pull` operation while
-in the `master` branch will fetch multiple heads and
-merge the remote `origin` head into the current,
-local `master` branch.
+The final command then merges the newly fetched `tmp` into master.
 
 
 If you tried a pull which resulted in a complex conflicts and
@@ -112,7 +93,7 @@ gitlink:git-reset[1].
 
 SEE ALSO
 --------
-gitlink:git-fetch[1], gitlink:git-merge[1]
+gitlink:git-fetch[1], gitlink:git-merge[1], gitlink:git-repo-config[1]
 
 
 Author
index f5f57e8f87d4271a634f13f33d6b33af43909473..ce63defffde85f4eb97d256417f78b5bc51365c6 100644 (file)
@@ -3,7 +3,7 @@ git-svn(1)
 
 NAME
 ----
-git-svn - bidirectional operation between a single Subversion branch and git
+git-svn - bidirectional operation between Subversion and git
 
 SYNOPSIS
 --------
@@ -11,24 +11,20 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-git-svn is a simple conduit for changesets between a single Subversion
-branch and git. It is not to be confused with gitlink:git-svnimport[1].
-They were designed with very different goals in mind.
+git-svn is a simple conduit for changesets between Subversion and git.
+It is not to be confused with gitlink:git-svnimport[1], which is
+read-only and geared towards tracking multiple branches.
 
-git-svn is designed for an individual developer who wants a
+git-svn was originally designed for an individual developer who wants a
 bidirectional flow of changesets between a single branch in Subversion
-and an arbitrary number of branches in git.  git-svnimport is designed
-for read-only operation on repositories that match a particular layout
-(albeit the recommended one by SVN developers).
+and an arbitrary number of branches in git.  Since its inception,
+git-svn has gained the ability to track multiple branches in a manner
+similar to git-svnimport; but it cannot (yet) automatically detect new
+branches and tags like git-svnimport does.
 
-For importing svn, git-svnimport is potentially more powerful when
-operating on repositories organized under the recommended
-trunk/branch/tags structure, and should be faster, too.
-
-git-svn mostly ignores the very limited view of branching that
-Subversion has.  This allows git-svn to be much easier to use,
-especially on repositories that are not organized in a manner that
-git-svnimport is designed for.
+git-svn is especially useful when it comes to tracking repositories
+not organized in the way Subversion developers recommend (trunk,
+branches, tags directories).
 
 COMMANDS
 --------
@@ -57,11 +53,13 @@ See '<<fetch-args,Additional Fetch Arguments>>' if you are interested in
 manually joining branches on commit.
 
 'dcommit'::
-       Commit all diffs from a specified head directly to the SVN
+       Commit each diff from a specified head directly to the SVN
        repository, and then rebase or reset (depending on whether or
-       not there is a diff between SVN and head).  It is recommended
-       that you run git-svn fetch and rebase (not pull) your commits
-       against the latest changes in the SVN repository.
+       not there is a diff between SVN and head).  This will create
+       a revision in SVN for each commit in git.
+       It is recommended that you run git-svn fetch and rebase (not
+       pull or merge) your commits against the latest changes in the
+       SVN repository.
        An optional command-line argument may be specified as an
        alternative to HEAD.
        This is advantageous over 'set-tree' (below) because it produces
@@ -370,7 +368,7 @@ SVN was very wrong.
 Basic Examples
 ~~~~~~~~~~~~~~
 
-Tracking and contributing to a Subversion-managed project:
+Tracking and contributing to a the trunk of a Subversion-managed project:
 
 ------------------------------------------------------------------------
 # Initialize a repo (like git init-db):
@@ -388,19 +386,44 @@ Tracking and contributing to a Subversion-managed project:
        git-svn show-ignore >> .git/info/exclude
 ------------------------------------------------------------------------
 
-REBASE VS. PULL
----------------
+Tracking and contributing to an entire Subversion-managed project
+(complete with a trunk, tags and branches):
+See also:
+'<<tracking-multiple-repos,Tracking Multiple Repositories or Branches>>'
+
+------------------------------------------------------------------------
+# Initialize a repo (like git init-db):
+       git-svn multi-init http://svn.foo.org/project \
+               -T trunk -b branches -t tags
+# Fetch remote revisions:
+       git-svn multi-fetch
+# Create your own branch of trunk to hack on:
+       git checkout -b my-trunk remotes/trunk
+# Do some work, and then commit your new changes to SVN, as well as
+# automatically updating your working HEAD:
+       git-svn dcommit -i trunk
+# Something has been committed to trunk, rebase the latest into your branch:
+       git-svn multi-fetch && git rebase remotes/trunk
+# Append svn:ignore settings of trunk to the default git exclude file:
+       git-svn show-ignore -i trunk >> .git/info/exclude
+# Check for new branches and tags (no arguments are needed):
+       git-svn multi-init
+------------------------------------------------------------------------
+
+REBASE VS. PULL/MERGE
+---------------------
 
 Originally, git-svn recommended that the remotes/git-svn branch be
-pulled from.  This is because the author favored 'git-svn set-tree B'
-to commit a single head rather than the 'git-svn set-tree A..B' notation
-to commit multiple commits.
-
-If you use 'git-svn set-tree A..B' to commit several diffs and you do not
-have the latest remotes/git-svn merged into my-branch, you should use
-'git rebase' to update your work branch instead of 'git pull'.  'pull'
-can cause non-linear history to be flattened when committing into SVN,
-which can lead to merge commits reversing previous commits in SVN.
+pulled or merged from.  This is because the author favored
+'git-svn set-tree B' to commit a single head rather than the
+'git-svn set-tree A..B' notation to commit multiple commits.
+
+If you use 'git-svn set-tree A..B' to commit several diffs and you do
+not have the latest remotes/git-svn merged into my-branch, you should
+use 'git rebase' to update your work branch instead of 'git pull' or
+'git merge'.  'pull/merge' can cause non-linear history to be flattened
+when committing into SVN, which can lead to merge commits reversing
+previous commits in SVN.
 
 DESIGN PHILOSOPHY
 -----------------
index 2c7c7dad54b5d2c773194b494d98ea521bc54d2f..b166cf3327380a8f386825a7fbce92f2447bb54e 100644 (file)
@@ -15,7 +15,7 @@ SYNOPSIS
                [ -b branch_subdir ] [ -T trunk_subdir ] [ -t tag_subdir ]
                [ -s start_chg ] [ -m ] [ -r ] [ -M regex ]
                [ -I <ignorefile_name> ] [ -A <author_file> ]
-               [ -P <path_from_trunk> ]
+               [ -R <repack_each_revs>] [ -P <path_from_trunk> ]
                <SVN_repository_URL> [ <path> ]
 
 
@@ -108,6 +108,14 @@ repository without -A.
 Formerly, this option controlled how many revisions to pull,
 due to SVN memory leaks. (These have been worked around.)
 
+-R <repack_each_revs>::
+       Specify how often git repository should be repacked.
++
+The default value is 1000. git-svnimport will do import in chunks of 1000
+revisions, after each chunk git repository will be repacked. To disable
+this behavior specify some big value here which is mote than number of
+revisions to import.
+
 -P <path_from_trunk>::
        Partial import of the SVN tree.
 +
index 48b82b86f83df4e3fb381da329b48fe12a4ce82d..80bece0775d14206028254c0bd2ed286b81aa180 100644 (file)
@@ -9,7 +9,7 @@ git-tag - Create a tag object signed with GPG
 SYNOPSIS
 --------
 [verse]
-'git-tag' [-a | -s | -u <key-id>] [-f | -d] [-m <msg> | -F <file>]
+'git-tag' [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg> | -F <file>]
         <name> [<head>]
 'git-tag' -l [<pattern>]
 
@@ -35,6 +35,8 @@ GnuPG key for signing.
 
 `-d <tag>` deletes the tag.
 
+`-v <tag>` verifies the gpg signature of the tag.
+
 `-l <pattern>` lists tags that match the given pattern (or all
 if no pattern is given).
 
@@ -55,6 +57,9 @@ OPTIONS
 -d::
        Delete an existing tag with the given name
 
+-v::
+       Verify the gpg signature of given the tag
+
 -l <pattern>::
        List tags that match the given pattern (or all if no pattern is given).
 
index 894883d7b60c4c79263323fc007406c4f3720bac..7c1a6592c112a76c23c2833537a13f40337c261c 100644 (file)
@@ -188,11 +188,12 @@ octopus::
        predator.
 
 origin::
-       The default upstream tracking branch. Most projects have at
+       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
-       will be fetched into this branch; you should never commit
-       to it yourself.
+       will be fetched into remote tracking branches named
+       origin/name-of-upstream-branch, which you can see using
+       "git branch -r".
 
 pack::
        A set of objects which have been compressed into one file (to save
index 517f49b5cc5016a20b171789b89f48cb6ef0ecd5..161123f142ff231bc8153242d7f6507c54b09e2b 100644 (file)
@@ -126,9 +126,9 @@ Another use suggested on the mailing list is to use this hook to
 implement access control which is finer grained than the one
 based on filesystem group.
 
-The standard output of this hook is sent to `/dev/null`; if you
+The standard output of this hook is sent to `stderr`, so if you
 want to report something to the `git-send-pack` on the other end,
-you can redirect your output to your `stderr`.
+you can simply `echo` your messages.
 
 
 post-update
index e852f41a322933aa010bdf79247508e42ee3f661..8d4e950abc65c233268d785dcbfee67ab0522f4c 100644 (file)
@@ -39,10 +39,6 @@ checkout -b my-B remote-B`).  Run `git fetch` to keep track of
 the progress of the remote side, and when you see something new
 on the remote branch, merge it into your development branch with
 `git pull . remote-B`, while you are on `my-B` branch.
-The common `Pull: master:origin` mapping of a remote `master`
-branch to a local `origin` branch, which is then merged to a
-local development branch, again typically named `master`, is made
-when you run `git clone` for you to follow this pattern.
 +
 [NOTE]
 There is a difference between listing multiple <refspec>
diff --git a/Documentation/technical/send-pack-pipeline.txt b/Documentation/technical/send-pack-pipeline.txt
new file mode 100644 (file)
index 0000000..681efe4
--- /dev/null
@@ -0,0 +1,63 @@
+git-send-pack
+=============
+
+Overall operation
+-----------------
+
+. Connects to the remote side and invokes git-receive-pack.
+
+. Learns what refs the remote has and what commit they point at.
+  Matches them to the refspecs we are pushing.
+
+. Checks if there are non-fast-forwards.  Unlike fetch-pack,
+  the repository send-pack runs in is supposed to be a superset
+  of the recipient in fast-forward cases, so there is no need
+  for want/have exchanges, and fast-forward check can be done
+  locally.  Tell the result to the other end.
+
+. Calls pack_objects() which generates a packfile and sends it
+  over to the other end.
+
+. If the remote side is new enough (v1.1.0 or later), wait for
+  the unpack and hook status from the other end.
+
+. Exit with appropriate error codes.
+
+
+Pack_objects pipeline
+---------------------
+
+This function gets one file descriptor (`fd`) which is either a
+socket (over the network) or a pipe (local).  What's written to
+this fd goes to git-receive-pack to be unpacked.
+
+    send-pack ---> fd ---> receive-pack
+
+The function pack_objects creates a pipe and then forks.  The
+forked child execs pack-objects with --revs to receive revision
+parameters from its standard input. This process will write the
+packfile to the other end.
+
+    send-pack
+       |
+       pack_objects() ---> fd ---> receive-pack
+          | ^ (pipe)
+         v |
+         (child)
+
+The child dup2's to arrange its standard output to go back to
+the other end, and read its standard input to come from the
+pipe.  After that it exec's pack-objects.  On the other hand,
+the parent process, before starting to feed the child pipeline,
+closes the reading side of the pipe and fd to receive-pack.
+
+    send-pack
+       |
+       pack_objects(parent)
+          |
+         v [0]
+         pack-objects [0] ---> receive-pack
+
+
+[jc: the pipeline was much more complex and needed documentation before
+ I understood an earlier bug, but now it is trivial and straightforward.]
index cb808d924be51e111b3e57b1b6e007cdf9f451bb..01d4a47a97fa864c6948b8ae98ece1f00b1003da 100644 (file)
@@ -43,8 +43,7 @@ Initialized empty Git repository in .git/
 
 You've now initialized the working directory--you may notice a new
 directory created, named ".git".  Tell git that you want it to track
-every file under the current directory with (notice the dot '.'
-that means the current directory):
+every file under the current directory (note the '.') with:
 
 ------------------------------------------------
 $ git add .
@@ -59,32 +58,40 @@ $ git commit
 will prompt you for a commit message, then record the current state
 of all the files to the repository.
 
+Making changes
+--------------
+
 Try modifying some files, then run
 
 ------------------------------------------------
 $ git diff
 ------------------------------------------------
 
-to review your changes.  When you're done,
+to review your changes.  When you're done, tell git that you
+want the updated contents of these files in the commit and then
+make a commit, like this:
 
 ------------------------------------------------
-$ git commit file1 file2...
+$ git add file1 file2 file3
+$ git commit
 ------------------------------------------------
 
-will again prompt your for a message describing the change, and then
-record the new versions of the files you listed.  It is cumbersome
-to list all files and you can say `-a` (which stands for 'all')
-instead.
+This will again prompt your for a message describing the change, and then
+record the new versions of the files you listed.
+
+Alternatively, instead of running `git add` beforehand, you can use
 
 ------------------------------------------------
 $ git commit -a
 ------------------------------------------------
 
+which will automatically notice modified (but not new) files.
+
 A note on commit messages: Though not required, it's a good idea to
 begin the commit message with a single short (less than 50 character)
 line summarizing the change, followed by a blank line and then a more
 thorough description.  Tools that turn commits into email, for
-example, use the first line on the Subject line and the rest of the
+example, use the first line on the Subject: line and the rest of the
 commit in the body.
 
 
@@ -142,6 +149,13 @@ If you also want to see complete diffs at each step, use
 $ git log -p
 ------------------------------------------------
 
+Often the overview of the change is useful to get a feel of
+each step
+
+------------------------------------------------
+$ git log --stat --summary
+------------------------------------------------
+
 Managing branches
 -----------------
 
@@ -222,6 +236,15 @@ $ gitk
 
 will show a nice graphical representation of the resulting history.
 
+At this point you could delete the experimental branch with
+
+------------------------------------------------
+$ git branch -d experimental
+------------------------------------------------
+
+This command ensures that the changes in the experimental branch are
+already in the current branch.
+
 If you develop on a branch crazy-idea, then regret it, you can always
 delete the branch with
 
@@ -323,20 +346,25 @@ $ git pull
 
 Note that he doesn't need to give the path to Alice's repository;
 when Bob cloned Alice's repository, git stored the location of her
-repository in the file .git/remotes/origin, and that location is used
-as the default for pulls.
-
-Bob may also notice a branch in his repository that he didn't create:
+repository in the repository configuration, and that location is
+used for pulls:
 
 -------------------------------------
-$ git branch
-* master
-  origin
+$ git repo-config --get remote.origin.url
+/home/bob/myrepo
 -------------------------------------
 
-The "origin" branch, which was created automatically by "git clone",
-is a pristine copy of Alice's master branch; Bob should never commit
-to it.
+(The complete configuration created by git-clone is visible using
+"git repo-config -l", and the gitlink:git-repo-config[1] man page
+explains the meaning of each option.)
+
+Git also keeps a pristine copy of Alice's master branch under the
+name "origin/master":
+
+-------------------------------------
+$ git branch -r
+  origin/master
+-------------------------------------
 
 If Bob later decides to work from a different host, he can still
 perform clones and pulls using the ssh protocol:
@@ -376,7 +404,7 @@ commit.
 $ git show c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
 -------------------------------------
 
-But there other ways to refer to commits.  You can use any initial
+But there are other ways to refer to commits.  You can use any initial
 part of the name that is long enough to uniquely identify the commit:
 
 -------------------------------------
@@ -386,8 +414,8 @@ $ git show HEAD             # the tip of the current branch
 $ git show experimental        # the tip of the "experimental" branch
 -------------------------------------
 
-Every commit has at least one "parent" commit, which points to the
-previous state of the project:
+Every commit usually has one "parent" commit
+which points to the previous state of the project:
 
 -------------------------------------
 $ git show HEAD^  # to see the parent of HEAD
@@ -505,10 +533,10 @@ of the file:
 $ git diff v2.5:Makefile HEAD:Makefile.in
 -------------------------------------
 
-You can also use "git cat-file -p" to see any such file:
+You can also use "git show" to see any such file:
 
 -------------------------------------
-$ git cat-file -p v2.5:Makefile
+$ git show v2.5:Makefile
 -------------------------------------
 
 Next Steps
index 870c95073bbbb74e6d40ea1dd5dc642410bc9153..745f9677d005b522f52496339abd5afc4267a815 100644 (file)
@@ -41,9 +41,10 @@ file in `$GIT_DIR/remotes` directory can be given; the
 named file should be in the following format:
 
 ------------
-URL: one of the above URL format
-Push: <refspec>
-Pull: <refspec>
+       URL: one of the above URL format
+       Push: <refspec>
+       Pull: <refspec>
+
 ------------
 
 Then such a short-hand is specified in place of
@@ -57,10 +58,11 @@ Or, equivalently, in the `$GIT_DIR/config` (note the use
 of `fetch` instead of `Pull:`):
 
 ------------
-[remote "<remote>"]
-       url = <url>
-       push = <refspec>
-       fetch = <refspec>
+       [remote "<remote>"]
+               url = <url>
+               push = <refspec>
+               fetch = <refspec>
+
 ------------
 
 The name of a file in `$GIT_DIR/branches` directory can be
@@ -73,14 +75,14 @@ without the fragment is equivalent to have this in the
 corresponding file in the `$GIT_DIR/remotes/` directory.
 
 ------------
-URL: <url>
-Pull: refs/heads/master:<remote>
-------------
+       URL: <url>
+       Pull: refs/heads/master:<remote>
 
+------------
 
 while having `<url>#<head>` is equivalent to
 
 ------------
-URL: <url>
-Pull: refs/heads/<head>:<remote>
+       URL: <url>
+       Pull: refs/heads/<head>:<remote>
 ------------
index fa1a02289c06630274eebbed25b022c4cfeea28b..5c712b94c11e3fb87f50c648932ecc4e7add409a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -179,7 +179,7 @@ SCRIPT_SH = \
 SCRIPT_PERL = \
        git-add--interactive.perl \
        git-archimport.perl git-cvsimport.perl git-relink.perl \
-       git-cvsserver.perl \
+       git-cvsserver.perl git-remote.perl \
        git-svnimport.perl git-cvsexportcommit.perl \
        git-send-email.perl git-svn.perl
 
@@ -818,7 +818,7 @@ test-sha1$X: test-sha1.o $(GITLIBS)
 check-sha1:: test-sha1$X
        ./test-sha1.sh
 
-check:
+check: common-cmds.h
        for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done
 
 
index af47fdc95572bfa67b9c67369222a69c32ddea02..7d52a061f4f8b5d29c52912889ca0d6ee46e4ee0 100644 (file)
@@ -15,7 +15,7 @@ static char block[BLOCKSIZE];
 static unsigned long offset;
 
 static time_t archive_time;
-static int tar_umask;
+static int tar_umask = 002;
 static int verbose;
 
 /* writes out the whole block, but only if it is full */
@@ -210,11 +210,10 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
        sprintf(header.size, "%011lo", S_ISREG(mode) ? size : 0);
        sprintf(header.mtime, "%011lo", archive_time);
 
-       /* XXX: should we provide more meaningful info here? */
        sprintf(header.uid, "%07o", 0);
        sprintf(header.gid, "%07o", 0);
-       strlcpy(header.uname, "git", sizeof(header.uname));
-       strlcpy(header.gname, "git", sizeof(header.gname));
+       strlcpy(header.uname, "root", sizeof(header.uname));
+       strlcpy(header.gname, "root", sizeof(header.gname));
        sprintf(header.devmajor, "%07o", 0);
        sprintf(header.devminor, "%07o", 0);
 
index 745ee04d6e4a8f8b3ea96d35ec65d7a03d1441de..d3df5a57f127f44d7ff48a419d51ca108c7c9618 100644 (file)
@@ -12,7 +12,7 @@
 #include "builtin.h"
 
 static const char builtin_branch_usage[] =
-  "git-branch [-r] (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [-r | -a] [-v [--abbrev=<length>]]";
+  "git-branch [-r] (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length>]]";
 
 #define REF_UNKNOWN_TYPE    0x00
 #define REF_LOCAL_BRANCH    0x01
@@ -231,29 +231,54 @@ static int ref_cmp(const void *r1, const void *r2)
        return strcmp(c1->name, c2->name);
 }
 
-static void print_ref_info(const unsigned char *sha1, int abbrev)
+static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
+                          int abbrev, int current)
 {
+       char c;
+       int color;
        struct commit *commit;
        char subject[256];
 
+       switch (item->kind) {
+       case REF_LOCAL_BRANCH:
+               color = COLOR_BRANCH_LOCAL;
+               break;
+       case REF_REMOTE_BRANCH:
+               color = COLOR_BRANCH_REMOTE;
+               break;
+       default:
+               color = COLOR_BRANCH_PLAIN;
+               break;
+       }
 
-       commit = lookup_commit(sha1);
-       if (commit && !parse_commit(commit))
-               pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
-                                   subject, sizeof(subject), 0,
-                                   NULL, NULL, 0);
-       else
-               strcpy(subject, " **** invalid ref ****");
+       c = ' ';
+       if (current) {
+               c = '*';
+               color = COLOR_BRANCH_CURRENT;
+       }
 
-       printf(" %s %s\n", find_unique_abbrev(sha1, abbrev), subject);
+       if (verbose) {
+               commit = lookup_commit(item->sha1);
+               if (commit && !parse_commit(commit))
+                       pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
+                                           subject, sizeof(subject), 0,
+                                           NULL, NULL, 0);
+               else
+                       strcpy(subject, " **** invalid ref ****");
+               printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color),
+                      maxwidth, item->name,
+                      branch_get_color(COLOR_BRANCH_RESET),
+                      find_unique_abbrev(item->sha1, abbrev), subject);
+       } else {
+               printf("%c %s%s%s\n", c, branch_get_color(color), item->name,
+                      branch_get_color(COLOR_BRANCH_RESET));
+       }
 }
 
 static void print_ref_list(int kinds, int verbose, int abbrev)
 {
        int i;
-       char c;
        struct ref_list ref_list;
-       int color;
 
        memset(&ref_list, 0, sizeof(ref_list));
        ref_list.kinds = kinds;
@@ -262,38 +287,10 @@ static void print_ref_list(int kinds, int verbose, int abbrev)
        qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
 
        for (i = 0; i < ref_list.index; i++) {
-               switch( ref_list.list[i].kind ) {
-                       case REF_LOCAL_BRANCH:
-                               color = COLOR_BRANCH_LOCAL;
-                               break;
-                       case REF_REMOTE_BRANCH:
-                               color = COLOR_BRANCH_REMOTE;
-                               break;
-                       default:
-                               color = COLOR_BRANCH_PLAIN;
-                               break;
-               }
-
-               c = ' ';
-               if (ref_list.list[i].kind == REF_LOCAL_BRANCH &&
-                               !strcmp(ref_list.list[i].name, head)) {
-                       c = '*';
-                       color = COLOR_BRANCH_CURRENT;
-               }
-
-               if (verbose) {
-                       printf("%c %s%-*s%s", c,
-                                       branch_get_color(color),
-                                       ref_list.maxwidth,
-                                       ref_list.list[i].name,
-                                       branch_get_color(COLOR_BRANCH_RESET));
-                       print_ref_info(ref_list.list[i].sha1, abbrev);
-               }
-               else
-                       printf("%c %s%s%s\n", c,
-                                       branch_get_color(color),
-                                       ref_list.list[i].name,
-                                       branch_get_color(COLOR_BRANCH_RESET));
+               int current = (ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
+                       !strcmp(ref_list.list[i].name, head);
+               print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose,
+                              abbrev, current);
        }
 
        free_ref_list(&ref_list);
index 00a53b36479a25a51734fa359ebf2f649795437b..b469c43bc55440d982005b81252ebfd711faa074 100644 (file)
@@ -253,6 +253,8 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
                usage(prune_usage);
        }
 
+       save_commit_buffer = 0;
+
        /*
         * Set up revision parsing, and mark us as being interested
         * in all object types, not just commits.
index 7442498deedfef1f6be9e550f3c0259aaa82e0ba..079c0bdf36abce6a34af2678f7efbb56dfcfdf41 100644 (file)
@@ -350,11 +350,10 @@ static int do_plain_rerere(struct path_list *rr, int fd)
                fprintf(stderr, "Recorded resolution for '%s'.\n", path);
                copy_file(path, rr_path(name, "postimage"));
 tail_optimization:
-               if (i < rr->nr - 1) {
+               if (i < rr->nr - 1)
                        memmove(rr->items + i,
-                                       rr->items + i + 1,
-                                       rr->nr - i - 1);
-               }
+                               rr->items + i + 1,
+                               sizeof(rr->items[0]) * (rr->nr - i - 1));
                rr->nr--;
                i--;
        }
diff --git a/cache.h b/cache.h
index a5fc23235ea57cc562074276845e23608eea4960..36be64e3868456b11bc9a6024be5e955074241b2 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -179,6 +179,7 @@ extern int refresh_cache(unsigned int flags);
 
 struct lock_file {
        struct lock_file *next;
+       char on_list;
        char filename[PATH_MAX];
 };
 extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
@@ -432,6 +433,7 @@ extern char *git_log_output_encoding;
 
 extern int copy_fd(int ifd, int ofd);
 extern void read_or_die(int fd, void *buf, size_t count);
+extern int write_in_full(int fd, const void *buf, size_t count, const char *);
 extern void write_or_die(int fd, const void *buf, size_t count);
 extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
 
index afdf27eeceb76f4f3842ba09b48d07bc218846d6..2a58175aca16dc211cdf5a380e82bc9c0f4d1326 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -679,11 +679,13 @@ static char *logmsg_reencode(const struct commit *commit)
        else if (!*output_encoding)
                return NULL;
        encoding = get_header(commit, "encoding");
-       if (!encoding || !strcmp(encoding, output_encoding)) {
-               free(encoding);
+       if (!encoding)
                return NULL;
-       }
-       out = reencode_string(commit->buffer, output_encoding, encoding);
+       if (!strcmp(encoding, output_encoding))
+               out = strdup(commit->buffer);
+       else
+               out = reencode_string(commit->buffer,
+                                     output_encoding, encoding);
        if (out)
                out = replace_encoding_header(out, output_encoding);
 
@@ -701,7 +703,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
                                  const char *after_subject,
                                  int relative_date)
 {
-       int hdr = 1, body = 0;
+       int hdr = 1, body = 0, seen_title = 0;
        unsigned long offset = 0;
        int indent = 4;
        int parents_shown = 0;
@@ -807,6 +809,8 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
                        body = 1;
 
                if (is_empty_line(line, &linelen)) {
+                       if (!seen_title)
+                               continue;
                        if (!body)
                                continue;
                        if (subject)
@@ -815,6 +819,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
                                break;
                }
 
+               seen_title = 1;
                if (subject) {
                        int slen = strlen(subject);
                        memcpy(buf + offset, subject, slen);
index 972c402ea0e79405ef7123dcdf748eccf9e6f3f7..ede3ab2bd8bd77f9bf110f83bb0dfb9688634aef 100644 (file)
@@ -49,6 +49,7 @@
 
 (eval-when-compile (require 'cl))
 (require 'ewoc)
+(require 'log-edit)
 
 
 ;;;; Customizations
@@ -147,6 +148,13 @@ if there is already one that displays the same directory."
 
 (defconst git-log-msg-separator "--- log message follows this line ---")
 
+(defvar git-log-edit-font-lock-keywords
+  `(("^\\(Author:\\|Date:\\|Parent:\\|Signed-off-by:\\)\\(.*\\)$"
+     (1 font-lock-keyword-face)
+     (2 font-lock-function-name-face))
+    (,(concat "^\\(" (regexp-quote git-log-msg-separator) "\\)$")
+     (1 font-lock-comment-face))))
+
 (defun git-get-env-strings (env)
   "Build a list of NAME=VALUE strings from a list of environment strings."
   (mapcar (lambda (entry) (concat (car entry) "=" (cdr entry))) env))
@@ -777,7 +785,7 @@ and returns the process output as a string."
   (interactive)
   (let ((files (git-marked-files-state 'unmerged)))
     (when files
-      (apply #'git-run-command nil nil "update-index" "--info-only" "--" (git-get-filenames files))
+      (apply #'git-run-command nil nil "update-index" "--" (git-get-filenames files))
       (git-set-files-state files 'modified)
       (git-refresh-files))))
 
@@ -894,14 +902,9 @@ and returns the process output as a string."
               (sign-off
                (insert (format "\n\nSigned-off-by: %s <%s>\n"
                                (git-get-committer-name) (git-get-committer-email)))))))
-    (let ((log-edit-font-lock-keywords
-           `(("^\\(Author:\\|Date:\\|Parent:\\|Signed-off-by:\\)\\(.*\\)"
-              (1 font-lock-keyword-face)
-              (2 font-lock-function-name-face))
-             (,(concat "^\\(" (regexp-quote git-log-msg-separator) "\\)$")
-              (1 font-lock-comment-face)))))
-      (log-edit #'git-do-commit nil #'git-log-edit-files buffer)
-      (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))))
+    (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))
+    (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."
index fc69fb92a50c3dff67da76fedf1bf1df561c6065..2c9be60ed9b47c2e563f69e6c8b60195fd51a917 100644 (file)
@@ -97,7 +97,7 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
                         * Show the diff for the 'ce' if we found the one
                         * from the desired stage.
                         */
-                       diff_unmerge(&revs->diffopt, ce->name);
+                       diff_unmerge(&revs->diffopt, ce->name, 0, null_sha1);
                        if (ce_stage(ce) != diff_unmerged_stage)
                                continue;
                }
@@ -297,9 +297,12 @@ static int diff_cache(struct rev_info *revs,
                            !show_modified(revs, ce, ac[1], 0,
                                           cached, match_missing))
                                break;
-                       /* fallthru */
+                       diff_unmerge(&revs->diffopt, ce->name,
+                                    ntohl(ce->ce_mode), ce->sha1);
+                       break;
                case 3:
-                       diff_unmerge(&revs->diffopt, ce->name);
+                       diff_unmerge(&revs->diffopt, ce->name,
+                                    0, null_sha1);
                        break;
 
                default:
diff --git a/diff.c b/diff.c
index 244292a70c5b8a86d23905036c4103d3d0c1df1c..e1b016f47f8eb37a89bdb6cb98435bad7d9dc111 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -2873,10 +2873,12 @@ void diff_change(struct diff_options *options,
 }
 
 void diff_unmerge(struct diff_options *options,
-                 const char *path)
+                 const char *path,
+                 unsigned mode, const unsigned char *sha1)
 {
        struct diff_filespec *one, *two;
        one = alloc_filespec(path);
        two = alloc_filespec(path);
-       diff_queue(&diff_queued_diff, one, two);
+       fill_filespec(one, sha1, mode);
+       diff_queue(&diff_queued_diff, one, two)->is_unmerged = 1;
 }
diff --git a/diff.h b/diff.h
index eff445596d98e46d40dd37843e690de27c5fabf1..7a347cf77d448817014ceeaed2d3cd99b5894ac6 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -144,7 +144,9 @@ extern void diff_change(struct diff_options *,
                        const char *base, const char *path);
 
 extern void diff_unmerge(struct diff_options *,
-                        const char *path);
+                        const char *path,
+                        unsigned mode,
+                        const unsigned char *sha1);
 
 extern int diff_scoreopt_parse(const char *opt);
 
index 2249bc2c05744ce5026744547fcb30195a19b3f1..1ea80671e30500f95fc1b648ccc6d5143ac0ac52 100644 (file)
@@ -54,9 +54,9 @@ struct diff_filepair {
        unsigned source_stays : 1; /* all of R/C are copies */
        unsigned broken_pair : 1;
        unsigned renamed_pair : 1;
+       unsigned is_unmerged : 1;
 };
-#define DIFF_PAIR_UNMERGED(p) \
-       (!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))
+#define DIFF_PAIR_UNMERGED(p) ((p)->is_unmerged)
 
 #define DIFF_PAIR_RENAME(p) ((p)->renamed_pair)
 
index c527bf9e9621519f72e1039313666a03ee12c586..1530a947947b68d879e01bb9161654e135716a3d 100644 (file)
@@ -625,6 +625,8 @@ static int remove_duplicates(int nr_heads, char **heads)
        return dst;
 }
 
+static struct lock_file lock;
+
 int main(int argc, char **argv)
 {
        int i, ret, nr_heads;
@@ -632,7 +634,6 @@ int main(int argc, char **argv)
        int fd[2];
        pid_t pid;
        struct stat st;
-       struct lock_file lock;
 
        setup_git_directory();
 
index 06c42b042d999856aacb9e156cffc39eb8dbce3f..1de14ea82fe678e3afb66ea11d6dd6e306158387 100755 (executable)
@@ -37,7 +37,6 @@ show
 show-branch
 status
 tag
-verify-tag
 EOF
 while read cmd
 do
index c3bbd78eabf6f3d5eaa0f31bb0c240a26f28e0c5..7c0bb6084b332db7f7b6b9058db4d92b828730f0 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -88,10 +88,12 @@ It does not apply to blobs recorded in its index."
     # This is not so wrong.  Depending on which base we picked,
     # orig_tree may be wildly different from ours, but his_tree
     # has the same set of wildly different changes in parts the
-    # patch did not touch, so resolve ends up canceling them,
+    # patch did not touch, so recursive ends up canceling them,
     # saying that we reverted all those changes.
 
-    git-merge-resolve $orig_tree -- HEAD $his_tree || {
+    eval GITHEAD_$his_tree='"$SUBJECT"'
+    export GITHEAD_$his_tree
+    git-merge-recursive $orig_tree -- HEAD $his_tree || {
            if test -d "$GIT_DIR/rr-cache"
            then
                git-rerere
@@ -99,6 +101,7 @@ It does not apply to blobs recorded in its index."
            echo Failed to merge in the changes.
            exit 1
     }
+    unset GITHEAD_$his_tree
 }
 
 prec=4
index 3834323bcf479b7e01af4e1f247fb13aac423f90..071b974f496b8deff3a2d1b869c35d4f556dc17f 100755 (executable)
@@ -18,7 +18,6 @@ SUBDIRECTORY_OK=Yes
 ignored=
 ignoredonly=
 cleandir=
-quiet=
 rmf="rm -f --"
 rmrf="rm -rf --"
 rm_refuse="echo Not removing"
@@ -31,14 +30,13 @@ do
                cleandir=1
                ;;
        -n)
-               quiet=1
                rmf="echo Would remove"
                rmrf="echo Would remove"
                rm_refuse="echo Would not remove"
                echo1=":"
                ;;
        -q)
-               quiet=1
+               echo1=":"
                ;;
        -x)
                ignored=1
index 6bce41af4dc73d90028e76ba174e4e7795103d6c..04aad5e5da6fad46bc71c859286615c2f6ce11ab 100755 (executable)
@@ -8,7 +8,6 @@ SUBDIRECTORY_OK=Yes
 . git-sh-setup
 
 git-rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
-branch=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD)
 
 case "$0" in
 *status)
index 8bd11f8b60c0ffec8a67f59bc7e12e1f63adfc91..466fe59e35e03a6f9955f78e4238d9e0a60b8dc0 100755 (executable)
@@ -382,13 +382,22 @@ fetch_main () {
       ;; # we are already done.
   *)
     ( : subshell because we muck with IFS
-      pack_lockfile=
       IFS="    $LF"
       (
-         git-fetch-pack --thin $exec $keep $shallow_depth "$remote" $rref || echo failed "$remote"
+         git-fetch-pack --thin $exec $keep $shallow_depth "$remote" $rref ||
+         echo failed "$remote"
       ) |
-      while read sha1 remote_name
-      do
+      (
+       trap '
+               if test -n "$keepfile" && test -f "$keepfile"
+               then
+                       rm -f "$keepfile"
+               fi
+       ' 0
+
+        keepfile=
+       while read sha1 remote_name
+       do
          case "$sha1" in
          failed)
                  echo >&2 "Fetch failure: $remote"
@@ -397,7 +406,7 @@ fetch_main () {
          pack)
                  continue ;;
          keep)
-                 pack_lockfile="$GIT_OBJECT_DIRECTORY/pack/pack-$remote_name.keep"
+                 keepfile="$GIT_OBJECT_DIRECTORY/pack/pack-$remote_name.keep"
                  continue ;;
          esac
          found=
@@ -429,8 +438,8 @@ fetch_main () {
          append_fetch_head "$sha1" "$remote" \
                  "$remote_name" "$remote_nick" "$local_name" \
                  "$not_for_merge" || exit
-      done &&
-      if [ "$pack_lockfile" ]; then rm -f "$pack_lockfile"; fi
+        done
+      )
     ) || exit ;;
   esac
 
index 16cd351f7f0d992be37be83017961c70731d4cc5..80adc8307b4310440b5de7df82c5200cc2c45cbb 100755 (executable)
@@ -53,6 +53,9 @@ start_httpd () {
                                return
                        fi
                done
+               echo "$httpd_only not found. Install $httpd_only or use" \
+                    "--httpd to specify another http daemon."
+               exit 1
        fi
        if test $? != 0; then
                echo "Could not execute http daemon $httpd."
@@ -160,10 +163,20 @@ apache2_conf () {
        test "$local" = true && bind='127.0.0.1:'
        echo 'text/css css' > $fqgitdir/mime.types
        cat > "$conf" <<EOF
+ServerName "git-instaweb"
 ServerRoot "$fqgitdir/gitweb"
 DocumentRoot "$fqgitdir/gitweb"
 PidFile "$fqgitdir/pid"
 Listen $bind$port
+EOF
+
+       for mod in mime dir; do
+               if test -e $module_path/mod_${mod}.so; then
+                       echo "LoadModule ${mod}_module " \
+                            "$module_path/mod_${mod}.so" >> "$conf"
+               fi
+       done
+       cat >> "$conf" <<EOF
 TypesConfig $fqgitdir/mime.types
 DirectoryIndex gitweb.cgi
 EOF
index ba42260426296b63070a1fbdc22a9b77ea5ac412..477002910ede7f6bc6bcb9a79f6ddebd248f6bd2 100755 (executable)
@@ -8,6 +8,9 @@ USAGE='[-n] [--no-commit] [--squash] [-s <strategy>] [-m=<merge-message>] <commi
 . git-sh-setup
 set_reflog_action "merge $*"
 
+test -z "$(git ls-files -u)" ||
+       die "You are in a middle of conflicted merge."
+
 LF='
 '
 
index 144f1701553a1e2f4204a2df15e66437e0670123..d2e4c2b9aede8310879cea9c47bd3cee323f11af 100755 (executable)
@@ -76,16 +76,32 @@ get_remote_default_refs_for_push () {
 # from get_remote_refs_for_fetch when it deals with refspecs
 # supplied on the command line.  $ls_remote_result has the list
 # of refs available at remote.
+#
+# The first token returned is either "explicit" or "glob"; this
+# is to help prevent randomly "globbed" ref from being chosen as
+# a merge candidate
 expand_refs_wildcard () {
+       first_one=yes
        for ref
        do
                lref=${ref#'+'}
                # a non glob pattern is given back as-is.
                expr "z$lref" : 'zrefs/.*/\*:refs/.*/\*$' >/dev/null || {
+                       if test -n "$first_one"
+                       then
+                               echo "explicit"
+                               first_one=
+                       fi
                        echo "$ref"
                        continue
                }
 
+               # glob
+               if test -n "$first_one"
+               then
+                       echo "glob"
+                       first_one=
+               fi
                from=`expr "z$lref" : 'z\(refs/.*/\)\*:refs/.*/\*$'`
                to=`expr "z$lref" : 'zrefs/.*/\*:\(refs/.*/\)\*$'`
                local_force=
@@ -116,7 +132,8 @@ canon_refs_list_for_fetch () {
        if test "$1" = "-d"
        then
                shift ; remote="$1" ; shift
-               set x $(expand_refs_wildcard "$@")
+               set $(expand_refs_wildcard "$@")
+               is_explicit="$1"
                shift
                if test "$remote" = "$(get_default_remote)"
                then
@@ -125,6 +142,10 @@ canon_refs_list_for_fetch () {
                        merge_branches=$(git-repo-config \
                            --get-all "branch.${curr_branch}.merge")
                fi
+               if test -z "$merge_branches" && test $is_explicit != explicit
+               then
+                       merge_branches=..this.will.never.match.any.ref..
+               fi
        fi
        for ref
        do
index 28d08195f0c84b7f3a89a5c5cff9945bb64ddfb3..c184fb81a4dab622d79120bcc986c3bf2b07df7f 100755 (executable)
@@ -9,6 +9,9 @@ LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEA
 . git-sh-setup
 set_reflog_action "pull $*"
 
+test -z "$(git ls-files -u)" ||
+       die "You are in a middle of conflicted merge."
+
 strategy_args= no_summary= no_commit= squash=
 while case "$#,$1" in 0) break ;; *,-*) ;; *) break ;; esac
 do
diff --git a/git-remote.perl b/git-remote.perl
new file mode 100755 (executable)
index 0000000..059c141
--- /dev/null
@@ -0,0 +1,277 @@
+#!/usr/bin/perl -w
+
+use Git;
+my $git = Git->repository();
+
+sub add_remote_config {
+       my ($hash, $name, $what, $value) = @_;
+       if ($what eq 'url') {
+               if (exists $hash->{$name}{'URL'}) {
+                       print STDERR "Warning: more than one remote.$name.url\n";
+               }
+               $hash->{$name}{'URL'} = $value;
+       }
+       elsif ($what eq 'fetch') {
+               $hash->{$name}{'FETCH'} ||= [];
+               push @{$hash->{$name}{'FETCH'}}, $value;
+       }
+       if (!exists $hash->{$name}{'SOURCE'}) {
+               $hash->{$name}{'SOURCE'} = 'config';
+       }
+}
+
+sub add_remote_remotes {
+       my ($hash, $file, $name) = @_;
+
+       if (exists $hash->{$name}) {
+               $hash->{$name}{'WARNING'} = 'ignored due to config';
+               return;
+       }
+
+       my $fh;
+       if (!open($fh, '<', $file)) {
+               print STDERR "Warning: cannot open $file\n";
+               return;
+       }
+       my $it = { 'SOURCE' => 'remotes' };
+       $hash->{$name} = $it;
+       while (<$fh>) {
+               chomp;
+               if (/^URL:\s*(.*)$/) {
+                       # Having more than one is Ok -- it is used for push.
+                       if (! exists $it->{'URL'}) {
+                               $it->{'URL'} = $1;
+                       }
+               }
+               elsif (/^Push:\s*(.*)$/) {
+                       ; # later
+               }
+               elsif (/^Pull:\s*(.*)$/) {
+                       $it->{'FETCH'} ||= [];
+                       push @{$it->{'FETCH'}}, $1;
+               }
+               elsif (/^\#/) {
+                       ; # ignore
+               }
+               else {
+                       print STDERR "Warning: funny line in $file: $_\n";
+               }
+       }
+       close($fh);
+}
+
+sub list_remote {
+       my ($git) = @_;
+       my %seen = ();
+       my @remotes = eval {
+               $git->command(qw(repo-config --get-regexp), '^remote\.');
+       };
+       for (@remotes) {
+               if (/^remote\.([^.]*)\.(\S*)\s+(.*)$/) {
+                       add_remote_config(\%seen, $1, $2, $3);
+               }
+       }
+
+       my $dir = $git->repo_path() . "/remotes";
+       if (opendir(my $dh, $dir)) {
+               local $_;
+               while ($_ = readdir($dh)) {
+                       chomp;
+                       next if (! -f "$dir/$_" || ! -r _);
+                       add_remote_remotes(\%seen, "$dir/$_", $_);
+               }
+       }
+
+       return \%seen;
+}
+
+sub add_branch_config {
+       my ($hash, $name, $what, $value) = @_;
+       if ($what eq 'remote') {
+               if (exists $hash->{$name}{'REMOTE'}) {
+                       print STDERR "Warning: more than one branch.$name.remote\n";
+               }
+               $hash->{$name}{'REMOTE'} = $value;
+       }
+       elsif ($what eq 'merge') {
+               $hash->{$name}{'MERGE'} ||= [];
+               push @{$hash->{$name}{'MERGE'}}, $value;
+       }
+}
+
+sub list_branch {
+       my ($git) = @_;
+       my %seen = ();
+       my @branches = eval {
+               $git->command(qw(repo-config --get-regexp), '^branch\.');
+       };
+       for (@branches) {
+               if (/^branch\.([^.]*)\.(\S*)\s+(.*)$/) {
+                       add_branch_config(\%seen, $1, $2, $3);
+               }
+       }
+
+       return \%seen;
+}
+
+my $remote = list_remote($git);
+my $branch = list_branch($git);
+
+sub update_ls_remote {
+       my ($harder, $info) = @_;
+
+       return if (($harder == 0) ||
+                  (($harder == 1) && exists $info->{'LS_REMOTE'}));
+
+       my @ref = map {
+               s|^[0-9a-f]{40}\s+refs/heads/||;
+               $_;
+       } $git->command(qw(ls-remote --heads), $info->{'URL'});
+       $info->{'LS_REMOTE'} = \@ref;
+}
+
+sub show_wildcard_mapping {
+       my ($forced, $ours, $ls) = @_;
+       my %refs;
+       for (@$ls) {
+               $refs{$_} = 01; # bit #0 to say "they have"
+       }
+       for ($git->command('for-each-ref', "refs/remotes/$ours")) {
+               chomp;
+               next unless (s|^[0-9a-f]{40}\s[a-z]+\srefs/remotes/$ours/||);
+               next if ($_ eq 'HEAD');
+               $refs{$_} ||= 0;
+               $refs{$_} |= 02; # bit #1 to say "we have"
+       }
+       my (@new, @stale, @tracked);
+       for (sort keys %refs) {
+               my $have = $refs{$_};
+               if ($have == 1) {
+                       push @new, $_;
+               }
+               elsif ($have == 2) {
+                       push @stale, $_;
+               }
+               elsif ($have == 3) {
+                       push @tracked, $_;
+               }
+       }
+       if (@new) {
+               print "  New remote branches (next fetch will store in remotes/$ours)\n";
+               print "    @new\n";
+       }
+       if (@stale) {
+               print "  Stale tracking branches in remotes/$ours (you'd better remove them)\n";
+               print "    @stale\n";
+       }
+       if (@tracked) {
+               print "  Tracked remote branches\n";
+               print "    @tracked\n";
+       }
+}
+
+sub show_mapping {
+       my ($name, $info) = @_;
+       my $fetch = $info->{'FETCH'};
+       my $ls = $info->{'LS_REMOTE'};
+       my (@stale, @tracked);
+
+       for (@$fetch) {
+               next unless (/(\+)?([^:]+):(.*)/);
+               my ($forced, $theirs, $ours) = ($1, $2, $3);
+               if ($theirs eq 'refs/heads/*' &&
+                   $ours =~ /^refs\/remotes\/(.*)\/\*$/) {
+                       # wildcard mapping
+                       show_wildcard_mapping($forced, $1, $ls);
+               }
+               elsif ($theirs =~ /\*/ || $ours =~ /\*/) {
+                       print STDERR "Warning: unrecognized mapping in remotes.$name.fetch: $_\n";
+               }
+               elsif ($theirs =~ s|^refs/heads/||) {
+                       if (!grep { $_ eq $theirs } @$ls) {
+                               push @stale, $theirs;
+                       }
+                       elsif ($ours ne '') {
+                               push @tracked, $theirs;
+                       }
+               }
+       }
+       if (@stale) {
+               print "  Stale tracking branches in remotes/$name (you'd better remove them)\n";
+               print "    @stale\n";
+       }
+       if (@tracked) {
+               print "  Tracked remote branches\n";
+               print "    @tracked\n";
+       }
+}
+
+sub show_remote {
+       my ($name, $ls_remote) = @_;
+       if (!exists $remote->{$name}) {
+               print STDERR "No such remote $name\n";
+               return;
+       }
+       my $info = $remote->{$name};
+       update_ls_remote($ls_remote, $info);
+
+       print "* remote $name\n";
+       print "  URL: $info->{'URL'}\n";
+       for my $branchname (sort keys %$branch) {
+               next if ($branch->{$branchname}{'REMOTE'} ne $name);
+               my @merged = map {
+                       s|^refs/heads/||;
+                       $_;
+               } split(' ',"@{$branch->{$branchname}{'MERGE'}}");
+               next unless (@merged);
+               print "  Remote branch(es) merged with 'git pull' while on branch $branchname\n";
+               print "    @merged\n";
+       }
+       if ($info->{'LS_REMOTE'}) {
+               show_mapping($name, $info);
+       }
+}
+
+sub add_remote {
+       my ($name, $url) = @_;
+       if (exists $remote->{$name}) {
+               print STDERR "remote $name already exists.\n";
+               exit(1);
+       }
+       $git->command('repo-config', "remote.$name.url", $url);
+       $git->command('repo-config', "remote.$name.fetch",
+                     "+refs/heads/*:refs/remotes/$name/*");
+}
+
+if (!@ARGV) {
+       for (sort keys %$remote) {
+               print "$_\n";
+       }
+}
+elsif ($ARGV[0] eq 'show') {
+       my $ls_remote = 1;
+       my $i;
+       for ($i = 1; $i < @ARGV; $i++) {
+               if ($ARGV[$i] eq '-n') {
+                       $ls_remote = 0;
+               }
+               else {
+                       last;
+               }
+       }
+       if ($i >= @ARGV) {
+               print STDERR "Usage: git remote show <remote>\n";
+               exit(1);
+       }
+       for (; $i < @ARGV; $i++) {
+               show_remote($ARGV[$i], $ls_remote);
+       }
+}
+elsif ($ARGV[0] eq 'add') {
+       if (@ARGV != 3) {
+               print STDERR "Usage: git remote add <name> <url>\n";
+               exit(1);
+       }
+       add_remote($ARGV[1], $ARGV[2]);
+}
+
index a9693701a34dac623fcddb1943ffe326bdb05405..76c8a818d421c796141fae3835e1cb4726d23ef3 100755 (executable)
@@ -44,8 +44,10 @@ if test $# != 0
 then
        test "$reset_type" == "--mixed" ||
                die "Cannot do partial $reset_type reset."
-       git ls-tree -r --full-name $rev -- "$@" |
-       git update-index --add --index-info || exit
+
+       git-diff-index --cached $rev -- "$@" |
+       sed -e 's/^:\([0-7][0-7]*\) [0-7][0-7]* \([0-9a-f][0-9a-f]*\) [0-9a-f][0-9a-f]* [A-Z]   \(.*\)$/\1 \2   \3/' |
+       git update-index --add --remove --index-info || exit
        git update-index --refresh
        exit
 fi
index b28c5bbc7238120d1d98445f1bf0f116dd7a0b8a..1da31fdc7cf9c90e6ec3dfd815201e36b9650890 100755 (executable)
 );
 
 my %cmd = (
-       fetch => [ \&fetch, "Download new revisions from SVN",
+       fetch => [ \&cmd_fetch, "Download new revisions from SVN",
                        { 'revision|r=s' => \$_revision, %fc_opts } ],
        init => [ \&init, "Initialize a repo for tracking" .
                          " (requires URL argument)",
@@ -293,6 +293,10 @@ sub init {
        setup_git_svn();
 }
 
+sub cmd_fetch {
+       fetch_child_id($GIT_SVN, @_);
+}
+
 sub fetch {
        check_upgrade_needed();
        $SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
@@ -532,7 +536,7 @@ sub show_ignore {
        my $repo;
        $SVN ||= libsvn_connect($SVN_URL);
        my $r = defined $_revision ? $_revision : $SVN->get_latest_revnum;
-       libsvn_traverse_ignore(\*STDOUT, $SVN->{svn_path}, $r);
+       libsvn_traverse_ignore(\*STDOUT, '', $r);
 }
 
 sub graft_branches {
@@ -571,28 +575,25 @@ sub graft_branches {
 
 sub multi_init {
        my $url = shift;
-       $_trunk ||= 'trunk';
-       $_trunk =~ s#/+$##;
-       $url =~ s#/+$## if $url;
-       if ($_trunk !~ m#^[a-z\+]+://#) {
-               $_trunk = '/' . $_trunk if ($_trunk !~ m#^/#);
-               unless ($url) {
-                       print STDERR "E: '$_trunk' is not a complete URL ",
-                               "and a separate URL is not specified\n";
-                       exit 1;
-               }
-               $_trunk = $url . $_trunk;
+       unless (defined $_trunk || defined $_branches || defined $_tags) {
+               usage(1);
        }
-       my $ch_id;
-       if ($GIT_SVN eq 'git-svn') {
-               $ch_id = 1;
-               $GIT_SVN = $ENV{GIT_SVN_ID} = 'trunk';
-       }
-       init_vars();
-       unless (-d $GIT_SVN_DIR) {
-               print "GIT_SVN_ID set to 'trunk' for $_trunk\n" if $ch_id;
-               init($_trunk);
-               command_noisy('repo-config', 'svn.trunk', $_trunk);
+       if (defined $_trunk) {
+               my $trunk_url = complete_svn_url($url, $_trunk);
+               my $ch_id;
+               if ($GIT_SVN eq 'git-svn') {
+                       $ch_id = 1;
+                       $GIT_SVN = $ENV{GIT_SVN_ID} = 'trunk';
+               }
+               init_vars();
+               unless (-d $GIT_SVN_DIR) {
+                       if ($ch_id) {
+                               print "GIT_SVN_ID set to 'trunk' for ",
+                                     "$trunk_url ($_trunk)\n";
+                       }
+                       init($trunk_url);
+                       command_noisy('repo-config', 'svn.trunk', $trunk_url);
+               }
        }
        complete_url_ls_init($url, $_branches, '--branches/-b', '');
        complete_url_ls_init($url, $_tags, '--tags/-t', 'tags/');
@@ -839,7 +840,6 @@ sub fetch_child_id {
        my $ref = "$GIT_DIR/refs/remotes/$id";
        defined(my $pid = open my $fh, '-|') or croak $!;
        if (!$pid) {
-               $_repack = undef;
                $GIT_SVN = $ENV{GIT_SVN_ID} = $id;
                init_vars();
                fetch(@_);
@@ -847,7 +847,7 @@ sub fetch_child_id {
        }
        while (<$fh>) {
                print $_;
-               check_repack() if (/^r\d+ = $sha1/);
+               check_repack() if (/^r\d+ = $sha1/o);
        }
        close $fh or croak $?;
 }
@@ -872,29 +872,34 @@ sub rec_fetch {
        }
 }
 
+sub complete_svn_url {
+       my ($url, $path) = @_;
+       $path =~ s#/+$##;
+       $url =~ s#/+$## if $url;
+       if ($path !~ m#^[a-z\+]+://#) {
+               $path = '/' . $path if ($path !~ m#^/#);
+               if (!defined $url || $url !~ m#^[a-z\+]+://#) {
+                       fatal("E: '$path' is not a complete URL ",
+                             "and a separate URL is not specified\n");
+               }
+               $path = $url . $path;
+       }
+       return $path;
+}
+
 sub complete_url_ls_init {
-       my ($url, $var, $switch, $pfx) = @_;
-       unless ($var) {
+       my ($url, $path, $switch, $pfx) = @_;
+       unless ($path) {
                print STDERR "W: $switch not specified\n";
                return;
        }
-       $var =~ s#/+$##;
-       if ($var !~ m#^[a-z\+]+://#) {
-               $var = '/' . $var if ($var !~ m#^/#);
-               unless ($url) {
-                       print STDERR "E: '$var' is not a complete URL ",
-                               "and a separate URL is not specified\n";
-                       exit 1;
-               }
-               $var = $url . $var;
-       }
-       my @ls = libsvn_ls_fullurl($var);
-       my $old = $GIT_SVN;
+       my $full_url = complete_svn_url($url, $path);
+       my @ls = libsvn_ls_fullurl($full_url);
        defined(my $pid = fork) or croak $!;
        if (!$pid) {
-               foreach my $u (map { "$var/$_" } (grep m!/$!, @ls)) {
+               foreach my $u (map { "$full_url/$_" } (grep m!/$!, @ls)) {
                        $u =~ s#/+$##;
-                       if ($u !~ m!\Q$var\E/(.+)$!) {
+                       if ($u !~ m!\Q$full_url\E/(.+)$!) {
                                print STDERR "W: Unrecognized URL: $u\n";
                                die "This should never happen\n";
                        }
@@ -912,7 +917,7 @@ sub complete_url_ls_init {
        waitpid $pid, 0;
        croak $? if $?;
        my ($n) = ($switch =~ /^--(\w+)/);
-       command_noisy('repo-config', "svn.$n", $var);
+       command_noisy('repo-config', "svn.$n", $full_url);
 }
 
 sub common_prefix {
@@ -1405,7 +1410,6 @@ sub git_commit {
 
        # this output is read via pipe, do not change:
        print "r$log_msg->{revision} = $commit\n";
-       check_repack();
        return $commit;
 }
 
index cbaa8ab37c78769002e10947d7ca08fc440e552f..afbbe63c622c29e86e3a7ac971e6f88ed93dd6a2 100755 (executable)
 $ENV{'TZ'}="UTC";
 
 our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T,
-    $opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D,$opt_S,$opt_F,$opt_P);
+    $opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D,$opt_S,$opt_F,
+    $opt_P,$opt_R);
 
 sub usage() {
        print STDERR <<END;
 Usage: ${\basename $0}     # fetch/update GIT from SVN
-       [-o branch-for-HEAD] [-h] [-v] [-l max_rev]
+       [-o branch-for-HEAD] [-h] [-v] [-l max_rev] [-R repack_each_revs]
        [-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname]
        [-d|-D] [-i] [-u] [-r] [-I ignorefilename] [-s start_chg]
        [-m] [-M regex] [-A author_file] [-S] [-F] [-P project_name] [SVN_URL]
@@ -44,7 +45,7 @@ END
        exit(1);
 }
 
-getopts("A:b:C:dDFhiI:l:mM:o:rs:t:T:SP:uv") or usage();
+getopts("A:b:C:dDFhiI:l:mM:o:rs:t:T:SP:R:uv") or usage();
 usage if $opt_h;
 
 my $tag_name = $opt_t || "tags";
@@ -52,6 +53,7 @@ END
 my $branch_name = $opt_b || "branches";
 my $project_name = $opt_P || "";
 $project_name = "/" . $project_name if ($project_name);
+my $repack_after = $opt_R || 1000;
 
 @ARGV == 1 or @ARGV == 2 or usage();
 
@@ -146,6 +148,7 @@ sub file {
        print "... $rev $path ...\n" if $opt_v;
        my (undef, $properties);
        my $pool = SVN::Pool->new();
+       $path =~ s#^/*##;
        eval { (undef, $properties)
                   = $self->{'svn'}->get_file($path,$rev,$fh,$pool); };
        $pool->clear;
@@ -181,6 +184,7 @@ sub ignore {
        my($self,$path,$rev) = @_;
 
        print "... $rev $path ...\n" if $opt_v;
+       $path =~ s#^/*##;
        my (undef,undef,$properties)
            = $self->{'svn'}->get_dir($path,$rev,undef);
        if (exists $properties->{'svn:ignore'}) {
@@ -197,6 +201,7 @@ sub ignore {
 
 sub dir_list {
        my($self,$path,$rev) = @_;
+       $path =~ s#^/*##;
        my ($dirents,undef,$properties)
            = $self->{'svn'}->get_dir($path,$rev,undef);
        return $dirents;
@@ -354,6 +359,7 @@ ($$)
 sub node_kind($$) {
        my ($svnpath, $revision) = @_;
        my $pool=SVN::Pool->new;
+       $svnpath =~ s#^/*##;
        my $kind = $svn->{'svn'}->check_path($svnpath,$revision,$pool);
        $pool->clear;
        return $kind;
@@ -934,11 +940,27 @@ sub commit_all {
     exit;
 }
 
-print "Fetching from $current_rev to $opt_l ...\n" if $opt_v;
+print "Processing from $current_rev to $opt_l ...\n" if $opt_v;
 
-my $pool=SVN::Pool->new;
-$svn->{'svn'}->get_log("/",$current_rev,$opt_l,0,1,1,\&commit_all,$pool);
-$pool->clear;
+my $from_rev;
+my $to_rev = $current_rev;
+
+while ($to_rev < $opt_l) {
+       $from_rev = $to_rev;
+       $to_rev = $from_rev + $repack_after;
+       $to_rev = $opt_l if $opt_l < $to_rev;
+       print "Fetching from $from_rev to $to_rev ...\n" if $opt_v;
+       my $pool=SVN::Pool->new;
+       $svn->{'svn'}->get_log("/",$from_rev,$to_rev,0,1,1,\&commit_all,$pool);
+       $pool->clear;
+       my $pid = fork();
+       die "Fork: $!\n" unless defined $pid;
+       unless($pid) {
+               exec("git-repack", "-d")
+                       or die "Cannot repack: $!\n";
+       }
+       waitpid($pid, 0);
+}
 
 
 unlink($git_index);
index e1bfa82f1ea1213fad8ff8c4b6ad2b17e6ca1f7e..ecb9100e4b3a7d1ce0227fde447f675376aaf29a 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 # Copyright (c) 2005 Linus Torvalds
 
-USAGE='-l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d] [-m <msg>] <tagname> [<head>]'
+USAGE='-l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg>] <tagname> [<head>]'
 SUBDIRECTORY_OK='Yes'
 . git-sh-setup
 
@@ -12,6 +12,7 @@ force=
 message=
 username=
 list=
+verify=
 while case "$#" in 0) break ;; esac
 do
     case "$1" in
@@ -69,6 +70,14 @@ do
                echo "Deleted tag $tag_name."
        exit $?
        ;;
+    -v)
+       shift
+       tag_name="$1"
+       tag=$(git-show-ref --verify --hash -- "refs/tags/$tag_name") ||
+               die "Seriously, what tag are you talking about?"
+       git-verify-tag -v "$tag"
+       exit $?
+       ;;
     -*)
         usage
        ;;
index 36f171b3028a3460c1c51f9c1f5747a9a0d2d850..8db7dd0b7d371cfeec84e70ff2a51ccf53a994e7 100755 (executable)
@@ -34,7 +34,10 @@ t)
        ;;
 esac
 
+trap 'rm -f "$GIT_DIR/.tmp-vtag"' 0
+
 git-cat-file tag "$1" >"$GIT_DIR/.tmp-vtag" || exit 1
+
 cat "$GIT_DIR/.tmp-vtag" |
 sed '/-----BEGIN PGP/Q' |
 gpg --verify "$GIT_DIR/.tmp-vtag" - || exit 1
index d845e91e202233916a91a7095e4a81142687c9c7..f46a42296da0e66c0946c392c232058dcd09e34a 100755 (executable)
@@ -2274,7 +2274,7 @@ sub git_difftree_body {
                        my $mode_chnge = "";
                        if ($diff{'from_mode'} != $diff{'to_mode'}) {
                                $mode_chnge = "<span class=\"file_status mode_chnge\">[changed";
-                               if ($from_file_type != $to_file_type) {
+                               if ($from_file_type ne $to_file_type) {
                                        $mode_chnge .= " from $from_file_type to $to_file_type";
                                }
                                if (($from_mode_oct & 0777) != ($to_mode_oct & 0777)) {
@@ -2378,7 +2378,6 @@ sub git_patchset_body {
        my $patch_line;
        my $diffinfo;
        my (%from, %to);
-       my ($from_id, $to_id);
 
        print "<div class=\"patchset\">\n";
 
@@ -2392,6 +2391,7 @@ sub git_patchset_body {
  PATCH:
        while ($patch_line) {
                my @diff_header;
+               my ($from_id, $to_id);
 
                # git diff header
                #assert($patch_line =~ m/^diff /) if DEBUG;
@@ -2403,7 +2403,7 @@ sub git_patchset_body {
                while ($patch_line = <$fd>) {
                        chomp $patch_line;
 
-                       last EXTENDED_HEADER if ($patch_line =~ m/^--- /);
+                       last EXTENDED_HEADER if ($patch_line =~ m/^--- |^diff /);
 
                        if ($patch_line =~ m/^index ([0-9a-fA-F]{40})..([0-9a-fA-F]{40})/) {
                                $from_id = $1;
@@ -2439,11 +2439,15 @@ sub git_patchset_body {
                                $from{'href'} = href(action=>"blob", hash_base=>$hash_parent,
                                                     hash=>$diffinfo->{'from_id'},
                                                     file_name=>$from{'file'});
+                       } else {
+                               delete $from{'href'};
                        }
                        if ($diffinfo->{'status'} ne "D") { # not deleted file
                                $to{'href'} = href(action=>"blob", hash_base=>$hash,
                                                   hash=>$diffinfo->{'to_id'},
                                                   file_name=>$to{'file'});
+                       } else {
+                               delete $to{'href'};
                        }
                        # this is first patch for raw difftree line with $patch_idx index
                        # we index @$difftree array from 0, but number patches from 1
@@ -2475,11 +2479,11 @@ sub git_patchset_body {
                        # match <path>
                        if ($patch_line =~ s!^((copy|rename) from ).*$!$1! && $from{'href'}) {
                                $patch_line .= $cgi->a({-href=>$from{'href'}, -class=>"path"},
-                                                       esc_path($from{'file'}));
+                                                      esc_path($from{'file'}));
                        }
                        if ($patch_line =~ s!^((copy|rename) to ).*$!$1! && $to{'href'}) {
-                               $patch_line = $cgi->a({-href=>$to{'href'}, -class=>"path"},
-                                                     esc_path($to{'file'}));
+                               $patch_line .= $cgi->a({-href=>$to{'href'}, -class=>"path"},
+                                                      esc_path($to{'file'}));
                        }
                        # match <mode>
                        if ($patch_line =~ m/\s(\d{6})$/) {
@@ -2518,8 +2522,10 @@ sub git_patchset_body {
 
                # from-file/to-file diff header
                $patch_line = $last_patch_line;
+               last PATCH unless $patch_line;
+               next PATCH if ($patch_line =~ m/^diff /);
                #assert($patch_line =~ m/^---/) if DEBUG;
-               if ($from{'href'}) {
+               if ($from{'href'} && $patch_line =~ m!^--- "?a/!) {
                        $patch_line = '--- a/' .
                                      $cgi->a({-href=>$from{'href'}, -class=>"path"},
                                              esc_path($from{'file'}));
@@ -2531,7 +2537,7 @@ sub git_patchset_body {
                chomp $patch_line;
 
                #assert($patch_line =~ m/^+++/) if DEBUG;
-               if ($to{'href'}) {
+               if ($to{'href'} && $patch_line =~ m!^\+\+\+ "?b/!) {
                        $patch_line = '+++ b/' .
                                      $cgi->a({-href=>$to{'href'}, -class=>"path"},
                                              esc_path($to{'file'}));
@@ -2813,8 +2819,12 @@ sub git_tags_body {
                        print "<tr class=\"light\">\n";
                }
                $alternate ^= 1;
-               print "<td><i>$tag{'age'}</i></td>\n" .
-                     "<td>" .
+               if (defined $tag{'age'}) {
+                       print "<td><i>$tag{'age'}</i></td>\n";
+               } else {
+                       print "<td></td>\n";
+               }
+               print "<td>" .
                      $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'}),
                               -class => "list name"}, esc_html($tag{'name'})) .
                      "</td>\n" .
@@ -2995,7 +3005,7 @@ sub git_project_index {
 
        foreach my $pr (@projects) {
                if (!exists $pr->{'owner'}) {
-                       $pr->{'owner'} = get_file_owner("$projectroot/$project");
+                       $pr->{'owner'} = get_file_owner("$projectroot/$pr->{'path'}");
                }
 
                my ($path, $owner) = ($pr->{'path'}, $pr->{'owner'});
@@ -3208,9 +3218,14 @@ sub git_blame2 {
                                      esc_html($rev));
                        print "</td>\n";
                }
+               open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^")
+                       or die_error("could not open git-rev-parse");
+               my $parent_commit = <$dd>;
+               close $dd;
+               chomp($parent_commit);
                my $blamed = href(action => 'blame',
                                  file_name => $meta->{'filename'},
-                                 hash_base => $full_rev);
+                                 hash_base => $parent_commit);
                print "<td class=\"linenr\">";
                print $cgi->a({ -href => "$blamed#l$orig_lineno",
                                -id => "l$lineno",
@@ -4423,7 +4438,7 @@ sub git_shortlog {
        }
        my $refs = git_get_references();
 
-       my @commitlist = parse_commits($head, 101, (100 * $page));
+       my @commitlist = parse_commits($hash, 101, (100 * $page));
 
        my $paging_nav = format_paging_nav('shortlog', $hash, $head, $page, (100 * ($page+1)));
        my $next_link = '';
index 261baff049cd8b2e4d1b1a269992851eb00b2aa8..4824f4dc026e7b3f978fe4e9b2154335359e9d2e 100644 (file)
@@ -27,9 +27,12 @@ static int lock_file(struct lock_file *lk, const char *path)
        sprintf(lk->filename, "%s.lock", path);
        fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
        if (0 <= fd) {
-               if (!lk->next) {
+               if (!lk->on_list) {
                        lk->next = lock_file_list;
                        lock_file_list = lk;
+                       lk->on_list = 1;
+               }
+               if (lock_file_list) {
                        signal(SIGINT, remove_lock_file_on_signal);
                        atexit(remove_lock_file);
                }
@@ -37,6 +40,8 @@ static int lock_file(struct lock_file *lk, const char *path)
                        return error("cannot fix permission bits on %s",
                                     lk->filename);
        }
+       else
+               lk->filename[0] = 0;
        return fd;
 }
 
@@ -44,7 +49,7 @@ int hold_lock_file_for_update(struct lock_file *lk, const char *path, int die_on
 {
        int fd = lock_file(lk, path);
        if (fd < 0 && die_on_error)
-               die("unable to create '%s': %s", path, strerror(errno));
+               die("unable to create '%s.lock': %s", path, strerror(errno));
        return fd;
 }
 
index ca4f19e34d4216fed7c8110ba8f75585c69f92c7..bac16f577c95937a76738dce5d659cff71bd03ab 100644 (file)
@@ -1248,6 +1248,18 @@ static int merge(struct commit *h1,
        return clean;
 }
 
+static const char *better_branch_name(const char *branch)
+{
+       static char githead_env[8 + 40 + 1];
+       char *name;
+
+       if (strlen(branch) != 40)
+               return branch;
+       sprintf(githead_env, "GITHEAD_%s", branch);
+       name = getenv(githead_env);
+       return name ? name : branch;
+}
+
 static struct commit *get_ref(const char *ref)
 {
        unsigned char sha1[20];
@@ -1256,6 +1268,9 @@ static struct commit *get_ref(const char *ref)
        if (get_sha1(ref, sha1))
                die("Could not resolve ref '%s'", ref);
        object = deref_tag(parse_object(sha1), ref, strlen(ref));
+       if (object->type == OBJ_TREE)
+               return make_virtual_commit((struct tree*)object,
+                       better_branch_name(ref));
        if (object->type != OBJ_COMMIT)
                return NULL;
        if (parse_commit((struct commit *)object))
@@ -1263,18 +1278,6 @@ static struct commit *get_ref(const char *ref)
        return (struct commit *)object;
 }
 
-static const char *better_branch_name(const char *branch)
-{
-       static char githead_env[8 + 40 + 1];
-       char *name;
-
-       if (strlen(branch) != 40)
-               return branch;
-       sprintf(githead_env, "GITHEAD_%s", branch);
-       name = getenv(githead_env);
-       return name ? name : branch;
-}
-
 int main(int argc, char *argv[])
 {
        static const char *bases[2];
diff --git a/refs.c b/refs.c
index 121774cb328a4d8a773a6a870c642a030bf3752d..52057455a20cb4641005c9248b5499cca83c77c1 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -726,7 +726,6 @@ static int repack_without_ref(const char *refname)
        }
        if (!found)
                return 0;
-       memset(&packlock, 0, sizeof(packlock));
        fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
        if (fd < 0)
                return error("cannot delete '%s' from packed refs", refname);
index cc884f3b2debbaadfc3c42cda20074257fa48a65..c195d080db7c80420e1226f7591c9e9c3059536a 100644 (file)
@@ -14,114 +14,49 @@ static int send_all;
 static int force_update;
 static int use_thin_pack;
 
-static int is_zero_sha1(const unsigned char *sha1)
-{
-       int i;
-
-       for (i = 0; i < 20; i++) {
-               if (*sha1++)
-                       return 0;
-       }
-       return 1;
-}
-
-static void exec_pack_objects(void)
-{
-       static const char *args[] = {
-               "pack-objects",
-               "--all-progress",
-               "--stdout",
-               NULL
-       };
-       execv_git_cmd(args);
-       die("git-pack-objects exec failed (%s)", strerror(errno));
-}
-
-static void exec_rev_list(struct ref *refs)
-{
-       static const char *args[4];
-       int i = 0;
-
-       args[i++] = "rev-list"; /* 0 */
-       if (use_thin_pack)      /* 1 */
-               args[i++] = "--objects-edge";
-       else
-               args[i++] = "--objects";
-
-       args[i++] = "--stdin";
-
-       args[i] = NULL;
-       execv_git_cmd(args);
-       die("git-rev-list exec failed (%s)", strerror(errno));
-}
-
-/*
- * Run "rev-list --stdin | pack-objects" pipe.
- */
-static void rev_list(int fd, struct ref *refs)
-{
-       int pipe_fd[2];
-       pid_t pack_objects_pid;
-
-       if (pipe(pipe_fd) < 0)
-               die("rev-list setup: pipe failed");
-       pack_objects_pid = fork();
-       if (!pack_objects_pid) {
-               /* The child becomes pack-objects; reads from pipe
-                * and writes to the original fd
-                */
-               dup2(pipe_fd[0], 0);
-               dup2(fd, 1);
-               close(pipe_fd[0]);
-               close(pipe_fd[1]);
-               close(fd);
-               exec_pack_objects();
-               die("pack-objects setup failed");
-       }
-       if (pack_objects_pid < 0)
-               die("pack-objects fork failed");
-
-       /* We become rev-list --stdin; output goes to pipe. */
-       dup2(pipe_fd[1], 1);
-       close(pipe_fd[0]);
-       close(pipe_fd[1]);
-       close(fd);
-       exec_rev_list(refs);
-}
-
 /*
- * Create "rev-list --stdin | pack-objects" pipe and feed
- * the refs into the pipeline.
+ * Make a pack stream and spit it out into file descriptor fd
  */
-static void rev_list_generate(int fd, struct ref *refs)
+static int pack_objects(int fd, struct ref *refs)
 {
        int pipe_fd[2];
-       pid_t rev_list_generate_pid;
+       pid_t pid;
 
        if (pipe(pipe_fd) < 0)
-               die("rev-list-generate setup: pipe failed");
-       rev_list_generate_pid = fork();
-       if (!rev_list_generate_pid) {
-               /* The child becomes the "rev-list | pack-objects"
-                * pipeline.  It takes input from us, and its output
-                * goes to fd.
+               return error("send-pack: pipe failed");
+       pid = fork();
+       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);
-               rev_list(fd, refs);
-               die("rev-list setup failed");
+               execv_git_cmd(args);
+               die("git-pack-objects exec failed (%s)", strerror(errno));
        }
-       if (rev_list_generate_pid < 0)
-               die("rev-list-generate fork failed");
 
-       /* We feed the rev parameters to them.  We do not write into
-        * fd nor read from the pipe.
+       /*
+        * 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];
 
@@ -130,38 +65,38 @@ static void rev_list_generate(int fd, struct ref *refs)
                        memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
                        buf[0] = '^';
                        buf[41] = '\n';
-                       write(pipe_fd[1], buf, 42);
+                       if (!write_in_full(pipe_fd[1], 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';
-                       write(pipe_fd[1], buf, 41);
+                       if (!write_in_full(pipe_fd[1], buf, 41,
+                                               "send-pack: send refs"))
+                               break;
                }
                refs = refs->next;
        }
        close(pipe_fd[1]);
-       // waitpid(rev_list_generate_pid);
-       exit(0);
-}
 
-/*
- * Make a pack stream and spit it out into file descriptor fd
- */
-static void pack_objects(int fd, struct ref *refs)
-{
-       pid_t rev_list_pid;
+       for (;;) {
+               int status, code;
+               pid_t waiting = waitpid(pid, &status, 0);
 
-       rev_list_pid = fork();
-       if (!rev_list_pid) {
-               rev_list_generate(fd, refs);
-               die("rev-list setup failed");
+               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 (rev_list_pid < 0)
-               die("rev-list fork failed");
-       /*
-        * We don't wait for the rev-list pipeline in the parent:
-        * we end up waiting for the other end instead
-        */
 }
 
 static void unmark_and_free(struct commit_list *list, unsigned int mark)
@@ -341,7 +276,7 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
 
                if (!force_update &&
                    !delete_ref &&
-                   !is_zero_sha1(ref->old_sha1) &&
+                   !is_null_sha1(ref->old_sha1) &&
                    !ref->force) {
                        if (!has_sha1_file(ref->old_sha1) ||
                            !ref_newer(ref->peer_ref->new_sha1,
@@ -393,7 +328,7 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
 
        packet_flush(out);
        if (new_refs)
-               pack_objects(out, remote_refs);
+               ret = pack_objects(out, remote_refs);
        close(out);
 
        if (expect_status_report) {
index 99ada71349c7eaa0536bba1d622f7f7ffa56110e..af42ccc8d157e54b97de435194d22e5ed895ab4b 100644 (file)
@@ -7,26 +7,10 @@ then
        exit
 fi
 
-perl -e 'use SVN::Core; $SVN::Core::VERSION gt "1.1.0" or die' >/dev/null 2>&1
-if test $? -ne 0
-then
-       test_expect_success 'Perl SVN libraries not found, skipping test' :
-       test_done
-       exit
-fi
-
 GIT_DIR=$PWD/.git
 GIT_SVN_DIR=$GIT_DIR/svn/git-svn
 SVN_TREE=$GIT_SVN_DIR/svn-tree
 
-svnadmin >/dev/null 2>&1
-if test $? -ne 1
-then
-    test_expect_success 'skipping git-svn tests, svnadmin not found' :
-    test_done
-    exit
-fi
-
 svn >/dev/null 2>&1
 if test $? -ne 1
 then
@@ -37,13 +21,24 @@ fi
 
 svnrepo=$PWD/svnrepo
 
-set -e
-
-if svnadmin create --help | grep fs-type >/dev/null
+perl -w -e "
+use SVN::Core;
+use SVN::Repos;
+\$SVN::Core::VERSION gt '1.1.0' or exit(42);
+SVN::Repos::create('$svnrepo', undef, undef, undef,
+                           { 'fs-config' => 'fsfs'});
+"
+x=$?
+if test $x -ne 0
 then
-       svnadmin create --fs-type fsfs "$svnrepo"
-else
-       svnadmin create "$svnrepo"
+       if test $x -eq 42; then
+               err='Perl SVN libraries must be >= 1.1.0'
+       else
+               err='Perl SVN libraries not found or unusable, skipping test'
+       fi
+       test_expect_success "$err" :
+       test_done
+       exit
 fi
 
 svnrepo="file://$svnrepo"
diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh
new file mode 100755 (executable)
index 0000000..cd8cee6
--- /dev/null
@@ -0,0 +1,81 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Shawn O. Pearce
+#
+
+test_description='Test the update hook infrastructure.'
+. ./test-lib.sh
+
+test_expect_success setup '
+       echo This is a test. >a &&
+       git-update-index --add a &&
+       tree0=$(git-write-tree) &&
+       commit0=$(echo setup | git-commit-tree $tree0) &&
+       git-update-ref HEAD $commit0 &&
+       git-clone ./. victim &&
+       echo We hope it works. >a &&
+       git-update-index a &&
+       tree1=$(git-write-tree) &&
+       commit1=$(echo modify | git-commit-tree $tree1 -p $commit0) &&
+       git-update-ref HEAD $commit1
+'
+
+cat >victim/.git/hooks/update <<'EOF'
+#!/bin/sh
+echo "$@" >$GIT_DIR/update.args
+read x; echo -n "$x" >$GIT_DIR/update.stdin
+echo STDOUT update
+echo STDERR update >&2
+EOF
+chmod u+x victim/.git/hooks/update
+
+cat >victim/.git/hooks/post-update <<'EOF'
+#!/bin/sh
+echo "$@" >$GIT_DIR/post-update.args
+read x; echo -n "$x" >$GIT_DIR/post-update.stdin
+echo STDOUT post-update
+echo STDERR post-update >&2
+EOF
+chmod u+x victim/.git/hooks/post-update
+
+test_expect_success push '
+       git-send-pack ./victim/.git/ master >send.out 2>send.err
+'
+
+test_expect_success 'hooks ran' '
+       test -f victim/.git/update.args &&
+       test -f victim/.git/update.stdin &&
+       test -f victim/.git/post-update.args &&
+       test -f victim/.git/post-update.stdin
+'
+
+test_expect_success 'update hook arguments' '
+       echo refs/heads/master $commit0 $commit1 |
+       diff -u - victim/.git/update.args
+'
+
+test_expect_success 'post-update hook arguments' '
+       echo refs/heads/master |
+       diff -u - victim/.git/post-update.args
+'
+
+test_expect_failure 'update hook stdin is /dev/null' '
+       test -s victim/.git/update.stdin
+'
+
+test_expect_failure 'post-update hook stdin is /dev/null' '
+       test -s victim/.git/post-update.stdin
+'
+
+test_expect_failure 'send-pack produced no output' '
+       test -s send.out
+'
+
+test_expect_success 'send-pack stderr contains hook messages' '
+       grep "STDOUT update" send.err &&
+       grep "STDERR update" send.err &&
+       grep "STDOUT post-update" send.err &&
+       grep "STDERR post-update" send.err
+'
+
+test_done
index c22fe47213a8fccbb954aef4b175d2ea1a920e16..040da92756787371d584f33b53b54a3fc84f55b1 100755 (executable)
@@ -24,10 +24,7 @@ test_expect_success \
        mkdir import &&
        cd import &&
        echo foo > foo &&
-       if test -z '$NO_SYMLINK'
-       then
-               ln -s foo foo.link
-       fi
+       ln -s foo foo.link
        mkdir -p dir/a/b/c/d/e &&
        echo 'deep dir' > dir/a/b/c/d/e/file &&
        mkdir bar &&
@@ -136,48 +133,43 @@ test_expect_success "$name" "
        test -x '$SVN_TREE'/exec.sh"
 
 
-if test -z "$NO_SYMLINK"
-then
-       name='executable file becomes a symlink to bar/zzz (file)'
-
-       test_expect_success "$name" "
-               rm exec.sh &&
-               ln -s bar/zzz exec.sh &&
-               git update-index exec.sh &&
-               git commit -m '$name' &&
-               git-svn set-tree --find-copies-harder --rmdir \
-                       remotes/git-svn..mybranch5 &&
-               svn up '$SVN_TREE' &&
-               test -L '$SVN_TREE'/exec.sh"
+name='executable file becomes a symlink to bar/zzz (file)'
+test_expect_success "$name" "
+       rm exec.sh &&
+       ln -s bar/zzz exec.sh &&
+       git update-index exec.sh &&
+       git commit -m '$name' &&
+       git-svn set-tree --find-copies-harder --rmdir \
+               remotes/git-svn..mybranch5 &&
+       svn up '$SVN_TREE' &&
+       test -L '$SVN_TREE'/exec.sh"
 
-       name='new symlink is added to a file that was also just made executable'
+name='new symlink is added to a file that was also just made executable'
 
-       test_expect_success "$name" "
-               chmod +x bar/zzz &&
-               ln -s bar/zzz exec-2.sh &&
-               git update-index --add bar/zzz exec-2.sh &&
-               git commit -m '$name' &&
-               git-svn set-tree --find-copies-harder --rmdir \
-                       remotes/git-svn..mybranch5 &&
-               svn up '$SVN_TREE' &&
-               test -x '$SVN_TREE'/bar/zzz &&
-               test -L '$SVN_TREE'/exec-2.sh"
-
-       name='modify a symlink to become a file'
-       test_expect_success "$name" "
-               echo git help > help || true &&
-               rm exec-2.sh &&
-               cp help exec-2.sh &&
-               git update-index exec-2.sh &&
-               git commit -m '$name' &&
-               git-svn set-tree --find-copies-harder --rmdir \
-                       remotes/git-svn..mybranch5 &&
-               svn up '$SVN_TREE' &&
-               test -f '$SVN_TREE'/exec-2.sh &&
-               test ! -L '$SVN_TREE'/exec-2.sh &&
-               diff -u help $SVN_TREE/exec-2.sh"
-fi
+test_expect_success "$name" "
+       chmod +x bar/zzz &&
+       ln -s bar/zzz exec-2.sh &&
+       git update-index --add bar/zzz exec-2.sh &&
+       git commit -m '$name' &&
+       git-svn set-tree --find-copies-harder --rmdir \
+               remotes/git-svn..mybranch5 &&
+       svn up '$SVN_TREE' &&
+       test -x '$SVN_TREE'/bar/zzz &&
+       test -L '$SVN_TREE'/exec-2.sh"
 
+name='modify a symlink to become a file'
+test_expect_success "$name" "
+       echo git help > help || true &&
+       rm exec-2.sh &&
+       cp help exec-2.sh &&
+       git update-index exec-2.sh &&
+       git commit -m '$name' &&
+       git-svn set-tree --find-copies-harder --rmdir \
+               remotes/git-svn..mybranch5 &&
+       svn up '$SVN_TREE' &&
+       test -f '$SVN_TREE'/exec-2.sh &&
+       test ! -L '$SVN_TREE'/exec-2.sh &&
+       diff -u help $SVN_TREE/exec-2.sh"
 
 if test "$have_utf8" = t
 then
@@ -203,12 +195,6 @@ test_expect_success "$name" \
      git-rev-list --pretty=raw remotes/alt | grep ^tree | uniq > b &&
      diff -u a b"
 
-if test -n "$NO_SYMLINK"
-then
-       test_done
-       exit 0
-fi
-
 name='check imported tree checksums expected tree checksums'
 rm -f expected
 if test "$have_utf8" = t
index 5543b07f16d54bb5aa6fab6ab9cde79837a3e68b..46fcec50a5271d80464d7f37e5d402b1a3c0b6d0 100755 (executable)
@@ -57,13 +57,10 @@ test_expect_success 'setup some commits to svn' \
        'cd test_wc &&
                echo Greetings >> kw.c &&
                svn commit -m "Not yet an Id" &&
-               svn up &&
                echo Hello world >> kw.c &&
                svn commit -m "Modified file, but still not yet an Id" &&
-               svn up &&
                svn propset svn:keywords Id kw.c &&
-               svn commit -m "Propset Id" &&
-               svn up &&
+               svn commit -m "Propset Id"
        cd ..'
 
 test_expect_success 'initialize git-svn' "git-svn init $svnrepo"
@@ -86,8 +83,7 @@ test_expect_success "propset CR on crlf files" \
                svn propset svn:eol-style CR empty &&
                svn propset svn:eol-style CR crlf &&
                svn propset svn:eol-style CR ne_crlf &&
-               svn commit -m "propset CR on crlf files" &&
-               svn up &&
+               svn commit -m "propset CR on crlf files"
         cd ..'
 
 test_expect_success 'fetch and pull latest from svn and checkout a new wc' \
@@ -111,8 +107,7 @@ cd test_wc
         svn propset svn:eol-style CRLF ne_cr &&
         svn propset svn:keywords Id cr &&
         svn propset svn:keywords Id ne_cr &&
-        svn commit -m "propset CRLF on cr files" &&
-        svn up'
+        svn commit -m "propset CRLF on cr files"'
 cd ..
 test_expect_success 'fetch and pull latest from svn' \
        'git-svn fetch && git pull . remotes/git-svn'
index 293b98f92850375aa3223b6af85461a22cd151ea..b5f76770213a99f22a66441aebdc94fcd85070a7 100755 (executable)
@@ -16,25 +16,19 @@ test_expect_success 'initialize repo' "
        cd wc &&
        echo feedme >> branches/a/readme &&
        svn commit -m hungry &&
-       svn up &&
        cd trunk &&
        svn merge -r3:4 $svnrepo/branches/a &&
        svn commit -m 'merge with a' &&
        cd ../.. &&
-       svn log -v $svnrepo &&
-       git-svn init -i trunk $svnrepo/trunk &&
-       git-svn init -i a $svnrepo/branches/a &&
-       git-svn init -i tags/a $svnrepo/tags/a &&
-       git-svn fetch -i tags/a &&
-       git-svn fetch -i a &&
-       git-svn fetch -i trunk
+       git-svn multi-init $svnrepo -T trunk -b branches -t tags &&
+       git-svn multi-fetch
        "
 
 r1=`git-rev-list remotes/trunk | tail -n1`
 r2=`git-rev-list remotes/tags/a | tail -n1`
 r3=`git-rev-list remotes/a | tail -n1`
-r4=`git-rev-list remotes/a | head -n1`
-r5=`git-rev-list remotes/trunk | head -n1`
+r4=`git-rev-parse remotes/a`
+r5=`git-rev-parse remotes/trunk`
 
 test_expect_success 'test graft-branches regexes and copies' "
        test -n "$r1" &&
index 8d2e2fec395a328f4bf6a65af3c7eba5a98cc133..400c21cd49b6307eeef6d4d25cb34e612b1d0a71 100755 (executable)
@@ -17,7 +17,6 @@ test_expect_success 'initialize repo' "
        cd wc &&
        echo world >> trunk/readme &&
        svn commit -m 'another commit' &&
-       svn up &&
        svn mv -m 'rename to thunk' trunk thunk &&
        svn up &&
        echo goodbye >> thunk/readme &&
index bf108d4226fabf847f42420547471dfb3cef8a8e..72ea2b259875e209752022f63bd57f9ca83a0cc4 100755 (executable)
@@ -99,12 +99,12 @@ trap 'echo >&5 "FATAL: Unexpected exit with code $?"; exit 1' exit
 test_tick () {
        if test -z "${test_tick+set}"
        then
-               test_tick=432630000
+               test_tick=1112911993
        else
                test_tick=$(($test_tick + 60))
        fi
-       GIT_COMMITTER_DATE=$test_tick
-       GIT_AUTHOR_DATE=$test_tick
+       GIT_COMMITTER_DATE="$test_tick -0700"
+       GIT_AUTHOR_DATE="$test_tick -0700"
        export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
 }
 
index 14cc5aea6c4eefbf4b0fa9c72ccb70ec550b44b4..22f4550b6ca4d2cb185fc19f6be414a8a0874a60 100644 (file)
@@ -113,7 +113,6 @@ void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callb
        struct name_entry *entry = xmalloc(n*sizeof(*entry));
 
        for (;;) {
-               struct name_entry entry[3];
                unsigned long mask = 0;
                int i, last;
 
index 8cf6486025a0fffba889618ce07a0d4eadd139df..6db1d3123d1761dee4850e0a56a9b86b4bf1722a 100644 (file)
@@ -59,3 +59,26 @@ int write_or_whine(int fd, const void *buf, size_t count, const char *msg)
 
        return 1;
 }
+
+int write_in_full(int fd, const void *buf, size_t count, const char *msg)
+{
+       const char *p = buf;
+       ssize_t written;
+
+       while (count > 0) {
+               written = xwrite(fd, p, count);
+               if (written == 0) {
+                       fprintf(stderr, "%s: disk full?\n", msg);
+                       return 0;
+               }
+               else if (written < 0) {
+                       fprintf(stderr, "%s: write error (%s)\n",
+                               msg, strerror(errno));
+                       return 0;
+               }
+               count -= written;
+               p += written;
+       }
+
+       return 1;
+}
index db427384ff454aa07e38c588537cbaa6b835dee6..8aac526440bc31e0ade55c77e6b2b6120b5132ee 100644 (file)
@@ -15,7 +15,7 @@ static char wt_status_colors[][COLOR_MAXLEN] = {
        "\033[31m", /* WT_STATUS_CHANGED: red */
        "\033[31m", /* WT_STATUS_UNTRACKED: red */
 };
-static const char* use_add_msg = "use \"git add file1 file2\" to include for commit";
+static const char* use_add_msg = "use \"git add <file>...\" to incrementally add content to commit";
 
 static int parse_status_slot(const char *var, int offset)
 {
@@ -41,8 +41,6 @@ void wt_status_prepare(struct wt_status *s)
        unsigned char sha1[20];
        const char *head;
 
-       s->is_initial = get_sha1("HEAD", sha1) ? 1 : 0;
-
        head = resolve_ref("HEAD", sha1, 0, NULL);
        s->branch = head ? xstrdup(head) : NULL;
 
@@ -51,6 +49,20 @@ void wt_status_prepare(struct wt_status *s)
        s->verbose = 0;
        s->commitable = 0;
        s->untracked = 0;
+
+       s->workdir_clean = 1;
+}
+
+static void wt_status_print_cached_header(const char *reference)
+{
+       const char *c = color(WT_STATUS_HEADER);
+       color_printf_ln(c, "# Cached changes to be committed:");
+       if (reference) {
+               color_printf_ln(c, "#   (use \"git reset %s <file>...\" and \"git rm --cached <file>...\" to unstage)", reference);
+       } else {
+               color_printf_ln(c, "#   (use \"git rm --cached <file>...\" to unstage)");
+       }
+       color_printf_ln(c, "#");
 }
 
 static void wt_status_print_header(const char *main, const char *sub)
@@ -147,8 +159,7 @@ static void wt_status_print_updated_cb(struct diff_queue_struct *q,
                if (q->queue[i]->status == 'U')
                        continue;
                if (!shown_header) {
-                       wt_status_print_header("Added but not yet committed",
-                                       "will commit");
+                       wt_status_print_cached_header(s->reference);
                        s->commitable = 1;
                        shown_header = 1;
                }
@@ -162,9 +173,12 @@ static void wt_status_print_changed_cb(struct diff_queue_struct *q,
                         struct diff_options *options,
                         void *data)
 {
+       struct wt_status *s = data;
        int i;
-       if (q->nr)
+       if (q->nr) {
+               s->workdir_clean = 0;
                wt_status_print_header("Changed but not added", use_add_msg);
+       }
        for (i = 0; i < q->nr; i++)
                wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]);
        if (q->nr)
@@ -179,8 +193,7 @@ void wt_status_print_initial(struct wt_status *s)
        read_cache();
        if (active_nr) {
                s->commitable = 1;
-               wt_status_print_header("Added but not yet committed",
-                               "will commit");
+               wt_status_print_cached_header(NULL);
        }
        for (i = 0; i < active_nr; i++) {
                color_printf(color(WT_STATUS_HEADER), "#\t");
@@ -215,7 +228,7 @@ static void wt_status_print_changed(struct wt_status *s)
        run_diff_files(&rev, 0);
 }
 
-static void wt_status_print_untracked(const struct wt_status *s)
+static void wt_status_print_untracked(struct wt_status *s)
 {
        struct dir_struct dir;
        const char *x;
@@ -250,6 +263,7 @@ static void wt_status_print_untracked(const struct wt_status *s)
                                continue;
                }
                if (!shown_header) {
+                       s->workdir_clean = 0;
                        wt_status_print_header("Untracked files", use_add_msg);
                        shown_header = 1;
                }
@@ -271,6 +285,9 @@ static void wt_status_print_verbose(struct wt_status *s)
 
 void wt_status_print(struct wt_status *s)
 {
+       unsigned char sha1[20];
+       s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
+
        if (s->branch)
                color_printf_ln(color(WT_STATUS_HEADER),
                        "# On branch %s", s->branch);
@@ -291,10 +308,16 @@ void wt_status_print(struct wt_status *s)
 
        if (s->verbose && !s->is_initial)
                wt_status_print_verbose(s);
-       if (!s->commitable)
-               printf("%s (%s)\n",
-                       s->amend ? "# No changes" : "nothing to commit",
-                       use_add_msg);
+       if (!s->commitable) {
+               if (s->amend)
+                       printf("# No changes\n");
+               else if (s->workdir_clean)
+                       printf(s->is_initial
+                              ? "nothing to commit\n"
+                              : "nothing to commit (working directory matches HEAD)\n");
+               else
+                       printf("no changes added to commit (use \"git add\" and/or \"git commit [-a|-i|-o]\")\n");
+       }
 }
 
 int git_status_config(const char *k, const char *v)
index 0a5a5b7ba9fc1d50c7dcab8220a4fb77ffecddb4..892a86c76abe69ce69327a10a5d491cc47f8e82b 100644 (file)
@@ -16,6 +16,7 @@ struct wt_status {
        int verbose;
        int amend;
        int untracked;
+       int workdir_clean;
 };
 
 int git_status_config(const char *var, const char *value);