Merge branch 'bc/filter-branch'
authorJunio C Hamano <gitster@pobox.com>
Tue, 6 May 2008 02:16:20 +0000 (19:16 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 6 May 2008 02:16:20 +0000 (19:16 -0700)
* bc/filter-branch:
filter-branch.sh: support nearly proper tag name filtering

157 files changed:
Documentation/RelNotes-1.5.5.1.txt [new file with mode: 0644]
Documentation/RelNotes-1.5.5.txt
Documentation/RelNotes-1.5.6.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/core-tutorial.txt
Documentation/cvs-migration.txt
Documentation/diff-options.txt
Documentation/everyday.txt
Documentation/git-add.txt
Documentation/git-bisect.txt
Documentation/git-branch.txt
Documentation/git-checkout.txt
Documentation/git-clone.txt
Documentation/git-commit.txt
Documentation/git-config.txt
Documentation/git-cvsserver.txt
Documentation/git-filter-branch.txt
Documentation/git-fsck.txt
Documentation/git-gc.txt
Documentation/git-init.txt
Documentation/git-pack-objects.txt
Documentation/git-prune.txt
Documentation/git-push.txt
Documentation/git-remote.txt
Documentation/git-request-pull.txt
Documentation/git-rev-parse.txt
Documentation/git-rm.txt
Documentation/git-shortlog.txt
Documentation/git-status.txt
Documentation/git-submodule.txt
Documentation/git-svn.txt
Documentation/git-tag.txt
Documentation/git-unpack-objects.txt
Documentation/git.txt
Documentation/gitk.txt
Documentation/hooks.txt
Documentation/howto/setup-git-server-over-http.txt
Documentation/pretty-formats.txt
Documentation/repository-layout.txt
Documentation/technical/pack-format.txt
Documentation/user-manual.txt
GIT-VERSION-GEN
INSTALL
Makefile
RelNotes
archive-tar.c
archive-zip.c
archive.c
attr.c
builtin-apply.c
builtin-branch.c
builtin-checkout.c
builtin-clean.c
builtin-commit.c
builtin-config.c
builtin-fetch-pack.c
builtin-fetch.c
builtin-fmt-merge-msg.c
builtin-gc.c
builtin-init-db.c
builtin-log.c
builtin-push.c
builtin-remote.c
builtin-rev-parse.c
builtin-shortlog.c
builtin-tag.c
builtin-unpack-objects.c
builtin-verify-tag.c
cache-tree.c
cache.h
combine-diff.c
commit.h
config.c
contrib/completion/git-completion.bash
contrib/emacs/git.el
contrib/hooks/post-receive-email
contrib/hooks/pre-auto-gc-battery [new file with mode: 0644]
copy.c
diff-lib.c
diff.c
diff.h
diffcore-rename.c
dir.c
environment.c
git-add--interactive.perl
git-am.sh
git-bisect.sh
git-clone.sh
git-compat-util.h
git-cvsimport.perl
git-cvsserver.perl
git-filter-branch.sh
git-gui/GIT-VERSION-GEN
git-gui/git-gui.sh
git-gui/po/fr.po
git-rebase.sh
git-send-email.perl
git-submodule.sh
git-svn.perl
git.c
gitk-git/gitk
gitweb/INSTALL
gitweb/README
gitweb/gitweb.perl
help.c
imap-send.c
log-tree.c
log-tree.h
mktag.c
pack-write.c
parse-options.c
path.c
perl/Git.pm
pkt-line.c
pretty.c
receive-pack.c
refs.c
remote.c
remote.h
revision.c
revision.h
setup.c
sha1-lookup.c [new file with mode: 0644]
sha1-lookup.h [new file with mode: 0644]
sha1_file.c
sha1_name.c
t/t0002-gitfile.sh [new file with mode: 0755]
t/t0003-attributes.sh
t/t0004-unwritable.sh [new file with mode: 0755]
t/t1300-repo-config.sh
t/t1301-shared-repo.sh
t/t2002-checkout-cache-u.sh
t/t3201-branch-contains.sh
t/t3408-rebase-multi-line.sh [new file with mode: 0755]
t/t3701-add-interactive.sh
t/t3800-mktag.sh
t/t4104-apply-boundary.sh
t/t5000-tar-tree.sh
t/t5300-pack-object.sh
t/t5505-remote.sh
t/t5516-fetch-push.sh
t/t5517-push-mirror.sh
t/t5601-clone.sh [new file with mode: 0755]
t/t6030-bisect-porcelain.sh
t/t7003-filter-branch.sh
t/t7004-tag.sh
t/t7005-editor.sh
t/t7300-clean.sh
t/t7401-submodule-summary.sh
t/t7502-status.sh
t/t9121-git-svn-fetch-renamed-dir.sh [new file with mode: 0755]
t/t9121/renamed-dir.dump [new file with mode: 0644]
t/t9400-git-cvsserver-server.sh
t/t9500-gitweb-standalone-no-errors.sh
var.c
write_or_die.c
wt-status.c
diff --git a/Documentation/RelNotes-1.5.5.1.txt b/Documentation/RelNotes-1.5.5.1.txt
new file mode 100644 (file)
index 0000000..7de4197
--- /dev/null
@@ -0,0 +1,44 @@
+GIT v1.5.5.1 Release Notes
+==========================
+
+Fixes since v1.5.5
+------------------
+
+ * "git archive --prefix=$path/" mishandled gitattributes.
+
+ * "git fetch -v" that fetches into FETCH_HEAD did not report the summary
+   the same way as done for updating the tracking refs.
+
+ * "git svn" misbehaved when the configuration file customized the "git
+   log" output format using format.pretty.
+
+ * "git submodule status" leaked an unnecessary error message.
+
+ * "git log --date-order --topo-order" did not override the earlier
+   date-order with topo-order as expected.
+
+ * "git bisect good $this" did not check the validity of the revision
+   given properly.
+
+ * "url.<there>.insteadOf" did not work correctly.
+
+ * "git clean" ran inside subdirectory behaved as if the directory was
+   explicitly specified for removal by the end user from the top level.
+
+ * "git bisect" from a detached head leaked an unnecessary error message.
+
+ * "git bisect good $a $b" when $a is Ok but $b is bogus should have
+   atomically failed before marking $a as good.
+
+ * "git fmt-merge-msg" did not clean up leading empty lines from commit
+   log messages like "git log" family does.
+
+ * "git am" recorded a commit with empty Subject: line without
+   complaining.
+
+ * when given a commit log message whose first paragraph consists of
+   multiple lines, "git rebase" squashed it into a single line.
+
+ * "git remote add $bogus_name $url" did not complain properly.
+
+Also comes with various documentation updates.
index b299e8792c3fb4ad875ad243ea98198383b3c6d1..29322124881bf65c3ee6f5d613251b09f4a98d9a 100644 (file)
@@ -6,7 +6,7 @@ Updates since v1.5.4
 
 (subsystems)
 
- * Comes with git-gui 0.9.3.
+ * Comes with git-gui 0.10.1
 
 (portability)
 
@@ -166,8 +166,8 @@ Updates since v1.5.4
    symmetric difference between the HEAD version and the work tree version
    of the submodule commits.
 
- * Various "git cvsimport", "git cvsexportcommit", "git svn" and
-   "git p4" improvements.
+ * Various "git cvsimport", "git cvsexportcommit", "git cvsserver",
+   "git svn" and "git p4" improvements.
 
 (internal)
 
@@ -205,9 +205,3 @@ this release, unless otherwise noted.
 
  * "git imap-send" without setting imap.host did not error out but
    segfaulted.
-
----
-exec >/var/tmp/1
-O=v1.5.5-rc1-21-g319a36a
-echo O=`git describe refs/heads/master`
-git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
diff --git a/Documentation/RelNotes-1.5.6.txt b/Documentation/RelNotes-1.5.6.txt
new file mode 100644 (file)
index 0000000..f3256fb
--- /dev/null
@@ -0,0 +1,59 @@
+GIT v1.5.6 Release Notes
+========================
+
+Updates since v1.5.5
+--------------------
+
+(subsystems)
+
+
+(portability)
+
+
+(performance)
+
+* "git rebase --onto $there $from $branch" used to switch to the tip of
+  $branch only to immediately reset back to $from, smudging work tree
+  files unnecessarily.  This has been optimized.
+
+(usability, bells and whistles)
+
+* "git add -p" (and the "patch" subcommand of "git add -i") can choose to
+  apply (or not apply) mode changes independently from contents changes.
+
+* "git bisect help" gives longer and more helpful usage information.
+
+* "git diff/log --dirstat" output is consistent between binary and textual
+  changes.
+
+* "git gc --auto" honors a new pre-aut-gc hook to temporarily disable it.
+
+* "git log --pretty=tformat:<custom format>" gives a LF after each entry,
+  instead of giving a LF between each pair of entries which is how
+  "git log --pretty=format:<custom format>" works.
+
+* "git send-email" now can send out messages outside a git repository.
+
+* "git status" can optionally include output from "git submodule
+  summary".
+
+* "gitweb" can read from a system-wide configuration file.
+
+(internal)
+
+* "git unpack-objects" and "git receive-pack" is now more strict about
+  detecting breakage in the objects they receive over the wire.
+
+
+Fixes since v1.5.5
+------------------
+
+All of the fixes in v1.5.5 maintenance series are included in
+this release, unless otherwise noted.
+
+
+--
+exec >/var/tmp/1
+O=v1.5.5-56-g5f0734f
+echo O=`git describe refs/heads/master`
+git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
index 3017d640cff918e33544272cb2b9f7b9cfd5db4c..00f089fee4178b8d402307979b8adc898af6fdc7 100644 (file)
@@ -234,7 +234,13 @@ core.worktree::
        used in combination with repositories found automatically in
        a .git directory (i.e. $GIT_DIR is not set).
        This can be overridden by the GIT_WORK_TREE environment
-       variable and the '--work-tree' command line option.
+       variable and the '--work-tree' command line option. It can be
+       a absolute path or relative path to the directory specified by
+       --git-dir or GIT_DIR.
+       Note: If --git-dir or GIT_DIR are specified but none of
+       --work-tree, GIT_WORK_TREE and core.worktree is specified,
+       the current working directory is regarded as the top directory
+       of your working tree.
 
 core.logAllRefUpdates::
        Enable the reflog. Updates to a ref <ref> is logged to the file
@@ -261,7 +267,12 @@ core.sharedRepository::
        group-writable). When 'all' (or 'world' or 'everybody'), the
        repository will be readable by all users, additionally to being
        group-shareable. When 'umask' (or 'false'), git will use permissions
-       reported by umask(2). See linkgit:git-init[1]. False by default.
+       reported by umask(2). When '0xxx', where '0xxx' is an octal number,
+       files in the repository will have this mode value. '0xxx' will override
+       user's umask value, and thus, users with a safe umask (0077) can use
+       this option. Examples: '0660' is equivalent to 'group'. '0640' is a
+       repository that is group-readable but not group-writable.
+       See linkgit:git-init[1]. False by default.
 
 core.warnAmbiguousRefs::
        If true, git will warn you if the ref name you passed it is ambiguous
@@ -661,6 +672,13 @@ gitcvs.dbuser, gitcvs.dbpass::
        'gitcvs.dbuser' supports variable substitution (see
        linkgit:git-cvsserver[1] for details).
 
+gitcvs.dbTableNamePrefix::
+       Database table name prefix.  Prepended to the names of any
+       database tables used, allowing a single database to be used
+       for several repositories.  Supports variable substitution (see
+       linkgit:git-cvsserver[1] for details).  Any non-alphabetic
+       characters will be replaced with underscores.
+
 All gitcvs variables except for 'gitcvs.allbinary' can also be
 specified as 'gitcvs.<access_method>.<varname>' (where 'access_method'
 is one of "ext" and "pserver") to make them apply only for the given
@@ -903,6 +921,10 @@ remote.<name>.push::
        The default set of "refspec" for linkgit:git-push[1]. See
        linkgit:git-push[1].
 
+remote.<name>.mirror::
+       If true, pushing to this remote will automatically behave
+       as if the `\--mirror` option was given on the command line.
+
 remote.<name>.skipDefaultUpdate::
        If true, this remote will be skipped by default when updating
        using the update subcommand of linkgit:git-remote[1].
@@ -984,6 +1006,12 @@ imap::
        The configuration variables in the 'imap' section are described
        in linkgit:git-imap-send[1].
 
+receive.fsckObjects::
+       If it is set to true, git-receive-pack will check all received
+       objects. It will abort in the case of a malformed object or a
+       broken link. The result of an abort are only dangling objects.
+       Defaults to false.
+
 receive.unpackLimit::
        If the number of objects received in a push is below this
        limit then the objects will be unpacked into loose object
index aa40dfd36a6f210c5808791225014e023d18c0de..5a5531222d8f514b27ba63d9fc9ecc17335cdc3c 100644 (file)
@@ -535,18 +535,18 @@ with the associated patches use the more complex (and much more
 powerful)
 
 ----------------
-$ git-whatchanged -p --root
+$ git-whatchanged -p
 ----------------
 
 and you will see exactly what has changed in the repository over its
 short history.
 
 [NOTE]
-The `\--root` flag is a flag to `git-diff-tree` to tell it to
-show the initial aka 'root' commit too. Normally you'd probably not
-want to see the initial import diff, but since the tutorial project
-was started from scratch and is so small, we use it to make the result
-a bit more interesting.
+When using the above two commands, the initial commit will be shown.
+If this is a problem because it is huge, you can hide it by setting
+the log.showroot configuration variable to false. Having this, you
+can still show it for each command just adding the `\--root` option,
+which is a flag for `git-diff-tree` accepted by both commands.
 
 With that, you should now be having some inkling of what git does, and
 can explore on your own.
index ea98900228bda97cd4a3da190de9de5ecc3ec6e6..00f2e36b2ec162c6ef6d9c731ff549643ecb3ce7 100644 (file)
@@ -8,7 +8,8 @@ designating a single shared repository which people can synchronize with;
 this document explains how to do that.
 
 Some basic familiarity with git is required.  This
-link:tutorial.html[tutorial introduction to git] should be sufficient.
+link:tutorial.html[tutorial introduction to git] and the
+link:glossary.html[git glossary] should be sufficient.
 
 Developing against a shared repository
 --------------------------------------
index 8dc5b001c42376be1b9f814d516ea43f5c711071..13234fa280b279bfccad0a9aec6989727957567a 100644 (file)
@@ -58,6 +58,14 @@ endif::git-format-patch[]
        number of modified files, as well as number of added and deleted
        lines.
 
+--dirstat[=limit]::
+       Output only the sub-directories that are impacted by a diff,
+       and to what degree they are impacted.  You can override the
+       default cut-off in percent (3) by "--dirstat=limit".  If you
+       want to enable "cumulative" directory statistics, you can use
+       the "--cumulative" flag, which adds up percentages recursively
+       even when they have been already reported for a sub-directory.
+
 --summary::
        Output a condensed summary of extended header information
        such as creations, renames and mode changes.
@@ -75,7 +83,8 @@ endif::git-format-patch[]
        Show only names of changed files.
 
 --name-status::
-       Show only names and status of changed files.
+       Show only names and status of changed files. See the description
+       of the `--diff-filter` option on what the status letters mean.
 
 --color::
        Show colored diff.
index fdbd15a18158f0b0a531c2d3df21afd7b1aa26c8..e598cdda45cf0b953a106d6786765b3316e2cc16 100644 (file)
@@ -48,14 +48,12 @@ $ git gc <3>
 repository health reasonably well.
 <2> check how many loose objects there are and how much
 disk space is wasted by not repacking.
-<3> repacks the local repository and performs other housekeeping tasks. Running
-without `--prune` is a safe operation even while other ones are in progress.
+<3> repacks the local repository and performs other housekeeping tasks.
 
 Repack a small project into single pack.::
 +
 ------------
 $ git gc <1>
-$ git gc --prune
 ------------
 +
 <1> pack all the objects reachable from the refs into one pack,
@@ -182,7 +180,7 @@ $ git pull <3>
 $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <4>
 $ git pull git://git.kernel.org/pub/.../jgarzik/libata-dev.git ALL <5>
 $ git reset --hard ORIG_HEAD <6>
-$ git gc --prune <7>
+$ git gc <7>
 $ git fetch --tags <8>
 ------------
 +
index c751a17d079dc2a6c5d3160573a42a7b6be3d5e2..35e67a06e4248182c05c98b4b9b421bb3e02940a 100644 (file)
@@ -226,6 +226,12 @@ diff::
   This lets you review what will be committed (i.e. between
   HEAD and index).
 
+Bugs
+----
+The interactive mode does not work with files whose names contain
+characters that need C-quoting.  `core.quotepath` configuration can be
+used to work this limitation around to some degree, but backslash,
+double-quote and control characters will still have problems.
 
 See Also
 --------
index 96585ae8d9455654b63e83d61e7c34a7aeb94291..698ffde7ce844e38abe2719b2f8043783742f134 100644 (file)
@@ -15,6 +15,7 @@ DESCRIPTION
 The command takes various subcommands, and different options depending
 on the subcommand:
 
+ git bisect help
  git bisect start [<bad> [<good>...]] [--] [<paths>...]
  git bisect bad [<rev>]
  git bisect good [<rev>...]
@@ -29,6 +30,12 @@ This command uses 'git-rev-list --bisect' option to help drive the
 binary search process to find which change introduced a bug, given an
 old "good" commit object name and a later "bad" commit object name.
 
+Getting help
+~~~~~~~~~~~~
+
+Use "git bisect" to get a short usage description, and "git bisect
+help" or "git bisect -h" to get a long usage description.
+
 Basic bisect commands: start, bad, good
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
index 6f07a17a2c310a67f23034c520ab10670407c006..c824d887420754f98db0553a006865d31d01cf1d 100644 (file)
@@ -8,7 +8,7 @@ git-branch - List, create, or delete branches
 SYNOPSIS
 --------
 [verse]
-'git-branch' [--color | --no-color] [-r | -a]
+'git-branch' [--color | --no-color] [-r | -a] [--merged | --no-merged]
           [-v [--abbrev=<length> | --no-abbrev]]
           [--contains <commit>]
 'git-branch' [--track | --no-track] [-l] [-f] <branchname> [<start-point>]
@@ -24,6 +24,8 @@ and option `-a` shows both.
 With `--contains <commit>`, shows only the branches that
 contains the named commit (in other words, the branches whose
 tip commits are descendant of the named commit).
+With `--merged`, only branches merged into HEAD will be listed, and
+with `--no-merged` only branches not merged into HEAD will be listed.
 
 In its second form, a new branch named <branchname> will be created.
 It will start out with a head equal to the one given as <start-point>.
@@ -118,6 +120,15 @@ OPTIONS
 --no-track::
        Ignore the branch.autosetupmerge configuration variable.
 
+--contains <commit>::
+       Only list branches which contain the specified commit.
+
+--merged::
+       Only list branches which are fully contained by HEAD.
+
+--no-merged::
+       Do not list branches which are fully contained by HEAD.
+
 <branchname>::
        The name of the branch to create or delete.
        The new branch name must pass all checks defined by
@@ -175,6 +186,18 @@ If you are creating a branch that you want to immediately checkout, it's
 easier to use the git checkout command with its `-b` option to create
 a branch and check it out with a single command.
 
+The options `--contains`, `--merged` and `--no-merged` serves three related
+but different purposes:
+
+- `--contains <commit>` is used to find all branches which will need
+  special attention if <commit> were to be rebased or amended, since those
+  branches contain the specified <commit>.
+
+- `--merged` is used to find all branches which can be safely deleted,
+  since those branches are fully contained by HEAD.
+
+- `--no-merged` is used to find branches which are candidates for merging
+  into HEAD, since those branches are not fully contained by HEAD.
 
 Author
 ------
index e11cddbfc96f742968789abd369a4f7e240db27c..a644173e15da7e3203fa8de8a6cb917f138f93c2 100644 (file)
@@ -47,7 +47,7 @@ OPTIONS
        by linkgit:git-check-ref-format[1].  Some of these checks
        may restrict the characters allowed in a branch name.
 
---track::
+-t, --track::
        When creating a new branch, set up configuration so that git-pull
        will automatically retrieve data from the start point, which must be
        a branch. Use this if you always pull from the same upstream branch
index 975824301a45bfa297f2ecd5c19e429cb9c86c65..9b564420c53e33616c605b6564e2f1f227725805 100644 (file)
@@ -65,10 +65,13 @@ OPTIONS
 +
 *NOTE*: this is a possibly dangerous operation; do *not* use
 it unless you understand what it does. If you clone your
-repository using this option, then delete branches in the
-source repository and then run linkgit:git-gc[1] using the
-'--prune' option in the source repository, it may remove
-objects which are referenced by the cloned repository.
+repository using this option and then delete branches (or use any
+other git command that makes any existing commit unreferenced) in the
+source repository, some objects may become unreferenced (or dangling).
+These objects may be removed by normal git operations (such as git-commit[1])
+which automatically call git-gc[1]. If these objects are removed and
+were referenced by the cloned repository, then the cloned repository
+will become corrupt.
 
 
 
@@ -79,6 +82,8 @@ objects which are referenced by the cloned repository.
        an already existing repository as an alternate will
        require fewer objects to be copied from the repository
        being cloned, reducing network and local storage costs.
++
+*NOTE*: see NOTE to --shared option.
 
 --quiet::
 -q::
index b4ae61ff46f4b7ff9e71e9e91a999fdc8fc17560..4bb51cc06ec5e1f47b748b14353e49a0acfdc775 100644 (file)
@@ -139,6 +139,17 @@ but can be used to amend a merge commit.
        as well.  This is usually not what you want unless you
        are concluding a conflicted merge.
 
+-o|--only::
+       Make a commit only from the paths specified on the
+       command line, disregarding any contents that have been
+       staged so far. This is the default mode of operation of
+       'git commit' if any paths are given on the command line,
+       in which case this option can be omitted.
+       If this option is specified together with '--amend', then
+       no paths need be specified, which can be used to amend
+       the last commit without committing changes that have
+       already been staged.
+
 -u|--untracked-files::
        Show all untracked files, also those in uninteresting
        directories, in the "Untracked files:" section of commit
index fa161718dd33a0b4c3b58fb3898e74369bcc3d94..5de5d051b7917daabd0ef7c0681c6b75307a931a 100644 (file)
@@ -144,6 +144,8 @@ See also <<FILES>>.
        "auto".  If `stdout-is-tty` is missing, then checks the standard
        output of the command itself, and exits with status 0 if color
        is to be used, or exits with status 1 otherwise.
+       When the color setting for `name` is undefined, the command uses
+       `color.ui` as fallback.
 
 --get-color name default::
 
index d3e99931d7d344cdfb04b90133dd926a2aac1c9b..b1106714b2f7100124826819315e78f7f28325fb 100644 (file)
@@ -110,7 +110,9 @@ cvs -d ":ext;CVS_SERVER=git-cvsserver:user@server/path/repo.git" co <HEAD_name>
 ------
 This has the advantage that it will be saved in your 'CVS/Root' files and
 you don't need to worry about always setting the correct environment
-variable.
+variable.  SSH users restricted to git-shell don't need to override the default
+with CVS_SERVER (and shouldn't) as git-shell understands `cvs` to mean
+git-cvsserver and pretends that the other end runs the real cvs better.
 --
 2. For each repo that you want accessible from CVS you need to edit config in
    the repo and add the following section.
@@ -141,25 +143,29 @@ allowing access over SSH.
         enabled=1
 ------
 --
-3. On the client machine you need to set the following variables.
-   CVSROOT should be set as per normal, but the directory should point at the
-   appropriate git repo. For example:
+3. If you didn't specify the CVSROOT/CVS_SERVER directly in the checkout command,
+   automatically saving it in your 'CVS/Root' files, then you need to set them
+   explicitly in your environment.  CVSROOT should be set as per normal, but the
+   directory should point at the appropriate git repo.  As above, for SSH clients
+   _not_ restricted to git-shell, CVS_SERVER should be set to git-cvsserver.
 +
 --
-For SSH access, CVS_SERVER should be set to git-cvsserver
-
-Example:
-
 ------
      export CVSROOT=:ext:user@server:/var/git/project.git
      export CVS_SERVER=git-cvsserver
 ------
 --
-4. For SSH clients that will make commits, make sure their .bashrc file
-   sets the GIT_AUTHOR and GIT_COMMITTER variables.
+4. For SSH clients that will make commits, make sure their server-side
+   .ssh/environment files (or .bashrc, etc., according to their specific shell)
+   export appropriate values for GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL,
+   GIT_COMMITTER_NAME, and GIT_COMMITTER_EMAIL.  For SSH clients whose login
+   shell is bash, .bashrc may be a reasonable alternative.
 
 5. Clients should now be able to check out the project. Use the CVS 'module'
-   name to indicate what GIT 'head' you want to check out. Example:
+   name to indicate what GIT 'head' you want to check out.  This also sets the
+   name of your newly checked-out directory, unless you tell it otherwise with
+   `-d <dir_name>`.  For example, this checks out 'master' branch to the
+   `project-master` directory:
 +
 ------
      cvs co -d project-master master
@@ -227,6 +233,11 @@ gitcvs.dbpass::
        Database password.  Only useful if setting `dbdriver`, since
        SQLite has no concept of database passwords.
 
+gitcvs.dbTableNamePrefix::
+       Database table name prefix.  Supports variable substitution
+       (see below).  Any non-alphabetic characters will be replaced
+       with underscores.
+
 All variables can also be set per access method, see <<configaccessmethod,above>>.
 
 Variable substitution
index 9d11b418bb951d945e3c89dfef5c67ed7197287c..8d80f0d074c92fccc2a9746c5f374c1626fdde97 100644 (file)
@@ -249,12 +249,12 @@ committed a merge between P1 and P2, it will be propagated properly
 and all children of the merge will become merge commits with P1,P2
 as their parents instead of the merge commit.
 
-You can rewrite the commit log messages using `--message-filter`.  For
+You can rewrite the commit log messages using `--msg-filter`.  For
 example, `git-svn-id` strings in a repository created by `git-svn` can
 be removed this way:
 
 -------------------------------------------------------
-git filter-branch --message-filter '
+git filter-branch --msg-filter '
        sed -e "/^git-svn-id:/d"
 '
 -------------------------------------------------------
index f16cb986122c8560622bfbceebc325ee4d27b768..4cc26fb744e892115d8bfa099e48acc102a8689b 100644 (file)
@@ -22,7 +22,8 @@ OPTIONS
        An object to treat as the head of an unreachability trace.
 +
 If no objects are given, git-fsck defaults to using the
-index file and all SHA1 references in .git/refs/* as heads.
+index file, all SHA1 references in .git/refs/*, and all reflogs (unless
+--no-reflogs is given) as heads.
 
 --unreachable::
        Print out objects that exist but that aren't readable from any
index d424a4ecbe2c751550f50048e63ce640941a3ebb..b6b5ce15199ebfa8f92bff8586d04e502aa23b86 100644 (file)
@@ -104,6 +104,21 @@ The optional configuration variable 'gc.pruneExpire' controls how old
 the unreferenced loose objects have to be before they are pruned.  The
 default is "2 weeks ago".
 
+
+Notes
+-----
+
+git-gc tries very hard to be safe about the garbage it collects. In
+particular, it will keep not only objects referenced by your current set
+of branches and tags, but also objects referenced by the index, remote
+tracking branches, refs saved by linkgit:git-filter-branch[1] in
+refs/original/, or reflogs (which may references commits in branches
+that were later amended or rewound).
+
+If you are expecting some objects to be collected and they aren't, check
+all of those locations and decide whether it makes sense in your case to
+remove those references.
+
 See Also
 --------
 linkgit:git-prune[1]
index 62914da97b5b8335a2c2597606f046dfb2295e03..b17ae8485cf2d3eb4fc21acba769942a57d33e67 100644 (file)
@@ -31,7 +31,7 @@ structure, some suggested "exclude patterns", and copies of non-executing
 "hook" files.  The suggested patterns and hook files are all modifiable and
 extensible.
 
---shared[={false|true|umask|group|all|world|everybody}]::
+--shared[={false|true|umask|group|all|world|everybody|0xxx}]::
 
 Specify that the git repository is to be shared amongst several users.  This
 allows users belonging to the same group to push into that
@@ -52,6 +52,12 @@ is given:
  - 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository
    readable by all users.
 
+ - '0xxx': '0xxx' is an octal number and each file will have mode '0xxx'
+   Any option except 'umask' can be set using this option. '0xxx' will
+   override users umask(2) value, and thus, users with a safe umask (0077)
+   can use this option. '0640' will create a repository which is group-readable
+   but not writable. '0660' is equivalent to 'group'.
+
 By default, the configuration flag receive.denyNonFastForwards is enabled
 in shared repositories, so that you cannot force a non fast-forwarding push
 into it.
index eed0a94c6e4d6f520923a2ee86f1fcfbee228185..3a1be08186e9c87e5e3db5922a31b3f3f5aafdd7 100644 (file)
@@ -22,8 +22,9 @@ archive with specified base-name, or to the standard output.
 A packed archive is an efficient way to transfer set of objects
 between two repositories, and also is an archival format which
 is efficient to access.  The packed archive format (.pack) is
-designed to be unpackable without having anything else, but for
-random access, accompanied with the pack index file (.idx).
+designed to be self contained so that it can be unpacked without
+any further information, but for fast, random access to the objects
+in the pack, a pack index file (.idx) will be generated.
 
 Placing both in the pack/ subdirectory of $GIT_OBJECT_DIRECTORY (or
 any of the directories on $GIT_ALTERNATE_OBJECT_DIRECTORIES)
index f151cff5d968e5ed7f66a78d21ac74f74b3d4737..f92bb8cfa069283b0e8b78b717eedbef81bbe647 100644 (file)
@@ -13,6 +13,9 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
+NOTE: In most cases, users should run linkgit:git-gc[1], which calls
+git-prune. See the section "NOTES", below.
+
 This runs `git-fsck --unreachable` using all the refs
 available in `$GIT_DIR/refs`, optionally with additional set of
 objects specified on the command line, and prunes all
@@ -50,6 +53,23 @@ borrows from your repository via its
 $ git prune $(cd ../another && $(git-rev-parse --all))
 ------------
 
+Notes
+-----
+
+In most cases, users will not need to call git-prune directly, but
+should instead call linkgit:git-gc[1], which handles pruning along with
+many other housekeeping tasks.
+
+For a description of which objects are considered for pruning, see
+git-fsck's --unreachable option.
+
+See Also
+--------
+
+linkgit:git-fsck[1],
+linkgit:git-gc[1],
+linkgit:git-reflog[1]
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
index 3128170bcd1c3e3eb5981ed4c2f35221dfb06658..f06d94e318d6d0bf918430bafc746e96a2abe7a1 100644 (file)
@@ -35,14 +35,15 @@ OPTIONS
        by the source ref, followed by a colon `:`, followed by
        the destination ref.
 +
-The <src> side can be an
-arbitrary "SHA1 expression" that can be used as an
-argument to `git-cat-file -t`.  E.g. `master~4` (push
-four parents before the current master head).
+The <src> side represents the source branch (or arbitrary
+"SHA1 expression", such as `master~4` (four parents before the
+tip of `master` branch); see linkgit:git-rev-parse[1]) that you
+want to push.  The <dst> side represents the destination location.
 +
 The local ref that matches <src> is used
-to fast forward the remote ref that matches <dst>.  If
-the optional plus `+` is used, the remote ref is updated
+to fast forward the remote ref that matches <dst> (or, if no <dst> was
+specified, the same ref that <src> referred to locally).  If
+the optional leading plus `+` is used, the remote ref is updated
 even if it does not result in a fast forward update.
 +
 Note: If no explicit refspec is found, (that is neither
@@ -69,7 +70,9 @@ the remote repository.
        be mirrored to the remote repository.  Newly created local
        refs will be pushed to the remote end, locally updated refs
        will be force updated on the remote end, and deleted refs
-       will be removed from the remote end.
+       will be removed from the remote end.  This is the default
+       if the configuration option `remote.<remote>.mirror` is
+       set.
 
 \--dry-run::
        Do everything except actually send the updates.
@@ -165,7 +168,8 @@ git push origin master::
        Find a ref that matches `master` in the source repository
        (most likely, it would find `refs/heads/master`), and update
        the same ref (e.g. `refs/heads/master`) in `origin` repository
-       with it.
+       with it.  If `master` did not exist remotely, it would be
+       created.
 
 git push origin :experimental::
        Find a ref that matches `experimental` in the `origin` repository
@@ -179,9 +183,10 @@ git push origin master:satellite/master::
 
 git push origin master:refs/heads/experimental::
        Create the branch `experimental` in the `origin` repository
-       by copying the current `master` branch.  This form is usually
-       needed to create a new branch in the remote repository as
-       there is no `experimental` branch to match.
+       by copying the current `master` branch.  This form is only
+       needed to create a new branch or tag in the remote repository when
+       the local name and the remote name are different; otherwise,
+       the ref name on its own will work.
 
 Author
 ------
index 2cbd1f764b7d4d8e8c026f50b727c6cc70f5158e..b20e851973ae6ae1cee17b76f3002f77cd3a3329 100644 (file)
@@ -47,9 +47,11 @@ With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set
 up to point at remote's `<master>` branch instead of whatever
 branch the `HEAD` at the remote repository actually points at.
 +
-In mirror mode, enabled with `--mirror`, the refs will not be stored
+In mirror mode, enabled with `\--mirror`, the refs will not be stored
 in the 'refs/remotes/' namespace, but in 'refs/heads/'.  This option
-only makes sense in bare repositories.
+only makes sense in bare repositories.  If a remote uses mirror
+mode, furthermore, `git push` will always behave as if `\--mirror`
+was passed.
 
 'rm'::
 
index 270df9b185a53977665c2143bdffa6f3c275d326..9a14c04e39bd6c5c41de988f7dd9d18477e92c85 100644 (file)
@@ -24,7 +24,7 @@ OPTIONS
        URL to include in the summary.
 
 <end>::
-       Commit to send at; defaults to HEAD.
+       Commit to end at; defaults to HEAD.
 
 Author
 ------
index 6513c2efe17668d5fc25a21385ed117ec76b40b5..110e7ba71f48c54cb94f35e819583826e123a8b1 100644 (file)
@@ -52,6 +52,11 @@ OPTIONS
        The parameter given must be usable as a single, valid
        object name.  Otherwise barf and abort.
 
+-q, --quiet::
+       Only meaningful in `--verify` mode. Do not output an error
+       message if the first argument is not a valid object name;
+       instead exit with non-zero status silently.
+
 --sq::
        Usually the output is made one line per flag and
        parameter.  This option makes output a single line,
index dc36c662ae0d60b7dc706fce67a7c81849caace6..9c81b72dbebfec9b77128fb58d5aab33a143d594 100644 (file)
@@ -11,28 +11,37 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Remove files from the working tree and from the index.  The
-files have to be identical to the tip of the branch, and no
-updates to its contents must have been placed in the staging
-area (aka index).  When --cached is given, the staged content has to
-match either the tip of the branch *or* the file on disk.
+Remove files from the index, or from the working tree and the index.
+`git rm` will not remove a file from just your working directory.
+(There is no option to remove a file only from the work tree
+and yet keep it in the index; use `/bin/rm` if you want to do that.)
+The files being removed have to be identical to the tip of the branch,
+and no updates to their contents can be staged in the index,
+though that default behavior can be overridden with the `-f` option.
+When '--cached' is given, the staged content has to
+match either the tip of the branch or the file on disk,
+allowing the file to be removed from just the index.
 
 
 OPTIONS
 -------
 <file>...::
        Files to remove.  Fileglobs (e.g. `*.c`) can be given to
-       remove all matching files.  Also a leading directory name
-       (e.g. `dir` to add `dir/file1` and `dir/file2`) can be
-       given to remove all files in the directory, recursively,
-       but this requires `-r` option to be given for safety.
+       remove all matching files.  If you want git to expand
+       file glob characters, you may need to shell-escape them.
+       A leading directory name
+       (e.g. `dir` to remove `dir/file1` and `dir/file2`) can be
+       given to remove all files in the directory, and recursively
+       all sub-directories,
+       but this requires the `-r` option to be explicitly given.
 
 -f::
        Override the up-to-date check.
 
 -n, \--dry-run::
-        Don't actually remove the file(s), just show if they exist in
-        the index.
+       Don't actually remove any file(s).  Instead, just show
+       if they exist in the index and would otherwise be removed
+       by the command.
 
 -r::
         Allow recursive removal when a leading directory name is
@@ -44,9 +53,9 @@ OPTIONS
        for command-line options).
 
 \--cached::
-       This option can be used to tell the command to remove
-       the paths only from the index, leaving working tree
-       files.
+       Use this option to unstage and remove paths only from the index.
+       Working tree files, whether modified or not, will be
+       left alone.
 
 \--ignore-unmatch::
        Exit with a zero status even if no files matched.
@@ -59,11 +68,15 @@ OPTIONS
 DISCUSSION
 ----------
 
-The list of <file> given to the command can be exact pathnames,
-file glob patterns, or leading directory name.  The command
-removes only the paths that is known to git.  Giving the name of
+The <file> list given to the command can be exact pathnames,
+file glob patterns, or leading directory names.  The command
+removes only the paths that are known to git.  Giving the name of
 a file that you have not told git about does not remove that file.
 
+File globbing matches across directory boundaries.  Thus, given
+two directories `d` and `d2`, there is a difference between
+using `git rm \'d\*\'` and `git rm \'d/\*\'`, as the former will
+also remove all of directory `d2`.
 
 EXAMPLES
 --------
@@ -72,11 +85,10 @@ git-rm Documentation/\\*.txt::
        `Documentation` directory and any of its subdirectories.
 +
 Note that the asterisk `\*` is quoted from the shell in this
-example; this lets the command include the files from
-subdirectories of `Documentation/` directory.
+example; this lets git, and not the shell, expand the pathnames
+of files and subdirectories under the `Documentation/` directory.
 
 git-rm -f git-*.sh::
-       Remove all git-*.sh scripts that are in the index.
        Because this example lets the shell expand the asterisk
        (i.e. you are listing the files explicitly), it
        does not remove `subdir/git-foo.sh`.
index c7752575d8f5f35657cb9ef5a7e9e18b82ddc8b8..d7cb4c0468e713543042d41ff57a638a7fda2881 100644 (file)
@@ -8,8 +8,8 @@ git-shortlog - Summarize 'git log' output
 SYNOPSIS
 --------
 [verse]
-git-log --pretty=short | 'git-shortlog' [-h] [-n] [-s] [-e]
-git-shortlog [-n|--numbered] [-s|--summary] [-e|--email] [<committish>...]
+git-log --pretty=short | 'git-shortlog' [-h] [-n] [-s] [-e] [-w]
+git-shortlog [-n|--numbered] [-s|--summary] [-e|--email] [-w[<width>[,<indent1>[,<indent2>]]]] [<committish>...]
 
 DESCRIPTION
 -----------
@@ -35,6 +35,12 @@ OPTIONS
 -e, \--email::
        Show the email address of each author.
 
+-w[<width>[,<indent1>[,<indent2>]]]::
+       Linewrap the output by wrapping each line at `width`.  The first
+       line of each entry is indented by `indent1` spaces, and the second
+       and subsequent lines are indented by `indent2` spaces. `width`,
+       `indent1`, and `indent2` default to 76, 6 and 9 respectively.
+
 FILES
 -----
 
index 3ea269aa7a192d9f48ad2dc71f9cd790634c7852..ea4376a17f17eb2658bdf5d7faad1bd091a4de0e 100644 (file)
@@ -52,6 +52,11 @@ If the config variable `status.relativePaths` is set to false, then all
 paths shown are relative to the repository root, not to the current
 directory.
 
+If `status.submodulesummary` is set to a non zero number or true (identical
+to -1 or an unlimited number), the submodule summary will be enabled and a
+summary of commits for modified submodules will be shown (see --summary-limit
+option of linkgit:git-submodule[1]).
+
 See Also
 --------
 linkgit:gitignore[5]
index 41f9f635665b82806350c87de5117e1cfa806c1a..6ffd896fbc78b783a7e00ed462f879cbff29d758 100644 (file)
@@ -70,7 +70,7 @@ OPTIONS
 -n, --summary-limit::
        This option is only valid for the summary command.
        Limit the summary size (number of commits shown in total).
-       Giving 0 will disable the summary; a negative number means unlimted
+       Giving 0 will disable the summary; a negative number means unlimited
        (the default). This limit only applies to modified submodules. The
        size is always limited to 1 for added/deleted/typechanged submodules.
 
index bec9accc8915c712bdea5e8975598e4f26db633f..f4ba1056f0cab8d121a94797349a0bd789704580 100644 (file)
@@ -188,6 +188,12 @@ All arguments are passed directly to `git blame'.
        commit.  All merging is assumed to have taken place
        independently of git-svn functions.
 
+'create-ignore'::
+
+       Recursively finds the svn:ignore property on directories and
+       creates matching .gitignore files. The resulting files are staged to
+       be committed, but are not committed.
+
 'show-ignore'::
        Recursively finds and lists the svn:ignore property on
        directories.  The output is suitable for appending to
index c22fb71176984f40e4b549d0d492f75af85e0e1b..9712392f7946366129056ed8a0b7b8fab83c0ed5 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git-tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]  <name> [<head>]
 'git-tag' -d <name>...
-'git-tag' [-n [<num>]] -l [<pattern>]
+'git-tag' [-n[<num>]] -l [<pattern>]
 'git-tag' -v <name>...
 
 DESCRIPTION
@@ -57,7 +57,7 @@ OPTIONS
 -v::
        Verify the gpg signature of the given tag names.
 
--n <num>::
+-n<num>::
        <num> specifies how many lines from the annotation, if any,
        are printed when using -l.
        The default is not to print any annotation lines.
@@ -233,14 +233,14 @@ the tag object affects, for example, the ordering of tags in the
 gitweb interface.
 
 To set the date used in future tag objects, set the environment
-variable GIT_AUTHOR_DATE to one or more of the date and time.  The
+variable GIT_COMMITTER_DATE to one or more of the date and time.  The
 date and time can be specified in a number of ways; the most common
 is "YYYY-MM-DD HH:MM".
 
 An example follows.
 
 ------------
-$ GIT_AUTHOR_DATE="2006-10-02 10:31" git tag -s v1.0.1
+$ GIT_COMMITTER_DATE="2006-10-02 10:31" git tag -s v1.0.1
 ------------
 
 
index b79be3fd4ced9dac416fb0fb7f97ab2d10775d01..50947c50df1fe05b7eac8e046556c6ee070c7327 100644 (file)
@@ -8,7 +8,7 @@ git-unpack-objects - Unpack objects from a packed archive
 
 SYNOPSIS
 --------
-'git-unpack-objects' [-n] [-q] [-r] <pack-file
+'git-unpack-objects' [-n] [-q] [-r] [--strict] <pack-file
 
 
 DESCRIPTION
@@ -40,6 +40,9 @@ OPTIONS
        and make the best effort to recover as many objects as
        possible.
 
+--strict::
+       Don't write objects with broken content or links.
+
 
 Author
 ------
index aaf318993670b537d523bd556b2f24072500c08c..6f445b1e3bbefccdb865f8cb93fb470892b94e41 100644 (file)
@@ -43,6 +43,14 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
+* link:v1.5.5/git.html[documentation for release 1.5.5]
+
+* release notes for
+  link:RelNotes-1.5.5.1.txt[1.5.5.1],
+  link:RelNotes-1.5.5.txt[1.5.5].
+
+* link:v1.5.5.1/git.html[documentation for release 1.5.5.1]
+
 * link:v1.5.4.5/git.html[documentation for release 1.5.4.5]
 
 * release notes for
@@ -135,7 +143,8 @@ help ...'.
 
 --git-dir=<path>::
        Set the path to the repository. This can also be controlled by
-       setting the GIT_DIR environment variable.
+       setting the GIT_DIR environment variable. It can be an absolute
+       path or relative path to current working directory.
 
 --work-tree=<path>::
        Set the path to the working tree.  The value will not be
@@ -143,7 +152,12 @@ help ...'.
        a .git directory (i.e. $GIT_DIR is not set).
        This can also be controlled by setting the GIT_WORK_TREE
        environment variable and the core.worktree configuration
-       variable.
+       variable. It can be an absolute path or relative path to
+       the directory specified by --git-dir or GIT_DIR.
+       Note: If --git-dir or GIT_DIR are specified but none of
+       --work-tree, GIT_WORK_TREE and core.worktree is specified,
+       the current working directory is regarded as the top directory
+       of your working tree.
 
 --bare::
        Treat the repository as a bare repository.  If GIT_DIR
index 29edafcedac6bcb95315e7429ea56d2d283ef6f6..50d12da89fca37b60a024aeb2ac6de9cb2daf001 100644 (file)
@@ -41,6 +41,12 @@ frequently used options.
 
        Show all branches.
 
+--merge::
+
+       After an attempt to merge stops with conflicts, show the commits on
+       the history between two branches (i.e. the HEAD and the MERGE_HEAD)
+       that modify the conflicted files.
+
 <revs>::
 
        Limit the revisions to show. This can be either a single revision
@@ -74,6 +80,11 @@ gitk --max-count=100 --all \-- Makefile::
        Show at most 100 changes made to the file 'Makefile'. Instead of only
        looking for changes in the current branch look in all branches.
 
+Files
+-----
+Gitk creates the .gitk file in your $HOME directory to store preferences
+such as display options, font, and colors.
+
 See Also
 --------
 'qgit(1)'::
index 76b8d77460e961fda9a5505a4ce0cf20788de133..d89cc222610d01bd8b2a25681c6225cef51c2138 100644 (file)
@@ -28,10 +28,11 @@ The default 'applypatch-msg' hook, when enabled, runs the
 pre-applypatch
 --------------
 
-This hook is invoked by `git-am`.  It takes no parameter,
-and is invoked after the patch is applied, but before a commit
-is made.  Exiting with non-zero status causes the working tree
-after application of the patch not committed.
+This hook is invoked by `git-am`.  It takes no parameter, and is
+invoked after the patch is applied, but before a commit is made.
+
+If it exits with non-zero status, then the working tree will not be
+committed after applying the patch.
 
 It can be used to inspect the current working tree and refuse to
 make a commit if it does not pass certain test.
@@ -136,7 +137,8 @@ post-merge
 This hook is invoked by `git-merge`, which happens when a `git pull`
 is done on a local repository.  The hook takes a single parameter, a status
 flag specifying whether or not the merge being done was a squash merge.
-This hook cannot affect the outcome of `git-merge`.
+This hook cannot affect the outcome of `git-merge` and is not executed,
+if the merge failed due to conflicts.
 
 This hook can be used in conjunction with a corresponding pre-commit hook to
 save and restore any form of metadata associated with the working tree
@@ -276,3 +278,10 @@ probably enable this hook.
 Both standard output and standard error output are forwarded to
 `git-send-pack` on the other end, so you can simply `echo` messages
 for the user.
+
+pre-auto-gc
+-----------
+
+This hook is invoked by `git-gc --auto`. It takes no parameter, and
+exiting with non-zero status from this script causes the `git-gc --auto`
+to abort.
index 8eadc2049402cc21fd80818ca9f29d15f4cd8462..b7d09c1ec69ac0e76302cef44d5f6884d05a0e0f 100644 (file)
@@ -1,5 +1,5 @@
 From: Rutger Nijlunsing <rutger@nospam.com>
-Subject: Setting up a git repository which can be pushed into and pulled from over HTTP.
+Subject: Setting up a git repository which can be pushed into and pulled from over HTTP(S).
 Date: Thu, 10 Aug 2006 22:00:26 +0200
 
 Since Apache is one of those packages people like to compile
@@ -40,9 +40,13 @@ What's needed:
 
 - have permissions to chown a directory
 
-- have git installed at the server _and_ client
+- have git installed on the client, and
 
-In effect, this probably means you're going to be root.
+- either have git installed on the server or have a webdav client on
+  the client.
+
+In effect, this means you're going to be root, or that you're using a
+preconfigured WebDAV server.
 
 
 Step 1: setup a bare GIT repository
@@ -50,9 +54,9 @@ Step 1: setup a bare GIT repository
 
 At the time of writing, git-http-push cannot remotely create a GIT
 repository. So we have to do that at the server side with git. Another
-option would be to generate an empty repository at the client and copy
-it to the server with WebDAV. But then you're probably the first to
-try that out :)
+option is to generate an empty bare repository at the client and copy
+it to the server with a WebDAV client (which is the only option if Git
+is not installed on the server).
 
 Create the directory under the DocumentRoot of the directories served
 by Apache. As an example we take /usr/local/apache2, but try "grep
@@ -169,7 +173,9 @@ On Debian:
 
    Most tests should pass.
 
-A command line tool to test WebDAV is cadaver.
+A command line tool to test WebDAV is cadaver. If you prefer GUIs, for
+example, konqueror can open WebDAV URLs as "webdav://..." or
+"webdavs://...".
 
 If you're into Windows, from XP onwards Internet Explorer supports
 WebDAV. For this, do Internet Explorer -> Open Location ->
@@ -179,8 +185,9 @@ http://<servername>/my-new-repo.git [x] Open as webfolder -> login .
 Step 3: setup the client
 ------------------------
 
-Make sure that you have HTTP support, i.e. your git was built with curl.
-The easiest way to check is to look for the executable 'git-http-push'.
+Make sure that you have HTTP support, i.e. your git was built with
+curl (version more recent than 7.10). The command 'git http-push' with
+no argument should display a usage message.
 
 Then, add the following to your $HOME/.netrc (you can do without, but will be
 asked to input your password a _lot_ of times):
@@ -197,10 +204,10 @@ instead of the server name.
 
 To check whether all is OK, do:
 
-   curl --netrc --location -v http://<username>@<servername>/my-new-repo.git/
-
-...this should give a directory listing in HTML of /var/www/my-new-repo.git .
+   curl --netrc --location -v http://<username>@<servername>/my-new-repo.git/HEAD
 
+...this should give something like 'ref: refs/heads/master', which is
+the content of the file HEAD on the server.
 
 Now, add the remote in your existing repository which contains the project
 you want to export:
@@ -225,6 +232,15 @@ want to export) to repository called 'upload', which we previously
 defined with git-config.
 
 
+Using a proxy:
+--------------
+
+If you have to access the WebDAV server from behind an HTTP(S) proxy,
+set the variable 'all_proxy' to 'http://proxy-host.com:port', or
+'http://login-on-proxy:passwd-on-proxy@proxy-host.com:port'. See 'man
+curl' for details.
+
+
 Troubleshooting:
 ----------------
 
@@ -248,9 +264,14 @@ Reading /usr/local/apache2/logs/error_log is often helpful.
 
   On Debian: Read /var/log/apache2/error.log instead.
 
+If you access HTTPS locations, git may fail verifying the SSL
+certificate (this is return code 60). Setting http.sslVerify=false can
+help diagnosing the problem, but removes security checks.
+
 
 Debian References: http://www.debian-administration.org/articles/285
 
 Authors
   Johannes Schindelin <Johannes.Schindelin@gmx.de>
   Rutger Nijlunsing <git@wingding.demon.nl>
+  Matthieu Moy <Matthieu.Moy@imag.fr>
index 0193c3ce58de4f51a164d43e68023fdf5639a920..e8bea3e18e569e702233d0bb986fc7e52266d445 100644 (file)
@@ -123,3 +123,4 @@ The placeholders are:
 - '%Creset': reset color
 - '%m': left, right or boundary mark
 - '%n': newline
+- '%x00': print a byte from a hex code
index 69391300944438576e11cff19b4ce1aa2215b97b..bbaed2e129fb9ced1752183099fdb40ea8e341b3 100644 (file)
@@ -3,7 +3,10 @@ git repository layout
 
 You may find these things in your git repository (`.git`
 directory for a repository associated with your working tree, or
-`'project'.git` directory for a public 'bare' repository).
+`'project'.git` directory for a public 'bare' repository. It is
+also possible to have a working tree where `.git` is a plain
+ascii file containing `gitdir: <path>`, i.e. the path to the
+real git repository).
 
 objects::
        Object store associated with this repository.  Usually
index aa87756a55dfe6337f06b8aece0ed88d092036b1..1803e64e465fa4f8f0fe520fc0fd95d0c9def5bd 100644 (file)
@@ -103,10 +103,24 @@ Pack file entry: <+
      packed object data:
         If it is not DELTA, then deflated bytes (the size above
                is the size before compression).
-       If it is DELTA, then
+       If it is REF_DELTA, then
          20-byte base object name SHA1 (the size above is the
                size of the delta data that follows).
           delta data, deflated.
+       If it is OFS_DELTA, then
+         n-byte offset (see below) interpreted as a negative
+               offset from the type-byte of the header of the
+               ofs-delta entry (the size above is the size of
+               the delta data that follows).
+         delta data, deflated.
+
+     offset encoding:
+         n bytes with MSB set in all but the last one.
+         The offset is then the number constructed by
+         concatenating the lower 7 bit of each byte, and
+         for n >= 2 adding 2^7 + 2^14 + ... + 2^(7*(n-1))
+         to the result.
+
 
 
 = Version 2 pack-*.idx files support packs larger than 4 GiB, and
index 565aeb98047573bd29cfa0c53aa460c2b7be543c..86b91a53e5c822782ed7682c83d435180d7b682f 100644 (file)
@@ -1548,22 +1548,7 @@ dangling tree b24c2473f1fd3d91352a624795be026d64c8841f
 
 Dangling objects are not a problem.  At worst they may take up a little
 extra disk space.  They can sometimes provide a last-resort method for
-recovering lost work--see <<dangling-objects>> for details.  However, if
-you wish, you can remove them with linkgit:git-prune[1] or the `--prune`
-option to linkgit:git-gc[1]:
-
--------------------------------------------------
-$ git gc --prune
--------------------------------------------------
-
-This may be time-consuming.  Unlike most other git operations (including
-git-gc when run without any options), it is not safe to prune while
-other git operations are in progress in the same repository.
-
-If linkgit:git-fsck[1] complains about sha1 mismatches or missing
-objects, you may have a much more serious problem; your best option is
-probably restoring from backups.  See
-<<recovering-from-repository-corruption>> for a detailed discussion.
+recovering lost work--see <<dangling-objects>> for details.
 
 [[recovering-lost-changes]]
 Recovering lost changes
index 7529af931536d2d2060dbb48aeb0c3fddfb2eb81..3cabc92e7a1f8ce30eb6e5151a4c81c82468bcee 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.5.5-rc2.GIT
+DEF_VER=v1.5.5.GIT
 
 LF='
 '
@@ -11,7 +11,7 @@ LF='
 if test -f version
 then
        VN=$(cat version) || VN="$DEF_VER"
-elif test -d .git &&
+elif test -d .git -o -f .git &&
        VN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&
        case "$VN" in
        *$LF*) (exit 1) ;;
diff --git a/INSTALL b/INSTALL
index 6f3bcb45253367e0bf7fc8e43e45ccb404b23824..d9b425fa73e806751ff2636300ea24bd1e7f1e56 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -38,6 +38,10 @@ Issues of note:
    has been actively developed since 1997, and people have moved over to
    graphical file managers.
 
+   NOTE: As of gnuit-4.9.2, the GNU interactive tools package has been
+         renamed. You can compile gnuit with the --disable-transition
+         option and then it will not conflict with git.
+
  - You can use git after building but without installing if you
    wanted to.  Various git commands need to find other git
    commands and scripts to do their work, so you would need to
index 7c70b00b826f8f51c0b39ca2454e960b2747c403..9d84c8d799fc1044177197fb804411b05569dc83 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -189,6 +189,7 @@ ETC_GITCONFIG = $(sysconfdir)/gitconfig
 
 # default configuration for gitweb
 GITWEB_CONFIG = gitweb_config.perl
+GITWEB_CONFIG_SYSTEM = /etc/gitweb.conf
 GITWEB_HOME_LINK_STR = projects
 GITWEB_SITENAME =
 GITWEB_PROJECTROOT = /pub/git
@@ -365,6 +366,7 @@ LIB_H += refs.h
 LIB_H += remote.h
 LIB_H += revision.h
 LIB_H += run-command.h
+LIB_H += sha1-lookup.h
 LIB_H += sideband.h
 LIB_H += strbuf.h
 LIB_H += tag.h
@@ -445,6 +447,7 @@ LIB_OBJS += run-command.o
 LIB_OBJS += server-info.o
 LIB_OBJS += setup.o
 LIB_OBJS += sha1_file.o
+LIB_OBJS += sha1-lookup.o
 LIB_OBJS += sha1_name.o
 LIB_OBJS += shallow.o
 LIB_OBJS += sideband.o
@@ -1034,6 +1037,7 @@ gitweb/gitweb.cgi: gitweb/gitweb.perl
            -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
            -e 's|++GIT_BINDIR++|$(bindir)|g' \
            -e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \
+           -e 's|++GITWEB_CONFIG_SYSTEM++|$(GITWEB_CONFIG_SYSTEM)|g' \
            -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
            -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
            -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
index 3e77358f704fde7271f18ccbf90a57419550b265..e29d6504d9e8fe45d3f7ff7f82f1e8f1f57b9f6f 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.5.5.txt
\ No newline at end of file
+Documentation/RelNotes-1.5.6.txt
\ No newline at end of file
index 30aa2e23fdbb1630dffb27db4509bc529bbd884a..4add80284e4570d1da44861e9025fa75eeb775d6 100644 (file)
@@ -17,6 +17,7 @@ static time_t archive_time;
 static int tar_umask = 002;
 static int verbose;
 static const struct commit *commit;
+static size_t base_len;
 
 /* writes out the whole block, but only if it is full */
 static void write_if_needed(void)
@@ -251,8 +252,8 @@ static int write_tar_entry(const unsigned char *sha1,
                buffer = NULL;
                size = 0;
        } else {
-               buffer = sha1_file_to_archive(path.buf, sha1, mode, &type,
-                                             &size, commit);
+               buffer = sha1_file_to_archive(path.buf + base_len, sha1, mode,
+                               &type, &size, commit);
                if (!buffer)
                        die("cannot read %s", sha1_to_hex(sha1));
        }
@@ -272,6 +273,7 @@ int write_tar_archive(struct archiver_args *args)
        archive_time = args->time;
        verbose = args->verbose;
        commit = args->commit;
+       base_len = args->base ? strlen(args->base) : 0;
 
        if (args->commit_sha1)
                write_global_extended_header(args->commit_sha1);
index 74e30f6205f41112dc2bafe9371a790aca55f70c..18c0f8710c6150e3b0211d311a70bf4b34608370 100644 (file)
@@ -13,6 +13,7 @@ static int verbose;
 static int zip_date;
 static int zip_time;
 static const struct commit *commit;
+static size_t base_len;
 
 static unsigned char *zip_dir;
 static unsigned int zip_dir_size;
@@ -197,8 +198,8 @@ static int write_zip_entry(const unsigned char *sha1,
                if (S_ISREG(mode) && zlib_compression_level != 0)
                        method = 8;
                result = 0;
-               buffer = sha1_file_to_archive(path, sha1, mode, &type, &size,
-                                             commit);
+               buffer = sha1_file_to_archive(path + base_len, sha1, mode,
+                               &type, &size, commit);
                if (!buffer)
                        die("cannot read %s", sha1_to_hex(sha1));
                crc = crc32(crc, buffer, size);
@@ -321,6 +322,7 @@ int write_zip_archive(struct archiver_args *args)
        zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
        verbose = args->verbose;
        commit = args->commit;
+       base_len = args->base ? strlen(args->base) : 0;
 
        if (args->base && plen > 0 && args->base[plen - 1] == '/') {
                char *base = xstrdup(args->base);
index fb159fe59e9e6fc40db584468c7b1ddf8495ccb3..7a32c19d3ca8043f3ca22dadfdbc60dbbb747d59 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -16,9 +16,9 @@ static void format_subst(const struct commit *commit,
                const char *b, *c;
 
                b = memmem(src, len, "$Format:", 8);
-               if (!b || src + len < b + 9)
+               if (!b)
                        break;
-               c = memchr(b + 8, '$', len - 8);
+               c = memchr(b + 8, '$', (src + len) - b - 8);
                if (!c)
                        break;
 
diff --git a/attr.c b/attr.c
index 64b77b16636482d3448027d7823e5dc07861faa5..1a15fad294c5db1f922e686995e215b94b89b9a0 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -546,7 +546,9 @@ static int path_matches(const char *pathname, int pathlen,
            (baselen && pathname[baselen] != '/') ||
            strncmp(pathname, base, baselen))
                return 0;
-       return fnmatch(pattern, pathname + baselen + 1, FNM_PATHNAME) == 0;
+       if (baselen != 0)
+               baselen++;
+       return fnmatch(pattern, pathname + baselen, FNM_PATHNAME) == 0;
 }
 
 static int fill_one(const char *what, struct match_attr *a, int rem)
index b5f78ac3a78679fdb747380305fb6772c39874e0..caa3f2aa0ca147091d28013c3d2c11346e350675 100644 (file)
@@ -1937,21 +1937,24 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
        trailing = frag->trailing;
 
        /*
-        * If we don't have any leading/trailing data in the patch,
-        * we want it to match at the beginning/end of the file.
+        * A hunk to change lines at the beginning would begin with
+        * @@ -1,L +N,M @@
         *
-        * But that would break if the patch is generated with
-        * --unified=0; sane people wouldn't do that to cause us
-        * trouble, but we try to please not so sane ones as well.
+        * And a hunk to add to an empty file would begin with
+        * @@ -0,0 +N,M @@
+        *
+        * In other words, a hunk that is (frag->oldpos <= 1) with or
+        * without leading context must match at the beginning.
         */
-       if (unidiff_zero) {
-               match_beginning = (!leading && !frag->oldpos);
-               match_end = 0;
-       }
-       else {
-               match_beginning = !leading && (frag->oldpos == 1);
-               match_end = !trailing;
-       }
+       match_beginning = frag->oldpos <= 1;
+
+       /*
+        * A hunk without trailing lines must match at the end.
+        * However, we simply cannot tell if a hunk must match end
+        * from the lack of trailing lines if the patch was generated
+        * with unidiff without any context.
+        */
+       match_end = !unidiff_zero && !trailing;
 
        pos = frag->newpos ? (frag->newpos - 1) : 0;
        preimage.buf = oldlines;
@@ -2978,12 +2981,8 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
 
 static int git_apply_config(const char *var, const char *value)
 {
-       if (!strcmp(var, "apply.whitespace")) {
-               if (!value)
-                       return config_error_nonbool(var);
-               apply_default_whitespace = xstrdup(value);
-               return 0;
-       }
+       if (!strcmp(var, "apply.whitespace"))
+               return git_config_string(&apply_default_whitespace, var, value);
        return git_default_config(var, value);
 }
 
@@ -3118,7 +3117,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
 
                fd = open(arg, O_RDONLY);
                if (fd < 0)
-                       usage(apply_usage);
+                       die("can't open patch '%s': %s", arg, strerror(errno));
                read_stdin = 0;
                set_default_whitespace_mode(whitespace_option);
                errs |= apply_patch(fd, arg, inaccurate_eof);
index 5bc4526f645d40a4a32c50240975ad26f49b8e3b..19c508a60827c9c7c744da995bf5737733864a76 100644 (file)
@@ -15,7 +15,7 @@
 #include "branch.h"
 
 static const char * const builtin_branch_usage[] = {
-       "git-branch [options] [-r | -a]",
+       "git-branch [options] [-r | -a] [--merged | --no-merged]",
        "git-branch [options] [-l] [-f] <branchname> [<start-point>]",
        "git-branch [options] [-r] (-d | -D) <branchname>",
        "git-branch [options] (-m | -M) [<oldbranch>] <newbranch>",
@@ -46,6 +46,8 @@ enum color_branch {
        COLOR_BRANCH_CURRENT = 4,
 };
 
+static int mergefilter = -1;
+
 static int parse_branch_color_slot(const char *var, int ofs)
 {
        if (!strcasecmp(var+ofs, "plain"))
@@ -210,6 +212,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
        struct ref_item *newitem;
        int kind = REF_UNKNOWN_TYPE;
        int len;
+       static struct commit_list branch;
 
        /* Detect kind */
        if (!prefixcmp(refname, "refs/heads/")) {
@@ -231,6 +234,16 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
        if ((kind & ref_list->kinds) == 0)
                return 0;
 
+       if (mergefilter > -1) {
+               branch.item = lookup_commit_reference_gently(sha1, 1);
+               if (!branch.item)
+                       die("Unable to lookup tip of branch %s", refname);
+               if (mergefilter == 0 && has_commit(head_sha1, &branch))
+                       return 0;
+               if (mergefilter == 1 && !has_commit(head_sha1, &branch))
+                       return 0;
+       }
+
        /* Resize buffer */
        if (ref_list->index >= ref_list->alloc) {
                ref_list->alloc = alloc_nr(ref_list->alloc);
@@ -444,6 +457,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
                OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"),
                OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"),
+               OPT_SET_INT(0, "merged", &mergefilter, "list only merged branches", 1),
                OPT_END(),
        };
 
index 7deb504837d252dd43fa9746c5d12342e93a5923..10ec137ccec7d7f281562817d81a1035c4aa6124 100644 (file)
@@ -142,7 +142,7 @@ static void describe_detached_head(char *msg, struct commit *commit)
        struct strbuf sb;
        strbuf_init(&sb, 0);
        parse_commit(commit);
-       pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, 0, "", "", 0, 0);
+       pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, 0, NULL, NULL, 0, 0);
        fprintf(stderr, "%s %s... %s\n", msg,
                find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf);
        strbuf_release(&sb);
@@ -504,7 +504,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                OPT__QUIET(&opts.quiet),
                OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
                OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
-               OPT_SET_INT( 0 , "track",  &opts.track, "track",
+               OPT_SET_INT('t', "track",  &opts.track, "track",
                        BRANCH_TRACK_EXPLICIT),
                OPT_BOOLEAN('f', NULL, &opts.force, "force"),
                OPT_BOOLEAN('m', NULL, &opts.merge, "merge"),
index fefec3010c434219234e7832051ada9ed2e52335..6778a03ae4c901d216e4b91cd41f2073e0e98589 100644 (file)
@@ -95,7 +95,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 
        for (i = 0; i < dir.nr; i++) {
                struct dir_entry *ent = dir.entries[i];
-               int len, pos, matches;
+               int len, pos;
+               int matches = 0;
                struct cache_entry *ce;
                struct stat st;
 
@@ -127,18 +128,18 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 
                if (pathspec) {
                        memset(seen, 0, argc > 0 ? argc : 1);
-                       matches = match_pathspec(pathspec, ent->name, ent->len,
+                       matches = match_pathspec(pathspec, ent->name, len,
                                                 baselen, seen);
-               } else {
-                       matches = 0;
                }
 
                if (S_ISDIR(st.st_mode)) {
                        strbuf_addstr(&directory, ent->name);
                        qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
-                       if (show_only && (remove_directories || matches)) {
+                       if (show_only && (remove_directories ||
+                           (matches == MATCHED_EXACTLY))) {
                                printf("Would remove %s\n", qname);
-                       } else if (remove_directories || matches) {
+                       } else if (remove_directories ||
+                                  (matches == MATCHED_EXACTLY)) {
                                if (!quiet)
                                        printf("Removing %s\n", qname);
                                if (remove_dir_recursively(&directory, 0) != 0) {
index 660a3458f7f4ef24dfa4fd5bdf902174da1eefb4..256181a68bd991278fda4ecb48911d03b5f23141 100644 (file)
@@ -90,7 +90,7 @@ static struct option builtin_commit_options[] = {
        OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m),
        OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit "),
        OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"),
-       OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by: header"),
+       OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
        OPT_STRING('t', "template", &template_file, "FILE", "use specified template file"),
        OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
 
@@ -98,7 +98,7 @@ static struct option builtin_commit_options[] = {
        OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
        OPT_BOOLEAN('i', "include", &also, "add specified files to index for commit"),
        OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
-       OPT_BOOLEAN('o', "only", &only, ""),
+       OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
        OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
        OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
        OPT_BOOLEAN(0, "untracked-files", &untracked_files, "show all untracked files"),
@@ -745,10 +745,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
                die("No paths with --include/--only does not make sense.");
        if (argc == 0 && only && amend)
                only_include_assumed = "Clever... amending the last one with dirty index.";
-       if (argc > 0 && !also && !only) {
+       if (argc > 0 && !also && !only)
                only_include_assumed = "Explicit paths specified without -i nor -o; assuming --only paths...";
-               also = 0;
-       }
        if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
                cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;
        else if (!strcmp(cleanup_arg, "verbatim"))
@@ -810,7 +808,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
 
        rev.verbose_header = 1;
        rev.show_root_diff = 1;
-       rev.commit_format = get_commit_format("format:%h: %s");
+       get_commit_format("format:%h: %s", &rev);
        rev.always_show_header = 0;
        rev.diffopt.detect_rename = 1;
        rev.diffopt.rename_limit = 100;
index c34bc8b6a6957ccee1db23a3c44c99e058384689..8ee01bdecde1ede4062a630761ce56f3366f3a14 100644 (file)
@@ -3,7 +3,7 @@
 #include "color.h"
 
 static const char git_config_set_usage[] =
-"git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default] | --get-colorbool name [stdout-is-tty]";
+"git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int | --bool-or-int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default] | --get-colorbool name [stdout-is-tty]";
 
 static char *key;
 static regex_t *key_regexp;
@@ -16,7 +16,7 @@ static int seen;
 static char delim = '=';
 static char key_delim = ' ';
 static char term = '\n';
-static enum { T_RAW, T_INT, T_BOOL } type = T_RAW;
+static enum { T_RAW, T_INT, T_BOOL, T_BOOL_OR_INT } type = T_RAW;
 
 static int show_all_config(const char *key_, const char *value_)
 {
@@ -53,6 +53,14 @@ static int show_config(const char* key_, const char* value_)
                sprintf(value, "%d", git_config_int(key_, value_?value_:""));
        else if (type == T_BOOL)
                vptr = git_config_bool(key_, value_) ? "true" : "false";
+       else if (type == T_BOOL_OR_INT) {
+               int is_bool, v;
+               v = git_config_bool_or_int(key_, value_, &is_bool);
+               if (is_bool)
+                       vptr = v ? "true" : "false";
+               else
+                       sprintf(value, "%d", v);
+       }
        else
                vptr = value_?value_:"";
        seen++;
@@ -157,6 +165,14 @@ char *normalize_value(const char *key, const char *value)
                else if (type == T_BOOL)
                        sprintf(normalized, "%s",
                                git_config_bool(key, value) ? "true" : "false");
+               else if (type == T_BOOL_OR_INT) {
+                       int is_bool, v;
+                       v = git_config_bool_or_int(key, value, &is_bool);
+                       if (!is_bool)
+                               sprintf(normalized, "%d", v);
+                       else
+                               sprintf(normalized, "%s", v ? "true" : "false");
+               }
        }
 
        return normalized;
@@ -224,6 +240,10 @@ static int git_get_colorbool_config(const char *var, const char *value)
                get_diff_color_found =
                        git_config_colorbool(var, value, stdout_is_tty);
        }
+       if (!strcmp(var, "color.ui")) {
+               git_use_color_default = git_config_colorbool(var, value, stdout_is_tty);
+               return 0;
+       }
        return 0;
 }
 
@@ -251,7 +271,7 @@ static int get_colorbool(int argc, const char **argv)
                if (!strcmp(get_color_slot, "color.diff"))
                        get_colorbool_found = get_diff_color_found;
                if (get_colorbool_found < 0)
-                       get_colorbool_found = 0;
+                       get_colorbool_found = git_use_color_default;
        }
 
        if (argc == 1) {
@@ -273,6 +293,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                        type = T_INT;
                else if (!strcmp(argv[1], "--bool"))
                        type = T_BOOL;
+               else if (!strcmp(argv[1], "--bool-or-int"))
+                       type = T_BOOL_OR_INT;
                else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) {
                        if (argc != 2)
                                usage(git_config_set_usage);
index 65350ca52240bad95c1632ae604d66279d9a8997..c97a42739d23ac7ed90ae207e5a3770c388e66a7 100644 (file)
@@ -117,15 +117,15 @@ static const unsigned char* get_rev(void)
 
        while (commit == NULL) {
                unsigned int mark;
-               struct commit_list *parents = NULL;
+               struct commit_list *parents;
 
                if (rev_list == NULL || non_common_revs == 0)
                        return NULL;
 
                commit = rev_list->item;
-               if (!(commit->object.parsed))
-                       if (!parse_commit(commit))
-                               parents = commit->parents;
+               if (!commit->object.parsed)
+                       parse_commit(commit);
+               parents = commit->parents;
 
                commit->object.flags |= POPPED;
                if (!(commit->object.flags & COMMON))
index a11548c8943f75b8d3e9ddfa1e6e2e3d13eaa431..e56617e32e5f878e1ae9f0d94ceced28ba18a94e 100644 (file)
@@ -215,13 +215,6 @@ static int update_local_ref(struct ref *ref,
        if (type < 0)
                die("object %s not found", sha1_to_hex(ref->new_sha1));
 
-       if (!*ref->name) {
-               /* Not storing */
-               if (verbose)
-                       sprintf(display, "* branch %s -> FETCH_HEAD", remote);
-               return 0;
-       }
-
        if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
                if (verbose)
                        sprintf(display, "= %-*s %-*s -> %s", SUMMARY_WIDTH,
@@ -365,16 +358,19 @@ static int store_updated_refs(const char *url, struct ref *ref_map)
                        rm->merge ? "" : "not-for-merge",
                        note);
 
-               if (ref) {
+               if (ref)
                        update_local_ref(ref, what, verbose, note);
-                       if (*note) {
-                               if (!shown_url) {
-                                       fprintf(stderr, "From %.*s\n",
-                                                       url_len, url);
-                                       shown_url = 1;
-                               }
-                               fprintf(stderr, " %s\n", note);
+               else
+                       sprintf(note, "* %-*s %-*s -> FETCH_HEAD",
+                               SUMMARY_WIDTH, *kind ? kind : "branch",
+                                REFCOL_WIDTH, *what ? what : "HEAD");
+               if (*note) {
+                       if (!shown_url) {
+                               fprintf(stderr, "From %.*s\n",
+                                               url_len, url);
+                               shown_url = 1;
                        }
+                       fprintf(stderr, " %s\n", note);
                }
        }
        fclose(fp);
@@ -579,8 +575,6 @@ static int do_fetch(struct transport *transport,
                free_refs(ref_map);
        }
 
-       transport_disconnect(transport);
-
        return 0;
 }
 
@@ -601,6 +595,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        int i;
        static const char **refs = NULL;
        int ref_nr = 0;
+       int exit_code;
 
        /* Record the command line for the reflog */
        strbuf_addstr(&default_rla, "fetch");
@@ -637,6 +632,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                        if (!strcmp(argv[i], "tag")) {
                                char *ref;
                                i++;
+                               if (i >= argc)
+                                       die("You need to specify a tag name.");
                                ref = xmalloc(strlen(argv[i]) * 2 + 22);
                                strcpy(ref, "refs/tags/");
                                strcat(ref, argv[i]);
@@ -652,6 +649,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
 
        signal(SIGINT, unlock_pack_on_signal);
        atexit(unlock_pack);
-       return do_fetch(transport,
+       exit_code = do_fetch(transport,
                        parse_fetch_refspec(ref_nr, refs), ref_nr);
+       transport_disconnect(transport);
+       transport = NULL;
+       return exit_code;
 }
index ebb3f37cf158dc479f364111893279805fa9a230..7077d524776e748e0add4ff85478cd62512add15 100644 (file)
@@ -201,6 +201,15 @@ static void shortlog(const char *name, unsigned char *sha1,
                        continue;
 
                bol = strstr(commit->buffer, "\n\n");
+               if (bol) {
+                       unsigned char c;
+                       do {
+                               c = *++bol;
+                       } while (isspace(c));
+                       if (!c)
+                               bol = NULL;
+               }
+
                if (!bol) {
                        append_to_list(&subjects, xstrdup(sha1_to_hex(
                                                        commit->object.sha1)),
@@ -208,7 +217,6 @@ static void shortlog(const char *name, unsigned char *sha1,
                        continue;
                }
 
-               bol += 2;
                eol = strchr(bol, '\n');
                if (eol) {
                        oneline = xmemdupz(bol, eol - bol);
index 8cef36f6a4445b163b299f282785f66af9662cb8..f99ebc7926b95b04f1140d257da7a52bfc33ee50 100644 (file)
@@ -157,6 +157,34 @@ static int too_many_packs(void)
        return gc_auto_pack_limit <= cnt;
 }
 
+static int run_hook(void)
+{
+       const char *argv[2];
+       struct child_process hook;
+       int ret;
+
+       argv[0] = git_path("hooks/pre-auto-gc");
+       argv[1] = NULL;
+
+       if (access(argv[0], X_OK) < 0)
+               return 0;
+
+       memset(&hook, 0, sizeof(hook));
+       hook.argv = argv;
+       hook.no_stdin = 1;
+       hook.stdout_to_stderr = 1;
+
+       ret = start_command(&hook);
+       if (ret) {
+               warning("Could not spawn %s", argv[0]);
+               return ret;
+       }
+       ret = finish_command(&hook);
+       if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL)
+               warning("%s exited due to uncaught signal", argv[0]);
+       return ret;
+}
+
 static int need_to_gc(void)
 {
        /*
@@ -176,6 +204,9 @@ static int need_to_gc(void)
                append_option(argv_repack, "-A", MAX_ADD);
        else if (!too_many_loose_objects())
                return 0;
+
+       if (run_hook())
+               return 0;
        return 1;
 }
 
index 2854868b4e4185d33024b9fe5d00ad631c18bb36..a76f5d3474f9d1e16bfc988ad6fd1d971876fe91 100644 (file)
@@ -400,9 +400,16 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                char buf[10];
                /* We do not spell "group" and such, so that
                 * the configuration can be read by older version
-                * of git.
+                * of git. Note, we use octal numbers for new share modes,
+                * and compatibility values for PERM_GROUP and
+                * PERM_EVERYBODY.
                 */
-               sprintf(buf, "%d", shared_repository);
+               if (shared_repository == PERM_GROUP)
+                       sprintf(buf, "%d", OLD_PERM_GROUP);
+               else if (shared_repository == PERM_EVERYBODY)
+                       sprintf(buf, "%d", OLD_PERM_EVERYBODY);
+               else
+                       sprintf(buf, "0%o", shared_repository);
                git_config_set("core.sharedrepository", buf);
                git_config_set("receive.denyNonFastforwards", "true");
        }
index 5c00725f030460ba34c702b9ad31e577ca70d361..256bbac93a3927adfbd12b124f9c838a4101120d 100644 (file)
@@ -56,7 +56,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
        rev->abbrev = DEFAULT_ABBREV;
        rev->commit_format = CMIT_FMT_DEFAULT;
        if (fmt_pretty)
-               rev->commit_format = get_commit_format(fmt_pretty);
+               get_commit_format(fmt_pretty, rev);
        rev->verbose_header = 1;
        DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
        rev->show_root_diff = default_show_root;
@@ -400,6 +400,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
         * allow us to set a different default.
         */
        rev.commit_format = CMIT_FMT_ONELINE;
+       rev.use_terminator = 1;
        rev.always_show_header = 1;
 
        /*
@@ -769,7 +770,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        rev.diff = 1;
        rev.combine_merges = 0;
        rev.ignore_merges = 1;
-       rev.diffopt.msg_sep = "";
        DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
 
        rev.subject_prefix = fmt_patch_subject_prefix;
index b68c6813b8c71e0e00790eb422e57f8e219095fb..b35aad68e9154bb755c51323106c738ab4fc61e9 100644 (file)
@@ -56,6 +56,17 @@ static int do_push(const char *repo, int flags)
        if (!remote)
                die("bad repository '%s'", repo);
 
+       if (remote->mirror)
+               flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
+
+       if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) && refspec)
+               return -1;
+
+       if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
+                               (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
+               return error("--all and --mirror are incompatible");
+       }
+
        if (!refspec
                && !(flags & TRANSPORT_PUSH_ALL)
                && remote->push_refspec_nr) {
@@ -95,6 +106,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
        int dry_run = 0;
        int force = 0;
        int tags = 0;
+       int rc;
        const char *repo = NULL;        /* default repository */
 
        struct option options[] = {
@@ -130,14 +142,10 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                repo = argv[0];
                set_refspecs(argv + 1, argc - 1);
        }
-       if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) && refspec)
-               usage_with_options(push_usage, options);
 
-       if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
-                               (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
-               error("--all and --mirror are incompatible");
+       rc = do_push(repo, flags);
+       if (rc == -1)
                usage_with_options(push_usage, options);
-       }
-
-       return do_push(repo, flags);
+       else
+               return rc;
 }
index d77f10a0eaa64466b919bcede37ad8c67b70b3fc..8b63619ef08a2ac3d96000908fe4986396ddd6a2 100644 (file)
@@ -19,6 +19,8 @@ static const char * const builtin_remote_usage[] = {
 
 static int verbose;
 
+static int show_all(void);
+
 static inline int postfixcmp(const char *string, const char *postfix)
 {
        int len1 = strlen(string), len2 = strlen(postfix);
@@ -88,19 +90,24 @@ static int add(int argc, const char **argv)
        strbuf_init(&buf, 0);
        strbuf_init(&buf2, 0);
 
+       strbuf_addf(&buf2, "refs/heads/test:refs/remotes/%s/test", name);
+       if (!valid_fetch_refspec(buf2.buf))
+               die("'%s' is not a valid remote name", name);
+
        strbuf_addf(&buf, "remote.%s.url", name);
        if (git_config_set(buf.buf, url))
                return 1;
 
+       strbuf_reset(&buf);
+       strbuf_addf(&buf, "remote.%s.fetch", name);
+
        if (track.nr == 0)
                path_list_append("*", &track);
        for (i = 0; i < track.nr; i++) {
                struct path_list_item *item = track.items + i;
 
-               strbuf_reset(&buf);
-               strbuf_addf(&buf, "remote.%s.fetch", name);
-
                strbuf_reset(&buf2);
+               strbuf_addch(&buf2, '+');
                if (mirror)
                        strbuf_addf(&buf2, "refs/%s:refs/%s",
                                        item->path, item->path);
@@ -111,6 +118,13 @@ static int add(int argc, const char **argv)
                        return 1;
        }
 
+       if (mirror) {
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "remote.%s.mirror", name);
+               if (git_config_set(buf.buf, "yes"))
+                       return 1;
+       }
+
        if (fetch && fetch_remote(name))
                return 1;
 
@@ -380,8 +394,11 @@ static int show_or_prune(int argc, const char **argv, int prune)
 
        argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
 
-       if (argc < 1)
+       if (argc < 1) {
+               if (!prune)
+                       return show_all();
                usage_with_options(builtin_remote_usage, options);
+       }
 
        memset(&states, 0, sizeof(states));
        for (; argc; argc--, argv++) {
index 0351d54435b566d5030f3a83df57fb140fffc143..0e597073239c97160582fbb7047794796f67987f 100644 (file)
@@ -365,9 +365,17 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
+static void die_no_single_rev(int quiet)
+{
+       if (quiet)
+               exit(1);
+       else
+               die("Needed a single revision");
+}
+
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
-       int i, as_is = 0, verify = 0;
+       int i, as_is = 0, verify = 0, quiet = 0;
        unsigned char sha1[20];
 
        if (argc > 1 && !strcmp("--parseopt", argv[1]))
@@ -432,6 +440,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                verify = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) {
+                               quiet = 1;
+                               continue;
+                       }
                        if (!strcmp(arg, "--short") ||
                            !prefixcmp(arg, "--short=")) {
                                filter &= ~(DO_FLAGS|DO_NOREV);
@@ -549,7 +561,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (show_flag(arg) && verify)
-                               die("Needed a single revision");
+                               die_no_single_rev(quiet);
                        continue;
                }
 
@@ -564,15 +576,15 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                        show_rev(REVERSED, sha1, arg+1);
                        continue;
                }
+               if (verify)
+                       die_no_single_rev(quiet);
                as_is = 1;
                if (!show_file(arg))
                        continue;
-               if (verify)
-                       die("Needed a single revision");
                verify_filename(prefix, arg);
        }
        show_default();
        if (verify && revs_count != 1)
-               die("Needed a single revision");
+               die_no_single_rev(quiet);
        return 0;
 }
index bd795b1db7a4054a0218d1ec96794ad25d656896..e6a2865019cceadfcbfc8575a2bd1f97b7159dcb 100644 (file)
@@ -9,7 +9,7 @@
 #include "shortlog.h"
 
 static const char shortlog_usage[] =
-"git-shortlog [-n] [-s] [-e] [<commit-id>... ]";
+"git-shortlog [-n] [-s] [-e] [-w] [<commit-id>... ]";
 
 static int compare_by_number(const void *a1, const void *a2)
 {
index 8dd959fe1c74507023f8e82c7f4682c1588ebac6..129ff57f11d2cdfdacc6be9685736ada07caef7f 100644 (file)
@@ -16,7 +16,7 @@
 static const char * const git_tag_usage[] = {
        "git-tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
        "git-tag -d <tagname>...",
-       "git-tag -l [-n [<num>]] [<pattern>]",
+       "git-tag -l [-n[<num>]] [<pattern>]",
        "git-tag -v <tagname>...",
        NULL
 };
index 50e07faa12f495b3270e1446573b88a255336877..fecf0be7796f34fbb06a6bbbe61e9e3d042f6780 100644 (file)
@@ -7,11 +7,13 @@
 #include "commit.h"
 #include "tag.h"
 #include "tree.h"
+#include "tree-walk.h"
 #include "progress.h"
 #include "decorate.h"
+#include "fsck.h"
 
-static int dry_run, quiet, recover, has_errors;
-static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] < pack-file";
+static int dry_run, quiet, recover, has_errors, strict;
+static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] [--strict] < pack-file";
 
 /* We always read in 4kB chunks. */
 static unsigned char buffer[4096];
@@ -19,6 +21,11 @@ static unsigned int offset, len;
 static off_t consumed_bytes;
 static SHA_CTX ctx;
 
+/*
+ * When running under --strict mode, objects whose reachability are
+ * suspect are kept in core without getting written in the object
+ * store.
+ */
 struct obj_buffer {
        char *buffer;
        unsigned long size;
@@ -31,6 +38,16 @@ static struct obj_buffer *lookup_object_buffer(struct object *base)
        return lookup_decoration(&obj_decorate, base);
 }
 
+static void add_object_buffer(struct object *object, char *buffer, unsigned long size)
+{
+       struct obj_buffer *obj;
+       obj = xcalloc(1, sizeof(struct obj_buffer));
+       obj->buffer = buffer;
+       obj->size = size;
+       if (add_decoration(&obj_decorate, object, obj))
+               die("object %s tried to add buffer twice!", sha1_to_hex(object->sha1));
+}
+
 /*
  * Make sure at least "min" bytes are available in the buffer, and
  * return the pointer to the buffer.
@@ -134,19 +151,110 @@ static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
 struct obj_info {
        off_t offset;
        unsigned char sha1[20];
+       struct object *obj;
 };
 
+#define FLAG_OPEN (1u<<20)
+#define FLAG_WRITTEN (1u<<21)
+
 static struct obj_info *obj_list;
+unsigned nr_objects;
+
+/*
+ * Called only from check_object() after it verified this object
+ * is Ok.
+ */
+static void write_cached_object(struct object *obj)
+{
+       unsigned char sha1[20];
+       struct obj_buffer *obj_buf = lookup_object_buffer(obj);
+       if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), sha1) < 0)
+               die("failed to write object %s", sha1_to_hex(obj->sha1));
+       obj->flags |= FLAG_WRITTEN;
+}
+
+/*
+ * At the very end of the processing, write_rest() scans the objects
+ * that have reachability requirements and calls this function.
+ * Verify its reachability and validity recursively and write it out.
+ */
+static int check_object(struct object *obj, int type, void *data)
+{
+       if (!obj)
+               return 0;
+
+       if (obj->flags & FLAG_WRITTEN)
+               return 1;
+
+       if (type != OBJ_ANY && obj->type != type)
+               die("object type mismatch");
+
+       if (!(obj->flags & FLAG_OPEN)) {
+               unsigned long size;
+               int type = sha1_object_info(obj->sha1, &size);
+               if (type != obj->type || type <= 0)
+                       die("object of unexpected type");
+               obj->flags |= FLAG_WRITTEN;
+               return 1;
+       }
+
+       if (fsck_object(obj, 1, fsck_error_function))
+               die("Error in object");
+       if (!fsck_walk(obj, check_object, 0))
+               die("Error on reachable objects of %s", sha1_to_hex(obj->sha1));
+       write_cached_object(obj);
+       return 1;
+}
+
+static void write_rest(void)
+{
+       unsigned i;
+       for (i = 0; i < nr_objects; i++)
+               check_object(obj_list[i].obj, OBJ_ANY, 0);
+}
 
 static void added_object(unsigned nr, enum object_type type,
                         void *data, unsigned long size);
 
+/*
+ * Write out nr-th object from the list, now we know the contents
+ * of it.  Under --strict, this buffers structured objects in-core,
+ * to be checked at the end.
+ */
 static void write_object(unsigned nr, enum object_type type,
                         void *buf, unsigned long size)
 {
-       if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
-               die("failed to write object");
-       added_object(nr, type, buf, size);
+       if (!strict) {
+               if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
+                       die("failed to write object");
+               added_object(nr, type, buf, size);
+               free(buf);
+               obj_list[nr].obj = NULL;
+       } else if (type == OBJ_BLOB) {
+               struct blob *blob;
+               if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
+                       die("failed to write object");
+               added_object(nr, type, buf, size);
+               free(buf);
+
+               blob = lookup_blob(obj_list[nr].sha1);
+               if (blob)
+                       blob->object.flags |= FLAG_WRITTEN;
+               else
+                       die("invalid blob object");
+               obj_list[nr].obj = NULL;
+       } else {
+               struct object *obj;
+               int eaten;
+               hash_sha1_file(buf, size, typename(type), obj_list[nr].sha1);
+               added_object(nr, type, buf, size);
+               obj = parse_object_buffer(obj_list[nr].sha1, type, size, buf, &eaten);
+               if (!obj)
+                       die("invalid %s", typename(type));
+               add_object_buffer(obj, buf, size);
+               obj->flags |= FLAG_OPEN;
+               obj_list[nr].obj = obj;
+       }
 }
 
 static void resolve_delta(unsigned nr, enum object_type type,
@@ -163,9 +271,12 @@ static void resolve_delta(unsigned nr, enum object_type type,
                die("failed to apply delta");
        free(delta);
        write_object(nr, type, result, result_size);
-       free(result);
 }
 
+/*
+ * We now know the contents of an object (which is nr-th in the pack);
+ * resolve all the deltified objects that are based on it.
+ */
 static void added_object(unsigned nr, enum object_type type,
                         void *data, unsigned long size)
 {
@@ -193,7 +304,24 @@ static void unpack_non_delta_entry(enum object_type type, unsigned long size,
 
        if (!dry_run && buf)
                write_object(nr, type, buf, size);
-       free(buf);
+       else
+               free(buf);
+}
+
+static int resolve_against_held(unsigned nr, const unsigned char *base,
+                               void *delta_data, unsigned long delta_size)
+{
+       struct object *obj;
+       struct obj_buffer *obj_buffer;
+       obj = lookup_object(base);
+       if (!obj)
+               return 0;
+       obj_buffer = lookup_object_buffer(obj);
+       if (!obj_buffer)
+               return 0;
+       resolve_delta(nr, obj->type, obj_buffer->buffer,
+                     obj_buffer->size, delta_data, delta_size);
+       return 1;
 }
 
 static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
@@ -202,7 +330,6 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
        void *delta_data, *base;
        unsigned long base_size;
        unsigned char base_sha1[20];
-       struct object *obj;
 
        if (type == OBJ_REF_DELTA) {
                hashcpy(base_sha1, fill(20));
@@ -212,7 +339,13 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
                        free(delta_data);
                        return;
                }
-               if (!has_sha1_file(base_sha1)) {
+               if (has_sha1_file(base_sha1))
+                       ; /* Ok we have this one */
+               else if (resolve_against_held(nr, base_sha1,
+                                             delta_data, delta_size))
+                       return; /* we are done */
+               else {
+                       /* cannot resolve yet --- queue it */
                        hashcpy(obj_list[nr].sha1, null_sha1);
                        add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size);
                        return;
@@ -258,22 +391,18 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
                        }
                }
                if (!base_found) {
-                       /* The delta base object is itself a delta that
-                          has not been resolved yet. */
+                       /*
+                        * The delta base object is itself a delta that
+                        * has not been resolved yet.
+                        */
                        hashcpy(obj_list[nr].sha1, null_sha1);
                        add_delta_to_list(nr, null_sha1, base_offset, delta_data, delta_size);
                        return;
                }
        }
 
-       obj = lookup_object(base_sha1);
-       if (obj) {
-               struct obj_buffer *obj_buf = lookup_object_buffer(obj);
-               if (obj_buf) {
-                       resolve_delta(nr, obj->type, obj_buf->buffer, obj_buf->size, delta_data, delta_size);
-                       return;
-               }
-       }
+       if (resolve_against_held(nr, base_sha1, delta_data, delta_size))
+               return;
 
        base = read_sha1_file(base_sha1, &type, &base_size);
        if (!base) {
@@ -336,7 +465,8 @@ static void unpack_all(void)
        int i;
        struct progress *progress = NULL;
        struct pack_header *hdr = fill(sizeof(struct pack_header));
-       unsigned nr_objects = ntohl(hdr->hdr_entries);
+
+       nr_objects = ntohl(hdr->hdr_entries);
 
        if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE)
                die("bad pack file");
@@ -347,6 +477,7 @@ static void unpack_all(void)
        if (!quiet)
                progress = start_progress("Unpacking objects", nr_objects);
        obj_list = xmalloc(nr_objects * sizeof(*obj_list));
+       memset(obj_list, 0, nr_objects * sizeof(*obj_list));
        for (i = 0; i < nr_objects; i++) {
                unpack_one(i);
                display_progress(progress, i + 1);
@@ -382,6 +513,10 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
                                recover = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "--strict")) {
+                               strict = 1;
+                               continue;
+                       }
                        if (!prefixcmp(arg, "--pack_header=")) {
                                struct pack_header *hdr;
                                char *c;
@@ -407,6 +542,8 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
        unpack_all();
        SHA1_Update(&ctx, buffer, offset);
        SHA1_Final(sha1, &ctx);
+       if (strict)
+               write_rest();
        if (hashcmp(fill(20), sha1))
                die("final sha1 did not match");
        use(20);
index f3ef11fa2d582682b428d6e165da65f05e2d7511..db81496b464e253341a42e01eb72d6845e87199c 100644 (file)
@@ -46,8 +46,10 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose)
        gpg.argv = args_gpg;
        gpg.in = -1;
        args_gpg[2] = path;
-       if (start_command(&gpg))
+       if (start_command(&gpg)) {
+               unlink(path);
                return error("could not run gpg.");
+       }
 
        write_in_full(gpg.in, buf, len);
        close(gpg.in);
index 39da54d1e56b5905655eafed1aff0f51c2540a8e..73cb3407077275f82677839d2c9e794c12833c95 100644 (file)
@@ -341,8 +341,11 @@ static int update_one(struct cache_tree *it,
 
        if (dryrun)
                hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1);
-       else
-               write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1);
+       else if (write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1)) {
+               strbuf_release(&buffer);
+               return -1;
+       }
+
        strbuf_release(&buffer);
        it->entry_count = i;
 #if DEBUG
diff --git a/cache.h b/cache.h
index 2a1e7ec6b2bf712af80813936b5aed434d02090e..d5d5dad1463e2251dc84cf0a85095a7e3e0eaeae 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -311,6 +311,7 @@ extern char *get_index_file(void);
 extern char *get_graft_file(void);
 extern int set_git_dir(const char *path);
 extern const char *get_git_work_tree(void);
+extern const char *read_gitfile_gently(const char *path);
 
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
@@ -474,10 +475,20 @@ static inline void hashclr(unsigned char *hash)
 
 int git_mkstemp(char *path, size_t n, const char *template);
 
+/*
+ * NOTE NOTE NOTE!!
+ *
+ * PERM_UMASK, OLD_PERM_GROUP and OLD_PERM_EVERYBODY enumerations must
+ * not be changed. Old repositories have core.sharedrepository written in
+ * numeric format, and therefore these values are preserved for compatibility
+ * reasons.
+ */
 enum sharedrepo {
-       PERM_UMASK = 0,
-       PERM_GROUP,
-       PERM_EVERYBODY
+       PERM_UMASK          = 0,
+       OLD_PERM_GROUP      = 1,
+       OLD_PERM_EVERYBODY  = 2,
+       PERM_GROUP          = 0660,
+       PERM_EVERYBODY      = 0664,
 };
 int git_config_perm(const char *var, const char *value);
 int adjust_shared_perm(const char *path);
@@ -692,6 +703,7 @@ extern int git_parse_long(const char *, long *);
 extern int git_parse_ulong(const char *, unsigned long *);
 extern int git_config_int(const char *, const char *);
 extern unsigned long git_config_ulong(const char *, const char *);
+extern int git_config_bool_or_int(const char *, const char *, int *);
 extern int git_config_bool(const char *, const char *);
 extern int git_config_string(const char **, const char *, const char *);
 extern int git_config_set(const char *, const char *);
@@ -715,8 +727,8 @@ extern const char *git_log_output_encoding;
 extern void maybe_flush_or_die(FILE *, const char *);
 extern int copy_fd(int ifd, int ofd);
 extern int copy_file(const char *dst, const char *src, int mode);
-extern int read_in_full(int fd, void *buf, size_t count);
-extern int write_in_full(int fd, const void *buf, size_t count);
+extern ssize_t read_in_full(int fd, void *buf, size_t count);
+extern ssize_t write_in_full(int fd, const void *buf, size_t count);
 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);
 extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
index 0e19cbaacc1099fd69f7f2d9b4a17c94a327baa9..588c58bc55dfe78998d40a4dcce2a7b6ea6f7f5a 100644 (file)
@@ -701,7 +701,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                else if (0 <= (fd = open(elem->path, O_RDONLY)) &&
                         !fstat(fd, &st)) {
                        size_t len = xsize_t(st.st_size);
-                       size_t sz = 0;
+                       ssize_t done;
                        int is_file, i;
 
                        elem->mode = canon_mode(st.st_mode);
@@ -716,14 +716,13 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
 
                        result_size = len;
                        result = xmalloc(len + 1);
-                       while (sz < len) {
-                               ssize_t done = xread(fd, result+sz, len-sz);
-                               if (done == 0)
-                                       break;
-                               if (done < 0)
-                                       die("read error '%s'", elem->path);
-                               sz += done;
-                       }
+
+                       done = read_in_full(fd, result, len);
+                       if (done < 0)
+                               die("read error '%s'", elem->path);
+                       else if (done < len)
+                               die("early EOF '%s'", elem->path);
+
                        result[len] = 0;
                }
                else {
@@ -798,7 +797,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                int deleted = 0;
 
                if (rev->loginfo && !rev->no_commit_id)
-                       show_log(rev, opt->msg_sep);
+                       show_log(rev);
                dump_quoted_path(dense ? "diff --cc " : "diff --combined ",
                                 "", elem->path, c_meta, c_reset);
                printf("%sindex ", c_meta);
@@ -881,7 +880,7 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct re
                inter_name_termination = 0;
 
        if (rev->loginfo && !rev->no_commit_id)
-               show_log(rev, opt->msg_sep);
+               show_log(rev);
 
        if (opt->output_format & DIFF_FORMAT_RAW) {
                offset = strlen(COLONS) - num_parent;
@@ -962,7 +961,7 @@ void diff_tree_combined(const unsigned char *sha1,
                paths = intersect_paths(paths, i, num_parent);
 
                if (show_log_first && i == 0) {
-                       show_log(rev, opt->msg_sep);
+                       show_log(rev);
                        if (rev->verbose_header && opt->output_format)
                                putchar(opt->line_termination);
                }
index 2f63bc8b2fac0d748e13d4435d550d61002092b3..2d94d4148ed4048469ba79cae4f6ff2a2e3f9bca 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -63,7 +63,8 @@ enum cmit_fmt {
 };
 
 extern int non_ascii(int);
-extern enum cmit_fmt get_commit_format(const char *arg);
+struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
+extern void get_commit_format(const char *arg, struct rev_info *);
 extern void format_commit_message(const struct commit *commit,
                                   const void *format, struct strbuf *sb);
 extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,
index 062449459e1a4cfc2a605c065ed281669e0e7452..b0ada515b9d839fc8691bc9af320353ff323b251 100644 (file)
--- a/config.c
+++ b/config.c
@@ -303,8 +303,9 @@ unsigned long git_config_ulong(const char *name, const char *value)
        return ret;
 }
 
-int git_config_bool(const char *name, const char *value)
+int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
 {
+       *is_bool = 1;
        if (!value)
                return 1;
        if (!*value)
@@ -313,7 +314,14 @@ int git_config_bool(const char *name, const char *value)
                return 1;
        if (!strcasecmp(value, "false") || !strcasecmp(value, "no"))
                return 0;
-       return git_config_int(name, value) != 0;
+       *is_bool = 0;
+       return git_config_int(name, value);
+}
+
+int git_config_bool(const char *name, const char *value)
+{
+       int discard;
+       return !!git_config_bool_or_int(name, value, &discard);
 }
 
 int git_config_string(const char **dest, const char *var, const char *value)
index 5046f699349aa87bedbc79a997dfcb9ea54e7618..23db664f48057b7fa778f0b265510d5b95457c16 100755 (executable)
@@ -152,7 +152,7 @@ __git_heads ()
                done
                return
        fi
-       for i in $(git-ls-remote "$1" 2>/dev/null); do
+       for i in $(git ls-remote "$1" 2>/dev/null); do
                case "$is_hash,$i" in
                y,*) is_hash=n ;;
                n,*^{}) is_hash=y ;;
@@ -173,7 +173,7 @@ __git_tags ()
                done
                return
        fi
-       for i in $(git-ls-remote "$1" 2>/dev/null); do
+       for i in $(git ls-remote "$1" 2>/dev/null); do
                case "$is_hash,$i" in
                y,*) is_hash=n ;;
                n,*^{}) is_hash=y ;;
@@ -200,7 +200,7 @@ __git_refs ()
                done
                return
        fi
-       for i in $(git-ls-remote "$dir" 2>/dev/null); do
+       for i in $(git ls-remote "$dir" 2>/dev/null); do
                case "$is_hash,$i" in
                y,*) is_hash=n ;;
                n,*^{}) is_hash=y ;;
@@ -223,7 +223,7 @@ __git_refs2 ()
 __git_refs_remotes ()
 {
        local cmd i is_hash=y
-       for i in $(git-ls-remote "$1" 2>/dev/null); do
+       for i in $(git ls-remote "$1" 2>/dev/null); do
                case "$is_hash,$i" in
                n,refs/heads/*)
                        is_hash=y
@@ -639,7 +639,10 @@ _git_diff ()
                        --find-copies-harder --pickaxe-all --pickaxe-regex
                        --text --ignore-space-at-eol --ignore-space-change
                        --ignore-all-space --exit-code --quiet --ext-diff
-                       --no-ext-diff"
+                       --no-ext-diff
+                       --no-prefix --src-prefix= --dst-prefix=
+                       --base --ours --theirs
+                       "
                return
                ;;
        esac
@@ -696,6 +699,7 @@ _git_format_patch ()
                        --full-index --binary
                        --not --all
                        --cover-letter
+                       --no-prefix --src-prefix= --dst-prefix=
                        "
                return
                ;;
@@ -865,7 +869,7 @@ _git_rebase ()
                return
                ;;
        --*)
-               __gitcomp "--onto --merge --strategy"
+               __gitcomp "--onto --merge --strategy --interactive"
                return
        esac
        __gitcomp "$(__git_refs)"
@@ -999,7 +1003,8 @@ _git_config ()
                gitcvs.enabled
                gitcvs.logfile
                gitcvs.allbinary
-               gitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dvpass
+               gitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dbpass
+               gitcvs.dbtablenameprefix
                gc.packrefs
                gc.reflogexpire
                gc.reflogexpireunreachable
@@ -1048,6 +1053,7 @@ _git_remote ()
        local subcommands="add rm show prune update"
        local subcommand="$(__git_find_subcommand "$subcommands")"
        if [ -z "$subcommand" ]; then
+               __gitcomp "$subcommands"
                return
        fi
 
@@ -1340,9 +1346,14 @@ _git ()
 _gitk ()
 {
        local cur="${COMP_WORDS[COMP_CWORD]}"
+       local g="$(git rev-parse --git-dir 2>/dev/null)"
+       local merge=""
+       if [ -f $g/MERGE_HEAD ]; then
+               merge="--merge"
+       fi
        case "$cur" in
        --*)
-               __gitcomp "--not --all"
+               __gitcomp "--not --all $merge"
                return
                ;;
        esac
index 4fa853fae76dc2ac132e489f5f1b630b3fc0f1de..2557a7667f1d3a272698b213e8cf9cfbafddaea2 100644 (file)
@@ -232,10 +232,8 @@ and returns the process output as a string, or nil if the git failed."
 
 (defun git-run-command-region (buffer start end env &rest args)
   "Run a git command with specified buffer region as input."
-  (unless (eq 0 (if env
-                    (git-run-process-region
-                     buffer start end "env"
-                     (append (git-get-env-strings env) (list "git") args))
+  (unless (eq 0 (let ((process-environment (append (git-get-env-strings env)
+                                                   process-environment)))
                   (git-run-process-region
                    buffer start end "git" args)))
     (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string))))
@@ -250,9 +248,8 @@ and returns the process output as a string, or nil if the git failed."
             (erase-buffer)
             (cd dir)
             (setq status
-                  (if env
-                      (apply #'call-process "env" nil (list buffer t) nil
-                             (append (git-get-env-strings env) (list hook-name) args))
+                  (let ((process-environment (append (git-get-env-strings env)
+                                                     process-environment)))
                     (apply #'call-process hook-name nil (list buffer t) nil args))))
           (display-message-or-buffer buffer)
           (eq 0 status)))))
index 62a740c48248d94e1e0577c8676b13b7a726f21c..41368950d6b29121089ee9239b8e07ece209a31e 100644 (file)
@@ -202,11 +202,12 @@ generate_email_header()
 
 generate_email_footer()
 {
+       SPACE=" "
        cat <<-EOF
 
 
        hooks/post-receive
-       --
+       --${SPACE}
        $projectdesc
        EOF
 }
diff --git a/contrib/hooks/pre-auto-gc-battery b/contrib/hooks/pre-auto-gc-battery
new file mode 100644 (file)
index 0000000..0096f57
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# An example hook script to verify if you are on battery, in case you
+# are running Linux. Called by git-gc --auto with no arguments. The hook
+# should exit with non-zero status after issuing an appropriate message
+# if it wants to stop the auto repacking.
+#
+# This hook is stored in the contrib/hooks directory. Your distribution
+# may have put this somewhere else. If you want to use this hook, you
+# should make this script executable then link to it in the repository
+# you would like to use it in.
+#
+# For example, if the hook is stored in
+# /usr/share/git-core/contrib/hooks/pre-auto-gc-battery:
+#
+# chmod a+x pre-auto-gc-battery
+# cd /path/to/your/repository.git
+# ln -sf /usr/share/git-core/contrib/hooks/pre-auto-gc-battery \
+#      hooks/pre-auto-gc
+
+if test -x /sbin/on_ac_power && /sbin/on_ac_power
+then
+       exit 0
+elif test "$(cat /sys/class/power_supply/AC/online 2>/dev/null)" = 1
+then
+       exit 0
+elif grep -q 'on-line' /proc/acpi/ac_adapter/AC/state 2>/dev/null
+then
+       exit 0
+elif grep -q '0x01$' /proc/apm 2>/dev/null
+then
+       exit 0
+fi
+
+echo "Auto packing deferred; not on AC"
+exit 1
diff --git a/copy.c b/copy.c
index afc4fbf41405d42d2751ea35ec7a9a32f8df6274..e54d15aced7595ccb11423b0de121db9051ad1f3 100644 (file)
--- a/copy.c
+++ b/copy.c
@@ -9,8 +9,7 @@ int copy_fd(int ifd, int ofd)
                if (!len)
                        break;
                if (len < 0) {
-                       int read_error;
-                       read_error = errno;
+                       int read_error = errno;
                        close(ifd);
                        return error("copy-fd: read returned %s",
                                     strerror(read_error));
@@ -25,9 +24,10 @@ int copy_fd(int ifd, int ofd)
                                close(ifd);
                                return error("copy-fd: write returned 0");
                        } else {
+                               int write_error = errno;
                                close(ifd);
                                return error("copy-fd: write returned %s",
-                                            strerror(errno));
+                                            strerror(write_error));
                        }
                }
        }
@@ -48,7 +48,7 @@ int copy_file(const char *dst, const char *src, int mode)
        }
        status = copy_fd(fdi, fdo);
        if (close(fdo) != 0)
-               return error("%s: write error: %s", dst, strerror(errno));
+               return error("%s: close error: %s", dst, strerror(errno));
 
        if (!status && adjust_shared_perm(dst))
                return -1;
index 069e4507ae7caa70f79d5369bc61dfefd0f174e2..cfd629da48526f5949124b5d9baeb52dfadd5f64 100644 (file)
@@ -264,6 +264,9 @@ int setup_diff_no_index(struct rev_info *revs,
                        DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS);
                        break;
                }
+       if (nongit && argc != i + 2)
+               die("git diff [--no-index] takes two paths");
+
        if (argc != i + 2 || (!is_outside_repo(argv[i + 1], nongit, prefix) &&
                                !is_outside_repo(argv[i], nongit, prefix)))
                return -1;
diff --git a/diff.c b/diff.c
index 8022e678d1fc26eab75d73d01110d635fba11fd7..e35384b4442fbaedbf460ddf61c32b85dde2a4d7 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -991,18 +991,23 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options)
        }
 }
 
-struct diffstat_dir {
-       struct diffstat_file **files;
-       int nr, percent, cumulative;
+struct dirstat_file {
+       const char *name;
+       unsigned long changed;
 };
 
-static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen)
+struct dirstat_dir {
+       struct dirstat_file *files;
+       int alloc, nr, percent, cumulative;
+};
+
+static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long changed, const char *base, int baselen)
 {
        unsigned long this_dir = 0;
        unsigned int sources = 0;
 
        while (dir->nr) {
-               struct diffstat_file *f = *dir->files;
+               struct dirstat_file *f = dir->files;
                int namelen = strlen(f->name);
                unsigned long this;
                char *slash;
@@ -1017,10 +1022,7 @@ static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long c
                        this = gather_dirstat(file, dir, changed, f->name, newbaselen);
                        sources++;
                } else {
-                       if (f->is_unmerged || f->is_binary)
-                               this = 0;
-                       else
-                               this = f->added + f->deleted;
+                       this = f->changed;
                        dir->files++;
                        dir->nr--;
                        sources += 2;
@@ -1048,19 +1050,58 @@ static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long c
        return this_dir;
 }
 
-static void show_dirstat(struct diffstat_t *data, struct diff_options *options)
+static void show_dirstat(struct diff_options *options)
 {
        int i;
        unsigned long changed;
-       struct diffstat_dir dir;
+       struct dirstat_dir dir;
+       struct diff_queue_struct *q = &diff_queued_diff;
+
+       dir.files = NULL;
+       dir.alloc = 0;
+       dir.nr = 0;
+       dir.percent = options->dirstat_percent;
+       dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE;
 
-       /* Calculate total changes */
        changed = 0;
-       for (i = 0; i < data->nr; i++) {
-               if (data->files[i]->is_binary || data->files[i]->is_unmerged)
+       for (i = 0; i < q->nr; i++) {
+               struct diff_filepair *p = q->queue[i];
+               const char *name;
+               unsigned long copied, added, damage;
+
+               name = p->one->path ? p->one->path : p->two->path;
+
+               if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
+                       diff_populate_filespec(p->one, 0);
+                       diff_populate_filespec(p->two, 0);
+                       diffcore_count_changes(p->one, p->two, NULL, NULL, 0,
+                                              &copied, &added);
+                       diff_free_filespec_data(p->one);
+                       diff_free_filespec_data(p->two);
+               } else if (DIFF_FILE_VALID(p->one)) {
+                       diff_populate_filespec(p->one, 1);
+                       copied = added = 0;
+                       diff_free_filespec_data(p->one);
+               } else if (DIFF_FILE_VALID(p->two)) {
+                       diff_populate_filespec(p->two, 1);
+                       copied = 0;
+                       added = p->two->size;
+                       diff_free_filespec_data(p->two);
+               } else
                        continue;
-               changed += data->files[i]->added;
-               changed += data->files[i]->deleted;
+
+               /*
+                * Original minus copied is the removed material,
+                * added is the new material.  They are both damages
+                * made to the preimage.
+                */
+               damage = (p->one->size - copied) + added;
+
+               ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);
+               dir.files[dir.nr].name = name;
+               dir.files[dir.nr].changed = damage;
+               changed += damage;
+               dir.nr++;
        }
 
        /* This can happen even with many files, if everything was renames */
@@ -1068,10 +1109,6 @@ static void show_dirstat(struct diffstat_t *data, struct diff_options *options)
                return;
 
        /* Show all directories with more than x% of the changes */
-       dir.files = data->files;
-       dir.nr = data->nr;
-       dir.percent = options->dirstat_percent;
-       dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE;
        gather_dirstat(options->file, &dir, changed, "", 0);
 }
 
@@ -2183,7 +2220,6 @@ void diff_setup(struct diff_options *options)
        options->rename_limit = -1;
        options->dirstat_percent = 3;
        options->context = 3;
-       options->msg_sep = "";
 
        options->change = diff_change;
        options->add_remove = diff_addremove;
@@ -3095,7 +3131,7 @@ void diff_flush(struct diff_options *options)
                separator++;
        }
 
-       if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIRSTAT)) {
+       if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {
                struct diffstat_t diffstat;
 
                memset(&diffstat, 0, sizeof(struct diffstat_t));
@@ -3105,8 +3141,6 @@ void diff_flush(struct diff_options *options)
                        if (check_pair_status(p))
                                diff_flush_stat(p, options, &diffstat);
                }
-               if (output_format & DIFF_FORMAT_DIRSTAT)
-                       show_dirstat(&diffstat, options);
                if (output_format & DIFF_FORMAT_NUMSTAT)
                        show_numstat(&diffstat, options);
                if (output_format & DIFF_FORMAT_DIFFSTAT)
@@ -3116,6 +3150,8 @@ void diff_flush(struct diff_options *options)
                free_diffstat_info(&diffstat);
                separator++;
        }
+       if (output_format & DIFF_FORMAT_DIRSTAT)
+               show_dirstat(options);
 
        if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {
                for (i = 0; i < q->nr; i++)
diff --git a/diff.h b/diff.h
index f2c77391a965bcc731de6478a6553cdc652319ec..1bd94a4807c741e7cb2e4d8eb9f6476eba691388 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -88,7 +88,6 @@ struct diff_options {
        int abbrev;
        const char *prefix;
        int prefix_length;
-       const char *msg_sep;
        const char *stat_sep;
        long xdl_opts;
 
index 31941bcbbf457fe02097f8c46d25f1f9fc7c4f9f..1369a5ec45349c1ef04e4df2d7d7d4dd28cd3596 100644 (file)
@@ -112,8 +112,8 @@ static int basename_same(struct diff_filespec *src, struct diff_filespec *dst)
 struct diff_score {
        int src; /* index in rename_src */
        int dst; /* index in rename_dst */
-       int score;
-       int name_score;
+       unsigned short score;
+       short name_score;
 };
 
 static int estimate_similarity(struct diff_filespec *src,
@@ -223,6 +223,12 @@ static int score_compare(const void *a_, const void *b_)
 {
        const struct diff_score *a = a_, *b = b_;
 
+       /* sink the unused ones to the bottom */
+       if (a->dst < 0)
+               return (0 <= b->dst);
+       else if (b->dst < 0)
+               return -1;
+
        if (a->score == b->score)
                return b->name_score - a->name_score;
 
@@ -387,6 +393,22 @@ static int find_exact_renames(void)
        return i;
 }
 
+#define NUM_CANDIDATE_PER_DST 4
+static void record_if_better(struct diff_score m[], struct diff_score *o)
+{
+       int i, worst;
+
+       /* find the worst one */
+       worst = 0;
+       for (i = 1; i < NUM_CANDIDATE_PER_DST; i++)
+               if (score_compare(&m[i], &m[worst]) > 0)
+                       worst = i;
+
+       /* is it better than the worst one? */
+       if (score_compare(&m[worst], o) > 0)
+               m[worst] = *o;
+}
+
 void diffcore_rename(struct diff_options *options)
 {
        int detect_rename = options->detect_rename;
@@ -474,47 +496,61 @@ void diffcore_rename(struct diff_options *options)
                goto cleanup;
        }
 
-       mx = xmalloc(sizeof(*mx) * num_create * num_src);
+       mx = xcalloc(num_create * NUM_CANDIDATE_PER_DST, sizeof(*mx));
        for (dst_cnt = i = 0; i < rename_dst_nr; i++) {
-               int base = dst_cnt * num_src;
                struct diff_filespec *two = rename_dst[i].two;
+               struct diff_score *m;
+
                if (rename_dst[i].pair)
                        continue; /* dealt with exact match already. */
+
+               m = &mx[dst_cnt * NUM_CANDIDATE_PER_DST];
+               for (j = 0; j < NUM_CANDIDATE_PER_DST; j++)
+                       m[j].dst = -1;
+
                for (j = 0; j < rename_src_nr; j++) {
                        struct diff_filespec *one = rename_src[j].one;
-                       struct diff_score *m = &mx[base+j];
-                       m->src = j;
-                       m->dst = i;
-                       m->score = estimate_similarity(one, two,
-                                                      minimum_score);
-                       m->name_score = basename_same(one, two);
+                       struct diff_score this_src;
+                       this_src.score = estimate_similarity(one, two,
+                                                            minimum_score);
+                       this_src.name_score = basename_same(one, two);
+                       this_src.dst = i;
+                       this_src.src = j;
+                       record_if_better(m, &this_src);
                        diff_free_filespec_blob(one);
                }
                /* We do not need the text anymore */
                diff_free_filespec_blob(two);
                dst_cnt++;
        }
+
        /* cost matrix sorted by most to least similar pair */
-       qsort(mx, num_create * num_src, sizeof(*mx), score_compare);
-       for (i = 0; i < num_create * num_src; i++) {
-               struct diff_rename_dst *dst = &rename_dst[mx[i].dst];
-               struct diff_filespec *src;
+       qsort(mx, dst_cnt * NUM_CANDIDATE_PER_DST, sizeof(*mx), score_compare);
+
+       for (i = 0; i < dst_cnt * NUM_CANDIDATE_PER_DST; i++) {
+               struct diff_rename_dst *dst;
+
+               if ((mx[i].dst < 0) ||
+                   (mx[i].score < minimum_score))
+                       break; /* there is no more usable pair. */
+               dst = &rename_dst[mx[i].dst];
                if (dst->pair)
                        continue; /* already done, either exact or fuzzy. */
-               if (mx[i].score < minimum_score)
-                       break; /* there is no more usable pair. */
-               src = rename_src[mx[i].src].one;
-               if (src->rename_used)
+               if (rename_src[mx[i].src].one->rename_used)
                        continue;
                record_rename_pair(mx[i].dst, mx[i].src, mx[i].score);
                rename_count++;
        }
-       for (i = 0; i < num_create * num_src; i++) {
-               struct diff_rename_dst *dst = &rename_dst[mx[i].dst];
+
+       for (i = 0; i < dst_cnt * NUM_CANDIDATE_PER_DST; i++) {
+               struct diff_rename_dst *dst;
+
+               if ((mx[i].dst < 0) ||
+                   (mx[i].score < minimum_score))
+                       break; /* there is no more usable pair. */
+               dst = &rename_dst[mx[i].dst];
                if (dst->pair)
                        continue; /* already done, either exact or fuzzy. */
-               if (mx[i].score < minimum_score)
-                       break; /* there is no more usable pair. */
                record_rename_pair(mx[i].dst, mx[i].src, mx[i].score);
                rename_count++;
        }
diff --git a/dir.c b/dir.c
index edc458e020772a7ab704e9cf69786d3aa641bcd4..d79762c7c0bc9e762ed8dc5f00fb2fd3ce01ad57 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -80,7 +80,7 @@ static int match_one(const char *match, const char *name, int namelen)
        if (strncmp(match, name, matchlen))
                return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0;
 
-       if (!name[matchlen])
+       if (namelen == matchlen)
                return MATCHED_EXACTLY;
        if (match[matchlen-1] == '/' || name[matchlen] == '/')
                return MATCHED_RECURSIVELY;
index 6739a3f41745fe1c01dc767e09af8746d6f2815f..fcd1ee5ef8c0999fce0095a2d6a792e5d221c50a 100644 (file)
@@ -49,6 +49,8 @@ static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
 static void setup_git_env(void)
 {
        git_dir = getenv(GIT_DIR_ENVIRONMENT);
+       if (!git_dir)
+               git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
        if (!git_dir)
                git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
        git_object_dir = getenv(DB_ENVIRONMENT);
index a0a81f134a6288dfc1d87431698f29597ed5e488..903953e68e98535e754b5a5dd6ba22eb6074ef20 100755 (executable)
@@ -550,6 +550,21 @@ sub parse_diff {
        return @hunk;
 }
 
+sub parse_diff_header {
+       my $src = shift;
+
+       my $head = { TEXT => [], DISPLAY => [] };
+       my $mode = { TEXT => [], DISPLAY => [] };
+
+       for (my $i = 0; $i < @{$src->{TEXT}}; $i++) {
+               my $dest = $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ?
+                       $mode : $head;
+               push @{$dest->{TEXT}}, $src->{TEXT}->[$i];
+               push @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i];
+       }
+       return ($head, $mode);
+}
+
 sub hunk_splittable {
        my ($text) = @_;
 
@@ -795,9 +810,40 @@ sub patch_update_file {
        my ($ix, $num);
        my $path = shift;
        my ($head, @hunk) = parse_diff($path);
+       ($head, my $mode) = parse_diff_header($head);
        for (@{$head->{DISPLAY}}) {
                print;
        }
+
+       if (@{$mode->{TEXT}}) {
+               while (1) {
+                       print @{$mode->{DISPLAY}};
+                       print colored $prompt_color,
+                               "Stage mode change [y/n/a/d/?]? ";
+                       my $line = <STDIN>;
+                       if ($line =~ /^y/i) {
+                               $mode->{USE} = 1;
+                               last;
+                       }
+                       elsif ($line =~ /^n/i) {
+                               $mode->{USE} = 0;
+                               last;
+                       }
+                       elsif ($line =~ /^a/i) {
+                               $_->{USE} = 1 foreach ($mode, @hunk);
+                               last;
+                       }
+                       elsif ($line =~ /^d/i) {
+                               $_->{USE} = 0 foreach ($mode, @hunk);
+                               last;
+                       }
+                       else {
+                               help_patch_cmd('');
+                               next;
+                       }
+               }
+       }
+
        $num = scalar @hunk;
        $ix = 0;
 
@@ -920,6 +966,9 @@ sub patch_update_file {
 
        my $n_lofs = 0;
        my @result = ();
+       if ($mode->{USE}) {
+               push @result, @{$mode->{TEXT}};
+       }
        for (@hunk) {
                my $text = $_->{TEXT};
                my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =
index ac5c388060789e559f80d61d6eae1db926b66ad4..75886a8f2fb0c2244ba921398ae2339dc9ffea26 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -107,7 +107,7 @@ It does not apply to blobs recorded in its index."
     # patch did not touch, so recursive ends up canceling them,
     # saying that we reverted all those changes.
 
-    eval GITHEAD_$his_tree='"$SUBJECT"'
+    eval GITHEAD_$his_tree='"$FIRSTLINE"'
     export GITHEAD_$his_tree
     git-merge-recursive $orig_tree -- HEAD $his_tree || {
            git rerere
@@ -117,10 +117,6 @@ It does not apply to blobs recorded in its index."
     unset GITHEAD_$his_tree
 }
 
-reread_subject () {
-       git stripspace <"$1" | sed -e 1q
-}
-
 prec=4
 dotest=".dotest"
 sign= utf8=t keep= skip= interactive= resolved= binary= rebasing=
@@ -331,7 +327,20 @@ do
                        echo "Patch is empty.  Was it split wrong?"
                        stop_here $this
                }
-               git stripspace < "$dotest/msg" > "$dotest/msg-clean"
+               if test -f "$dotest/rebasing" &&
+                       commit=$(sed -e 's/^From \([0-9a-f]*\) .*/\1/' \
+                               -e q "$dotest/$msgnum") &&
+                       test "$(git cat-file -t "$commit")" = commit
+               then
+                       git cat-file commit "$commit" |
+                       sed -e '1,/^$/d' >"$dotest/msg-clean"
+               else
+                       SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
+                       case "$keep_subject" in -k)  SUBJECT="[PATCH] $SUBJECT" ;; esac
+
+                       (printf '%s\n\n' "$SUBJECT"; cat "$dotest/msg") |
+                               git stripspace > "$dotest/msg-clean"
+               fi
                ;;
        esac
 
@@ -347,9 +356,6 @@ do
 
        export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
 
-       SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
-       case "$keep_subject" in -k)  SUBJECT="[PATCH] $SUBJECT" ;; esac
-
        case "$resume" in
        '')
            if test '' != "$SIGNOFF"
@@ -368,10 +374,8 @@ do
                ADD_SIGNOFF=
            fi
            {
-               printf '%s\n' "$SUBJECT"
                if test -s "$dotest/msg-clean"
                then
-                       echo
                        cat "$dotest/msg-clean"
                fi
                if test '' != "$ADD_SIGNOFF"
@@ -408,7 +412,6 @@ do
                [aA]*) action=yes interactive= ;;
                [nN]*) action=skip ;;
                [eE]*) git_editor "$dotest/final-commit"
-                      SUBJECT=$(reread_subject "$dotest/final-commit")
                       action=again ;;
                [vV]*) action=again
                       LESS=-S ${PAGER:-less} "$dotest/patch" ;;
@@ -418,6 +421,7 @@ do
        else
            action=yes
        fi
+       FIRSTLINE=$(head -1 "$dotest/final-commit")
 
        if test $action = skip
        then
@@ -431,7 +435,7 @@ do
                stop_here $this
        fi
 
-       printf 'Applying %s\n' "$SUBJECT"
+       printf 'Applying %s\n' "$FIRSTLINE"
 
        case "$resolved" in
        '')
@@ -489,7 +493,7 @@ do
        tree=$(git write-tree) &&
        parent=$(git rev-parse --verify HEAD) &&
        commit=$(git commit-tree $tree -p $parent <"$dotest/final-commit") &&
-       git update-ref -m "$GIT_REFLOG_ACTION: $SUBJECT" HEAD $commit $parent ||
+       git update-ref -m "$GIT_REFLOG_ACTION: $FIRSTLINE" HEAD $commit $parent ||
        stop_here $this
 
        if test -x "$GIT_DIR"/hooks/post-applypatch
index 48fb92d612f065166072e09f49ee4c1e58b34a3a..d8d9bfde4cdd4b558992c68cb7cdaaa1b8a1212d 100755 (executable)
@@ -1,7 +1,9 @@
 #!/bin/sh
 
-USAGE='[start|bad|good|skip|next|reset|visualize|replay|log|run]'
-LONG_USAGE='git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
+USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]'
+LONG_USAGE='git bisect help
+        print this long help message.
+git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
         reset bisect state and start bisection.
 git bisect bad [<rev>]
         mark <rev> a known-bad revision.
@@ -20,7 +22,9 @@ git bisect replay <logfile>
 git bisect log
         show bisect log.
 git bisect run <cmd>...
-        use <cmd>... to automatically bisect.'
+        use <cmd>... to automatically bisect.
+
+Please use "git help bisect" to get the full man page.'
 
 OPTIONS_SPEC=
 . git-sh-setup
@@ -62,9 +66,10 @@ bisect_start() {
        # Verify HEAD. If we were bisecting before this, reset to the
        # top-of-line master first!
        #
-       head=$(GIT_DIR="$GIT_DIR" git symbolic-ref HEAD) ||
+       head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
        head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
        die "Bad HEAD - I need a HEAD"
+       start_head=''
        case "$head" in
        refs/heads/bisect)
                if [ -s "$GIT_DIR/BISECT_START" ]; then
@@ -78,7 +83,7 @@ bisect_start() {
                # This error message should only be triggered by cogito usage,
                # and cogito users should understand it relates to cg-seek.
                [ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
-               echo "${head#refs/heads/}" >"$GIT_DIR/BISECT_START"
+               start_head="${head#refs/heads/}"
                ;;
        *)
                die "Bad HEAD - strange symbolic ref"
@@ -99,6 +104,7 @@ bisect_start() {
        done
        orig_args=$(sq "$@")
        bad_seen=0
+       eval=''
        while [ $# -gt 0 ]; do
            arg="$1"
            case "$arg" in
@@ -116,13 +122,15 @@ bisect_start() {
                0) state='bad' ; bad_seen=1 ;;
                *) state='good' ;;
                esac
-               bisect_write "$state" "$rev" 'nolog'
+               eval="$eval bisect_write '$state' '$rev' 'nolog'; "
                shift
                ;;
            esac
        done
 
        sq "$@" >"$GIT_DIR/BISECT_NAMES"
+       test -n "$start_head" && echo "$start_head" >"$GIT_DIR/BISECT_START"
+       eval "$eval"
        echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG"
        bisect_auto_next
 }
@@ -151,20 +159,18 @@ bisect_state() {
                rev=$(git rev-parse --verify HEAD) ||
                        die "Bad rev input: HEAD"
                bisect_write "$state" "$rev" ;;
-       2,bad)
-               rev=$(git rev-parse --verify "$2^{commit}") ||
-                       die "Bad rev input: $2"
-               bisect_write "$state" "$rev" ;;
-       *,good|*,skip)
+       2,bad|*,good|*,skip)
                shift
-               revs=$(git rev-parse --revs-only --no-flags "$@") &&
-                       test '' != "$revs" || die "Bad rev input: $@"
-               for rev in $revs
+               eval=''
+               for rev in "$@"
                do
-                       rev=$(git rev-parse --verify "$rev^{commit}") ||
-                               die "Bad rev commit: $rev^{commit}"
-                       bisect_write "$state" "$rev"
-               done ;;
+                       sha=$(git rev-parse --verify "$rev^{commit}") ||
+                               die "Bad rev input: $rev"
+                       eval="$eval bisect_write '$state' '$sha'; "
+               done
+               eval "$eval" ;;
+       *,bad)
+               die "'git bisect bad' can take only one argument." ;;
        *)
                usage ;;
        esac
@@ -465,6 +471,8 @@ case "$#" in
     cmd="$1"
     shift
     case "$cmd" in
+    help)
+        git bisect -h ;;
     start)
         bisect_start "$@" ;;
     bad|good|skip)
index 2636159aaa7a6278d9b8fc64b853b319ac8da123..8c7fc7f6317113fcef923dcc625fb94e13fb77a0 100755 (executable)
@@ -219,6 +219,7 @@ fi
 if test -n "$2"
 then
        dir="$2"
+       test $# = 2 || die "excess parameter to git-clone"
 else
        # Derive one from the repository name
        # Try using "humanish" part of source repo if user didn't specify one
index a18235e6d053322a85805d1b07b631c6739deb93..167c3fe63a4fd9ca4abf5c16694e22a9ae22717b 100644 (file)
@@ -268,6 +268,12 @@ static inline void *xmalloc(size_t size)
        return ret;
 }
 
+/*
+ * xmemdupz() allocates (len + 1) bytes of memory, duplicates "len" bytes of
+ * "data" to the allocated memory, zero terminates the allocated memory,
+ * and returns a pointer to the allocated memory. If the allocation fails,
+ * the program dies.
+ */
 static inline void *xmemdupz(const void *data, size_t len)
 {
        char *p = xmalloc(len + 1);
@@ -329,6 +335,11 @@ static inline void *xmmap(void *start, size_t length,
        return ret;
 }
 
+/*
+ * xread() is the same a read(), but it automatically restarts read()
+ * operations with a recoverable error (EAGAIN and EINTR). xread()
+ * DOES NOT GUARANTEE that "len" bytes is read even if the data is available.
+ */
 static inline ssize_t xread(int fd, void *buf, size_t len)
 {
        ssize_t nr;
@@ -340,6 +351,11 @@ static inline ssize_t xread(int fd, void *buf, size_t len)
        }
 }
 
+/*
+ * xwrite() is the same a write(), but it automatically restarts write()
+ * operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT
+ * GUARANTEE that "len" bytes is written even if the operation is successful.
+ */
 static inline ssize_t xwrite(int fd, const void *buf, size_t len)
 {
        ssize_t nr;
index 95c5eec51ecc6ab6f142cef51eb4bd3b5842debb..bdac5d51b6dfb721bb648e455cb8f3c5078217e4 100755 (executable)
@@ -772,7 +772,7 @@ sub commit {
        waitpid($pid,0);
        die "Error running git-commit-tree: $?\n" if $?;
 
-       system("git-update-ref $remote/$branch $cid") == 0
+       system('git-update-ref', "$remote/$branch", $cid) == 0
                or die "Cannot write branch $branch for update: $!\n";
 
        if ($tag) {
index 7f632af20defd7a3cfd92008c51f7e599be13830..29dbfc940b6c8ec10c9e0eb12d08a70581fe870c 100755 (executable)
@@ -73,8 +73,8 @@
     'status'          => \&req_status,
     'admin'           => \&req_CATCHALL,
     'history'         => \&req_CATCHALL,
-    'watchers'        => \&req_CATCHALL,
-    'editors'         => \&req_CATCHALL,
+    'watchers'        => \&req_EMPTY,
+    'editors'         => \&req_EMPTY,
     'annotate'        => \&req_annotate,
     'Global_option'   => \&req_Globaloption,
     #'annotate'        => \&req_CATCHALL,
@@ -199,6 +199,11 @@ sub req_CATCHALL
     $log->warn("Unhandled command : req_$cmd : $data");
 }
 
+# This method invariably succeeds with an empty response.
+sub req_EMPTY
+{
+    print "ok\n";
+}
 
 # Root pathname \n
 #     Response expected: no. Tell the server which CVSROOT to use. Note that
@@ -958,6 +963,17 @@ sub req_update
             $meta = $updater->getmeta($filename);
         }
 
+        # If -p was given, "print" the contents of the requested revision.
+        if ( exists ( $state->{opt}{p} ) ) {
+            if ( defined ( $meta->{revision} ) ) {
+                $log->info("Printing '$filename' revision " . $meta->{revision});
+
+                transmitfile($meta->{filehash}, { print => 1 });
+            }
+
+            next;
+        }
+
        if ( ! defined $meta )
        {
            $meta = {
@@ -1091,9 +1107,9 @@ sub req_update
             my $file_local = $filepart . ".mine";
             system("ln","-s",$state->{entries}{$filename}{modified_filename}, $file_local);
             my $file_old = $filepart . "." . $oldmeta->{revision};
-            transmitfile($oldmeta->{filehash}, $file_old);
+            transmitfile($oldmeta->{filehash}, { targetfile => $file_old });
             my $file_new = $filepart . "." . $meta->{revision};
-            transmitfile($meta->{filehash}, $file_new);
+            transmitfile($meta->{filehash}, { targetfile => $file_new });
 
             # we need to merge with the local changes ( M=successful merge, C=conflict merge )
             $log->info("Merging $file_local, $file_old, $file_new");
@@ -1423,6 +1439,8 @@ sub req_status
     {
         $filename = filecleanup($filename);
 
+        next if exists($state->{opt}{l}) && index($filename, '/', length($state->{prependdir})) >= 0;
+
         my $meta = $updater->getmeta($filename);
         my $oldmeta = $meta;
 
@@ -1466,8 +1484,10 @@ sub req_status
 
         $status ||= "Unknown";
 
+        my ($filepart) = filenamesplit($filename);
+
         print "M ===================================================================\n";
-        print "M File: $filename\tStatus: $status\n";
+        print "M File: $filepart\tStatus: $status\n";
         if ( defined($state->{entries}{$filename}{revision}) )
         {
             print "M Working revision:\t" . $state->{entries}{$filename}{revision} . "\n";
@@ -1541,14 +1561,14 @@ sub req_diff
                 print "E File $filename at revision 1.$revision1 doesn't exist\n";
                 next;
             }
-            transmitfile($meta1->{filehash}, $file1);
+            transmitfile($meta1->{filehash}, { targetfile => $file1 });
         }
         # otherwise we just use the working copy revision
         else
         {
             ( undef, $file1 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 );
             $meta1 = $updater->getmeta($filename, $wrev);
-            transmitfile($meta1->{filehash}, $file1);
+            transmitfile($meta1->{filehash}, { targetfile => $file1 });
         }
 
         # if we have a second -r switch, use it too
@@ -1563,7 +1583,7 @@ sub req_diff
                 next;
             }
 
-            transmitfile($meta2->{filehash}, $file2);
+            transmitfile($meta2->{filehash}, { targetfile => $file2 });
         }
         # otherwise we just use the working copy
         else
@@ -1576,7 +1596,7 @@ sub req_diff
         {
             ( undef, $file2 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 );
             $meta2 = $updater->getmeta($filename, $wrev);
-            transmitfile($meta2->{filehash}, $file2);
+            transmitfile($meta2->{filehash}, { targetfile => $file2 });
         }
 
         # We need to have retrieved something useful
@@ -1708,8 +1728,7 @@ sub req_log
             print "M revision 1.$revision->{revision}\n";
             # reformat the date for log output
             $revision->{modified} = sprintf('%04d/%02d/%02d %s', $3, $DATE_LIST->{$2}, $1, $4 ) if ( $revision->{modified} =~ /(\d+)\s+(\w+)\s+(\d+)\s+(\S+)/ and defined($DATE_LIST->{$2}) );
-            $revision->{author} =~ s/\s+.*//;
-            $revision->{author} =~ s/^(.{8}).*/$1/;
+            $revision->{author} = cvs_author($revision->{author});
             print "M date: $revision->{modified};  author: $revision->{author};  state: " . ( $revision->{filehash} eq "deleted" ? "dead" : "Exp" ) . ";  lines: +2 -3\n";
             my $commitmessage = $updater->commitmessage($revision->{commithash});
             $commitmessage =~ s/^/M /mg;
@@ -1824,8 +1843,7 @@ sub req_annotate
                 unless ( defined ( $metadata->{$commithash} ) )
                 {
                     $metadata->{$commithash} = $updater->getmeta($filename, $commithash);
-                    $metadata->{$commithash}{author} =~ s/\s+.*//;
-                    $metadata->{$commithash}{author} =~ s/^(.{8}).*/$1/;
+                    $metadata->{$commithash}{author} = cvs_author($metadata->{$commithash}{author});
                     $metadata->{$commithash}{modified} = sprintf("%02d-%s-%02d", $1, $2, $3) if ( $metadata->{$commithash}{modified} =~ /^(\d+)\s(\w+)\s\d\d(\d\d)/ );
                 }
                 printf("M 1.%-5d      (%-8s %10s): %s\n",
@@ -2005,14 +2023,17 @@ sub revparse
     return undef;
 }
 
-# This method takes a file hash and does a CVS "file transfer" which transmits the
-# size of the file, and then the file contents.
-# If a second argument $targetfile is given, the file is instead written out to
-# a file by the name of $targetfile
+# This method takes a file hash and does a CVS "file transfer".  Its
+# exact behaviour depends on a second, optional hash table argument:
+# - If $options->{targetfile}, dump the contents to that file;
+# - If $options->{print}, use M/MT to transmit the contents one line
+#   at a time;
+# - Otherwise, transmit the size of the file, followed by the file
+#   contents.
 sub transmitfile
 {
     my $filehash = shift;
-    my $targetfile = shift;
+    my $options = shift;
 
     if ( defined ( $filehash ) and $filehash eq "deleted" )
     {
@@ -2034,11 +2055,20 @@ sub transmitfile
 
     if ( open my $fh, '-|', "git-cat-file", "blob", $filehash )
     {
-        if ( defined ( $targetfile ) )
+        if ( defined ( $options->{targetfile} ) )
         {
+            my $targetfile = $options->{targetfile};
             open NEWFILE, ">", $targetfile or die("Couldn't open '$targetfile' for writing : $!");
             print NEWFILE $_ while ( <$fh> );
             close NEWFILE or die("Failed to write '$targetfile': $!");
+        } elsif ( defined ( $options->{print} ) && $options->{print} ) {
+            while ( <$fh> ) {
+                if( /\n\z/ ) {
+                    print 'M ', $_;
+                } else {
+                    print 'MT text ', $_, "\n";
+                }
+            }
         } else {
             print "$size\n";
             print while ( <$fh> );
@@ -2107,6 +2137,16 @@ sub kopts_from_path
     }
 }
 
+# Generate a CVS author name from Git author information, by taking
+# the first eight characters of the user part of the email address.
+sub cvs_author
+{
+    my $author_line = shift;
+    (my $author) = $author_line =~ /<([^>@]{1,8})/;
+
+    $author;
+}
+
 package GITCVS::log;
 
 ####
@@ -2311,6 +2351,14 @@ sub new
 
     bless $self, $class;
 
+    $self->{valid_tables} = {'revision' => 1,
+                             'revision_ix1' => 1,
+                             'revision_ix2' => 1,
+                             'head' => 1,
+                             'head_ix1' => 1,
+                             'properties' => 1,
+                             'commitmsgs' => 1};
+
     $self->{module} = $module;
     $self->{git_path} = $config . "/";
 
@@ -2326,6 +2374,8 @@ sub new
         $cfg->{gitcvs}{dbuser} || "";
     $self->{dbpass} = $cfg->{gitcvs}{$state->{method}}{dbpass} ||
         $cfg->{gitcvs}{dbpass} || "";
+    $self->{dbtablenameprefix} = $cfg->{gitcvs}{$state->{method}}{dbtablenameprefix} ||
+        $cfg->{gitcvs}{dbtablenameprefix} || "";
     my %mapping = ( m => $module,
                     a => $state->{method},
                     u => getlogin || getpwuid($<) || $<,
@@ -2334,6 +2384,8 @@ sub new
                     );
     $self->{dbname} =~ s/%([mauGg])/$mapping{$1}/eg;
     $self->{dbuser} =~ s/%([mauGg])/$mapping{$1}/eg;
+    $self->{dbtablenameprefix} =~ s/%([mauGg])/$mapping{$1}/eg;
+    $self->{dbtablenameprefix} = mangle_tablename($self->{dbtablenameprefix});
 
     die "Invalid char ':' in dbdriver" if $self->{dbdriver} =~ /:/;
     die "Invalid char ';' in dbname" if $self->{dbname} =~ /;/;
@@ -2349,10 +2401,13 @@ sub new
     }
 
     # Construct the revision table if required
-    unless ( $self->{tables}{revision} )
+    unless ( $self->{tables}{$self->tablename("revision")} )
     {
+        my $tablename = $self->tablename("revision");
+        my $ix1name = $self->tablename("revision_ix1");
+        my $ix2name = $self->tablename("revision_ix2");
         $self->{dbh}->do("
-            CREATE TABLE revision (
+            CREATE TABLE $tablename (
                 name       TEXT NOT NULL,
                 revision   INTEGER NOT NULL,
                 filehash   TEXT NOT NULL,
@@ -2363,20 +2418,22 @@ sub new
             )
         ");
         $self->{dbh}->do("
-            CREATE INDEX revision_ix1
-            ON revision (name,revision)
+            CREATE INDEX $ix1name
+            ON $tablename (name,revision)
         ");
         $self->{dbh}->do("
-            CREATE INDEX revision_ix2
-            ON revision (name,commithash)
+            CREATE INDEX $ix2name
+            ON $tablename (name,commithash)
         ");
     }
 
     # Construct the head table if required
-    unless ( $self->{tables}{head} )
+    unless ( $self->{tables}{$self->tablename("head")} )
     {
+        my $tablename = $self->tablename("head");
+        my $ix1name = $self->tablename("head_ix1");
         $self->{dbh}->do("
-            CREATE TABLE head (
+            CREATE TABLE $tablename (
                 name       TEXT NOT NULL,
                 revision   INTEGER NOT NULL,
                 filehash   TEXT NOT NULL,
@@ -2387,16 +2444,17 @@ sub new
             )
         ");
         $self->{dbh}->do("
-            CREATE INDEX head_ix1
-            ON head (name)
+            CREATE INDEX $ix1name
+            ON $tablename (name)
         ");
     }
 
     # Construct the properties table if required
-    unless ( $self->{tables}{properties} )
+    unless ( $self->{tables}{$self->tablename("properties")} )
     {
+        my $tablename = $self->tablename("properties");
         $self->{dbh}->do("
-            CREATE TABLE properties (
+            CREATE TABLE $tablename (
                 key        TEXT NOT NULL PRIMARY KEY,
                 value      TEXT
             )
@@ -2404,10 +2462,11 @@ sub new
     }
 
     # Construct the commitmsgs table if required
-    unless ( $self->{tables}{commitmsgs} )
+    unless ( $self->{tables}{$self->tablename("commitmsgs")} )
     {
+        my $tablename = $self->tablename("commitmsgs");
         $self->{dbh}->do("
-            CREATE TABLE commitmsgs (
+            CREATE TABLE $tablename (
                 key        TEXT NOT NULL PRIMARY KEY,
                 value      TEXT
             )
@@ -2417,6 +2476,21 @@ sub new
     return $self;
 }
 
+=head2 tablename
+
+=cut
+sub tablename
+{
+    my $self = shift;
+    my $name = shift;
+
+    if (exists $self->{valid_tables}{$name}) {
+        return $self->{dbtablenameprefix} . $name;
+    } else {
+        return undef;
+    }
+}
+
 =head2 update
 
 =cut
@@ -2633,7 +2707,7 @@ sub update
                     };
                     $self->insert_rev($name, $head->{$name}{revision}, $hash, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
                 }
-                elsif ( $change eq "M" )
+                elsif ( $change eq "M" || $change eq "T" )
                 {
                     #$log->debug("MODIFIED $name");
                     $head->{$name} = {
@@ -2782,8 +2856,9 @@ sub insert_rev
     my $modified = shift;
     my $author = shift;
     my $mode = shift;
+    my $tablename = $self->tablename("revision");
 
-    my $insert_rev = $self->{dbh}->prepare_cached("INSERT INTO revision (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
+    my $insert_rev = $self->{dbh}->prepare_cached("INSERT INTO $tablename (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
     $insert_rev->execute($name, $revision, $filehash, $commithash, $modified, $author, $mode);
 }
 
@@ -2792,16 +2867,18 @@ sub insert_mergelog
     my $self = shift;
     my $key = shift;
     my $value = shift;
+    my $tablename = $self->tablename("commitmsgs");
 
-    my $insert_mergelog = $self->{dbh}->prepare_cached("INSERT INTO commitmsgs (key, value) VALUES (?,?)",{},1);
+    my $insert_mergelog = $self->{dbh}->prepare_cached("INSERT INTO $tablename (key, value) VALUES (?,?)",{},1);
     $insert_mergelog->execute($key, $value);
 }
 
 sub delete_head
 {
     my $self = shift;
+    my $tablename = $self->tablename("head");
 
-    my $delete_head = $self->{dbh}->prepare_cached("DELETE FROM head",{},1);
+    my $delete_head = $self->{dbh}->prepare_cached("DELETE FROM $tablename",{},1);
     $delete_head->execute();
 }
 
@@ -2815,8 +2892,9 @@ sub insert_head
     my $modified = shift;
     my $author = shift;
     my $mode = shift;
+    my $tablename = $self->tablename("head");
 
-    my $insert_head = $self->{dbh}->prepare_cached("INSERT INTO head (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
+    my $insert_head = $self->{dbh}->prepare_cached("INSERT INTO $tablename (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
     $insert_head->execute($name, $revision, $filehash, $commithash, $modified, $author, $mode);
 }
 
@@ -2824,8 +2902,9 @@ sub _headrev
 {
     my $self = shift;
     my $filename = shift;
+    my $tablename = $self->tablename("head");
 
-    my $db_query = $self->{dbh}->prepare_cached("SELECT filehash, revision, mode FROM head WHERE name=?",{},1);
+    my $db_query = $self->{dbh}->prepare_cached("SELECT filehash, revision, mode FROM $tablename WHERE name=?",{},1);
     $db_query->execute($filename);
     my ( $hash, $revision, $mode ) = $db_query->fetchrow_array;
 
@@ -2836,8 +2915,9 @@ sub _get_prop
 {
     my $self = shift;
     my $key = shift;
+    my $tablename = $self->tablename("properties");
 
-    my $db_query = $self->{dbh}->prepare_cached("SELECT value FROM properties WHERE key=?",{},1);
+    my $db_query = $self->{dbh}->prepare_cached("SELECT value FROM $tablename WHERE key=?",{},1);
     $db_query->execute($key);
     my ( $value ) = $db_query->fetchrow_array;
 
@@ -2849,13 +2929,14 @@ sub _set_prop
     my $self = shift;
     my $key = shift;
     my $value = shift;
+    my $tablename = $self->tablename("properties");
 
-    my $db_query = $self->{dbh}->prepare_cached("UPDATE properties SET value=? WHERE key=?",{},1);
+    my $db_query = $self->{dbh}->prepare_cached("UPDATE $tablename SET value=? WHERE key=?",{},1);
     $db_query->execute($value, $key);
 
     unless ( $db_query->rows )
     {
-        $db_query = $self->{dbh}->prepare_cached("INSERT INTO properties (key, value) VALUES (?,?)",{},1);
+        $db_query = $self->{dbh}->prepare_cached("INSERT INTO $tablename (key, value) VALUES (?,?)",{},1);
         $db_query->execute($key, $value);
     }
 
@@ -2869,10 +2950,11 @@ =head2 gethead
 sub gethead
 {
     my $self = shift;
+    my $tablename = $self->tablename("head");
 
     return $self->{gethead_cache} if ( defined ( $self->{gethead_cache} ) );
 
-    my $db_query = $self->{dbh}->prepare_cached("SELECT name, filehash, mode, revision, modified, commithash, author FROM head ORDER BY name ASC",{},1);
+    my $db_query = $self->{dbh}->prepare_cached("SELECT name, filehash, mode, revision, modified, commithash, author FROM $tablename ORDER BY name ASC",{},1);
     $db_query->execute();
 
     my $tree = [];
@@ -2894,8 +2976,9 @@ sub getlog
 {
     my $self = shift;
     my $filename = shift;
+    my $tablename = $self->tablename("revision");
 
-    my $db_query = $self->{dbh}->prepare_cached("SELECT name, filehash, author, mode, revision, modified, commithash FROM revision WHERE name=? ORDER BY revision DESC",{},1);
+    my $db_query = $self->{dbh}->prepare_cached("SELECT name, filehash, author, mode, revision, modified, commithash FROM $tablename WHERE name=? ORDER BY revision DESC",{},1);
     $db_query->execute($filename);
 
     my $tree = [];
@@ -2919,19 +3002,21 @@ sub getmeta
     my $self = shift;
     my $filename = shift;
     my $revision = shift;
+    my $tablename_rev = $self->tablename("revision");
+    my $tablename_head = $self->tablename("head");
 
     my $db_query;
     if ( defined($revision) and $revision =~ /^\d+$/ )
     {
-        $db_query = $self->{dbh}->prepare_cached("SELECT * FROM revision WHERE name=? AND revision=?",{},1);
+        $db_query = $self->{dbh}->prepare_cached("SELECT * FROM $tablename_rev WHERE name=? AND revision=?",{},1);
         $db_query->execute($filename, $revision);
     }
     elsif ( defined($revision) and $revision =~ /^[a-zA-Z0-9]{40}$/ )
     {
-        $db_query = $self->{dbh}->prepare_cached("SELECT * FROM revision WHERE name=? AND commithash=?",{},1);
+        $db_query = $self->{dbh}->prepare_cached("SELECT * FROM $tablename_rev WHERE name=? AND commithash=?",{},1);
         $db_query->execute($filename, $revision);
     } else {
-        $db_query = $self->{dbh}->prepare_cached("SELECT * FROM head WHERE name=?",{},1);
+        $db_query = $self->{dbh}->prepare_cached("SELECT * FROM $tablename_head WHERE name=?",{},1);
         $db_query->execute($filename);
     }
 
@@ -2947,11 +3032,12 @@ sub commitmessage
 {
     my $self = shift;
     my $commithash = shift;
+    my $tablename = $self->tablename("commitmsgs");
 
     die("Need commithash") unless ( defined($commithash) and $commithash =~ /^[a-zA-Z0-9]{40}$/ );
 
     my $db_query;
-    $db_query = $self->{dbh}->prepare_cached("SELECT value FROM commitmsgs WHERE key=?",{},1);
+    $db_query = $self->{dbh}->prepare_cached("SELECT value FROM $tablename WHERE key=?",{},1);
     $db_query->execute($commithash);
 
     my ( $message ) = $db_query->fetchrow_array;
@@ -2979,9 +3065,10 @@ sub gethistory
 {
     my $self = shift;
     my $filename = shift;
+    my $tablename = $self->tablename("revision");
 
     my $db_query;
-    $db_query = $self->{dbh}->prepare_cached("SELECT revision, filehash, commithash FROM revision WHERE name=? ORDER BY revision DESC",{},1);
+    $db_query = $self->{dbh}->prepare_cached("SELECT revision, filehash, commithash FROM $tablename WHERE name=? ORDER BY revision DESC",{},1);
     $db_query->execute($filename);
 
     return $db_query->fetchall_arrayref;
@@ -3001,9 +3088,10 @@ sub gethistorydense
 {
     my $self = shift;
     my $filename = shift;
+    my $tablename = $self->tablename("revision");
 
     my $db_query;
-    $db_query = $self->{dbh}->prepare_cached("SELECT revision, filehash, commithash FROM revision WHERE name=? AND filehash!='deleted' ORDER BY revision DESC",{},1);
+    $db_query = $self->{dbh}->prepare_cached("SELECT revision, filehash, commithash FROM $tablename WHERE name=? AND filehash!='deleted' ORDER BY revision DESC",{},1);
     $db_query->execute($filename);
 
     return $db_query->fetchall_arrayref;
@@ -3061,4 +3149,19 @@ sub mangle_dirname {
     return $dirname;
 }
 
+=head2 mangle_tablename
+
+create a string from a that is suitable to use as part of an SQL table
+name, mainly by converting all chars except \w to _
+
+=cut
+sub mangle_tablename {
+    my $tablename = shift;
+    return unless defined $tablename;
+
+    $tablename =~ s/[^\w_]/_/g;
+
+    return $tablename;
+}
+
 1;
index ee906606c410fd1e367007ac1b9a3a90cca52aee..333f6a8f3b783d46a0ce3b2f59bae152936cd11c 100755 (executable)
@@ -281,7 +281,7 @@ while read commit parents; do
                        die "Could not checkout the index"
                # files that $commit removed are now still in the working tree;
                # remove them, else they would be added again
-               git clean -q -f -x
+               git clean -d -q -f -x
                eval "$filter_tree" < /dev/null ||
                        die "tree filter failed: $filter_tree"
 
index cfe46a857e5fd319fa6eb49474ddbb37bd47c537..0ab478ef90aba9048a2de785d538da16e1bae431 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=0.9.GITGUI
+DEF_VER=0.10.GITGUI
 
 LF='
 '
index 5e97fbf03e3f79fd3be52c332e2b13f68673b39e..7c25bb98089ec4c0a57993020a30d2d1afad8dae 100755 (executable)
@@ -1846,6 +1846,22 @@ proc add_range_to_selection {w x y} {
        $w tag add in_sel $begin.0 [expr {$end + 1}].0
 }
 
+proc show_more_context {} {
+       global repo_config
+       if {$repo_config(gui.diffcontext) < 99} {
+               incr repo_config(gui.diffcontext)
+               reshow_diff
+       }
+}
+
+proc show_less_context {} {
+       global repo_config
+       if {$repo_config(gui.diffcontext) >= 1} {
+               incr repo_config(gui.diffcontext) -1
+               reshow_diff
+       }
+}
+
 ######################################################################
 ##
 ## ui construction
@@ -2046,6 +2062,16 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {
 
        .mbar.commit add separator
 
+       .mbar.commit add command -label [mc "Show Less Context"] \
+               -command show_less_context \
+               -accelerator $M1T-\-
+
+       .mbar.commit add command -label [mc "Show More Context"] \
+               -command show_more_context \
+               -accelerator $M1T-=
+
+       .mbar.commit add separator
+
        .mbar.commit add command -label [mc "Sign Off"] \
                -command do_signoff \
                -accelerator $M1T-S
@@ -2593,17 +2619,11 @@ lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]
 $ctxm add separator
 $ctxm add command \
        -label [mc "Show Less Context"] \
-       -command {if {$repo_config(gui.diffcontext) >= 1} {
-               incr repo_config(gui.diffcontext) -1
-               reshow_diff
-       }}
+       -command show_less_context
 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
 $ctxm add command \
        -label [mc "Show More Context"] \
-       -command {if {$repo_config(gui.diffcontext) < 99} {
-               incr repo_config(gui.diffcontext)
-               reshow_diff
-       }}
+       -command show_more_context
 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
 $ctxm add separator
 $ctxm add command \
@@ -2695,6 +2715,11 @@ bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
 bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
 bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
 bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
+bind $ui_comm <$M1B-Key-minus> {show_less_context;break}
+bind $ui_comm <$M1B-Key-KP_Subtract> {show_less_context;break}
+bind $ui_comm <$M1B-Key-equal> {show_more_context;break}
+bind $ui_comm <$M1B-Key-plus> {show_more_context;break}
+bind $ui_comm <$M1B-Key-KP_Add> {show_more_context;break}
 
 bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
 bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
@@ -2738,6 +2763,11 @@ bind .   <$M1B-Key-t> do_add_selection
 bind .   <$M1B-Key-T> do_add_selection
 bind .   <$M1B-Key-i> do_add_all
 bind .   <$M1B-Key-I> do_add_all
+bind .   <$M1B-Key-minus> {show_less_context;break}
+bind .   <$M1B-Key-KP_Subtract> {show_less_context;break}
+bind .   <$M1B-Key-equal> {show_more_context;break}
+bind .   <$M1B-Key-plus> {show_more_context;break}
+bind .   <$M1B-Key-KP_Add> {show_more_context;break}
 bind .   <$M1B-Key-Return> do_commit
 foreach i [list $ui_index $ui_workdir] {
        bind $i <Button-1>       "toggle_or_diff         $i %x %y; break"
index d281938e33d86bf0e6dc1de479595d3a8b1ffaac..89b6d51ea011d5b0fc8a343ffdda078d659571cb 100644 (file)
@@ -9,7 +9,7 @@ msgstr ""
 "Project-Id-Version: fr\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2008-03-14 07:18+0100\n"
-"PO-Revision-Date: 2008-01-14 21:08+0100\n"
+"PO-Revision-Date: 2008-04-04 22:05+0200\n"
 "Last-Translator: Christian Couder <chriscool@tuxfamily.org>\n"
 "Language-Team: French\n"
 "MIME-Version: 1.0\n"
@@ -344,8 +344,7 @@ msgstr "Documentation en ligne"
 #: git-gui.sh:2238
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
-msgstr ""
-"fatale : pas d'infos sur le chemin %s : Fichier ou répertoire inexistant"
+msgstr "erreur fatale : pas d'infos sur le chemin %s : Fichier ou répertoire inexistant"
 
 #: git-gui.sh:2271
 msgid "Current Branch:"
@@ -630,8 +629,7 @@ msgstr "Merci de choisir une branche de suivi"
 #: lib/branch_create.tcl:140
 #, tcl-format
 msgid "Tracking branch %s is not a branch in the remote repository."
-msgstr ""
-"La branche de suivi %s n'est pas une branche dans le référentiel distant."
+msgstr "La branche de suivi %s n'est pas une branche dans le référentiel distant."
 
 #: lib/branch_create.tcl:153 lib/branch_rename.tcl:86
 msgid "Please supply a branch name."
@@ -751,7 +749,7 @@ msgstr "Récupération de %s Ã  partir de %s"
 #: lib/checkout_op.tcl:127
 #, tcl-format
 msgid "fatal: Cannot resolve %s"
-msgstr "Erreur fatale : Impossible de résoudre %s"
+msgstr "erreur fatale : Impossible de résoudre %s"
 
 #: lib/checkout_op.tcl:140 lib/console.tcl:81 lib/database.tcl:31
 msgid "Close"
@@ -798,12 +796,9 @@ msgid ""
 "\n"
 "The rescan will be automatically started now.\n"
 msgstr ""
-"L'état lors de la dernière synchronisation ne correspond plus Ã  l'état du "
-"référentiel.\n"
+"L'état lors de la dernière synchronisation ne correspond plus Ã  l'état du référentiel.\n"
 "\n"
-"Un autre programme Git a modifié ce référentiel depuis la dernière "
-"synchronisation. Une resynchronisation doit Ãªtre effectuée avant de pouvoir "
-"modifier la branche courante.\n"
+"Un autre programme Git a modifié ce référentiel depuis la dernière synchronisation. Une resynchronisation doit Ãªtre effectuée avant de pouvoir modifier la branche courante.\n"
 "\n"
 "Cela va Ãªtre fait tout de suite automatiquement.\n"
 
@@ -814,13 +809,12 @@ msgstr "Mise Ã  jour du répertoire courant avec '%s'..."
 
 #: lib/checkout_op.tcl:323
 msgid "files checked out"
-msgstr ""
+msgstr "fichiers empruntés"
 
 #: lib/checkout_op.tcl:353
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
-msgstr ""
-"Emprunt de '%s' abandonné. (Il est nécessaire de fusionner des fichiers.)"
+msgstr "Emprunt de '%s' abandonné. (Il est nécessaire de fusionner des fichiers.)"
 
 #: lib/checkout_op.tcl:354
 msgid "File level merge required."
@@ -1089,8 +1083,7 @@ msgstr ""
 
 #: lib/choose_repository.tcl:880
 msgid "Cannot determine HEAD.  See console output for details."
-msgstr ""
-"Impossible de déterminer HEAD. Voir la sortie console pour plus de détails."
+msgstr "Impossible de déterminer HEAD. Voir la sortie console pour plus de détails."
 
 #: lib/choose_repository.tcl:889
 #, tcl-format
@@ -1292,32 +1285,31 @@ msgstr "attention : Tcl ne supporte pas l'encodage '%s'."
 
 #: lib/commit.tcl:221
 msgid "Calling pre-commit hook..."
-msgstr ""
+msgstr "Appel du programme externe d'avant commit..."
 
 #: lib/commit.tcl:236
 msgid "Commit declined by pre-commit hook."
-msgstr ""
+msgstr "Commit refusé par le programme externe d'avant commit."
 
 #: lib/commit.tcl:259
 msgid "Calling commit-msg hook..."
-msgstr ""
+msgstr "Appel du programme externe de message de commit..."
 
 #: lib/commit.tcl:274
 msgid "Commit declined by commit-msg hook."
-msgstr ""
+msgstr "Commit refusé par le programme externe de message de commit."
 
 #: lib/commit.tcl:287
 msgid "Committing changes..."
-msgstr ""
+msgstr "Commit des modifications..."
 
 #: lib/commit.tcl:303
 msgid "write-tree failed:"
 msgstr "write-tree a Ã©choué :"
 
 #: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
-#, fuzzy
 msgid "Commit failed."
-msgstr "Le clonage a Ã©choué."
+msgstr "Le commit a Ã©choué."
 
 #: lib/commit.tcl:321
 #, tcl-format
@@ -1479,8 +1471,7 @@ msgstr "Erreur lors du chargement des différences :"
 
 #: lib/diff.tcl:303
 msgid "Failed to unstage selected hunk."
-msgstr ""
-"La suppression dans le pré-commit de la section sélectionnée a Ã©chouée."
+msgstr "La suppression dans le pré-commit de la section sélectionnée a Ã©chouée."
 
 #: lib/diff.tcl:310
 msgid "Failed to stage selected hunk."
@@ -1510,8 +1501,7 @@ msgstr "Erreur de pré-commit"
 msgid ""
 "Updating the Git index failed.  A rescan will be automatically started to "
 "resynchronize git-gui."
-msgstr ""
-"Le pré-commit a Ã©choué. Une resynchronisation va Ãªtre lancée automatiquement."
+msgstr "Le pré-commit a Ã©choué. Une resynchronisation va Ãªtre lancée automatiquement."
 
 #: lib/index.tcl:27
 msgid "Continue"
@@ -1527,9 +1517,8 @@ msgid "Unstaging %s from commit"
 msgstr "Supprimer %s du commit"
 
 #: lib/index.tcl:313
-#, fuzzy
 msgid "Ready to commit."
-msgstr "Pré-commité"
+msgstr "Prêt ÃƒÂ  ÃƒÂªtre commité."
 
 #: lib/index.tcl:326
 #, tcl-format
@@ -1627,9 +1616,9 @@ msgid "%s of %s"
 msgstr "%s de %s"
 
 #: lib/merge.tcl:119
-#, fuzzy, tcl-format
+#, tcl-format
 msgid "Merging %s and %s..."
-msgstr "Fusion de %s et %s"
+msgstr "Fusion de %s et %s..."
 
 #: lib/merge.tcl:130
 msgid "Merge completed successfully."
@@ -1693,9 +1682,8 @@ msgid "Aborting"
 msgstr "Abandon"
 
 #: lib/merge.tcl:238
-#, fuzzy
 msgid "files reset"
-msgstr "fichiers"
+msgstr "fichiers réinitialisés"
 
 #: lib/merge.tcl:265
 msgid "Abort failed."
@@ -1759,9 +1747,8 @@ msgid "Number of Diff Context Lines"
 msgstr "Nombre de lignes de contexte dans les diffs"
 
 #: lib/option.tcl:127
-#, fuzzy
 msgid "Commit Message Text Width"
-msgstr "Message de commit :"
+msgstr "Largeur du texte de message de commit"
 
 #: lib/option.tcl:128
 msgid "New Branch Name Template"
@@ -1769,7 +1756,7 @@ msgstr "Nouveau modèle de nom de branche"
 
 #: lib/option.tcl:192
 msgid "Spelling Dictionary:"
-msgstr ""
+msgstr "Dictionnaire d'orthographe :"
 
 #: lib/option.tcl:216
 msgid "Change Font"
@@ -1898,40 +1885,40 @@ msgstr "Impossible d'écrire l'icône :"
 
 #: lib/spellcheck.tcl:57
 msgid "Unsupported spell checker"
-msgstr ""
+msgstr "Vérificateur d'orthographe non supporté"
 
 #: lib/spellcheck.tcl:65
 msgid "Spell checking is unavailable"
-msgstr ""
+msgstr "La vérification d'orthographe n'est pas disponible"
 
 #: lib/spellcheck.tcl:68
 msgid "Invalid spell checking configuration"
-msgstr ""
+msgstr "Configuration de vérification d'orthographe invalide"
 
 #: lib/spellcheck.tcl:70
 #, tcl-format
 msgid "Reverting dictionary to %s."
-msgstr ""
+msgstr "Réinitialisation du dictionnaire Ã  %s."
 
 #: lib/spellcheck.tcl:73
 msgid "Spell checker silently failed on startup"
-msgstr ""
+msgstr "La vérification d'orthographe a Ã©chouée silentieusement au démarrage"
 
 #: lib/spellcheck.tcl:80
 msgid "Unrecognized spell checker"
-msgstr ""
+msgstr "Vérificateur d'orthographe non reconnu"
 
 #: lib/spellcheck.tcl:180
 msgid "No Suggestions"
-msgstr ""
+msgstr "Aucune suggestion"
 
 #: lib/spellcheck.tcl:381
 msgid "Unexpected EOF from spell checker"
-msgstr ""
+msgstr "Fin de fichier innatendue envoyée par le vérificateur d'orthographe"
 
 #: lib/spellcheck.tcl:385
 msgid "Spell Checker Failed"
-msgstr ""
+msgstr "Le vérificateur d'orthographe a Ã©choué"
 
 #: lib/status_bar.tcl:83
 #, tcl-format
@@ -2002,3 +1989,4 @@ msgstr "Utiliser des petits paquets (pour les connexions lentes)"
 #: lib/transport.tcl:168
 msgid "Include tags"
 msgstr "Inclure les marques"
+
index 60c458f201f8f98bb1c6e115c009525659c2ee20..9b13b833cb5762542848ee3e85e23d3ca0f76fa6 100755 (executable)
@@ -309,22 +309,42 @@ then
        }
 fi
 
-# If the branch to rebase is given, first switch to it.
+# If the branch to rebase is given, that is the branch we will rebase
+# $branch_name -- branch being rebased, or HEAD (already detached)
+# $orig_head -- commit object name of tip of the branch before rebasing
+# $head_name -- refs/heads/<that-branch> or "detached HEAD"
+switch_to=
 case "$#" in
 2)
+       # Is it "rebase other $branchname" or "rebase other $commit"?
        branch_name="$2"
-       git-checkout "$2" || usage
+       switch_to="$2"
+
+       if git show-ref --verify --quiet -- "refs/heads/$2" &&
+          branch=$(git rev-parse --verify "refs/heads/$2" 2>/dev/null)
+       then
+               head_name="refs/heads/$2"
+       elif branch=$(git rev-parse --verify "$2" 2>/dev/null)
+       then
+               head_name="detached HEAD"
+       else
+               usage
+       fi
        ;;
 *)
+       # Do not need to switch branches, we are already on it.
        if branch_name=`git symbolic-ref -q HEAD`
        then
+               head_name=$branch_name
                branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'`
        else
+               head_name="detached HEAD"
                branch_name=HEAD ;# detached
        fi
+       branch=$(git rev-parse --verify "${branch_name}^0") || exit
        ;;
 esac
-branch=$(git rev-parse --verify "${branch_name}^0") || exit
+orig_head=$branch
 
 # Now we are rebasing commits $upstream..$branch on top of $onto
 
@@ -335,6 +355,8 @@ if test "$upstream" = "$onto" && test "$mb" = "$onto" &&
        # linear history?
        ! git rev-list --parents "$onto".."$branch" | grep " .* " > /dev/null
 then
+       # Lazily switch to the target branch if needed...
+       test -z "$switch_to" || git checkout "$switch_to"
        echo >&2 "Current branch $branch_name is up to date."
        exit 0
 fi
@@ -346,22 +368,11 @@ then
        GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
 fi
 
-# move to a detached HEAD
-orig_head=$(git rev-parse HEAD^0)
-head_name=$(git symbolic-ref HEAD 2> /dev/null)
-case "$head_name" in
-'')
-       head_name="detached HEAD"
-       ;;
-*)
-       git checkout "$orig_head" > /dev/null 2>&1 ||
-               die "could not detach HEAD"
-       ;;
-esac
-
-# Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
+# Detach HEAD and reset the tree
 echo "First, rewinding head to replay your work on top of it..."
-git-reset --hard "$onto"
+git checkout "$onto^0" >/dev/null 2>&1 ||
+       die "could not detach HEAD"
+# git reset --hard "$onto^0"
 
 # If the $onto is a proper descendant of the tip of the branch, then
 # we just fast forwarded.
@@ -374,7 +385,8 @@ fi
 
 if test -z "$do_merge"
 then
-       git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
+       git format-patch -k --stdout --full-index --ignore-if-in-upstream \
+               "$upstream..$orig_head" |
        git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
        move_to_original_branch
        ret=$?
@@ -397,7 +409,7 @@ echo "$orig_head" > "$dotest/orig-head"
 echo "$head_name" > "$dotest/head-name"
 
 msgnum=0
-for cmt in `git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD`
+for cmt in `git rev-list --reverse --no-merges "$upstream..$orig_head"`
 do
        msgnum=$(($msgnum + 1))
        echo "$cmt" > "$dotest/cmt.$msgnum"
index be4a20d7cd562e9b00d93fe71313aed70afe50af..9e568bf9c012b610fd8db7e4444cb7ff8c6e0a0f 100755 (executable)
@@ -168,7 +168,8 @@ sub format_2822_time {
 # Example reply to:
 #$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>';
 
-my $repo = Git->repository();
+my $repo = eval { Git->repository() };
+my @repo = $repo ? ($repo) : ();
 my $term = eval {
        $ENV{"GIT_SEND_EMAIL_NOTTY"}
                ? new Term::ReadLine 'git-send-email', \*STDIN, \*STDOUT
@@ -271,25 +272,25 @@ sub read_config {
 
        foreach my $setting (keys %config_bool_settings) {
                my $target = $config_bool_settings{$setting}->[0];
-               $$target = $repo->config_bool("$prefix.$setting") unless (defined $$target);
+               $$target = Git::config_bool(@repo, "$prefix.$setting") unless (defined $$target);
        }
 
        foreach my $setting (keys %config_settings) {
                my $target = $config_settings{$setting};
                if (ref($target) eq "ARRAY") {
                        unless (@$target) {
-                               my @values = $repo->config("$prefix.$setting");
+                               my @values = Git::config(@repo, "$prefix.$setting");
                                @$target = @values if (@values && defined $values[0]);
                        }
                }
                else {
-                       $$target = $repo->config("$prefix.$setting") unless (defined $$target);
+                       $$target = Git::config(@repo, "$prefix.$setting") unless (defined $$target);
                }
        }
 }
 
 # read configuration from [sendemail "$identity"], fall back on [sendemail]
-$identity = $repo->config("sendemail.identity") unless (defined $identity);
+$identity = Git::config(@repo, "sendemail.identity") unless (defined $identity);
 read_config("sendemail.$identity") if (defined $identity);
 read_config("sendemail");
 
@@ -327,8 +328,9 @@ sub read_config {
        }
 }
 
-my ($repoauthor) = $repo->ident_person('author');
-my ($repocommitter) = $repo->ident_person('committer');
+my ($repoauthor, $repocommitter);
+($repoauthor) = Git::ident_person(@repo, 'author');
+($repocommitter) = Git::ident_person(@repo, 'committer');
 
 # Verify the user input
 
@@ -415,7 +417,7 @@ sub read_config {
 
 my $prompting = 0;
 if (!defined $sender) {
-       $sender = $repoauthor || $repocommitter;
+       $sender = $repoauthor || $repocommitter || '';
 
        while (1) {
                $_ = $term->readline("Who should the emails appear to be from? [$sender] ");
@@ -509,7 +511,7 @@ sub expand_aliases {
 EOT
        close(C);
 
-       my $editor = $ENV{GIT_EDITOR} || $repo->config("core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+       my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
        system('sh', '-c', '$0 $@', $editor, $compose_filename);
 
        open(C2,">",$compose_filename . ".final")
index 56ec3536e0c40190329b9867b46e36753f30efb2..67f7a28cb30ae5cb16e5b5f7d947e00621267ff5 100755 (executable)
@@ -300,7 +300,7 @@ cmd_update()
                        continue
                fi
 
-               if ! test -d "$path"/.git
+               if ! test -d "$path"/.git -o -f "$path"/.git
                then
                        module_clone "$path" "$url" || exit
                        subsha1=
@@ -327,7 +327,8 @@ set_name_rev () {
                cd "$1" && {
                        git describe "$2" 2>/dev/null ||
                        git describe --tags "$2" 2>/dev/null ||
-                       git describe --contains --tags "$2"
+                       git describe --contains "$2" 2>/dev/null ||
+                       git describe --all --always "$2"
                }
        ) )
        test -z "$revname" || revname=" ($revname)"
@@ -342,6 +343,7 @@ set_name_rev () {
 #
 cmd_summary() {
        summary_limit=-1
+       for_status=
 
        # parse $args after "submodule ... summary".
        while test $# -ne 0
@@ -350,6 +352,9 @@ cmd_summary() {
                --cached)
                        cached="$1"
                        ;;
+               --for-status)
+                       for_status="$1"
+                       ;;
                -n|--summary-limit)
                        if summary_limit=$(($2 + 0)) 2>/dev/null && test "$summary_limit" = "$2"
                        then
@@ -397,7 +402,8 @@ cmd_summary() {
                done
        )
 
-       test -n "$modules" &&
+       test -z "$modules" && return
+
        git diff-index $cached --raw $head -- $modules |
        grep -e '^:160000' -e '^:[0-7]* 160000' |
        cut -c2- |
@@ -499,7 +505,14 @@ cmd_summary() {
                        echo
                fi
                echo
-       done
+       done |
+       if test -n "$for_status"; then
+               echo "# Modified submodules:"
+               echo "#"
+               sed -e 's|^|# |' -e 's|^# $|#|'
+       else
+               cat
+       fi
 }
 #
 # List all submodules, prefixed with:
@@ -542,7 +555,7 @@ cmd_status()
        do
                name=$(module_name "$path") || exit
                url=$(git config submodule."$name".url)
-               if test -z "$url" || ! test -d "$path"/.git
+               if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git
                then
                        say "-$sha1 $path"
                        continue;
index 1b44cbe10682401b9296d19f7a3595819e60702b..e47b1ea6c1c19d3b5b9bf26ecbf6b0a0f32d08d6 100755 (executable)
@@ -410,10 +410,12 @@ sub cmd_dcommit {
        $head ||= 'HEAD';
        my @refs;
        my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs);
-       print "Committing to $url ...\n";
+       if ($url) {
+               print "Committing to $url ...\n";
+       }
        unless ($gs) {
                die "Unable to determine upstream SVN information from ",
-                   "$head history\n";
+                   "$head history.\nPerhaps the repository is empty.";
        }
        my $last_rev;
        my ($linear_refs, $parents) = linearize_history($gs, \@refs);
@@ -612,7 +614,7 @@ sub cmd_create_ignore {
                print GITIGNORE "$s\n";
                close(GITIGNORE)
                  or fatal("Failed to close `$ignore': $!");
-               command_noisy('add', $ignore);
+               command_noisy('add', '-f', $ignore);
        });
 }
 
@@ -1120,7 +1122,7 @@ sub cmt_metadata {
 
 sub working_head_info {
        my ($head, $refs) = @_;
-       my @args = ('log', '--no-color', '--first-parent');
+       my @args = ('log', '--no-color', '--first-parent', '--pretty=medium');
        my ($fh, $ctx) = command_output_pipe(@args, $head);
        my $hash;
        my %max;
@@ -2239,12 +2241,13 @@ sub find_parent_branch {
                # just grow a tail if we're not unique enough :x
                $ref_id .= '-' while find_ref($ref_id);
                print STDERR "Initializing parent: $ref_id\n";
-               my ($u, $p) = ($new_url, '');
+               my ($u, $p, $repo_id) = ($new_url, '', $ref_id);
                if ($u =~ s#^\Q$url\E(/|$)##) {
                        $p = $u;
                        $u = $url;
+                       $repo_id = $self->{repo_id};
                }
-               $gs = Git::SVN->init($u, $p, $self->{repo_id}, $ref_id, 1);
+               $gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1);
        }
        my ($r0, $parent) = $gs->find_rev_before($r, 1);
        if (!defined $r0 || !defined $parent) {
@@ -2374,8 +2377,7 @@ sub check_author {
        my ($author) = @_;
        if (!defined $author || length $author == 0) {
                $author = '(no author)';
-       }
-       if (defined $::_authors && ! defined $::users{$author}) {
+       } elsif (defined $::_authors && ! defined $::users{$author}) {
                die "Author: $author not defined in $::_authors file\n";
        }
        $author;
@@ -2426,13 +2428,15 @@ sub make_log_entry {
                        $name_field = $1;
                }
                if (!defined $name_field) {
-                       #
+                       if (!defined $email) {
+                               $email = $name;
+                       }
                } elsif ($name_field =~ /(.*?)\s+<(.*)>/) {
                        ($name, $email) = ($1, $2);
                } elsif ($name_field =~ /(.*)@/) {
                        ($name, $email) = ($1, $name_field);
                } else {
-                       ($name, $email) = ($name_field, 'unknown');
+                       ($name, $email) = ($name_field, $name_field);
                }
        }
        if (defined $headrev && $self->use_svm_props) {
@@ -2518,6 +2522,7 @@ sub rebuild_from_rev_db {
        my ($self, $path) = @_;
        my $r = -1;
        open my $fh, '<', $path or croak "open: $!";
+       binmode $fh or croak "binmode: $!";
        while (<$fh>) {
                length($_) == 41 or croak "inconsistent size in ($_) != 41";
                chomp($_);
@@ -2615,6 +2620,7 @@ sub rebuild {
 sub _rev_map_set {
        my ($fh, $rev, $commit) = @_;
 
+       binmode $fh or croak "binmode: $!";
        my $size = (stat($fh))[7];
        ($size % 24) == 0 or croak "inconsistent size: $size";
 
@@ -2718,6 +2724,7 @@ sub rev_map_max {
        my $map_path = $self->map_path;
        stat $map_path or return $want_commit ? (0, undef) : 0;
        sysopen(my $fh, $map_path, O_RDONLY) or croak "open: $!";
+       binmode $fh or croak "binmode: $!";
        my $size = (stat($fh))[7];
        ($size % 24) == 0 or croak "inconsistent size: $size";
 
@@ -2750,6 +2757,7 @@ sub rev_map_get {
        return undef unless -e $map_path;
 
        sysopen(my $fh, $map_path, O_RDONLY) or croak "open: $!";
+       binmode $fh or croak "binmode: $!";
        my $size = (stat($fh))[7];
        ($size % 24) == 0 or croak "inconsistent size: $size";
 
diff --git a/git.c b/git.c
index b7729d72cf8dcec32215e968d39dcf675302e216..89b431fa28162adbd0f64d7a036a71b5bfce9eee 100644 (file)
--- a/git.c
+++ b/git.c
@@ -148,8 +148,9 @@ static int handle_alias(int *argcp, const char ***argv)
        const char** new_argv;
        const char *alias_command;
        char *alias_string;
+       int unused_nongit;
 
-       subdir = setup_git_directory_gently(NULL);
+       subdir = setup_git_directory_gently(&unused_nongit);
 
        alias_command = (*argv)[0];
        alias_string = alias_lookup(alias_command);
@@ -346,7 +347,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "shortlog", cmd_shortlog, USE_PAGER },
                { "show-branch", cmd_show_branch, RUN_SETUP },
                { "show", cmd_show, RUN_SETUP | USE_PAGER },
-               { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
+               { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE | USE_PAGER },
                { "stripspace", cmd_stripspace },
                { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
                { "tag", cmd_tag, RUN_SETUP },
index 84ab02e15f6bdecdd04e8835e3aefd0875efea44..9a4d9c40cf6ec1bed01a2c2b950efb2d9887a8a5 100644 (file)
@@ -8045,11 +8045,11 @@ proc doprefs {} {
     grid $top.cdisp - -sticky w -pady 10
     label $top.bg -padx 40 -relief sunk -background $bgcolor
     button $top.bgbut -text [mc "Background"] -font optionfont \
-       -command [list choosecolor bgcolor 0 $top.bg background setbg]
+       -command [list choosecolor bgcolor {} $top.bg background setbg]
     grid x $top.bgbut $top.bg -sticky w
     label $top.fg -padx 40 -relief sunk -background $fgcolor
     button $top.fgbut -text [mc "Foreground"] -font optionfont \
-       -command [list choosecolor fgcolor 0 $top.fg foreground setfg]
+       -command [list choosecolor fgcolor {} $top.fg foreground setfg]
     grid x $top.fgbut $top.fg -sticky w
     label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
     button $top.diffoldbut -text [mc "Diff: old lines"] -font optionfont \
@@ -8069,7 +8069,7 @@ proc doprefs {} {
     grid x $top.hunksepbut $top.hunksep -sticky w
     label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor
     button $top.selbgbut -text [mc "Select bg"] -font optionfont \
-       -command [list choosecolor selectbgcolor 0 $top.selbgsep background setselbg]
+       -command [list choosecolor selectbgcolor {} $top.selbgsep background setselbg]
     grid x $top.selbgbut $top.selbgsep -sticky w
 
     label $top.cfont -text [mc "Fonts: press to choose"]
index 9cd5b0a2b111de7e252c407d1ad77e05722d8385..f7194dbef79f3b03cc3524469c4b504a195807b6 100644 (file)
@@ -29,40 +29,40 @@ Build time configuration
 See also "How to configure gitweb for your local system" in README
 file for gitweb (in gitweb/README).
 
-- There are many configuration variables which affects building of
+- There are many configuration variables which affect building of
   gitweb.cgi; see "default configuration for gitweb" section in main
   (top dir) Makefile, and instructions for building gitweb/gitweb.cgi
   target.
 
-  One of most important is where to find git wrapper binary. Gitweb
-  tries to find git wrapper at $(bindir)/git, so you have to set $bindir
+  One of the most important is where to find the git wrapper binary. Gitweb
+  tries to find the git wrapper at $(bindir)/git, so you have to set $bindir
   when building gitweb.cgi, or $prefix from which $bindir is derived. If
-  you build and install gitweb together with the rest of git suite,
+  you build and install gitweb together with the rest of the git suite,
   there should be no problems. Otherwise, if git was for example
   installed from a binary package, you have to set $prefix (or $bindir)
   accordingly.
 
 - Another important issue is where are git repositories you want to make
-  available to gitweb. By default gitweb search for repositories under
+  available to gitweb. By default gitweb searches for repositories under
   /pub/git; if you want to have projects somewhere else, like /home/git,
   use GITWEB_PROJECTROOT build configuration variable.
 
   By default all git repositories under projectroot are visible and
-  available to gitweb. List of projects is generated by default by
+  available to gitweb. The list of projects is generated by default by
   scanning the projectroot directory for git repositories. This can be
   changed (configured) as described in "Gitweb repositories" section
   below.
 
-  Note that gitweb deals directly with object database, and does not
-  need working directory; the name of the project is the name of its
+  Note that gitweb deals directly with the object database, and does not
+  need working directory; the name of the project is the name of its
   repository object database, usually projectname.git for bare
   repositories. If you want to provide gitweb access to non-bare (live)
-  repository, you can make projectname.git symbolic link under
+  repositories, you can make projectname.git a symbolic link under
   projectroot linking to projectname/.git (but it is just
   a suggestion).
 
 - You can control where gitweb tries to find its main CSS style file,
-  its favicon and logo with GITWEB_CSS, GITWEB_FAVICON and GITWEB_LOGO
+  its favicon and logo with the GITWEB_CSS, GITWEB_FAVICON and GITWEB_LOGO
   build configuration variables. By default gitweb tries to find them
   in the same directory as gitweb.cgi script.
 
@@ -91,13 +91,17 @@ Gitweb config file
 See also "Runtime gitweb configuration" section in README file
 for gitweb (in gitweb/README).
 
-- You can configure gitweb further using gitweb configuration file;
-  by default it is file named gitweb_config.perl in the same place as
-  gitweb.cgi script. You can control default place for config file
-  using GITWEB_CONFIG build configuration variable, and you can set it
-  using GITWEB_CONFIG environmental variable.
-
-- Gitweb config file is [fragment] of perl code. You can set variables
+- You can configure gitweb further using the gitweb configuration file;
+  by default this is a file named gitweb_config.perl in the same place as
+  gitweb.cgi script. You can control the default place for the config file
+  using the GITWEB_CONFIG build configuration variable, and you can set it
+  using the GITWEB_CONFIG environment variable. If this file does not
+  exist, gitweb looks for a system-wide configuration file, normally
+  /etc/gitweb.conf. You can change the default using the
+  GITWEB_CONFIG_SYSTEM build configuration variable, and override it
+  through the GITWEB_CONFIG_SYSTEM environment variable.
+
+- The gitweb config file is a fragment of perl code. You can set variables
   using "our $variable = value"; text from "#" character until the end
   of a line is ignored. See perlsyn(1) for details.
 
@@ -124,36 +128,37 @@ Gitweb repositories
 -------------------
 
 - By default all git repositories under projectroot are visible and
-  available to gitweb. List of projects is generated by default by
+  available to gitweb. The list of projects is generated by default by
   scanning the projectroot directory for git repositories (for object
   databases to be more exact).
 
-  You can provide pre-generated list of [visible] repositories,
+  You can provide pre-generated list of [visible] repositories,
   together with information about their owners (the project ownership
-  is taken from owner of repository directory otherwise), by setting
-  GITWEB_LIST build configuration variable (or $projects_list variable
-  in gitweb config file) to point to a plain file.
-
-  Each line of projects list file should consist of url-encoded path
-  to project repository database (relative to projectroot) separated
-  by space from url-encoded project owner; spaces in both project path
-  and project owner have to be encoded as either '%20' or '+'.
-
-  You can generate projects list index file using project_index action
-  (the 'TXT' link on projects list page) directly from gitweb.
-
-- By default even if project is not visible on projects list page, you
-  can view it nevertheless by hand-crafting gitweb URL. You can set
-  GITWEB_STRICT_EXPORT build configuration variable (or $strict_export
-  variable in gitweb config file) to only allow viewing of
+  defaults to the owner of the repository directory otherwise), by setting
+  the GITWEB_LIST build configuration variable (or the $projects_list
+  variable in the gitweb config file) to point to a plain file.
+
+  Each line of the projects list file should consist of the url-encoded path
+  to the project repository database (relative to projectroot), followed
+  by the url-encoded project owner on the same line (separated by a space).
+  Spaces in both project path and project owner have to be encoded as either
+  '%20' or '+'.
+
+  You can generate the projects list index file using the project_index
+  action (the 'TXT' link on projects list page) directly from gitweb.
+
+- By default, even if a project is not visible on projects list page, you
+  can view it nevertheless by hand-crafting a gitweb URL. You can set the
+  GITWEB_STRICT_EXPORT build configuration variable (or the $strict_export
+  variable in the gitweb config file) to only allow viewing of
   repositories also shown on the overview page.
 
 - Alternatively, you can configure gitweb to only list and allow
-  viewing of the explicitly exported repositories, via
-  GITWEB_EXPORT_OK build configuration variable (or $export_ok
+  viewing of the explicitly exported repositories, via the
+  GITWEB_EXPORT_OK build configuration variable (or the $export_ok
   variable in gitweb config file). If it evaluates to true, gitweb
-  show repository only if this file exists in its object database
-  (if directory has the magic file $export_ok).
+  shows repositories only if this file exists in its object database
+  (if directory has the magic file named $export_ok).
 
 Generating projects list using gitweb
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
index 2163071047a0f4a7620eebf2f4e18008f592c049..8f7ea367bae72ea3ce25b10b968554f9b842fffe 100644 (file)
@@ -2,7 +2,7 @@ GIT web Interface
 =================
 
 The one working on:
-  http://www.kernel.org/git/
+  http://git.kernel.org/
 
 From the git version 1.4.0 gitweb is bundled with git.
 
@@ -10,13 +10,13 @@ From the git version 1.4.0 gitweb is bundled with git.
 How to configure gitweb for your local system
 ---------------------------------------------
 
-See also "Build time configuration" section in INSTALL
+See also the "Build time configuration" section in the INSTALL
 file for gitweb (in gitweb/INSTALL).
 
 You can specify the following configuration variables when building GIT:
  * GIT_BINDIR
-   Points out where to find git executable.  You should set up it to
-   the place where git binary was installed (usually /usr/bin) if you
+   Points where to find the git executable.  You should set it up to
+   the place where the git binary was installed (usually /usr/bin) if you
    don't install git from sources together with gitweb.  [Default: $(bindir)]
  * GITWEB_SITENAME
    Shown in the title of all generated pages, defaults to the server name
@@ -24,13 +24,13 @@ You can specify the following configuration variables when building GIT:
  * GITWEB_PROJECTROOT
    The root directory for all projects shown by gitweb. Must be set
    correctly for gitweb to find repositories to display.  See also
-   "Gitweb repositories" in INSTALL file for gitweb.  [Default: /pub/git]
+   "Gitweb repositories" in the INSTALL file for gitweb.  [Default: /pub/git]
  * GITWEB_PROJECT_MAXDEPTH
-   The filesystem traversing limit for getting projects list; the number
+   The filesystem traversing limit for getting the project list; the number
    is taken as depth relative to the projectroot.  It is used when
    GITWEB_LIST is a directory (or is not set; then project root is used).
    Is is meant to speed up project listing on large work trees by limiting
-   find depth.  [Default: 2007]
+   search depth.  [Default: 2007]
  * GITWEB_LIST
    Points to a directory to scan for projects (defaults to project root
    if not set / if empty) or to a file with explicit listing of projects
@@ -72,15 +72,15 @@ You can specify the following configuration variables when building GIT:
    Git base URLs used for URL to where fetch project from, i.e. full
    URL is "$git_base_url/$project".  Shown on projects summary page.
    Repository URL for project can be also configured per repository; this
-   takes precendence over URL composed from base URL and project name.
+   takes precedence over URLs composed from base URL and a project name.
    Note that you can setup multiple base URLs (for example one for
-   git:// protocol access, one for http:// access) from gitweb config
-   file.  [No default]
+   git:// protocol access, another for http:// access) from the gitweb
+   config file.  [No default]
  * GITWEB_CSS
    Points to the location where you put gitweb.css on your web server
-   (or to be more generic URI of gitweb stylesheet).  Relative to base
-   URI of gitweb.  Note that you can setup multiple stylesheets from
-   gitweb config file.  [Default: gitweb.css]
+   (or to be more generic, the URI of gitweb stylesheet).  Relative to the
+   base URI of gitweb.  Note that you can setup multiple stylesheets from
+   the gitweb config file.  [Default: gitweb.css]
  * GITWEB_LOGO
    Points to the location where you put git-logo.png on your web server
    (or to be more generic URI of logo, 72x27 size, displayed in top right
@@ -100,13 +100,20 @@ You can specify the following configuration variables when building GIT:
    is set when gitweb.cgi is executed, then the file specified in the
    environment variable will be loaded instead of the file specified
    when gitweb.cgi was created.  [Default: gitweb_config.perl]
+ * GITWEB_CONFIG_SYSTEM
+   This Perl file will be loaded using 'do' as a fallback if GITWEB_CONFIG
+   does not exist.  If the environment variable GITWEB_CONFIG_SYSTEM is set
+   when gitweb.cgi is executed, then the file specified in the environment
+   variable will be loaded instead of the file specified when gitweb.cgi was
+   created.  [Default: /etc/gitweb.conf]
 
 
 Runtime gitweb configuration
 ----------------------------
 
 You can adjust gitweb behaviour using the file specified in `GITWEB_CONFIG`
-(defaults to 'gitweb_config.perl' in the same directory as the CGI).
+(defaults to 'gitweb_config.perl' in the same directory as the CGI), and
+as a fallback `GITWEB_CONFIG_SYSTEM` (defaults to /etc/gitweb.conf).
 The most notable thing that is not configurable at compile time are the
 optional features, stored in the '%features' variable.
 
@@ -114,15 +121,15 @@ Ultimate description on how to reconfigure the default features setting
 in your `GITWEB_CONFIG` or per-project in `project.git/config` can be found
 as comments inside 'gitweb.cgi'.
 
-See also "Gitweb config file" (with example of gitweb config file), and
-"Gitweb repositories" sections in INSTALL file for gitweb.
+See also the "Gitweb config file" (with an example of config file), and
+the "Gitweb repositories" sections in INSTALL file for gitweb.
 
 
-Gitweb config file is [fragment] of perl code. You can set variables
+The gitweb config file is a fragment of perl code. You can set variables
 using "our $variable = value"; text from "#" character until the end
 of a line is ignored. See perlsyn(1) man page for details.
 
-Below there is list of vaiables which you might want to set in gitweb config.
+Below is the list of variables which you might want to set in gitweb config.
 See the top of 'gitweb.cgi' for the full list of variables and their
 descriptions.
 
@@ -133,7 +140,7 @@ You can set, among others, the following variables in gitweb config files
 (with the exception of $projectroot and $projects_list this list does
 not include variables usually directly set during build):
  * $GIT
-   Cure git executable to use.  By default set to "$GIT_BINDIR/git", which
+   Core git executable to use.  By default set to "$GIT_BINDIR/git", which
    in turn is by default set to "$(bindir)/git".  If you use git from binary
    package, set this to "/usr/bin/git".  This can just be "git" if your
    webserver has a sensible PATH.  If you have multiple git versions
@@ -169,7 +176,7 @@ not include variables usually directly set during build):
    to make it easier to upgrade gitweb. You can add 'site' stylesheet
    for example by using
       push @stylesheets, "gitweb-site.css";
-   in gitweb config file.
+   in the gitweb config file.
  * $logo_url, $logo_label
    URI and label (title) of GIT logo link (or your site logo, if you choose
    to use different logo image). By default they point to git homepage;
@@ -191,12 +198,12 @@ not include variables usually directly set during build):
    Default mimetype for blob_plain (raw) view, if mimetype checking
    doesn't result in some other type; by default 'text/plain'.
  * $default_text_plain_charset
-   Default charset for text files. If not set, web serwer configuration
+   Default charset for text files. If not set, web server configuration
    would be used.
  * $mimetypes_file
    File to use for (filename extension based) guessing of MIME types before
-   trying /etc/mime.types. Path, if relative, is taken currently as taken
-   relative to current git repositoy.
+   trying /etc/mime.types. Path, if relative, is taken currently as
+   relative to the current git repository.
  * $fallback_encoding
    Gitweb assumes this charset if line contains non-UTF-8 characters.
    Fallback decoding is used without error checking, so it can be even
@@ -225,14 +232,14 @@ You can use the following files in repository:
    single line description of a project (of a repository). Plain text file;
    HTML will be escaped. By default set to
      Unnamed repository; edit this file to name it for gitweb.
-   from the template during creating repository. You can use
+   from the template during repository creation. You can use the
    gitweb.description repo configuration variable, but the file takes
-   precendence.
+   precedence.
  * cloneurl (or multiple-valued gitweb.url)
    File with repository URL (used for clone and fetch), one per line.
    Displayed in the project summary page. You can use multiple-valued
    gitweb.url repository configuration variable for that, but the file
-   takes precendence.
+   takes precedence.
  * gitweb.owner
    You can use the gitweb.owner repository configuration variable to set
    repository's owner. It is displayed in the project list and summary
index ec73cb1256ba99a990648c548348ecdc3340a592..f83567ec39e8781cc10d3f3f3cb39c7cb6aa11ef 100755 (executable)
@@ -369,7 +369,12 @@ sub filter_snapshot_fmts {
 }
 
 our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
-do $GITWEB_CONFIG if -e $GITWEB_CONFIG;
+if (-e $GITWEB_CONFIG) {
+       do $GITWEB_CONFIG;
+} else {
+       our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++";
+       do $GITWEB_CONFIG_SYSTEM if -e $GITWEB_CONFIG_SYSTEM;
+}
 
 # version of the core git binary
 our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown";
@@ -506,7 +511,7 @@ sub evaluate_path_info {
        }
        # do not change any parameters if an action is given using the query string
        return if $action;
-       $path_info =~ s,^$project/*,,;
+       $path_info =~ s,^\Q$project\E/*,,;
        my ($refname, $pathname) = split(/:/, $path_info, 2);
        if (defined $pathname) {
                # we got "project.git/branch:filename" or "project.git/branch:dir/"
@@ -628,7 +633,7 @@ (%)
        my ($use_pathinfo) = gitweb_check_feature('pathinfo');
        if ($use_pathinfo) {
                # use PATH_INFO for project name
-               $href .= "/$params{'project'}" if defined $params{'project'};
+               $href .= "/".esc_url($params{'project'}) if defined $params{'project'};
                delete $params{'project'};
 
                # Summary just uses the project path URL
@@ -2164,7 +2169,7 @@ sub parse_difftree_raw_line {
                $res{'to_mode'} = $2;
                $res{'from_id'} = $3;
                $res{'to_id'} = $4;
-               $res{'status'} = $res{'status_str'} = $5;
+               $res{'status'} = $5;
                $res{'similarity'} = $6;
                if ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied
                        ($res{'from_file'}, $res{'to_file'}) = map { unquote($_) } split("\t", $7);
@@ -2180,7 +2185,6 @@ sub parse_difftree_raw_line {
                $res{'to_mode'} = pop @{$res{'from_mode'}};
                $res{'from_id'} = [ split(' ', $3) ];
                $res{'to_id'} = pop @{$res{'from_id'}};
-               $res{'status_str'} = $4;
                $res{'status'} = [ split('', $4) ];
                $res{'to_file'} = unquote($5);
        }
@@ -2571,7 +2575,7 @@ sub git_header_html {
                my $action = $my_uri;
                my ($use_pathinfo) = gitweb_check_feature('pathinfo');
                if ($use_pathinfo) {
-                       $action .= "/$project";
+                       $action .= "/".esc_url($project);
                } else {
                        $cgi->param("p", $project);
                }
@@ -3002,7 +3006,7 @@ sub fill_from_file_info {
 sub is_deleted {
        my $diffinfo = shift;
 
-       return $diffinfo->{'status_str'} =~ /D/;
+       return $diffinfo->{'to_id'} eq ('0' x 40);
 }
 
 # does patch correspond to [previous] difftree raw line
@@ -5172,14 +5176,26 @@ sub git_history {
        my $refs = git_get_references();
        my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
 
+       my @commitlist = parse_commits($hash_base, 101, (100 * $page),
+                                      $file_name, "--full-history");
+       if (!@commitlist) {
+               die_error('404 Not Found', "No such file or directory on given branch");
+       }
+
        if (!defined $hash && defined $file_name) {
-               $hash = git_get_hash_by_path($hash_base, $file_name);
+               # some commits could have deleted file in question,
+               # and not have it in tree, but one of them has to have it
+               for (my $i = 0; $i <= @commitlist; $i++) {
+                       $hash = git_get_hash_by_path($commitlist[$i]{'id'}, $file_name);
+                       last if defined $hash;
+               }
        }
        if (defined $hash) {
                $ftype = git_get_type($hash);
        }
-
-       my @commitlist = parse_commits($hash_base, 101, (100 * $page), $file_name, "--full-history");
+       if (!defined $ftype) {
+               die_error(undef, "Unknown type of object");
+       }
 
        my $paging_nav = '';
        if ($page > 0) {
diff --git a/help.c b/help.c
index ecaca770d398e8068ec18e9f4c5058044001d4ea..10298fb0a197f18008783214de11e424fbadb77d 100644 (file)
--- a/help.c
+++ b/help.c
@@ -30,6 +30,7 @@ static struct option builtin_help_options[] = {
                        HELP_FORMAT_WEB),
        OPT_SET_INT('i', "info", &help_format, "show info page",
                        HELP_FORMAT_INFO),
+       OPT_END(),
 };
 
 static const char * const builtin_help_usage[] = {
index 04afbc492485d95a85a7f0f57bbb48def6337a99..db6559725e6867131800b6682348ebbf896ddbf5 100644 (file)
@@ -1303,8 +1303,11 @@ main(int argc, char **argv)
                return 1;
        }
        if (!server.host) {
-               fprintf( stderr, "no imap host specified\n" );
-               return 1;
+               if (!server.tunnel) {
+                       fprintf( stderr, "no imap host specified\n" );
+                       return 1;
+               }
+               server.host = "tunnel";
        }
 
        /* read the messages */
index 5b2963998cc497177d913b6224799531d45dbb4a..d3fb0e520c749e0d57afaab7666861a1a4e7cd9f 100644 (file)
@@ -208,14 +208,13 @@ void log_write_email_headers(struct rev_info *opt, const char *name,
        *extra_headers_p = extra_headers;
 }
 
-void show_log(struct rev_info *opt, const char *sep)
+void show_log(struct rev_info *opt)
 {
        struct strbuf msgbuf;
        struct log_info *log = opt->loginfo;
        struct commit *commit = log->commit, *parent = log->parent;
        int abbrev = opt->diffopt.abbrev;
        int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
-       const char *extra;
        const char *subject = NULL, *extra_headers = opt->extra_headers;
        int need_8bit_cte = 0;
 
@@ -240,18 +239,11 @@ void show_log(struct rev_info *opt, const char *sep)
        }
 
        /*
-        * The "oneline" format has several special cases:
-        *  - The pretty-printed commit lacks a newline at the end
-        *    of the buffer, but we do want to make sure that we
-        *    have a newline there. If the separator isn't already
-        *    a newline, add an extra one.
-        *  - unlike other log messages, the one-line format does
-        *    not have an empty line between entries.
+        * If use_terminator is set, add a newline at the end of the entry.
+        * Otherwise, add a diffopt.line_termination character before all
+        * entries but the first.  (IOW, as a separator between entries)
         */
-       extra = "";
-       if (*sep != '\n' && opt->commit_format == CMIT_FMT_ONELINE)
-               extra = "\n";
-       if (opt->shown_one && opt->commit_format != CMIT_FMT_ONELINE)
+       if (opt->shown_one && !opt->use_terminator)
                putchar(opt->diffopt.line_termination);
        opt->shown_one = 1;
 
@@ -292,10 +284,8 @@ void show_log(struct rev_info *opt, const char *sep)
                        show_reflog_message(opt->reflog_info,
                                    opt->commit_format == CMIT_FMT_ONELINE,
                                    opt->date_mode);
-                       if (opt->commit_format == CMIT_FMT_ONELINE) {
-                               printf("%s", sep);
+                       if (opt->commit_format == CMIT_FMT_ONELINE)
                                return;
-                       }
                }
        }
 
@@ -318,7 +308,9 @@ void show_log(struct rev_info *opt, const char *sep)
                printf("log size %i\n", (int)msgbuf.len);
 
        if (msgbuf.len)
-               printf("%s%s%s", msgbuf.buf, extra, sep);
+               fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+       if (opt->use_terminator)
+               putchar('\n');
        strbuf_release(&msgbuf);
 }
 
@@ -340,7 +332,7 @@ int log_tree_diff_flush(struct rev_info *opt)
                 * an extra newline between the end of log and the
                 * output for readability.
                 */
-               show_log(opt, opt->diffopt.msg_sep);
+               show_log(opt);
                if ((opt->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT) &&
                    opt->verbose_header &&
                    opt->commit_format != CMIT_FMT_ONELINE) {
@@ -428,7 +420,7 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)
        shown = log_tree_diff(opt, commit, &log);
        if (!shown && opt->loginfo && opt->always_show_header) {
                log.parent = NULL;
-               show_log(opt, "");
+               show_log(opt);
                shown = 1;
        }
        opt->loginfo = NULL;
index 8946ff377ca455dd1c4efaa609ce9af382005836..59ba4c48b7966db34c6345a445ab0b10e235ac83 100644 (file)
@@ -11,7 +11,7 @@ void init_log_tree_opt(struct rev_info *);
 int log_tree_diff_flush(struct rev_info *);
 int log_tree_commit(struct rev_info *, struct commit *);
 int log_tree_opt_parse(struct rev_info *, const char **, int);
-void show_log(struct rev_info *opt, const char *sep);
+void show_log(struct rev_info *opt);
 void show_decorations(struct commit *commit);
 void log_write_email_headers(struct rev_info *opt, const char *name,
                             const char **subject_p,
diff --git a/mktag.c b/mktag.c
index b05260c83fd8ef766eb2e16fa355501bf1f62fb5..0b34341f711a903d4a12fe96dc6ef63e55fb2f5b 100644 (file)
--- a/mktag.c
+++ b/mktag.c
@@ -8,10 +8,11 @@
  * message and a signature block that git itself doesn't care about,
  * but that can be verified with gpg or similar.
  *
- * The first three lines are guaranteed to be at least 63 bytes:
+ * The first four lines are guaranteed to be at least 83 bytes:
  * "object <sha1>\n" is 48 bytes, "type tag\n" at 9 bytes is the
- * shortest possible type-line, and "tag .\n" at 6 bytes is the
- * shortest single-character-tag line.
+ * shortest possible type-line, "tag .\n" at 6 bytes is the shortest
+ * single-character-tag line, and "tagger . <> 0 +0000\n" at 20 bytes is
+ * the shortest possible tagger-line.
  */
 
 /*
@@ -43,9 +44,10 @@ static int verify_tag(char *buffer, unsigned long size)
        int typelen;
        char type[20];
        unsigned char sha1[20];
-       const char *object, *type_line, *tag_line, *tagger_line;
+       const char *object, *type_line, *tag_line, *tagger_line, *lb, *rb;
+       size_t len;
 
-       if (size < 64)
+       if (size < 84)
                return error("wanna fool me ? you obviously got the size wrong !");
 
        buffer[size] = 0;
@@ -97,11 +99,51 @@ static int verify_tag(char *buffer, unsigned long size)
        /* Verify the tagger line */
        tagger_line = tag_line;
 
-       if (memcmp(tagger_line, "tagger", 6) || (tagger_line[6] == '\n'))
-               return error("char" PD_FMT ": could not find \"tagger\"", tagger_line - buffer);
-
-       /* TODO: check for committer info + blank line? */
-       /* Also, the minimum length is probably + "tagger .", or 63+8=71 */
+       if (memcmp(tagger_line, "tagger ", 7))
+               return error("char" PD_FMT ": could not find \"tagger \"",
+                       tagger_line - buffer);
+
+       /*
+        * Check for correct form for name and email
+        * i.e. " <" followed by "> " on _this_ line
+        * No angle brackets within the name or email address fields.
+        * No spaces within the email address field.
+        */
+       tagger_line += 7;
+       if (!(lb = strstr(tagger_line, " <")) || !(rb = strstr(lb+2, "> ")) ||
+               strpbrk(tagger_line, "<>\n") != lb+1 ||
+               strpbrk(lb+2, "><\n ") != rb)
+               return error("char" PD_FMT ": malformed tagger field",
+                       tagger_line - buffer);
+
+       /* Check for author name, at least one character, space is acceptable */
+       if (lb == tagger_line)
+               return error("char" PD_FMT ": missing tagger name",
+                       tagger_line - buffer);
+
+       /* timestamp, 1 or more digits followed by space */
+       tagger_line = rb + 2;
+       if (!(len = strspn(tagger_line, "0123456789")))
+               return error("char" PD_FMT ": missing tag timestamp",
+                       tagger_line - buffer);
+       tagger_line += len;
+       if (*tagger_line != ' ')
+               return error("char" PD_FMT ": malformed tag timestamp",
+                       tagger_line - buffer);
+       tagger_line++;
+
+       /* timezone, 5 digits [+-]hhmm, max. 1400 */
+       if (!((tagger_line[0] == '+' || tagger_line[0] == '-') &&
+             strspn(tagger_line+1, "0123456789") == 4 &&
+             tagger_line[5] == '\n' && atoi(tagger_line+1) <= 1400))
+               return error("char" PD_FMT ": malformed tag timezone",
+                       tagger_line - buffer);
+       tagger_line += 6;
+
+       /* Verify the blank line separating the header from the body */
+       if (*tagger_line != '\n')
+               return error("char" PD_FMT ": trailing garbage in tag header",
+                       tagger_line - buffer);
 
        /* The actual stuff afterwards we don't care about.. */
        return 0;
index 665e2b29b8817aa6a8aa83ccd859ab51e7bb2234..c66c8af725f6d37b4edaeab75eebac7a387f4922 100644 (file)
@@ -183,7 +183,6 @@ void fixup_pack_header_footer(int pack_fd,
 
 char *index_pack_lockfile(int ip_out)
 {
-       int len, s;
        char packname[46];
 
        /*
@@ -193,11 +192,8 @@ char *index_pack_lockfile(int ip_out)
         * case, we need it to remove the corresponding .keep file
         * later on.  If we don't get that then tough luck with it.
         */
-       for (len = 0;
-                len < 46 && (s = xread(ip_out, packname+len, 46-len)) > 0;
-                len += s);
-       if (len == 46 && packname[45] == '\n' &&
-               memcmp(packname, "keep\t", 5) == 0) {
+       if (read_in_full(ip_out, packname, 46) == 46 && packname[45] == '\n' &&
+           memcmp(packname, "keep\t", 5) == 0) {
                char path[PATH_MAX];
                packname[45] = 0;
                snprintf(path, sizeof(path), "%s/pack/pack-%s.keep",
index e87cafbe4199acb39fd9113e4b5096de19a3a61c..acf3fe3a1a82cd99e7bd6e0ff9d6d3d7e344a155 100644 (file)
@@ -344,7 +344,7 @@ void usage_with_options_internal(const char * const *usagestr,
                        break;
                case OPTION_INTEGER:
                        if (opts->flags & PARSE_OPT_OPTARG)
-                               pos += fprintf(stderr, " [<n>]");
+                               pos += fprintf(stderr, "[<n>]");
                        else
                                pos += fprintf(stderr, " <n>");
                        break;
diff --git a/path.c b/path.c
index f4ed979997b6a968ba1987a199beae39035d5da2..b7c24a2aacf87ffea6bd6380dbff44e5d317b240 100644 (file)
--- a/path.c
+++ b/path.c
@@ -91,7 +91,8 @@ int validate_headref(const char *path)
        struct stat st;
        char *buf, buffer[256];
        unsigned char sha1[20];
-       int len, fd;
+       int fd;
+       ssize_t len;
 
        if (lstat(path, &st) < 0)
                return -1;
@@ -266,24 +267,25 @@ int adjust_shared_perm(const char *path)
        if (lstat(path, &st) < 0)
                return -1;
        mode = st.st_mode;
-       if (mode & S_IRUSR)
-               mode |= (shared_repository == PERM_GROUP
-                        ? S_IRGRP
-                        : (shared_repository == PERM_EVERYBODY
-                           ? (S_IRGRP|S_IROTH)
-                           : 0));
-
-       if (mode & S_IWUSR)
-               mode |= S_IWGRP;
-
-       if (mode & S_IXUSR)
-               mode |= (shared_repository == PERM_GROUP
-                        ? S_IXGRP
-                        : (shared_repository == PERM_EVERYBODY
-                           ? (S_IXGRP|S_IXOTH)
-                           : 0));
-       if (S_ISDIR(mode))
+
+       if (shared_repository) {
+               int tweak = shared_repository;
+               if (!(mode & S_IWUSR))
+                       tweak &= ~0222;
+               mode = (mode & ~0777) | tweak;
+       } else {
+               /* Preserve old PERM_UMASK behaviour */
+               if (mode & S_IWUSR)
+                       mode |= S_IWGRP;
+       }
+
+       if (S_ISDIR(mode)) {
                mode |= FORCE_DIR_SET_GID;
+
+               /* Copy read bits to execute bits */
+               mode |= (shared_repository & 0444) >> 2;
+       }
+
        if ((mode & st.st_mode) != mode && chmod(path, mode) < 0)
                return -2;
        return 0;
index a2812ea612b997c1a76d89075dd1263a3af4fd37..2e7f896baec00d644903af5d967b6c781ee3503a 100644 (file)
@@ -487,22 +487,20 @@ sub wc_chdir {
 (exception is thrown otherwise), in array context returns allows the
 variable to be set multiple times and returns all the values.
 
-Must be called on a repository instance.
-
 This currently wraps command('config') so it is not so fast.
 
 =cut
 
 sub config {
-       my ($self, $var) = @_;
-       $self->repo_path()
-               or throw Error::Simple("not a repository");
+       my ($self, $var) = _maybe_self(@_);
 
        try {
+               my @cmd = ('config');
+               unshift @cmd, $self if $self;
                if (wantarray) {
-                       return $self->command('config', '--get-all', $var);
+                       return command(@cmd, '--get-all', $var);
                } else {
-                       return $self->command_oneline('config', '--get', $var);
+                       return command_oneline(@cmd, '--get', $var);
                }
        } catch Git::Error::Command with {
                my $E = shift;
@@ -522,20 +520,17 @@ sub config {
 is usable as a boolean in perl (and C<undef> if it's not defined,
 of course).
 
-Must be called on a repository instance.
-
 This currently wraps command('config') so it is not so fast.
 
 =cut
 
 sub config_bool {
-       my ($self, $var) = @_;
-       $self->repo_path()
-               or throw Error::Simple("not a repository");
+       my ($self, $var) = _maybe_self(@_);
 
        try {
-               my $val = $self->command_oneline('config', '--bool', '--get',
-                                             $var);
+               my @cmd = ('config', '--bool', '--get', $var);
+               unshift @cmd, $self if $self;
+               my $val = command_oneline(@cmd);
                return undef unless defined $val;
                return $val eq 'true';
        } catch Git::Error::Command with {
@@ -557,19 +552,17 @@ sub config_bool {
 by 1024, 1048576 (1024^2), or 1073741824 (1024^3) prior to output.
 It would return C<undef> if configuration variable is not defined,
 
-Must be called on a repository instance.
-
 This currently wraps command('config') so it is not so fast.
 
 =cut
 
 sub config_int {
-       my ($self, $var) = @_;
-       $self->repo_path()
-               or throw Error::Simple("not a repository");
+       my ($self, $var) = _maybe_self(@_);
 
        try {
-               return $self->command_oneline('config', '--int', '--get', $var);
+               my @cmd = ('config', '--int', '--get', $var);
+               unshift @cmd, $self if $self;
+               return command_oneline(@cmd);
        } catch Git::Error::Command with {
                my $E = shift;
                if ($E->value() == 1) {
@@ -639,15 +632,15 @@ sub get_color {
        "$name <$email>" eq ident_person($name);
        $time_tz =~ /^\d+ [+-]\d{4}$/;
 
-Both methods must be called on a repository instance.
-
 =cut
 
 sub ident {
-       my ($self, $type) = @_;
+       my ($self, $type) = _maybe_self(@_);
        my $identstr;
        if (lc $type eq lc 'committer' or lc $type eq lc 'author') {
-               $identstr = $self->command_oneline('var', 'GIT_'.uc($type).'_IDENT');
+               my @cmd = ('var', 'GIT_'.uc($type).'_IDENT');
+               unshift @cmd, $self if $self;
+               $identstr = command_oneline(@cmd);
        } else {
                $identstr = $type;
        }
@@ -659,8 +652,8 @@ sub ident {
 }
 
 sub ident_person {
-       my ($self, @ident) = @_;
-       $#ident == 0 and @ident = $self->ident($ident[0]);
+       my ($self, @ident) = _maybe_self(@_);
+       $#ident == 0 and @ident = $self ? $self->ident($ident[0]) : ident($ident[0]);
        return "$ident[0] <$ident[1]>";
 }
 
index 355546a1ad844492234dc7ee91528c525af5610a..f5d00863a6234c16db33637d19fefd2014780e87 100644 (file)
@@ -65,16 +65,11 @@ void packet_write(int fd, const char *fmt, ...)
 
 static void safe_read(int fd, void *buffer, unsigned size)
 {
-       size_t n = 0;
-
-       while (n < size) {
-               ssize_t ret = xread(fd, (char *) buffer + n, size - n);
-               if (ret < 0)
-                       die("read error (%s)", strerror(errno));
-               if (!ret)
-                       die("The remote end hung up unexpectedly");
-               n += ret;
-       }
+       ssize_t ret = read_in_full(fd, buffer, size);
+       if (ret < 0)
+               die("read error (%s)", strerror(errno));
+       else if (ret < size)
+               die("The remote end hung up unexpectedly");
 }
 
 int packet_read_line(int fd, char *buffer, unsigned size)
index 16bfb86cd3ce6d6b471cdc313114563ca78837dc..687293224c3bca20da413d132e17a436e2c79990 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -4,40 +4,49 @@
 #include "diff.h"
 #include "revision.h"
 
-static struct cmt_fmt_map {
-       const char *n;
-       size_t cmp_len;
-       enum cmit_fmt v;
-} cmt_fmts[] = {
-       { "raw",        1,      CMIT_FMT_RAW },
-       { "medium",     1,      CMIT_FMT_MEDIUM },
-       { "short",      1,      CMIT_FMT_SHORT },
-       { "email",      1,      CMIT_FMT_EMAIL },
-       { "full",       5,      CMIT_FMT_FULL },
-       { "fuller",     5,      CMIT_FMT_FULLER },
-       { "oneline",    1,      CMIT_FMT_ONELINE },
-       { "format:",    7,      CMIT_FMT_USERFORMAT},
-};
-
 static char *user_format;
 
-enum cmit_fmt get_commit_format(const char *arg)
+void get_commit_format(const char *arg, struct rev_info *rev)
 {
        int i;
-
-       if (!arg || !*arg)
-               return CMIT_FMT_DEFAULT;
+       static struct cmt_fmt_map {
+               const char *n;
+               size_t cmp_len;
+               enum cmit_fmt v;
+       } cmt_fmts[] = {
+               { "raw",        1,      CMIT_FMT_RAW },
+               { "medium",     1,      CMIT_FMT_MEDIUM },
+               { "short",      1,      CMIT_FMT_SHORT },
+               { "email",      1,      CMIT_FMT_EMAIL },
+               { "full",       5,      CMIT_FMT_FULL },
+               { "fuller",     5,      CMIT_FMT_FULLER },
+               { "oneline",    1,      CMIT_FMT_ONELINE },
+       };
+
+       rev->use_terminator = 0;
+       if (!arg || !*arg) {
+               rev->commit_format = CMIT_FMT_DEFAULT;
+               return;
+       }
        if (*arg == '=')
                arg++;
-       if (!prefixcmp(arg, "format:")) {
+       if (!prefixcmp(arg, "format:") || !prefixcmp(arg, "tformat:")) {
+               const char *cp = strchr(arg, ':') + 1;
                free(user_format);
-               user_format = xstrdup(arg + 7);
-               return CMIT_FMT_USERFORMAT;
+               user_format = xstrdup(cp);
+               if (arg[0] == 't')
+                       rev->use_terminator = 1;
+               rev->commit_format = CMIT_FMT_USERFORMAT;
+               return;
        }
        for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
                if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) &&
-                   !strncmp(arg, cmt_fmts[i].n, strlen(arg)))
-                       return cmt_fmts[i].v;
+                   !strncmp(arg, cmt_fmts[i].n, strlen(arg))) {
+                       if (cmt_fmts[i].v == CMIT_FMT_ONELINE)
+                               rev->use_terminator = 1;
+                       rev->commit_format = cmt_fmts[i].v;
+                       return;
+               }
        }
 
        die("invalid --pretty format: %s", arg);
@@ -457,6 +466,7 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
        const struct commit *commit = c->commit;
        const char *msg = commit->buffer;
        struct commit_list *p;
+       int h1, h2;
 
        /* these are independent of the commit */
        switch (placeholder[0]) {
@@ -478,6 +488,16 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
        case 'n':               /* newline */
                strbuf_addch(sb, '\n');
                return 1;
+       case 'x':
+               /* %x00 == NUL, %x0a == LF, etc. */
+               if (0 <= (h1 = hexval_table[0xff & placeholder[1]]) &&
+                   h1 <= 16 &&
+                   0 <= (h2 = hexval_table[0xff & placeholder[2]]) &&
+                   h2 <= 16) {
+                       strbuf_addch(sb, (h1<<4)|h2);
+                       return 3;
+               } else
+                       return 0;
        }
 
        /* these depend on the commit */
index f83ae87e150ff93728da989f1d35ce0ad7c10f60..828d49001d6c8494ede35e4ebbcea4fe482802dd 100644 (file)
@@ -10,6 +10,7 @@
 static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
 
 static int deny_non_fast_forwards = 0;
+static int receive_fsck_objects;
 static int receive_unpack_limit = -1;
 static int transfer_unpack_limit = -1;
 static int unpack_limit = 100;
@@ -35,6 +36,11 @@ static int receive_pack_config(const char *var, const char *value)
                return 0;
        }
 
+       if (strcmp(var, "receive.fsckobjects") == 0) {
+               receive_fsck_objects = git_config_bool(var, value);
+               return 0;
+       }
+
        return git_default_config(var, value);
 }
 
@@ -368,11 +374,13 @@ static const char *unpack(void)
                        ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries));
 
        if (ntohl(hdr.hdr_entries) < unpack_limit) {
-               int code;
-               const char *unpacker[3];
-               unpacker[0] = "unpack-objects";
-               unpacker[1] = hdr_arg;
-               unpacker[2] = NULL;
+               int code, i = 0;
+               const char *unpacker[4];
+               unpacker[i++] = "unpack-objects";
+               if (receive_fsck_objects)
+                       unpacker[i++] = "--strict";
+               unpacker[i++] = hdr_arg;
+               unpacker[i++] = NULL;
                code = run_command_v_opt(unpacker, RUN_GIT_CMD);
                switch (code) {
                case 0:
@@ -393,8 +401,8 @@ static const char *unpack(void)
                        return "unpacker exited with error code";
                }
        } else {
-               const char *keeper[6];
-               int s, status;
+               const char *keeper[7];
+               int s, status, i = 0;
                char keep_arg[256];
                struct child_process ip;
 
@@ -402,12 +410,14 @@ static const char *unpack(void)
                if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
                        strcpy(keep_arg + s, "localhost");
 
-               keeper[0] = "index-pack";
-               keeper[1] = "--stdin";
-               keeper[2] = "--fix-thin";
-               keeper[3] = hdr_arg;
-               keeper[4] = keep_arg;
-               keeper[5] = NULL;
+               keeper[i++] = "index-pack";
+               keeper[i++] = "--stdin";
+               if (receive_fsck_objects)
+                       keeper[i++] = "--strict";
+               keeper[i++] = "--fix-thin";
+               keeper[i++] = hdr_arg;
+               keeper[i++] = keep_arg;
+               keeper[i++] = NULL;
                memset(&ip, 0, sizeof(ip));
                ip.argv = keeper;
                ip.out = -1;
diff --git a/refs.c b/refs.c
index 1b0050eee4e037aec8396edd0ff6c14bda5985a2..9b495eb16ef03b14791aa44e1098cd918859f0cc 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -352,6 +352,7 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re
 {
        int len = strlen(path), retval;
        char *gitdir;
+       const char *tmp;
 
        while (len && path[len-1] == '/')
                len--;
@@ -359,16 +360,27 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re
                return -1;
        gitdir = xmalloc(len + MAXREFLEN + 8);
        memcpy(gitdir, path, len);
-       memcpy(gitdir + len, "/.git/", 7);
-
-       retval = resolve_gitlink_ref_recursive(gitdir, len+6, refname, result, 0);
+       memcpy(gitdir + len, "/.git", 6);
+       len += 5;
+
+       tmp = read_gitfile_gently(gitdir);
+       if (tmp) {
+               free(gitdir);
+               len = strlen(tmp);
+               gitdir = xmalloc(len + MAXREFLEN + 3);
+               memcpy(gitdir, tmp, len);
+       }
+       gitdir[len] = '/';
+       gitdir[++len] = '\0';
+       retval = resolve_gitlink_ref_recursive(gitdir, len, refname, result, 0);
        free(gitdir);
        return retval;
 }
 
 const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
 {
-       int depth = MAXDEPTH, len;
+       int depth = MAXDEPTH;
+       ssize_t len;
        char buffer[256];
        static char ref_buffer[256];
 
index 08af7f9de124f2e06dc8edd030212ecde9c22920..870d224a3931bf0468c0b74334c0fb49a06ad731 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -315,7 +315,7 @@ static int handle_config(const char *key, const char *value)
        }
        if (!prefixcmp(key, "url.")) {
                struct rewrite *rewrite;
-               name = key + 5;
+               name = key + 4;
                subkey = strrchr(name, '.');
                if (!subkey)
                        return 0;
@@ -337,44 +337,49 @@ static int handle_config(const char *key, const char *value)
                return 0;
        }
        remote = make_remote(name, subkey - name);
-       if (!value) {
-               /* if we ever have a boolean variable, e.g. "remote.*.disabled"
-                * [remote "frotz"]
-                *      disabled
-                * is a valid way to set it to true; we get NULL in value so
-                * we need to handle it here.
-                *
-                * if (!strcmp(subkey, ".disabled")) {
-                *      val = git_config_bool(key, value);
-                *      return 0;
-                * } else
-                *
-                */
-               return 0; /* ignore unknown booleans */
-       }
-       if (!strcmp(subkey, ".url")) {
-               add_url(remote, xstrdup(value));
+       if (!strcmp(subkey, ".mirror"))
+               remote->mirror = git_config_bool(key, value);
+       else if (!strcmp(subkey, ".skipdefaultupdate"))
+               remote->skip_default_update = git_config_bool(key, value);
+
+       else if (!strcmp(subkey, ".url")) {
+               const char *v;
+               if (git_config_string(&v, key, value))
+                       return -1;
+               add_url(remote, v);
        } else if (!strcmp(subkey, ".push")) {
-               add_push_refspec(remote, xstrdup(value));
+               const char *v;
+               if (git_config_string(&v, key, value))
+                       return -1;
+               add_push_refspec(remote, v);
        } else if (!strcmp(subkey, ".fetch")) {
-               add_fetch_refspec(remote, xstrdup(value));
+               const char *v;
+               if (git_config_string(&v, key, value))
+                       return -1;
+               add_fetch_refspec(remote, v);
        } else if (!strcmp(subkey, ".receivepack")) {
+               const char *v;
+               if (git_config_string(&v, key, value))
+                       return -1;
                if (!remote->receivepack)
-                       remote->receivepack = xstrdup(value);
+                       remote->receivepack = v;
                else
                        error("more than one receivepack given, using the first");
        } else if (!strcmp(subkey, ".uploadpack")) {
+               const char *v;
+               if (git_config_string(&v, key, value))
+                       return -1;
                if (!remote->uploadpack)
-                       remote->uploadpack = xstrdup(value);
+                       remote->uploadpack = v;
                else
                        error("more than one uploadpack given, using the first");
        } else if (!strcmp(subkey, ".tagopt")) {
                if (!strcmp(value, "--no-tags"))
                        remote->fetch_tags = -1;
        } else if (!strcmp(subkey, ".proxy")) {
-               remote->http_proxy = xstrdup(value);
-       } else if (!strcmp(subkey, ".skipdefaultupdate"))
-               remote->skip_default_update = 1;
+               return git_config_string((const char **)&remote->http_proxy,
+                                        key, value);
+       }
        return 0;
 }
 
@@ -409,7 +414,7 @@ static void read_config(void)
        alias_all_urls();
 }
 
-static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch)
+static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
 {
        int i;
        int st;
@@ -519,17 +524,32 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
        return rs;
 
  invalid:
+       if (verify) {
+               free(rs);
+               return NULL;
+       }
        die("Invalid refspec '%s'", refspec[i]);
 }
 
+int valid_fetch_refspec(const char *fetch_refspec_str)
+{
+       const char *fetch_refspec[] = { fetch_refspec_str };
+       struct refspec *refspec;
+
+       refspec = parse_refspec_internal(1, fetch_refspec, 1, 1);
+       if (refspec)
+               free(refspec);
+       return !!refspec;
+}
+
 struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)
 {
-       return parse_refspec_internal(nr_refspec, refspec, 1);
+       return parse_refspec_internal(nr_refspec, refspec, 1, 0);
 }
 
 struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
 {
-       return parse_refspec_internal(nr_refspec, refspec, 0);
+       return parse_refspec_internal(nr_refspec, refspec, 0, 0);
 }
 
 static int valid_remote_nick(const char *name)
@@ -797,6 +817,26 @@ static struct ref *make_linked_ref(const char *name, struct ref ***tail)
        return ret;
 }
 
+static char *guess_ref(const char *name, struct ref *peer)
+{
+       struct strbuf buf = STRBUF_INIT;
+       unsigned char sha1[20];
+
+       const char *r = resolve_ref(peer->name, sha1, 1, NULL);
+       if (!r)
+               return NULL;
+
+       if (!prefixcmp(r, "refs/heads/"))
+               strbuf_addstr(&buf, "refs/heads/");
+       else if (!prefixcmp(r, "refs/tags/"))
+               strbuf_addstr(&buf, "refs/tags/");
+       else
+               return NULL;
+
+       strbuf_addstr(&buf, name);
+       return strbuf_detach(&buf, NULL);
+}
+
 static int match_explicit(struct ref *src, struct ref *dst,
                          struct ref ***dst_tail,
                          struct refspec *rs,
@@ -805,6 +845,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
        struct ref *matched_src, *matched_dst;
 
        const char *dst_value = rs->dst;
+       char *dst_guess;
 
        if (rs->pattern)
                return errs;
@@ -851,10 +892,15 @@ static int match_explicit(struct ref *src, struct ref *dst,
        case 0:
                if (!memcmp(dst_value, "refs/", 5))
                        matched_dst = make_linked_ref(dst_value, dst_tail);
+               else if((dst_guess = guess_ref(dst_value, matched_src)))
+                       matched_dst = make_linked_ref(dst_guess, dst_tail);
                else
-                       error("dst refspec %s does not match any "
-                             "existing ref on the remote and does "
-                             "not start with refs/.", dst_value);
+                       error("unable to push to unqualified destination: %s\n"
+                             "The destination refspec neither matches an "
+                             "existing ref on the remote nor\n"
+                             "begins with refs/, and we are unable to "
+                             "guess a prefix based on the source ref.",
+                             dst_value);
                break;
        default:
                matched_dst = NULL;
index 7e9ae792dc69bb8b07cb9b8cf4e53845595d320e..6878c52ce0e3fa57693e919a9cdb66748bb6e6e3 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -26,6 +26,7 @@ struct remote {
         */
        int fetch_tags;
        int skip_default_update;
+       int mirror;
 
        const char *receivepack;
        const char *uploadpack;
@@ -67,6 +68,7 @@ void free_refs(struct ref *ref);
  */
 void ref_remove_duplicates(struct ref *ref_map);
 
+int valid_fetch_refspec(const char *refspec);
 struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);
 struct refspec *parse_push_refspec(int nr_refspec, const char **refspec);
 
index 196fedc9d1297617b74f058f570beb2f5168c3d6..4231ea2cce57c74a4110c9c69459a4caf67dc15c 100644 (file)
@@ -1083,6 +1083,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                continue;
                        }
                        if (!strcmp(arg, "--topo-order")) {
+                               revs->lifo = 1;
                                revs->topo_order = 1;
                                continue;
                        }
@@ -1198,7 +1199,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                        }
                        if (!prefixcmp(arg, "--pretty")) {
                                revs->verbose_header = 1;
-                               revs->commit_format = get_commit_format(arg+8);
+                               get_commit_format(arg+8, revs);
                                continue;
                        }
                        if (!strcmp(arg, "--root")) {
index c8b3b948ecc1cc8b45859a6e1ea11763addfb8b5..31217f8c67307aa09c2f470ccdbdebd5c2445890 100644 (file)
@@ -64,7 +64,8 @@ struct rev_info {
 
        /* Format info */
        unsigned int    shown_one:1,
-                       abbrev_commit:1;
+                       abbrev_commit:1,
+                       use_terminator:1;
        enum date_mode date_mode;
 
        const char **ignore_packed; /* pretend objects in these are unpacked */
diff --git a/setup.c b/setup.c
index 3d2d9580f3283b4e4bc741d98863777510bd5173..b8fd476395db782bf942a548b87242b3a9f990f4 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -314,6 +314,44 @@ static int check_repository_format_gently(int *nongit_ok)
        return 0;
 }
 
+/*
+ * Try to read the location of the git directory from the .git file,
+ * return path to git directory if found.
+ */
+const char *read_gitfile_gently(const char *path)
+{
+       char *buf;
+       struct stat st;
+       int fd;
+       size_t len;
+
+       if (stat(path, &st))
+               return NULL;
+       if (!S_ISREG(st.st_mode))
+               return NULL;
+       fd = open(path, O_RDONLY);
+       if (fd < 0)
+               die("Error opening %s: %s", path, strerror(errno));
+       buf = xmalloc(st.st_size + 1);
+       len = read_in_full(fd, buf, st.st_size);
+       close(fd);
+       if (len != st.st_size)
+               die("Error reading %s", path);
+       buf[len] = '\0';
+       if (prefixcmp(buf, "gitdir: "))
+               die("Invalid gitfile format: %s", path);
+       while (buf[len - 1] == '\n' || buf[len - 1] == '\r')
+               len--;
+       if (len < 9)
+               die("No path in gitfile: %s", path);
+       buf[len] = '\0';
+       if (!is_git_directory(buf + 8))
+               die("Not a git repository: %s", buf + 8);
+       path = make_absolute_path(buf + 8);
+       free(buf);
+       return path;
+}
+
 /*
  * We cannot decide in this function whether we are in the work tree or
  * not, since the config can only be read _after_ this function was called.
@@ -323,6 +361,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
        const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
        static char cwd[PATH_MAX+1];
        const char *gitdirenv;
+       const char *gitfile_dir;
        int len, offset;
 
        /*
@@ -377,8 +416,10 @@ const char *setup_git_directory_gently(int *nongit_ok)
 
        /*
         * Test in the following order (relative to the cwd):
+        * - .git (file containing "gitdir: <path>")
         * - .git/
         * - ./ (bare)
+        * - ../.git
         * - ../.git/
         * - ../ (bare)
         * - ../../.git/
@@ -386,6 +427,12 @@ const char *setup_git_directory_gently(int *nongit_ok)
         */
        offset = len = strlen(cwd);
        for (;;) {
+               gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
+               if (gitfile_dir) {
+                       if (set_git_dir(gitfile_dir))
+                               die("Repository setup failed");
+                       break;
+               }
                if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
                        break;
                if (is_git_directory(".")) {
@@ -428,21 +475,53 @@ const char *setup_git_directory_gently(int *nongit_ok)
 
 int git_config_perm(const char *var, const char *value)
 {
-       if (value) {
-               int i;
-               if (!strcmp(value, "umask"))
-                       return PERM_UMASK;
-               if (!strcmp(value, "group"))
-                       return PERM_GROUP;
-               if (!strcmp(value, "all") ||
-                   !strcmp(value, "world") ||
-                   !strcmp(value, "everybody"))
-                       return PERM_EVERYBODY;
-               i = atoi(value);
-               if (i > 1)
-                       return i;
+       int i;
+       char *endptr;
+
+       if (value == NULL)
+               return PERM_GROUP;
+
+       if (!strcmp(value, "umask"))
+               return PERM_UMASK;
+       if (!strcmp(value, "group"))
+               return PERM_GROUP;
+       if (!strcmp(value, "all") ||
+           !strcmp(value, "world") ||
+           !strcmp(value, "everybody"))
+               return PERM_EVERYBODY;
+
+       /* Parse octal numbers */
+       i = strtol(value, &endptr, 8);
+
+       /* If not an octal number, maybe true/false? */
+       if (*endptr != 0)
+               return git_config_bool(var, value) ? PERM_GROUP : PERM_UMASK;
+
+       /*
+        * Treat values 0, 1 and 2 as compatibility cases, otherwise it is
+        * a chmod value.
+        */
+       switch (i) {
+       case PERM_UMASK:               /* 0 */
+               return PERM_UMASK;
+       case OLD_PERM_GROUP:           /* 1 */
+               return PERM_GROUP;
+       case OLD_PERM_EVERYBODY:       /* 2 */
+               return PERM_EVERYBODY;
        }
-       return git_config_bool(var, value);
+
+       /* A filemode value was given: 0xxx */
+
+       if ((i & 0600) != 0600)
+               die("Problem with core.sharedRepository filemode value "
+                   "(0%.3o).\nThe owner of files must always have "
+                   "read and write permissions.", i);
+
+       /*
+        * Mask filemode value. Others can not get write permission.
+        * x flags for directories are handled separately.
+        */
+       return i & 0666;
 }
 
 int check_repository_format_version(const char *var, const char *value)
diff --git a/sha1-lookup.c b/sha1-lookup.c
new file mode 100644 (file)
index 0000000..da35747
--- /dev/null
@@ -0,0 +1,171 @@
+#include "cache.h"
+#include "sha1-lookup.h"
+
+/*
+ * Conventional binary search loop looks like this:
+ *
+ *     unsigned lo, hi;
+ *      do {
+ *              unsigned mi = (lo + hi) / 2;
+ *              int cmp = "entry pointed at by mi" minus "target";
+ *              if (!cmp)
+ *                      return (mi is the wanted one)
+ *              if (cmp > 0)
+ *                      hi = mi; "mi is larger than target"
+ *              else
+ *                      lo = mi+1; "mi is smaller than target"
+ *      } while (lo < hi);
+ *
+ * The invariants are:
+ *
+ * - When entering the loop, lo points at a slot that is never
+ *   above the target (it could be at the target), hi points at a
+ *   slot that is guaranteed to be above the target (it can never
+ *   be at the target).
+ *
+ * - We find a point 'mi' between lo and hi (mi could be the same
+ *   as lo, but never can be as same as hi), and check if it hits
+ *   the target.  There are three cases:
+ *
+ *    - if it is a hit, we are happy.
+ *
+ *    - if it is strictly higher than the target, we set it to hi,
+ *      and repeat the search.
+ *
+ *    - if it is strictly lower than the target, we update lo to
+ *      one slot after it, because we allow lo to be at the target.
+ *
+ *   If the loop exits, there is no matching entry.
+ *
+ * When choosing 'mi', we do not have to take the "middle" but
+ * anywhere in between lo and hi, as long as lo <= mi < hi is
+ * satisfied.  When we somehow know that the distance between the
+ * target and lo is much shorter than the target and hi, we could
+ * pick mi that is much closer to lo than the midway.
+ *
+ * Now, we can take advantage of the fact that SHA-1 is a good hash
+ * function, and as long as there are enough entries in the table, we
+ * can expect uniform distribution.  An entry that begins with for
+ * example "deadbeef..." is much likely to appear much later than in
+ * the midway of the table.  It can reasonably be expected to be near
+ * 87% (222/256) from the top of the table.
+ *
+ * However, we do not want to pick "mi" too precisely.  If the entry at
+ * the 87% in the above example turns out to be higher than the target
+ * we are looking for, we would end up narrowing the search space down
+ * only by 13%, instead of 50% we would get if we did a simple binary
+ * search.  So we would want to hedge our bets by being less aggressive.
+ *
+ * The table at "table" holds at least "nr" entries of "elem_size"
+ * bytes each.  Each entry has the SHA-1 key at "key_offset".  The
+ * table is sorted by the SHA-1 key of the entries.  The caller wants
+ * to find the entry with "key", and knows that the entry at "lo" is
+ * not higher than the entry it is looking for, and that the entry at
+ * "hi" is higher than the entry it is looking for.
+ */
+int sha1_entry_pos(const void *table,
+                  size_t elem_size,
+                  size_t key_offset,
+                  unsigned lo, unsigned hi, unsigned nr,
+                  const unsigned char *key)
+{
+       const unsigned char *base = table;
+       const unsigned char *hi_key, *lo_key;
+       unsigned ofs_0;
+       static int debug_lookup = -1;
+
+       if (debug_lookup < 0)
+               debug_lookup = !!getenv("GIT_DEBUG_LOOKUP");
+
+       if (!nr || lo >= hi)
+               return -1;
+
+       if (nr == hi)
+               hi_key = NULL;
+       else
+               hi_key = base + elem_size * hi + key_offset;
+       lo_key = base + elem_size * lo + key_offset;
+
+       ofs_0 = 0;
+       do {
+               int cmp;
+               unsigned ofs, mi, range;
+               unsigned lov, hiv, kyv;
+               const unsigned char *mi_key;
+
+               range = hi - lo;
+               if (hi_key) {
+                       for (ofs = ofs_0; ofs < 20; ofs++)
+                               if (lo_key[ofs] != hi_key[ofs])
+                                       break;
+                       ofs_0 = ofs;
+                       /*
+                        * byte 0 thru (ofs-1) are the same between
+                        * lo and hi; ofs is the first byte that is
+                        * different.
+                        */
+                       hiv = hi_key[ofs_0];
+                       if (ofs_0 < 19)
+                               hiv = (hiv << 8) | hi_key[ofs_0+1];
+               } else {
+                       hiv = 256;
+                       if (ofs_0 < 19)
+                               hiv <<= 8;
+               }
+               lov = lo_key[ofs_0];
+               kyv = key[ofs_0];
+               if (ofs_0 < 19) {
+                       lov = (lov << 8) | lo_key[ofs_0+1];
+                       kyv = (kyv << 8) | key[ofs_0+1];
+               }
+               assert(lov < hiv);
+
+               if (kyv < lov)
+                       return -1 - lo;
+               if (hiv < kyv)
+                       return -1 - hi;
+
+               /*
+                * Even if we know the target is much closer to 'hi'
+                * than 'lo', if we pick too precisely and overshoot
+                * (e.g. when we know 'mi' is closer to 'hi' than to
+                * 'lo', pick 'mi' that is higher than the target), we
+                * end up narrowing the search space by a smaller
+                * amount (i.e. the distance between 'mi' and 'hi')
+                * than what we would have (i.e. about half of 'lo'
+                * and 'hi').  Hedge our bets to pick 'mi' less
+                * aggressively, i.e. make 'mi' a bit closer to the
+                * middle than we would otherwise pick.
+                */
+               kyv = (kyv * 6 + lov + hiv) / 8;
+               if (lov < hiv - 1) {
+                       if (kyv == lov)
+                               kyv++;
+                       else if (kyv == hiv)
+                               kyv--;
+               }
+               mi = (range - 1) * (kyv - lov) / (hiv - lov) + lo;
+
+               if (debug_lookup) {
+                       printf("lo %u hi %u rg %u mi %u ", lo, hi, range, mi);
+                       printf("ofs %u lov %x, hiv %x, kyv %x\n",
+                              ofs_0, lov, hiv, kyv);
+               }
+               if (!(lo <= mi && mi < hi))
+                       die("assertion failure lo %u mi %u hi %u %s",
+                           lo, mi, hi, sha1_to_hex(key));
+
+               mi_key = base + elem_size * mi + key_offset;
+               cmp = memcmp(mi_key + ofs_0, key + ofs_0, 20 - ofs_0);
+               if (!cmp)
+                       return mi;
+               if (cmp > 0) {
+                       hi = mi;
+                       hi_key = mi_key;
+               } else {
+                       lo = mi + 1;
+                       lo_key = mi_key + elem_size;
+               }
+       } while (lo < hi);
+       return -lo-1;
+}
diff --git a/sha1-lookup.h b/sha1-lookup.h
new file mode 100644 (file)
index 0000000..3249a81
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef SHA1_LOOKUP_H
+#define SHA1_LOOKUP_H
+
+extern int sha1_entry_pos(const void *table,
+                         size_t elem_size,
+                         size_t key_offset,
+                         unsigned lo, unsigned hi, unsigned nr,
+                         const unsigned char *key);
+#endif
index 445a871db31673af20017d36ff22fd106f77f510..3516777bc7614c23da02dd7a9aa28a48294d56cb 100644 (file)
@@ -15,6 +15,7 @@
 #include "tree.h"
 #include "refs.h"
 #include "pack-revindex.h"
+#include "sha1-lookup.h"
 
 #ifndef O_NOATIME
 #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@ -1675,7 +1676,12 @@ off_t find_pack_entry_one(const unsigned char *sha1,
 {
        const uint32_t *level1_ofs = p->index_data;
        const unsigned char *index = p->index_data;
-       unsigned hi, lo;
+       unsigned hi, lo, stride;
+       static int use_lookup = -1;
+       static int debug_lookup = -1;
+
+       if (debug_lookup < 0)
+               debug_lookup = !!getenv("GIT_DEBUG_LOOKUP");
 
        if (!index) {
                if (open_pack_index(p))
@@ -1690,11 +1696,34 @@ off_t find_pack_entry_one(const unsigned char *sha1,
        index += 4 * 256;
        hi = ntohl(level1_ofs[*sha1]);
        lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
+       if (p->index_version > 1) {
+               stride = 20;
+       } else {
+               stride = 24;
+               index += 4;
+       }
+
+       if (debug_lookup)
+               printf("%02x%02x%02x... lo %u hi %u nr %u\n",
+                      sha1[0], sha1[1], sha1[2], lo, hi, p->num_objects);
+
+       if (use_lookup < 0)
+               use_lookup = !!getenv("GIT_USE_LOOKUP");
+       if (use_lookup) {
+               int pos = sha1_entry_pos(index, stride, 0,
+                                        lo, hi, p->num_objects, sha1);
+               if (pos < 0)
+                       return 0;
+               return nth_packed_object_offset(p, pos);
+       }
 
        do {
                unsigned mi = (lo + hi) / 2;
-               unsigned x = (p->index_version > 1) ? (mi * 20) : (mi * 24 + 4);
-               int cmp = hashcmp(index + x, sha1);
+               int cmp = hashcmp(index + mi * stride, sha1);
+
+               if (debug_lookup)
+                       printf("lo %u hi %u rg %u mi %u\n",
+                              lo, hi, hi - lo, mi);
                if (!cmp)
                        return nth_packed_object_offset(p, mi);
                if (cmp > 0)
@@ -2437,16 +2466,10 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write
 
 int read_pack_header(int fd, struct pack_header *header)
 {
-       char *c = (char*)header;
-       ssize_t remaining = sizeof(struct pack_header);
-       do {
-               ssize_t r = xread(fd, c, remaining);
-               if (r <= 0)
-                       /* "eof before pack header was fully read" */
-                       return PH_ERROR_EOF;
-               remaining -= r;
-               c += r;
-       } while (remaining > 0);
+       if (read_in_full(fd, header, sizeof(*header)) < sizeof(*header))
+               /* "eof before pack header was fully read" */
+               return PH_ERROR_EOF;
+
        if (header->hdr_signature != htonl(PACK_SIGNATURE))
                /* "protocol error (pack signature mismatch detected)" */
                return PH_ERROR_PACK_SIGNATURE;
index 491d2e7ebf19d5c582adbc5ece28d931b09b8a6d..b0b2167578a7baebeba676af0b33161fb688bae0 100644 (file)
@@ -351,8 +351,11 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                }
                if (0 <= nth)
                        at_time = 0;
-               else
-                       at_time = approxidate(str + at + 2);
+               else {
+                       char *tmp = xstrndup(str + at + 2, reflog_len);
+                       at_time = approxidate(tmp);
+                       free(tmp);
+               }
                if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
                                &co_time, &co_tz, &co_cnt)) {
                        if (at_time)
diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
new file mode 100755 (executable)
index 0000000..c5dbc72
--- /dev/null
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+test_description='.git file
+
+Verify that plumbing commands work when .git is a file
+'
+. ./test-lib.sh
+
+objpath() {
+    echo "$1" | sed -e 's|\(..\)|\1/|'
+}
+
+objck() {
+       p=$(objpath "$1")
+       if test ! -f "$REAL/objects/$p"
+       then
+               echo "Object not found: $REAL/objects/$p"
+               false
+       fi
+}
+
+
+test_expect_success 'initial setup' '
+       REAL="$(pwd)/.real" &&
+       mv .git "$REAL"
+'
+
+test_expect_success 'bad setup: invalid .git file format' '
+       echo "gitdir $REAL" >.git &&
+       if git rev-parse 2>.err
+       then
+               echo "git rev-parse accepted an invalid .git file"
+               false
+       fi &&
+       if ! grep -qe "Invalid gitfile format" .err
+       then
+               echo "git rev-parse returned wrong error"
+               false
+       fi
+'
+
+test_expect_success 'bad setup: invalid .git file path' '
+       echo "gitdir: $REAL.not" >.git &&
+       if git rev-parse 2>.err
+       then
+               echo "git rev-parse accepted an invalid .git file path"
+               false
+       fi &&
+       if ! grep -qe "Not a git repository" .err
+       then
+               echo "git rev-parse returned wrong error"
+               false
+       fi
+'
+
+test_expect_success 'final setup + check rev-parse --git-dir' '
+       echo "gitdir: $REAL" >.git &&
+       test "$REAL" = "$(git rev-parse --git-dir)"
+'
+
+test_expect_success 'check hash-object' '
+       echo "foo" >bar &&
+       SHA=$(cat bar | git hash-object -w --stdin) &&
+       objck $SHA
+'
+
+test_expect_success 'check cat-file' '
+       git cat-file blob $SHA >actual &&
+       diff -u bar actual
+'
+
+test_expect_success 'check update-index' '
+       if test -f "$REAL/index"
+       then
+               echo "Hmm, $REAL/index exists?"
+               false
+       fi &&
+       rm -f "$REAL/objects/$(objpath $SHA)" &&
+       git update-index --add bar &&
+       if ! test -f "$REAL/index"
+       then
+               echo "$REAL/index not found"
+               false
+       fi &&
+       objck $SHA
+'
+
+test_expect_success 'check write-tree' '
+       SHA=$(git write-tree) &&
+       objck $SHA
+'
+
+test_expect_success 'check commit-tree' '
+       SHA=$(echo "commit bar" | git commit-tree $SHA) &&
+       objck $SHA
+'
+
+test_expect_success 'check rev-list' '
+       echo $SHA >"$REAL/HEAD" &&
+       test "$SHA" = "$(git rev-list HEAD)"
+'
+
+test_done
index 3faf135e38ccbe71241679da464d7cf709ae73d7..c56d2fbabaa0fdd90547ce8b629a629c21fbbc0e 100755 (executable)
@@ -21,6 +21,7 @@ test_expect_success 'setup' '
        mkdir -p a/b/d a/c &&
        (
                echo "f test=f"
+               echo "a/i test=a/i"
        ) >.gitattributes &&
        (
                echo "g test=a/g" &&
@@ -46,4 +47,11 @@ test_expect_success 'attribute test' '
 
 '
 
+test_expect_success 'root subdir attribute test' '
+
+       attr_check a/i a/i &&
+       attr_check subdir/a/i unspecified
+
+'
+
 test_done
diff --git a/t/t0004-unwritable.sh b/t/t0004-unwritable.sh
new file mode 100755 (executable)
index 0000000..9255c63
--- /dev/null
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+test_description='detect unwritable repository and fail correctly'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+       >file &&
+       git add file &&
+       git commit -m initial &&
+       echo >file &&
+       git add file
+
+'
+
+test_expect_success 'write-tree should notice unwritable repository' '
+
+       (
+               chmod a-w .git/objects
+               test_must_fail git write-tree
+       )
+       status=$?
+       chmod 775 .git/objects
+       (exit $status)
+
+'
+
+test_expect_success 'commit should notice unwritable repository' '
+
+       (
+               chmod a-w .git/objects
+               test_must_fail git commit -m second
+       )
+       status=$?
+       chmod 775 .git/objects
+       (exit $status)
+
+'
+
+test_expect_success 'update-index should notice unwritable repository' '
+
+       (
+               echo a >file &&
+               chmod a-w .git/objects
+               test_must_fail git update-index file
+       )
+       status=$?
+       chmod 775 .git/objects
+       (exit $status)
+
+'
+
+test_expect_success 'add should notice unwritable repository' '
+
+       (
+               echo b >file &&
+               chmod a-w .git/objects
+               test_must_fail git add file
+       )
+       status=$?
+       chmod 775 .git/objects
+       (exit $status)
+
+'
+
+test_done
index b36a9012ecb3a34761027de77635741e1cd80aab..a675cbb51b5dafad22dd494784a0b4c8ec396db3 100755 (executable)
@@ -595,6 +595,64 @@ test_expect_success 'set --int' '
 
 rm .git/config
 
+cat >expect <<\EOF
+[bool]
+       true1 = true
+       true2 = true
+       false1 = false
+       false2 = false
+[int]
+       int1 = 0
+       int2 = 1
+       int3 = -1
+EOF
+
+test_expect_success 'get --bool-or-int' '
+       (
+               echo "[bool]"
+               echo true1
+               echo true2 = true
+               echo false = false
+               echo "[int]"
+               echo int1 = 0
+               echo int2 = 1
+               echo int3 = -1
+       ) >>.git/config &&
+       test $(git config --bool-or-int bool.true1) = true &&
+       test $(git config --bool-or-int bool.true2) = true &&
+       test $(git config --bool-or-int bool.false) = false &&
+       test $(git config --bool-or-int int.int1) = 0 &&
+       test $(git config --bool-or-int int.int2) = 1 &&
+       test $(git config --bool-or-int int.int3) = -1
+
+'
+
+rm .git/config
+cat >expect <<\EOF
+[bool]
+       true1 = true
+       false1 = false
+       true2 = true
+       false2 = false
+[int]
+       int1 = 0
+       int2 = 1
+       int3 = -1
+EOF
+
+test_expect_success 'set --bool-or-int' '
+       git config --bool-or-int bool.true1 true &&
+       git config --bool-or-int bool.false1 false &&
+       git config --bool-or-int bool.true2 yes &&
+       git config --bool-or-int bool.false2 no &&
+       git config --bool-or-int int.int1 0 &&
+       git config --bool-or-int int.int2 1 &&
+       git config --bool-or-int int.int3 -1 &&
+       test_cmp expect .git/config
+'
+
+rm .git/config
+
 git config quote.leading " test"
 git config quote.ending "test "
 git config quote.semicolon "test;test"
index 6bfe19a4e5e8a22bfd286636e62c0c2d5cd56846..5e4252a320d5f967eacb9bd68bb0a510fe748e80 100755 (executable)
@@ -7,6 +7,16 @@ test_description='Test shared repository initialization'
 
 . ./test-lib.sh
 
+# User must have read permissions to the repo -> failure on --shared=0400
+test_expect_success 'shared = 0400 (faulty permission u-w)' '
+       mkdir sub && (
+               cd sub && git init --shared=0400
+       )
+       ret="$?"
+       rm -rf sub
+       test $ret != "0"
+'
+
 test_expect_success 'shared=all' '
        mkdir sub &&
        cd sub &&
@@ -33,4 +43,44 @@ test_expect_success 'update-server-info honors core.sharedRepository' '
        esac
 '
 
+for u in       0660:rw-rw---- \
+               0640:rw-r----- \
+               0600:rw------- \
+               0666:rw-rw-rw- \
+               0664:rw-rw-r--
+do
+       x=$(expr "$u" : ".*:\([rw-]*\)") &&
+       y=$(echo "$x" | sed -e "s/w/-/g") &&
+       u=$(expr "$u" : "\([0-7]*\)") &&
+       git config core.sharedrepository "$u" &&
+       umask 0277 &&
+
+       test_expect_success "shared = $u ($y) ro" '
+
+               rm -f .git/info/refs &&
+               git update-server-info &&
+               actual="$(ls -l .git/info/refs)" &&
+               actual=${actual%% *} &&
+               test "x$actual" = "x-$y" || {
+                       ls -lt .git/info
+                       false
+               }
+       '
+
+       umask 077 &&
+       test_expect_success "shared = $u ($x) rw" '
+
+               rm -f .git/info/refs &&
+               git update-server-info &&
+               actual="$(ls -l .git/info/refs)" &&
+               actual=${actual%% *} &&
+               test "x$actual" = "x-$x" || {
+                       ls -lt .git/info
+                       false
+               }
+
+       '
+
+done
+
 test_done
index 0f441bcef768ca68ead9bfa2d06b714b8d2c35ac..70361c806e1baf1b26810983374c53eb49ea2f2d 100755 (executable)
@@ -21,13 +21,13 @@ test_expect_success \
 rm -f path0 &&
 git read-tree $t &&
 git checkout-index -f -a &&
-! git diff-files | diff - /dev/null'
+test_must_fail git diff-files --exit-code'
 
 test_expect_success \
 'with -u, git checkout-index picks up stat information from new files.' '
 rm -f path0 &&
 git read-tree $t &&
 git checkout-index -u -f -a &&
-git diff-files | diff - /dev/null'
+git diff-files --exit-code'
 
 test_done
index b4cf628d225d380d6c0bf73dee4c3e9df0cadb41..f86f4bc5ebcc0e36ddb4071a6aeb855e1039faa6 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='branch --contains <commit>'
+test_description='branch --contains <commit>, --merged, and --no-merged'
 
 . ./test-lib.sh
 
@@ -55,4 +55,44 @@ test_expect_success 'branch --contains=side' '
 
 '
 
+test_expect_success 'side: branch --merged' '
+
+       git branch --merged >actual &&
+       {
+               echo "  master" &&
+               echo "* side"
+       } >expect &&
+       test_cmp expect actual
+
+'
+
+test_expect_success 'side: branch --no-merged' '
+
+       git branch --no-merged >actual &&
+       >expect &&
+       test_cmp expect actual
+
+'
+
+test_expect_success 'master: branch --merged' '
+
+       git checkout master &&
+       git branch --merged >actual &&
+       {
+               echo "* master"
+       } >expect &&
+       test_cmp expect actual
+
+'
+
+test_expect_success 'master: branch --no-merged' '
+
+       git branch --no-merged >actual &&
+       {
+               echo "  side"
+       } >expect &&
+       test_cmp expect actual
+
+'
+
 test_done
diff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh
new file mode 100755 (executable)
index 0000000..e12cd57
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='rebasing a commit with multi-line first paragraph.'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+       >file &&
+       git add file &&
+       test_tick &&
+       git commit -m initial &&
+
+       echo hello >file &&
+       test_tick &&
+       git commit -a -m "A sample commit log message that has a long
+summary that spills over multiple lines.
+
+But otherwise with a sane description."
+
+       git branch side &&
+
+       git reset --hard HEAD^ &&
+       >elif &&
+       git add elif &&
+       test_tick &&
+       git commit -m second
+
+'
+
+test_expect_success rebase '
+
+       git checkout side &&
+       git rebase master &&
+       git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
+       git cat-file commit side@{1} | sed -e "1,/^$/d" >expect &&
+       test_cmp expect actual
+
+'
+
+test_done
index 77c90f6fa002ce00c3ffb30d0b79adaff46b3caa..f15be93e7709acbb517bb85020d4d6b94a50f725 100755 (executable)
@@ -66,4 +66,23 @@ test_expect_success 'revert works (commit)' '
        grep "unchanged *+3/-0 file" output
 '
 
+test_expect_success 'patch does not affect mode' '
+       git reset --hard &&
+       echo content >>file &&
+       chmod +x file &&
+       printf "n\\ny\\n" | git add -p &&
+       git show :file | grep content &&
+       git diff file | grep "new mode"
+'
+
+test_expect_success 'stage mode but not hunk' '
+       git reset --hard &&
+       echo content >>file &&
+       chmod +x file &&
+       printf "y\\nn\\n" | git add -p &&
+       git diff --cached file | grep "new mode" &&
+       git diff          file | grep "+content"
+'
+
+
 test_done
index bdc6e132cacbccc72a903c1aad1bbfe1364471ab..df1fd6f86f11b40667dfbd8132fef8da45d03d75 100755 (executable)
@@ -44,6 +44,8 @@ cat >tag.sig <<EOF
 xxxxxx 139e9b33986b1c2670fff52c5067603117b3e895
 type tag
 tag mytag
+tagger . <> 0 +0000
+
 EOF
 
 check_verify_failure '"object" line label check' '^error: char0: .*"object "$'
@@ -55,6 +57,8 @@ cat >tag.sig <<EOF
 object zz9e9b33986b1c2670fff52c5067603117b3e895
 type tag
 tag mytag
+tagger . <> 0 +0000
+
 EOF
 
 check_verify_failure '"object" line SHA1 check' '^error: char7: .*SHA1 hash$'
@@ -66,6 +70,8 @@ cat >tag.sig <<EOF
 object 779e9b33986b1c2670fff52c5067603117b3e895
 xxxx tag
 tag mytag
+tagger . <> 0 +0000
+
 EOF
 
 check_verify_failure '"type" line label check' '^error: char47: .*"\\ntype "$'
@@ -85,6 +91,8 @@ cat >tag.sig <<EOF
 object 779e9b33986b1c2670fff52c5067603117b3e895
 type tag
 xxx mytag
+tagger . <> 0 +0000
+
 EOF
 
 check_verify_failure '"tag" line label check #1' \
@@ -121,6 +129,8 @@ cat >tag.sig <<EOF
 object 779e9b33986b1c2670fff52c5067603117b3e895
 type tagggg
 tag mytag
+tagger . <> 0 +0000
+
 EOF
 
 check_verify_failure 'verify object (SHA1/type) check' \
@@ -133,6 +143,8 @@ cat >tag.sig <<EOF
 object $head
 type commit
 tag my tag
+tagger . <> 0 +0000
+
 EOF
 
 check_verify_failure 'verify tag-name check' \
@@ -145,10 +157,12 @@ cat >tag.sig <<EOF
 object $head
 type commit
 tag mytag
+
+This is filler
 EOF
 
 check_verify_failure '"tagger" line label check #1' \
-       '^error: char70: could not find "tagger"$'
+       '^error: char70: could not find "tagger "$'
 
 ############################################################
 # 12. tagger line label check #2
@@ -158,19 +172,180 @@ object $head
 type commit
 tag mytag
 tagger
+
+This is filler
 EOF
 
 check_verify_failure '"tagger" line label check #2' \
-       '^error: char70: could not find "tagger"$'
+       '^error: char70: could not find "tagger "$'
 
 ############################################################
-# 13. create valid tag
+# 13. disallow missing tag author name
 
 cat >tag.sig <<EOF
 object $head
 type commit
 tag mytag
-tagger another@example.com
+tagger  <> 0 +0000
+
+This is filler
+EOF
+
+check_verify_failure 'disallow missing tag author name' \
+       '^error: char77: missing tagger name$'
+
+############################################################
+# 14. disallow missing tag author name
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <
+ > 0 +0000
+
+EOF
+
+check_verify_failure 'disallow malformed tagger' \
+       '^error: char77: malformed tagger field$'
+
+############################################################
+# 15. allow empty tag email
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <> 0 +0000
+
+EOF
+
+test_expect_success \
+    'allow empty tag email' \
+    'git-mktag <tag.sig >.git/refs/tags/mytag 2>message'
+
+############################################################
+# 16. disallow spaces in tag email
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tag ger@example.com> 0 +0000
+
+EOF
+
+check_verify_failure 'disallow spaces in tag email' \
+       '^error: char77: malformed tagger field$'
+
+############################################################
+# 17. disallow missing tag timestamp
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com>  
+
+EOF
+
+check_verify_failure 'disallow missing tag timestamp' \
+       '^error: char107: missing tag timestamp$'
+
+############################################################
+# 18. detect invalid tag timestamp1
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> Tue Mar 25 15:47:44 2008
+
+EOF
+
+check_verify_failure 'detect invalid tag timestamp1' \
+       '^error: char107: missing tag timestamp$'
+
+############################################################
+# 19. detect invalid tag timestamp2
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 2008-03-31T12:20:15-0500
+
+EOF
+
+check_verify_failure 'detect invalid tag timestamp2' \
+       '^error: char111: malformed tag timestamp$'
+
+############################################################
+# 20. detect invalid tag timezone1
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 GMT
+
+EOF
+
+check_verify_failure 'detect invalid tag timezone1' \
+       '^error: char118: malformed tag timezone$'
+
+############################################################
+# 21. detect invalid tag timezone2
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 +  30
+
+EOF
+
+check_verify_failure 'detect invalid tag timezone2' \
+       '^error: char118: malformed tag timezone$'
+
+############################################################
+# 22. detect invalid tag timezone3
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 -1430
+
+EOF
+
+check_verify_failure 'detect invalid tag timezone3' \
+       '^error: char118: malformed tag timezone$'
+
+############################################################
+# 23. detect invalid header entry
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 -0500
+this line should not be here
+
+EOF
+
+check_verify_failure 'detect invalid header entry' \
+       '^error: char124: trailing garbage in tag header$'
+
+############################################################
+# 24. create valid tag
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 -0500
+
 EOF
 
 test_expect_success \
@@ -178,7 +353,7 @@ test_expect_success \
     'git-mktag <tag.sig >.git/refs/tags/mytag 2>message'
 
 ############################################################
-# 14. check mytag
+# 25. check mytag
 
 test_expect_success \
     'check mytag' \
index 64f34e329867cbef3c122c96a63cfd844b0953af..43943ab8ca181edbdcc6a68d208cf584b5cda86d 100755 (executable)
@@ -112,4 +112,17 @@ do
        '
 done
 
+test_expect_success 'two lines' '
+
+       >file &&
+       git add file &&
+       echo aaa >file &&
+       git diff >patch &&
+       git add file &&
+       echo bbb >file &&
+       git add file &&
+       test_must_fail git apply --check patch
+
+'
+
 test_done
index dca2067b2d0bcd4423d843561b9275be50fe0da3..fa62b6aa21f5d8f8774b7f2af3a4cd60b8c0d761 100755 (executable)
@@ -109,9 +109,10 @@ test_expect_success \
     'diff -r a c/prefix/a'
 
 test_expect_success \
-    'create an archive with a substfiles' \
+    'create archives with substfiles' \
     'echo "substfile?" export-subst >a/.gitattributes &&
      git archive HEAD >f.tar &&
+     git archive --prefix=prefix/ HEAD >g.tar &&
      rm a/.gitattributes'
 
 test_expect_success \
@@ -126,6 +127,18 @@ test_expect_success \
       diff a/substfile2 f/a/substfile2
 '
 
+test_expect_success \
+    'extract substfiles from archive with prefix' \
+    '(mkdir g && cd g && $TAR xf -) <g.tar'
+
+test_expect_success \
+     'validate substfile contents from archive with prefix' \
+     'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
+      >g/prefix/a/substfile1.expected &&
+      diff g/prefix/a/substfile1.expected g/prefix/a/substfile1 &&
+      diff a/substfile2 g/prefix/a/substfile2
+'
+
 test_expect_success \
     'git archive --format=zip' \
     'git archive --format=zip HEAD >d.zip'
index c955fe44f5b962ef305e49c21bc3fb0e5b58d462..983a39398f17dc2b261771e780e8c010f038a12a 100755 (executable)
@@ -274,4 +274,99 @@ test_expect_success \
      packname_4=$(git pack-objects test-4 <obj-list) &&
      test 3 = $(ls test-4-*.pack | wc -l)'
 
+test_expect_success 'unpacking with --strict' '
+
+       git config --unset pack.packsizelimit &&
+       for j in a b c d e f g
+       do
+               for i in 0 1 2 3 4 5 6 7 8 9
+               do
+                       o=$(echo $j$i | git hash-object -w --stdin) &&
+                       echo "100644 $o 0 $j$i"
+               done
+       done >LIST &&
+       rm -f .git/index &&
+       git update-index --index-info <LIST &&
+       LIST=$(git write-tree) &&
+       rm -f .git/index &&
+       head -n 10 LIST | git update-index --index-info &&
+       LI=$(git write-tree) &&
+       rm -f .git/index &&
+       tail -n 10 LIST | git update-index --index-info &&
+       ST=$(git write-tree) &&
+       PACK5=$( git rev-list --objects "$LIST" "$LI" "$ST" | \
+               git pack-objects test-5 ) &&
+       PACK6=$( (
+                       echo "$LIST"
+                       echo "$LI"
+                       echo "$ST"
+                ) | git pack-objects test-6 ) &&
+       test_create_repo test-5 &&
+       (
+               cd test-5 &&
+               git unpack-objects --strict <../test-5-$PACK5.pack &&
+               git ls-tree -r $LIST &&
+               git ls-tree -r $LI &&
+               git ls-tree -r $ST
+       ) &&
+       test_create_repo test-6 &&
+       (
+               # tree-only into empty repo -- many unreachables
+               cd test-6 &&
+               test_must_fail git unpack-objects --strict <../test-6-$PACK6.pack
+       ) &&
+       (
+               # already populated -- no unreachables
+               cd test-5 &&
+               git unpack-objects --strict <../test-6-$PACK6.pack
+       )
+'
+
+test_expect_success 'index-pack with --strict' '
+
+       for j in a b c d e f g
+       do
+               for i in 0 1 2 3 4 5 6 7 8 9
+               do
+                       o=$(echo $j$i | git hash-object -w --stdin) &&
+                       echo "100644 $o 0 $j$i"
+               done
+       done >LIST &&
+       rm -f .git/index &&
+       git update-index --index-info <LIST &&
+       LIST=$(git write-tree) &&
+       rm -f .git/index &&
+       head -n 10 LIST | git update-index --index-info &&
+       LI=$(git write-tree) &&
+       rm -f .git/index &&
+       tail -n 10 LIST | git update-index --index-info &&
+       ST=$(git write-tree) &&
+       PACK5=$( git rev-list --objects "$LIST" "$LI" "$ST" | \
+               git pack-objects test-5 ) &&
+       PACK6=$( (
+                       echo "$LIST"
+                       echo "$LI"
+                       echo "$ST"
+                ) | git pack-objects test-6 ) &&
+       test_create_repo test-7 &&
+       (
+               cd test-7 &&
+               git index-pack --strict --stdin <../test-5-$PACK5.pack &&
+               git ls-tree -r $LIST &&
+               git ls-tree -r $LI &&
+               git ls-tree -r $ST
+       ) &&
+       test_create_repo test-8 &&
+       (
+               # tree-only into empty repo -- many unreachables
+               cd test-8 &&
+               test_must_fail git index-pack --strict --stdin <../test-6-$PACK6.pack
+       ) &&
+       (
+               # already populated -- no unreachables
+               cd test-7 &&
+               git index-pack --strict --stdin <../test-6-$PACK6.pack
+       )
+'
+
 test_done
index 0a7fea865d2be6e1a74e2016f336af22956de414..48ff2d424d1587bde388422da874feb3d55cbf0d 100755 (executable)
@@ -77,6 +77,16 @@ test_expect_success 'add another remote' '
 )
 '
 
+test_expect_success 'remote forces tracking branches' '
+(
+       cd test &&
+       case `git config remote.second.fetch` in
+       +*) true ;;
+        *) false ;;
+       esac
+)
+'
+
 test_expect_success 'remove remote' '
 (
        cd test &&
@@ -253,4 +263,10 @@ test_expect_success '"remote show" does not show symbolic refs' '
 
 '
 
+test_expect_success 'reject adding remote with an invalid name' '
+
+       ! git remote add some:url desired-name
+
+'
+
 test_done
index 793ffc6600202431193887a12981105c099d40df..0a757d5b9fe660245ced7749e445eabd0369bcf0 100755 (executable)
@@ -103,9 +103,9 @@ test_expect_success 'fetch with wildcard' '
 test_expect_success 'fetch with insteadOf' '
        mk_empty &&
        (
-               TRASH=$(pwd) &&
+               TRASH=$(pwd)/ &&
                cd testrepo &&
-               git config url./$TRASH/.insteadOf trash/
+               git config url.$TRASH.insteadOf trash/
                git config remote.up.url trash/. &&
                git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
                git fetch up &&
@@ -145,8 +145,8 @@ test_expect_success 'push with wildcard' '
 
 test_expect_success 'push with insteadOf' '
        mk_empty &&
-       TRASH=$(pwd) &&
-       git config url./$TRASH/.insteadOf trash/ &&
+       TRASH=$(pwd)/ &&
+       git config url.$TRASH.insteadOf trash/ &&
        git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
        (
                cd testrepo &&
@@ -209,19 +209,7 @@ test_expect_success 'push with weak ambiguity (2)' '
 
 '
 
-test_expect_success 'push with ambiguity (1)' '
-
-       mk_test remotes/origin/master remotes/frotz/master &&
-       if git push testrepo master:master
-       then
-               echo "Oops, should have failed"
-               false
-       else
-               check_push_result $the_first_commit remotes/origin/master remotes/frotz/master
-       fi
-'
-
-test_expect_success 'push with ambiguity (2)' '
+test_expect_success 'push with ambiguity' '
 
        mk_test heads/frotz tags/frotz &&
        if git push testrepo master:frotz
@@ -285,6 +273,37 @@ test_expect_success 'push with colon-less refspec (4)' '
 
 '
 
+test_expect_success 'push head with non-existant, incomplete dest' '
+
+       mk_test &&
+       git push testrepo master:branch &&
+       check_push_result $the_commit heads/branch
+
+'
+
+test_expect_success 'push tag with non-existant, incomplete dest' '
+
+       mk_test &&
+       git tag -f v1.0 &&
+       git push testrepo v1.0:tag &&
+       check_push_result $the_commit tags/tag
+
+'
+
+test_expect_success 'push sha1 with non-existant, incomplete dest' '
+
+       mk_test &&
+       test_must_fail git push testrepo `git rev-parse master`:foo
+
+'
+
+test_expect_success 'push ref expression with non-existant, incomplete dest' '
+
+       mk_test &&
+       test_must_fail git push testrepo master^:branch
+
+'
+
 test_expect_success 'push with HEAD' '
 
        mk_test heads/master &&
@@ -323,6 +342,15 @@ test_expect_success 'push with +HEAD' '
 
 '
 
+test_expect_success 'push HEAD with non-existant, incomplete dest' '
+
+       mk_test &&
+       git checkout master &&
+       git push testrepo HEAD:branch &&
+       check_push_result $the_commit heads/branch
+
+'
+
 test_expect_success 'push with config remote.*.push = HEAD' '
 
        mk_test heads/local &&
index ed3fec192a8da73da269af03746fa7e9eda65d52..ea49dedbf8867694d83cd550c8212ff107361920 100755 (executable)
@@ -25,7 +25,7 @@ mk_repo_pair () {
        (
                cd master &&
                git init &&
-               git config remote.up.url ../mirror
+               git remote add $1 up ../mirror
        )
 }
 
@@ -225,4 +225,43 @@ test_expect_success 'push mirror adds, updates and removes tags together' '
 
 '
 
+test_expect_success 'remote.foo.mirror adds and removes branches' '
+
+       mk_repo_pair --mirror &&
+       (
+               cd master &&
+               echo one >foo && git add foo && git commit -m one &&
+               git branch keep master &&
+               git branch remove master &&
+               git push up &&
+               git branch -D remove
+               git push up
+       ) &&
+       (
+               cd mirror &&
+               git show-ref -s --verify refs/heads/keep &&
+               invert git show-ref -s --verify refs/heads/remove
+       )
+
+'
+
+test_expect_success 'remote.foo.mirror=no has no effect' '
+
+       mk_repo_pair &&
+       (
+               cd master &&
+               echo one >foo && git add foo && git commit -m one &&
+               git config --add remote.up.mirror no &&
+               git branch keep master &&
+               git push --mirror up &&
+               git branch -D keep &&
+               git push up
+       ) &&
+       (
+               cd mirror &&
+               git show-ref -s --verify refs/heads/keep
+       )
+
+'
+
 test_done
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
new file mode 100755 (executable)
index 0000000..dc9d63d
--- /dev/null
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+test_description=clone
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+       rm -fr .git &&
+       test_create_repo src &&
+       (
+               cd src
+               >file
+               git add file
+               git commit -m initial
+       )
+
+'
+
+test_expect_success 'clone with excess parameters' '
+
+       test_must_fail git clone -n "file://$(pwd)/src" dst junk
+
+'
+
+test_done
index f471c1526f34fa87227ac23b458299063b408ff1..5e3e5445c730140ad6c2fa9cc5bda4a7029e7fbf 100755 (executable)
@@ -71,6 +71,24 @@ test_expect_success 'bisect start with one bad and good' '
        git bisect next
 '
 
+test_expect_success 'bisect fails if given any junk instead of revs' '
+       git bisect reset &&
+       test_must_fail git bisect start foo $HASH1 -- &&
+       test_must_fail git bisect start $HASH4 $HASH1 bar -- &&
+       test -z "$(git for-each-ref "refs/bisect/*")" &&
+       test_must_fail ls .git/BISECT_* &&
+       git bisect start &&
+       test_must_fail git bisect good foo $HASH1 &&
+       test_must_fail git bisect good $HASH1 bar &&
+       test_must_fail git bisect bad frotz &&
+       test_must_fail git bisect bad $HASH3 $HASH4 &&
+       test_must_fail git bisect skip bar $HASH3 &&
+       test_must_fail git bisect skip $HASH1 foo &&
+       test -z "$(git for-each-ref "refs/bisect/*")" &&
+       git bisect good $HASH1 &&
+       git bisect bad $HASH4
+'
+
 test_expect_success 'bisect reset: back in the master branch' '
        git bisect reset &&
        echo "* master" > branch.expect &&
index 1daaf544d9ad1f3eafce024b92cf0640c4dee766..16df3d4adb1cc6bc970dd79829adbc46492bc66c 100755 (executable)
@@ -17,6 +17,8 @@ test_expect_success 'setup' '
        make_commit B
        git checkout -b branch B
        make_commit D
+       mkdir dir
+       make_commit dir/D
        make_commit E
        git checkout master
        make_commit C
@@ -41,9 +43,23 @@ test_expect_success 'rewrite, renaming a specific file' '
 '
 
 test_expect_success 'test that the file was renamed' '
-       test d = $(git show HEAD:doh) &&
+       test d = "$(git show HEAD:doh --)" &&
+       ! test -f d &&
        test -f doh &&
-       test d = $(cat doh)
+       test d = "$(cat doh)"
+'
+
+test_expect_success 'rewrite, renaming a specific directory' '
+       git-filter-branch -f --tree-filter "mv dir diroh || :" HEAD
+'
+
+test_expect_success 'test that the directory was renamed' '
+       test dir/d = "$(git show HEAD:diroh/d --)" &&
+       ! test -d dir &&
+       test -d diroh &&
+       ! test -d diroh/dir &&
+       test -f diroh/d &&
+       test dir/d = "$(cat diroh/d)"
 '
 
 git tag oldD HEAD~4
index 75cd33bde8e5906ad68ecdcc904248745d79975c..1a7141ecd7ad1ffcd5081181548920b6a199ab2c 100755 (executable)
@@ -578,6 +578,14 @@ test_expect_success \
        git diff expect actual
 '
 
+# subsequent tests require gpg; check if it is available
+gpg --version >/dev/null
+if [ $? -eq 127 ]; then
+       echo "gpg not found - skipping tag signing and verification tests"
+       test_done
+       exit
+fi
+
 # trying to verify annotated non-signed tags:
 
 test_expect_success \
@@ -600,13 +608,6 @@ test_expect_success \
 
 # creating and verifying signed tags:
 
-gpg --version >/dev/null
-if [ $? -eq 127 ]; then
-       echo "Skipping signed tags tests, because gpg was not found"
-       test_done
-       exit
-fi
-
 # As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
 # the gpg version 1.0.6 didn't parse trust packets correctly, so for
 # that version, creation of signed tags using the generated key fails.
index 6a74b3acfda6774d7996cf5787e9858654357328..2d919d69ef110408b820c76185d6b8da63ea183e 100755 (executable)
@@ -4,8 +4,6 @@ test_description='GIT_EDITOR, core.editor, and stuff'
 
 . ./test-lib.sh
 
-OLD_TERM="$TERM"
-
 for i in GIT_EDITOR core_editor EDITOR VISUAL vi
 do
        cat >e-$i.sh <<-EOF
@@ -116,6 +114,4 @@ test_expect_success 'core.editor with a space' '
 
 '
 
-TERM="$OLD_TERM"
-
 test_done
index afccfc9973ba864167387b1e1caf6fe8b12f68f9..a50492f7c065d034cb344eac965d51a82cb5a599 100755 (executable)
@@ -75,8 +75,8 @@ test_expect_success 'git-clean src/ src/' '
 
 test_expect_success 'git-clean with prefix' '
 
-       mkdir -p build docs &&
-       touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+       mkdir -p build docs src/test &&
+       touch a.out src/part3.c docs/manual.txt obj.o build/lib.so src/test/1.c &&
        (cd src/ && git-clean) &&
        test -f Makefile &&
        test -f README &&
@@ -84,6 +84,7 @@ test_expect_success 'git-clean with prefix' '
        test -f src/part2.c &&
        test -f a.out &&
        test ! -f src/part3.c &&
+       test -f src/test/1.c &&
        test -f docs/manual.txt &&
        test -f obj.o &&
        test -f build/lib.so
index 0f3c42ab35891d1dd671649ff38074a1d83e2a5b..bf12dbdeef6e307850a91eb6be5ebe537b2de0c8 100755 (executable)
@@ -30,7 +30,7 @@ commit_file () {
 }
 
 test_create_repo sm1 &&
-add_file . foo
+add_file . foo >/dev/null
 
 head1=$(add_file sm1 foo1 foo2)
 
@@ -192,4 +192,17 @@ test_expect_success 'given commit' "
 EOF
 "
 
+test_expect_success '--for-status' "
+    git submodule summary --for-status HEAD^ >actual &&
+    test_cmp actual - <<EOF
+# Modified submodules:
+#
+# * sm1 $head6...0000000:
+#
+# * sm2 0000000...$head7 (2):
+#   > Add foo9
+#
+EOF
+"
+
 test_done
index cd08516e6de6b5c4b39ffee14302d76c99229825..e4bfcaece0c3ac4ea4b33521e84bd7fc1c19422e 100755 (executable)
@@ -149,4 +149,138 @@ test_expect_success 'status of partial commit excluding new file in index' '
        test_cmp expect output
 '
 
+test_expect_success 'setup status submodule summary' '
+       test_create_repo sm && (
+               cd sm &&
+               >foo &&
+               git add foo &&
+               git commit -m "Add foo"
+       ) &&
+       git add sm
+'
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      new file:   dir2/added
+#      new file:   sm
+#
+# Changed but not updated:
+#   (use "git add <file>..." to update what will be committed)
+#
+#      modified:   dir1/modified
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#      dir1/untracked
+#      dir2/modified
+#      dir2/untracked
+#      expect
+#      output
+#      untracked
+EOF
+test_expect_success 'status submodule summary is disabled by default' '
+       git status >output &&
+       test_cmp expect output
+'
+
+head=$(cd sm && git rev-parse --short=7 --verify HEAD)
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      new file:   dir2/added
+#      new file:   sm
+#
+# Changed but not updated:
+#   (use "git add <file>..." to update what will be committed)
+#
+#      modified:   dir1/modified
+#
+# Modified submodules:
+#
+# * sm 0000000...$head (1):
+#   > Add foo
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#      dir1/untracked
+#      dir2/modified
+#      dir2/untracked
+#      expect
+#      output
+#      untracked
+EOF
+test_expect_success 'status submodule summary' '
+       git config status.submodulesummary 10 &&
+       git status >output &&
+       test_cmp expect output
+'
+
+
+cat >expect <<EOF
+# On branch master
+# Changed but not updated:
+#   (use "git add <file>..." to update what will be committed)
+#
+#      modified:   dir1/modified
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#      dir1/untracked
+#      dir2/modified
+#      dir2/untracked
+#      expect
+#      output
+#      untracked
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+test_expect_success 'status submodule summary (clean submodule)' '
+       git commit -m "commit submodule" &&
+       git config status.submodulesummary 10 &&
+       test_must_fail git status >output &&
+       test_cmp expect output
+'
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD^1 <file>..." to unstage)
+#
+#      new file:   dir2/added
+#      new file:   sm
+#
+# Changed but not updated:
+#   (use "git add <file>..." to update what will be committed)
+#
+#      modified:   dir1/modified
+#
+# Modified submodules:
+#
+# * sm 0000000...$head (1):
+#   > Add foo
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#      dir1/untracked
+#      dir2/modified
+#      dir2/untracked
+#      expect
+#      output
+#      untracked
+EOF
+test_expect_success 'status submodule summary (--amend)' '
+       git config status.submodulesummary 10 &&
+       git status --amend >output &&
+       test_cmp expect output
+'
+
 test_done
diff --git a/t/t9121-git-svn-fetch-renamed-dir.sh b/t/t9121-git-svn-fetch-renamed-dir.sh
new file mode 100755 (executable)
index 0000000..5143ed6
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Santhosh Kumar Mani
+
+
+test_description='git-svn can fetch renamed directories'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'load repository with renamed directory' "
+       svnadmin load -q $rawsvnrepo < ../t9121/renamed-dir.dump
+       "
+
+test_expect_success 'init and fetch repository' "
+       git svn init $svnrepo/newname &&
+       git svn fetch
+       "
+
+test_done
+
diff --git a/t/t9121/renamed-dir.dump b/t/t9121/renamed-dir.dump
new file mode 100644 (file)
index 0000000..5f9127b
--- /dev/null
@@ -0,0 +1,90 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 06b9b3ad-f546-4fbe-8328-fcb4e6ef5c3f
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2008-04-02T09:11:59.778557Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 117
+Content-length: 117
+
+K 7
+svn:log
+V 14
+initial import
+K 10
+svn:author
+V 8
+santhosh
+K 8
+svn:date
+V 27
+2008-04-02T09:13:03.170863Z
+PROPS-END
+
+Node-path: name
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: name/a.txt
+Node-kind: file
+Node-action: add
+Prop-content-length: 71
+Text-content-length: 6
+Text-content-md5: b1946ac92492d2347c6235b4d2611184
+Content-length: 77
+
+K 13
+svn:mime-type
+V 10
+text/plain
+K 13
+svn:eol-style
+V 2
+LF
+PROPS-END
+hello
+
+
+Revision-number: 2
+Prop-content-length: 109
+Content-length: 109
+
+K 7
+svn:log
+V 7
+renamed
+K 10
+svn:author
+V 8
+santhosh
+K 8
+svn:date
+V 27
+2008-04-02T09:14:22.952186Z
+PROPS-END
+
+Node-path: newname
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: name
+
+
+Node-path: name
+Node-action: delete
+
+
index b91b151417758758bba9a7afb89ed54bd9b1dc7a..166b43f783517debf1de716aed4c1952ab1fea4b 100755 (executable)
@@ -420,4 +420,54 @@ test_expect_success 'cvs update (merge no-op)' \
     GIT_CONFIG="$git_config" cvs -Q update &&
     diff -q merge ../merge'
 
+cd "$WORKDIR"
+test_expect_success 'cvs update (-p)' '
+    touch really-empty &&
+    echo Line 1 > no-lf &&
+    echo -n Line 2 >> no-lf &&
+    git add really-empty no-lf &&
+    git commit -q -m "Update -p test" &&
+    git push gitcvs.git >/dev/null &&
+    cd cvswork &&
+    GIT_CONFIG="$git_config" cvs update &&
+    rm -f failures &&
+    for i in merge no-lf empty really-empty; do
+        GIT_CONFIG="$git_config" cvs update -p "$i" >$i.out
+        diff $i.out ../$i >>failures 2>&1
+    done &&
+    test -z "$(cat failures)"
+'
+
+#------------
+# CVS STATUS
+#------------
+
+cd "$WORKDIR"
+test_expect_success 'cvs status' '
+    mkdir status.dir &&
+    echo Line > status.dir/status.file &&
+    echo Line > status.file &&
+    git add status.dir status.file &&
+    git commit -q -m "Status test" &&
+    git push gitcvs.git >/dev/null &&
+    cd cvswork &&
+    GIT_CONFIG="$git_config" cvs update &&
+    GIT_CONFIG="$git_config" cvs status | grep "^File: status.file" >../out &&
+    test $(wc -l <../out) = 2
+'
+
+cd "$WORKDIR"
+test_expect_success 'cvs status (nonrecursive)' '
+    cd cvswork &&
+    GIT_CONFIG="$git_config" cvs status -l | grep "^File: status.file" >../out &&
+    test $(wc -l <../out) = 1
+'
+
+cd "$WORKDIR"
+test_expect_success 'cvs status (no subdirs in header)' '
+    cd cvswork &&
+    GIT_CONFIG="$git_config" cvs status | grep ^File: >../out &&
+    ! grep / <../out
+'
+
 test_done
index 796cd7dba0cdc64e6b9e68e81663e9a70327d8e5..061a2596d3b63e7d737d735c4a6cb097bfcf387f 100755 (executable)
@@ -483,6 +483,22 @@ test_expect_success \
        'gitweb_run "p=.git;a=history;f=file"'
 test_debug 'cat gitweb.log'
 
+test_expect_success \
+       'logs: history (implicit HEAD, non-existent file)' \
+       'gitweb_run "p=.git;a=history;f=non-existent"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'logs: history (implicit HEAD, deleted file)' \
+       'git checkout master &&
+        echo "to be deleted" > deleted_file &&
+        git add deleted_file &&
+        git commit -m "Add file to be deleted" &&
+        git rm deleted_file &&
+        git commit -m "Delete file" &&
+        gitweb_run "p=.git;a=history;f=deleted_file"'
+test_debug 'cat gitweb.log'
+
 # ----------------------------------------------------------------------
 # feed generation
 
diff --git a/var.c b/var.c
index 0de0efa2aa3b216a0bc846135a54ed0dd0549f8b..c20ac919bdbe73c8cd7657d468d2dfbccf6d3aa2 100644 (file)
--- a/var.c
+++ b/var.c
@@ -51,11 +51,12 @@ static int show_config(const char *var, const char *value)
 int main(int argc, char **argv)
 {
        const char *val;
+       int nongit;
        if (argc != 2) {
                usage(var_usage);
        }
 
-       setup_git_directory();
+       setup_git_directory_gently(&nongit);
        val = NULL;
 
        if (strcmp(argv[1], "-l") == 0) {
index e125e11d3b63e3dab9077d7b414e83e7ff7d16ad..32f99140205f8969d8a884b3cf9448eec3f1dd16 100644 (file)
@@ -40,7 +40,7 @@ void maybe_flush_or_die(FILE *f, const char *desc)
        }
 }
 
-int read_in_full(int fd, void *buf, size_t count)
+ssize_t read_in_full(int fd, void *buf, size_t count)
 {
        char *p = buf;
        ssize_t total = 0;
@@ -57,7 +57,7 @@ int read_in_full(int fd, void *buf, size_t count)
        return total;
 }
 
-int write_in_full(int fd, const void *buf, size_t count)
+ssize_t write_in_full(int fd, const void *buf, size_t count)
 {
        const char *p = buf;
        ssize_t total = 0;
index b3fd57b79df3513d271c23caf3ab46b3ba405e40..532b4ea2c1960f18e41a5167a60f6de1b481606b 100644 (file)
@@ -8,9 +8,11 @@
 #include "revision.h"
 #include "diffcore.h"
 #include "quote.h"
+#include "run-command.h"
 
 int wt_status_relative_paths = 1;
 int wt_status_use_color = -1;
+int wt_status_submodule_summary;
 static char wt_status_colors[][COLOR_MAXLEN] = {
        "",         /* WT_STATUS_HEADER: normal */
        "\033[32m", /* WT_STATUS_UPDATED: green */
@@ -220,6 +222,36 @@ static void wt_status_print_changed(struct wt_status *s)
        run_diff_files(&rev, 0);
 }
 
+static void wt_status_print_submodule_summary(struct wt_status *s)
+{
+       struct child_process sm_summary;
+       char summary_limit[64];
+       char index[PATH_MAX];
+       const char *env[] = { index, NULL };
+       const char *argv[] = {
+               "submodule",
+               "summary",
+               "--cached",
+               "--for-status",
+               "--summary-limit",
+               summary_limit,
+               s->amend ? "HEAD^" : "HEAD",
+               NULL
+       };
+
+       sprintf(summary_limit, "%d", wt_status_submodule_summary);
+       snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
+
+       memset(&sm_summary, 0, sizeof(sm_summary));
+       sm_summary.argv = argv;
+       sm_summary.env = env;
+       sm_summary.git_cmd = 1;
+       sm_summary.no_stdin = 1;
+       fflush(s->fp);
+       sm_summary.out = dup(fileno(s->fp));    /* run_command closes it */
+       run_command(&sm_summary);
+}
+
 static void wt_status_print_untracked(struct wt_status *s)
 {
        struct dir_struct dir;
@@ -308,6 +340,8 @@ void wt_status_print(struct wt_status *s)
        }
 
        wt_status_print_changed(s);
+       if (wt_status_submodule_summary)
+               wt_status_print_submodule_summary(s);
        wt_status_print_untracked(s);
 
        if (s->verbose && !s->is_initial)
@@ -330,6 +364,13 @@ void wt_status_print(struct wt_status *s)
 
 int git_status_config(const char *k, const char *v)
 {
+       if (!strcmp(k, "status.submodulesummary")) {
+               int is_bool;
+               wt_status_submodule_summary = git_config_bool_or_int(k, v, &is_bool);
+               if (is_bool && wt_status_submodule_summary)
+                       wt_status_submodule_summary = -1;
+               return 0;
+       }
        if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
                wt_status_use_color = git_config_colorbool(k, v, -1);
                return 0;