]> rtime.felk.cvut.cz Git - git.git/commitdiff
Merge branch 'gp/bisect-fix'
authorJunio C Hamano <gitster@pobox.com>
Mon, 12 May 2008 22:44:43 +0000 (15:44 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 12 May 2008 22:44:43 +0000 (15:44 -0700)
* gp/bisect-fix:
  bisect: print an error message when "git rev-list --bisect-vars" fails
  git-bisect.sh: don't accidentally override existing branch "bisect"

138 files changed:
Documentation/RelNotes-1.5.6.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/git-add.txt
Documentation/git-bisect.txt
Documentation/git-branch.txt
Documentation/git-checkout.txt
Documentation/git-cherry-pick.txt
Documentation/git-config.txt
Documentation/git-filter-branch.txt
Documentation/git-fmt-merge-msg.txt
Documentation/git-format-patch.txt
Documentation/git-fsck.txt
Documentation/git-help.txt
Documentation/git-init.txt
Documentation/git-merge.txt
Documentation/git-prune.txt
Documentation/git-pull.txt
Documentation/git-push.txt
Documentation/git-remote.txt
Documentation/git-rev-parse.txt
Documentation/git-revert.txt
Documentation/git-status.txt
Documentation/git-svn.txt
Documentation/git-unpack-objects.txt
Documentation/git-web--browse.txt
Documentation/git.txt
Documentation/gitk.txt
Documentation/hooks.txt
Documentation/merge-config.txt [new file with mode: 0644]
Documentation/merge-options.txt
Documentation/pretty-formats.txt
Documentation/repository-layout.txt
GIT-VERSION-GEN
INSTALL
Makefile
RelNotes
branch.c
builtin-apply.c
builtin-branch.c
builtin-checkout.c
builtin-commit.c
builtin-config.c
builtin-fetch.c
builtin-fmt-merge-msg.c
builtin-gc.c
builtin-init-db.c
builtin-log.c
builtin-push.c
builtin-read-tree.c
builtin-remote.c
builtin-rev-parse.c
builtin-revert.c
builtin-unpack-objects.c
cache.h
combine-diff.c
commit.h
compat/fopen.c
config.c
contrib/completion/git-completion.bash
contrib/emacs/git.el
contrib/hooks/pre-auto-gc-battery [new file with mode: 0644]
diff-lib.c
diff.c
diff.h
diffcore-rename.c
dir.c
environment.c
git-add--interactive.perl
git-bisect.sh
git-clone.sh
git-compat-util.h
git-filter-branch.sh
git-merge.sh
git-mergetool.sh
git-pull.sh
git-rebase.sh
git-repack.sh
git-send-email.perl
git-submodule.sh
git-svn.perl
git.c
gitweb/INSTALL
gitweb/README
gitweb/gitweb.css
gitweb/gitweb.perl
help.c
http-push.c
http-walker.c
http.c
http.h
log-tree.c
log-tree.h
name-hash.c [new file with mode: 0644]
pack-write.c
path.c
perl/Git.pm
pkt-line.c
pretty.c
read-cache.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
symlinks.c
t/lib-git-svn.sh
t/t0002-gitfile.sh [new file with mode: 0755]
t/t1300-repo-config.sh
t/t1301-shared-repo.sh
t/t1503-rev-parse-verify.sh [new file with mode: 0755]
t/t2002-checkout-cache-u.sh
t/t3200-branch.sh
t/t3201-branch-contains.sh
t/t3701-add-interactive.sh
t/t4027-diff-submodule.sh
t/t5300-pack-object.sh
t/t5517-push-mirror.sh
t/t6200-fmt-merge-msg.sh
t/t7003-filter-branch.sh
t/t7401-submodule-summary.sh
t/t7502-status.sh
t/t7506-status-submodule.sh [new file with mode: 0755]
t/t7600-merge.sh
t/t9115-git-svn-dcommit-funky-renames.sh
transport.c
unpack-trees.c
unpack-trees.h
var.c
walker.c
walker.h
write_or_die.c
wt-status.c
wt-status.h

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 04c01c5fdca566b1760e8eed02687607017e89aa..217980f48d9d9ac71860d61313e9afa9c6b7c332 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
@@ -388,6 +399,21 @@ branch.autosetupmerge::
        done when the starting point is either a local branch or remote
        branch. This option defaults to true.
 
+branch.autosetuprebase::
+       When a new branch is created with `git-branch` or `git-checkout`
+       that tracks another branch, this variable tells git to set
+       up pull to rebase instead of merge (see "branch.<name>.rebase").
+       When `never`, rebase is never automatically set to true.
+       When `local`, rebase is set to true for tracked branches of
+       other local branches.
+       When `remote`, rebase is set to true for tracked branches of
+       remote branches.
+       When `always`, rebase will be set to true for all tracking
+       branches.
+       See "branch.autosetupmerge" for details on how to set up a
+       branch to track another branch.
+       This option defaults to never.
+
 branch.<name>.remote::
        When in branch <name>, it tells `git fetch` which remote to fetch.
        If this option is not given, `git fetch` defaults to remote "origin".
@@ -415,7 +441,8 @@ branch.<name>.mergeoptions::
 
 branch.<name>.rebase::
        When true, rebase the branch <name> on top of the fetched branch,
-       instead of merging the default branch from the default remote.
+       instead of merging the default branch from the default remote when
+       "git pull" is run.
        *NOTE*: this is a possibly dangerous operation; do *not* use
        it unless you understand the implications (see linkgit:git-rebase[1]
        for details).
@@ -673,6 +700,36 @@ specified as 'gitcvs.<access_method>.<varname>' (where 'access_method'
 is one of "ext" and "pserver") to make them apply only for the given
 access method.
 
+gui.commitmsgwidth::
+       Defines how wide the commit message window is in the
+       linkgit:git-gui[1]. "75" is the default.
+
+gui.diffcontext::
+       Specifies how many context lines should be used in calls to diff
+       made by the linkgit:git-gui[1]. The default is "5".
+
+gui.matchtrackingbranch::
+       Determines if new branches created with linkgit:git-gui[1] should
+       default to tracking remote branches with matching names or
+       not. Default: "false".
+
+gui.newbranchtemplate::
+       Is used as suggested name when creating new branches using the
+       linkgit:git-gui[1].
+
+gui.pruneduringfetch::
+       "true" if linkgit:git-gui[1] should prune tracking branches when
+       performing a fetch. The default value is "false".
+
+gui.trustmtime::
+       Determines if linkgit:git-gui[1] should trust the file modification
+       timestamp or not. By default the timestamps are not trusted.
+
+gui.spellingdictionary::
+       Specifies the dictionary used for spell checking commit messages in
+       the linkgit:git-gui[1]. When set to "none" spell checking is turned
+       off.
+
 help.browser::
        Specify the browser that will be used to display help in the
        'web' format. See linkgit:git-help[1].
@@ -768,37 +825,16 @@ man.viewer::
        Specify the programs that may be used to display help in the
        'man' format. See linkgit:git-help[1].
 
-merge.summary::
-       Whether to include summaries of merged commits in newly created
-       merge commit messages. False by default.
-
-merge.tool::
-       Controls which merge resolution program is used by
-       linkgit:git-mergetool[1].  Valid built-in values are: "kdiff3",
-       "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and
-       "opendiff".  Any other value is treated is custom merge tool
-       and there must be a corresponing mergetool.<tool>.cmd option.
-
-merge.verbosity::
-       Controls the amount of output shown by the recursive merge
-       strategy.  Level 0 outputs nothing except a final error
-       message if conflicts were detected. Level 1 outputs only
-       conflicts, 2 outputs conflicts and file changes.  Level 5 and
-       above outputs debugging information.  The default is level 2.
-       Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable.
-
-merge.<driver>.name::
-       Defines a human readable name for a custom low-level
-       merge driver.  See linkgit:gitattributes[5] for details.
-
-merge.<driver>.driver::
-       Defines the command that implements a custom low-level
-       merge driver.  See linkgit:gitattributes[5] for details.
-
-merge.<driver>.recursive::
-       Names a low-level merge driver to be used when
-       performing an internal merge between common ancestors.
-       See linkgit:gitattributes[5] for details.
+include::merge-config.txt[]
+
+man.<tool>.cmd::
+       Specify the command to invoke the specified man viewer. The
+       specified command is evaluated in shell with the man page
+       passed as argument. (See linkgit:git-help[1].)
+
+man.<tool>.path::
+       Override the path for the given tool that may be used to
+       display help in the 'man' format. See linkgit:git-help[1].
 
 mergetool.<tool>.path::
        Override the path for the given tool.  This is useful in case
@@ -910,6 +946,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].
@@ -991,6 +1031,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 35e67a06e4248182c05c98b4b9b421bb3e02940a..e0e730b6c44d473ad6cc9a23f7b727ab818d63a5 100644 (file)
@@ -71,7 +71,9 @@ OPTIONS
        the specified filepatterns before exiting.
 
 -u::
-       Update only files that git already knows about. This is similar
+       Update only files that git already knows about, staging modified
+       content for commit and marking deleted files for removal. This
+       is similar
        to what "git commit -a" does in preparation for making a commit,
        except that the update is limited to paths specified on the
        command line. If no paths are specified, all tracked files in the
@@ -98,21 +100,27 @@ those in info/exclude.  See link:repository-layout.html[repository layout].
 
 EXAMPLES
 --------
-git-add Documentation/\\*.txt::
 
-       Adds content from all `\*.txt` files under `Documentation`
-       directory and its subdirectories.
+* Adds content from all `\*.txt` files under `Documentation` directory
+and its subdirectories:
++
+------------
+$ git add Documentation/\\*.txt
+------------
 +
 Note that the asterisk `\*` is quoted from the shell in this
 example; this lets the command to include the files from
 subdirectories of `Documentation/` directory.
 
-git-add git-*.sh::
-
-       Considers adding content from all git-*.sh scripts.
-       Because this example lets shell expand the asterisk
-       (i.e. you are listing the files explicitly), it does not
-       consider `subdir/git-foo.sh`.
+* Considers adding content from all git-*.sh scripts:
++
+------------
+$ git add git-*.sh
+------------
++
+Because this example lets shell expand the asterisk (i.e. you are
+listing the files explicitly), it does not consider
+`subdir/git-foo.sh`.
 
 Interactive mode
 ----------------
index 0855b98b281c95830850b01a3db59a1ceb8f6eb2..0b8b0ebba758871ac9c79e729664b5057128e9ac 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
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -217,6 +224,55 @@ tree to the pristine state.  Finally the "run" script can exit with
 the status of the real test to let "git bisect run" command loop to
 know the outcome.
 
+EXAMPLES
+--------
+
+* Automatically bisect a broken build between v1.2 and HEAD:
++
+------------
+$ git bisect start HEAD v1.2 --      # HEAD is bad, v1.2 is good
+$ git bisect run make                # "make" builds the app
+------------
+
+* Automatically bisect a broken test suite:
++
+------------
+$ cat ~/test.sh
+#!/bin/sh
+make || exit 125                   # this "skip"s broken builds
+make test                          # "make test" runs the test suite
+$ git bisect start v1.3 v1.1 --    # v1.3 is bad, v1.1 is good
+$ git bisect run ~/test.sh
+------------
++
+Here we use a "test.sh" custom script. In this script, if "make"
+fails, we "skip" the current commit.
++
+It's safer to use a custom script outside the repo to prevent
+interactions between the bisect, make and test processes and the
+script.
++
+And "make test" should "exit 0", if the test suite passes, and
+"exit 1" (for example) otherwise.
+
+* Automatically bisect a broken test case:
++
+------------
+$ cat ~/test.sh
+#!/bin/sh
+make || exit 125                     # this "skip"s broken builds
+~/check_test_case.sh                 # does the test case passes ?
+$ git bisect start HEAD HEAD~10 --   # culprit is among the last 10
+$ git bisect run ~/test.sh
+------------
++
+Here "check_test_case.sh" should "exit 0", if the test case passes,
+and "exit 1" (for example) otherwise.
++
+It's safer if both "test.sh" and "check_test_case.sh" scripts are
+outside the repo to prevent interactions between the bisect, make and
+test processes and the scripts.
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
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 f0beb412e655f0e188b8a1427b41f72b6cc61239..ca048f46f664a52eb5b0174ff4177eba5b20e702 100644 (file)
@@ -7,7 +7,7 @@ git-cherry-pick - Apply the change introduced by an existing commit
 
 SYNOPSIS
 --------
-'git-cherry-pick' [--edit] [-n] [-m parent-number] [-x] <commit>
+'git-cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] <commit>
 
 DESCRIPTION
 -----------
@@ -64,6 +64,9 @@ OPTIONS
 This is useful when cherry-picking more than one commits'
 effect to your working tree in a row.
 
+-s|--signoff::
+       Add Signed-off-by line at the end of the commit message.
+
 
 Author
 ------
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 6454e49bf2805fcf0a000ed6f96ff9f73d718299..8d80f0d074c92fccc2a9746c5f374c1626fdde97 100644 (file)
@@ -133,10 +133,16 @@ use "--tag-name-filter cat" to simply update the tags.  In this
 case, be very careful and make sure you have the old tags
 backed up in case the conversion has run afoul.
 +
-Note that there is currently no support for proper rewriting of
-tag objects; in layman terms, if the tag has a message or signature
-attached, the rewritten tag won't have it.  Sorry.  (It is by
-definition impossible to preserve signatures at any rate.)
+Nearly proper rewriting of tag objects is supported. If the tag has
+a message attached, a new tag object will be created with the same message,
+author, and timestamp. If the tag has a signature attached, the
+signature will be stripped. It is by definition impossible to preserve
+signatures. The reason this is "nearly" proper, is because ideally if
+the tag did not change (points to the same object, has the same name, etc.)
+it should retain any signature. That is not the case, signatures will always
+be removed, buyer beware. There is also no support for changing the
+author or timestamp (or the tag message for that matter). Tags which point
+to other tags will be rewritten to point to the underlying commit.
 
 --subdirectory-filter <directory>::
        Only look at the history which touches the given subdirectory.
index 8615ae353e91fa1de0d17c7c3f8ea777ac62e209..457cf425619dc1680fae510aceb20574b02be602 100644 (file)
@@ -9,8 +9,8 @@ git-fmt-merge-msg - Produce a merge commit message
 SYNOPSIS
 --------
 [verse]
-git-fmt-merge-msg [--summary | --no-summary] <$GIT_DIR/FETCH_HEAD
-git-fmt-merge-msg [--summary | --no-summary] -F <file>
+git-fmt-merge-msg [--log | --no-log] <$GIT_DIR/FETCH_HEAD
+git-fmt-merge-msg [--log | --no-log] -F <file>
 
 DESCRIPTION
 -----------
@@ -24,15 +24,19 @@ automatically invoking `git-merge`.
 OPTIONS
 -------
 
---summary::
+--log::
        In addition to branch names, populate the log message with
        one-line descriptions from the actual commits that are being
        merged.
 
---no-summary::
+--no-log::
        Do not list one-line descriptions from the actual commits being
        merged.
 
+--summary,--no-summary::
+       Synonyms to --log and --no-log; these are deprecated and will be
+       removed in the future.
+
 --file <file>, -F <file>::
        Take the list of merged objects from <file> instead of
        stdin.
@@ -40,10 +44,14 @@ OPTIONS
 CONFIGURATION
 -------------
 
-merge.summary::
+merge.log::
        Whether to include summaries of merged commits in newly
        merge commit messages. False by default.
 
+merge.summary::
+       Synonym to `merge.log`; this is deprecated and will be removed in
+       the future.
+
 SEE ALSO
 --------
 linkgit:git-merge[1]
index b5207b76040d02e34452175779340658b943a84e..7548a21d352f0625a114f48c3063612f2a063436 100644 (file)
@@ -156,6 +156,12 @@ want a filename like `0001-description-of-my-change.patch`, and
 the first letter does not have to be a dot.  Leaving it empty would
 not add any suffix.
 
+--no-binary::
+       Don't output contents of changes in binary files, just take note
+       that they differ.  Note that this disable the patch to be properly
+       applied.  By default the contents of changes in those files are
+       encoded in the patch.
+
 CONFIGURATION
 -------------
 You can specify extra mail header lines to be added to each message
@@ -174,32 +180,47 @@ and file suffix, and number patches when outputting more than one.
 EXAMPLES
 --------
 
-git-format-patch -k --stdout R1..R2 | git-am -3 -k::
-       Extract commits between revisions R1 and R2, and apply
-       them on top of the current branch using `git-am` to
-       cherry-pick them.
-
-git-format-patch origin::
-       Extract all commits which are in the current branch but
-       not in the origin branch.  For each commit a separate file
-       is created in the current directory.
-
-git-format-patch \--root origin::
-       Extract all commits that lead to 'origin' since the
-       inception of the project.
-
-git-format-patch -M -B origin::
-       The same as the previous one.  Additionally, it detects
-       and handles renames and complete rewrites intelligently to
-       produce a renaming patch.  A renaming patch reduces the
-       amount of text output, and generally makes it easier to
-       review it.  Note that the "patch" program does not
-       understand renaming patches, so use it only when you know
-       the recipient uses git to apply your patch.
-
-git-format-patch -3::
-       Extract three topmost commits from the current branch
-       and format them as e-mailable patches.
+* Extract commits between revisions R1 and R2, and apply them on top of
+the current branch using `git-am` to cherry-pick them:
++
+------------
+$ git format-patch -k --stdout R1..R2 | git-am -3 -k
+------------
+
+* Extract all commits which are in the current branch but not in the
+origin branch:
++
+------------
+$ git format-patch origin
+------------
++
+For each commit a separate file is created in the current directory.
+
+* Extract all commits that lead to 'origin' since the inception of the
+project:
++
+------------
+$ git format-patch \--root origin
+------------
+
+* The same as the previous one:
++
+------------
+$ git format-patch -M -B origin
+------------
++
+Additionally, it detects and handles renames and complete rewrites
+intelligently to produce a renaming patch.  A renaming patch reduces
+the amount of text output, and generally makes it easier to review it.
+Note that the "patch" program does not understand renaming patches, so
+use it only when you know the recipient uses git to apply your patch.
+
+* Extract three topmost commits from the current branch and format them
+as e-mailable patches:
++
+------------
+$ git format-patch -3
+------------
 
 See Also
 --------
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 be2ae53b90610d6c89fdf281634d06609562101b..bfbba9e235495b0a7e31b4d3ed558bef105ad02d 100644 (file)
@@ -82,28 +82,75 @@ man.viewer
 ~~~~~~~~~~
 
 The 'man.viewer' config variable will be checked if the 'man' format
-is chosen. Only the following values are currently supported:
+is chosen. The following values are currently supported:
 
 * "man": use the 'man' program as usual,
 * "woman": use 'emacsclient' to launch the "woman" mode in emacs
 (this only works starting with emacsclient versions 22),
-* "konqueror": use a man KIO slave in konqueror.
+* "konqueror": use 'kfmclient' to open the man page in a new konqueror
+tab (see 'Note about konqueror' below).
 
-Multiple values may be given to this configuration variable. Their
-corresponding programs will be tried in the order listed in the
-configuration file.
+Values for other tools can be used if there is a corresponding
+'man.<tool>.cmd' configuration entry (see below).
+
+Multiple values may be given to the 'man.viewer' configuration
+variable. Their corresponding programs will be tried in the order
+listed in the configuration file.
 
 For example, this configuration:
 
+------------------------------------------------
        [man]
                viewer = konqueror
                viewer = woman
+------------------------------------------------
 
 will try to use konqueror first. But this may fail (for example if
 DISPLAY is not set) and in that case emacs' woman mode will be tried.
 
 If everything fails the 'man' program will be tried anyway.
 
+man.<tool>.path
+~~~~~~~~~~~~~~~
+
+You can explicitly provide a full path to your preferred man viewer by
+setting the configuration variable 'man.<tool>.path'. For example, you
+can configure the absolute path to konqueror by setting
+'man.konqueror.path'. Otherwise, 'git help' assumes the tool is
+available in PATH.
+
+man.<tool>.cmd
+~~~~~~~~~~~~~~
+
+When the man viewer, specified by the 'man.viewer' configuration
+variables, is not among the supported ones, then the corresponding
+'man.<tool>.cmd' configuration variable will be looked up. If this
+variable exists then the specified tool will be treated as a custom
+command and a shell eval will be used to run the command with the man
+page passed as arguments.
+
+Note about konqueror
+~~~~~~~~~~~~~~~~~~~~
+
+When 'konqueror' is specified in the 'man.viewer' configuration
+variable, we launch 'kfmclient' to try to open the man page on an
+already opened konqueror in a new tab if possible.
+
+For consistency, we also try such a trick if 'man.konqueror.path' is
+set to something like 'A_PATH_TO/konqueror'. That means we will try to
+launch 'A_PATH_TO/kfmclient' instead.
+
+If you really want to use 'konqueror', then you can use something like
+the following:
+
+------------------------------------------------
+       [man]
+               viewer = konq
+
+       [man "konq"]
+               cmd = A_PATH_TO/konqueror
+------------------------------------------------
+
 Note about git config --global
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
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 c136b1069230af55b62ba95a1b40bdcc019b7b42..ef1f055c8574e928c8e05c820f2f1cc962e3d422 100644 (file)
@@ -9,7 +9,7 @@ git-merge - Join two or more development histories together
 SYNOPSIS
 --------
 [verse]
-'git-merge' [-n] [--summary] [--no-commit] [--squash] [-s <strategy>]...
+'git-merge' [-n] [--stat] [--no-commit] [--squash] [-s <strategy>]...
        [-m <msg>] <remote> <remote>...
 'git-merge' <msg> HEAD <remote>...
 
@@ -46,18 +46,7 @@ linkgit:git-reset[1].
 
 CONFIGURATION
 -------------
-
-merge.summary::
-       Whether to include summaries of merged commits in newly
-       created merge commit. False by default.
-
-merge.verbosity::
-       Controls the amount of output shown by the recursive merge
-       strategy.  Level 0 outputs nothing except a final error
-       message if conflicts were detected. Level 1 outputs only
-       conflicts, 2 outputs conflicts and file changes.  Level 5 and
-       above outputs debugging information.  The default is level 2.
-       Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable.
+include::merge-config.txt[]
 
 branch.<name>.mergeoptions::
        Sets default options for merging into branch <name>. The syntax and
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 3405ca09e85e9c81b9d48c753a2f117e7edca4eb..66304f02557bf57721af215e6593db16ae0c40dc 100644 (file)
@@ -111,40 +111,58 @@ rules apply:
 EXAMPLES
 --------
 
-git pull, git pull origin::
-       Update the remote-tracking branches for the repository
-       you cloned from, then merge one of them into your
-       current branch.  Normally the branch merged in is
-       the HEAD of the remote repository, but the choice is
-       determined by the branch.<name>.remote and
-       branch.<name>.merge options; see linkgit:git-config[1]
-       for details.
-
-git pull origin next::
-       Merge into the current branch the remote branch `next`;
-       leaves a copy of `next` temporarily in FETCH_HEAD, but
-       does not update any remote-tracking branches.
-
-git pull . fixes enhancements::
-       Bundle local branch `fixes` and `enhancements` on top of
-       the current branch, making an Octopus merge.  This `git pull .`
-       syntax is equivalent to `git merge`.
-
-git pull -s ours . obsolete::
-       Merge local branch `obsolete` into the current branch,
-       using `ours` merge strategy.
-
-git pull --no-commit . maint::
-       Merge local branch `maint` into the current branch, but
-       do not make a commit automatically.  This can be used
-       when you want to include further changes to the merge,
-       or want to write your own merge commit message.
+* Update the remote-tracking branches for the repository
+  you cloned from, then merge one of them into your
+  current branch:
++
+------------------------------------------------
+$ git pull, git pull origin
+------------------------------------------------
++
+Normally the branch merged in is the HEAD of the remote repository,
+but the choice is determined by the branch.<name>.remote and
+branch.<name>.merge options; see linkgit:git-config[1] for details.
+
+* Merge into the current branch the remote branch `next`:
++
+------------------------------------------------
+$ git pull origin next
+------------------------------------------------
++
+This leaves a copy of `next` temporarily in FETCH_HEAD, but
+does not update any remote-tracking branches.
+
+* Bundle local branch `fixes` and `enhancements` on top of
+  the current branch, making an Octopus merge:
++
+------------------------------------------------
+$ git pull . fixes enhancements
+------------------------------------------------
++
+This `git pull .` syntax is equivalent to `git merge`.
+
+* Merge local branch `obsolete` into the current branch, using `ours`
+  merge strategy:
++
+------------------------------------------------
+$ git pull -s ours . obsolete
+------------------------------------------------
+
+* Merge local branch `maint` into the current branch, but do not make
+  a commit automatically:
++
+------------------------------------------------
+$ git pull --no-commit . maint
+------------------------------------------------
++
+This can be used when you want to include further changes to the
+merge, or want to write your own merge commit message.
 +
 You should refrain from abusing this option to sneak substantial
 changes into a merge commit.  Small fixups like bumping
 release/version name would be acceptable.
 
-Command line pull of multiple branches from one repository::
+* Command line pull of multiple branches from one repository:
 +
 ------------------------------------------------
 $ git checkout master
@@ -152,12 +170,12 @@ $ git fetch origin +pu:pu maint:tmp
 $ git pull . tmp
 ------------------------------------------------
 +
-This updates (or creates, as necessary) branches `pu` and `tmp`
-in the local repository by fetching from the branches
-(respectively) `pu` and `maint` from the remote repository.
+This updates (or creates, as necessary) branches `pu` and `tmp` in
+the local repository by fetching from the branches (respectively)
+`pu` and `maint` from the remote repository.
 +
-The `pu` branch will be updated even if it is does not
-fast-forward; the others will not be.
+The `pu` branch will be updated even if it is does not fast-forward;
+the others will not be.
 +
 The final command then merges the newly fetched `tmp` into master.
 
index 05859491378243639e81c9c8ab7deffb90d83d07..f06d94e318d6d0bf918430bafc746e96a2abe7a1 100644 (file)
@@ -70,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.
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 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 93e20f7752f0d48faca153eef144845b2db9ce1d..13ceabbcc8851344e54e13773a2a553360f931c4 100644 (file)
@@ -7,7 +7,7 @@ git-revert - Revert an existing commit
 
 SYNOPSIS
 --------
-'git-revert' [--edit | --no-edit] [-n] [-m parent-number] <commit>
+'git-revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>
 
 DESCRIPTION
 -----------
@@ -51,6 +51,9 @@ OPTIONS
 This is useful when reverting more than one commits'
 effect to your working tree in a row.
 
+-s|--signoff::
+       Add Signed-off-by line at the end of the commit message.
+
 
 Author
 ------
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 bec9accc8915c712bdea5e8975598e4f26db633f..c6b56b4ef35879124f9e1bb8468a59761d09cb3b 100644 (file)
@@ -166,11 +166,18 @@ environment). This command has the same behaviour.
 Any other arguments are passed directly to `git log'
 
 'blame'::
-       Show what revision and author last modified each line of a file. This is
-       identical to `git blame', but SVN revision numbers are shown instead of git
-       commit hashes.
+       Show what revision and author last modified each line of a file. The
+       output of this mode is format-compatible with the output of
+       `svn blame' by default. Like the SVN blame command,
+       local uncommitted changes in the working copy are ignored;
+       the version of the file in the HEAD revision is annotated. Unknown
+       arguments are passed directly to git-blame.
 +
-All arguments are passed directly to `git blame'.
+--git-format;;
+       Produce output in the same format as `git blame', but with
+       SVN revision numbers instead of git commit hashes. In this mode,
+       changes that haven't been committed to SVN (including local
+       working-copy edits) are shown as revision 0.
 
 --
 'find-rev'::
@@ -188,6 +195,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 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 ddbae5b194790d7f76b53885e3d4ea642fda034a..92ef57456581d98f066cabd2e6be84e73b7ae063 100644 (file)
@@ -20,7 +20,7 @@ The following browsers (or commands) are currently supported:
 
 * firefox (this is the default under X Window when not using KDE)
 * iceweasel
-* konqueror (this is the default under KDE)
+* konqueror (this is the default under KDE, see 'Note about konqueror' below)
 * w3m (this is the default outside graphical environments)
 * links
 * lynx
@@ -71,6 +71,28 @@ variable exists then "git web--browse" will treat the specified tool
 as a custom command and will use a shell eval to run the command with
 the URLs passed as arguments.
 
+Note about konqueror
+--------------------
+
+When 'konqueror' is specified by the a command line option or a
+configuration variable, we launch 'kfmclient' to try to open the HTML
+man page on an already opened konqueror in a new tab if possible.
+
+For consistency, we also try such a trick if 'brower.konqueror.path' is
+set to something like 'A_PATH_TO/konqueror'. That means we will try to
+launch 'A_PATH_TO/kfmclient' instead.
+
+If you really want to use 'konqueror', then you can use something like
+the following:
+
+------------------------------------------------
+       [web]
+               browser = konq
+
+       [browser "konq"]
+               cmd = A_PATH_TO/konqueror
+------------------------------------------------
+
 Note about git config --global
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
index a070e078e75389d1ea252b8f1b80bb5bc707e1aa..6f445b1e3bbefccdb865f8cb93fb470892b94e41 100644 (file)
@@ -143,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
@@ -151,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 ed3ba83c530411801dd1cd8980cad4d005962690..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
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.
diff --git a/Documentation/merge-config.txt b/Documentation/merge-config.txt
new file mode 100644 (file)
index 0000000..9719311
--- /dev/null
@@ -0,0 +1,35 @@
+merge.stat::
+       Whether to print the diffstat berween ORIG_HEAD and merge result
+       at the end of the merge.  True by default.
+
+merge.log::
+       Whether to include summaries of merged commits in newly created
+       merge commit messages. False by default.
+
+merge.tool::
+       Controls which merge resolution program is used by
+       linkgit:git-mergetool[1].  Valid built-in values are: "kdiff3",
+       "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and
+       "opendiff".  Any other value is treated is custom merge tool
+       and there must be a corresponing mergetool.<tool>.cmd option.
+
+merge.verbosity::
+       Controls the amount of output shown by the recursive merge
+       strategy.  Level 0 outputs nothing except a final error
+       message if conflicts were detected. Level 1 outputs only
+       conflicts, 2 outputs conflicts and file changes.  Level 5 and
+       above outputs debugging information.  The default is level 2.
+       Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable.
+
+merge.<driver>.name::
+       Defines a human readable name for a custom low-level
+       merge driver.  See linkgit:gitattributes[5] for details.
+
+merge.<driver>.driver::
+       Defines the command that implements a custom low-level
+       merge driver.  See linkgit:gitattributes[5] for details.
+
+merge.<driver>.recursive::
+       Names a low-level merge driver to be used when
+       performing an internal merge between common ancestors.
+       See linkgit:gitattributes[5] for details.
index 9f1fc825503a7c972fe162f4e2a87781e0f783f3..f37a77648934fcd8c707cfc1446ef5425cc7fa79 100644 (file)
@@ -1,10 +1,23 @@
---summary::
+--stat::
        Show a diffstat at the end of the merge. The diffstat is also
-       controlled by the configuration option merge.diffstat.
+       controlled by the configuration option merge.stat.
 
--n, \--no-summary::
+-n, \--no-stat::
        Do not show diffstat at the end of the merge.
 
+--summary, \--no-summary::
+       Synonyms to --stat and --no-stat; these are deprecated and will be
+       removed in the future.
+
+--log::
+       In addition to branch names, populate the log message with
+       one-line descriptions from the actual commits that are being
+       merged.
+
+--no-log::
+       Do not list one-line descriptions from the actual commits being
+       merged.
+
 --no-commit::
        Perform the merge but pretend the merge failed and do
        not autocommit, to give the user a chance to inspect and
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 f60bab896bd9a54714eff3596e38c9ffb9d38ac8..3cabc92e7a1f8ce30eb6e5151a4c81c82468bcee 100755 (executable)
@@ -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..649ee56c96f44c6b403907bf03826c2a3f577315 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
@@ -421,6 +423,7 @@ LIB_OBJS += log-tree.o
 LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
 LIB_OBJS += merge-file.o
+LIB_OBJS += name-hash.o
 LIB_OBJS += object.o
 LIB_OBJS += pack-check.o
 LIB_OBJS += pack-revindex.o
@@ -445,6 +448,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 +1038,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 730df98bdce7393f593534542beaa6119aa9776a..e29d6504d9e8fe45d3f7ff7f82f1e8f1f57b9f6f 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.5.5.1.txt
\ No newline at end of file
+Documentation/RelNotes-1.5.6.txt
\ No newline at end of file
index daf862e728622f9a59a2721e5cd297394be6c9da..56e949232cac1fe9dbe23aa93b50fe2f7c55c07d 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -32,6 +32,21 @@ static int find_tracked_branch(struct remote *remote, void *priv)
        return 0;
 }
 
+static int should_setup_rebase(const struct tracking *tracking)
+{
+       switch (autorebase) {
+       case AUTOREBASE_NEVER:
+               return 0;
+       case AUTOREBASE_LOCAL:
+               return tracking->remote == NULL;
+       case AUTOREBASE_REMOTE:
+               return tracking->remote != NULL;
+       case AUTOREBASE_ALWAYS:
+               return 1;
+       }
+       return 0;
+}
+
 /*
  * This is called when new_ref is branched off of orig_ref, and tries
  * to infer the settings for branch.<new_ref>.{remote,merge} from the
@@ -69,9 +84,14 @@ static int setup_tracking(const char *new_ref, const char *orig_ref,
        git_config_set(key, tracking.remote ?  tracking.remote : ".");
        sprintf(key, "branch.%s.merge", new_ref);
        git_config_set(key, tracking.src ? tracking.src : orig_ref);
-       free(tracking.src);
        printf("Branch %s set up to track %s branch %s.\n", new_ref,
                tracking.remote ? "remote" : "local", orig_ref);
+       if (should_setup_rebase(&tracking)) {
+               sprintf(key, "branch.%s.rebase", new_ref);
+               git_config_set(key, "true");
+               printf("This branch will rebase on pull.\n");
+       }
+       free(tracking.src);
 
        return 0;
 }
index 30d26e57b30821d6b11efe4f493da6d35fb1dc18..1103625a4a923d92ca8d335d1a872236630541e5 100644 (file)
@@ -2247,7 +2247,7 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists)
                 * In such a case, path "new_name" does not exist as
                 * far as git is concerned.
                 */
-               if (has_symlink_leading_path(new_name, NULL))
+               if (has_symlink_leading_path(strlen(new_name), new_name))
                        return 0;
 
                return error("%s: already exists in working directory", new_name);
@@ -2981,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);
 }
 
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 cf9875c13da9c194e1f4ad4ce456ed09808b849b..10ec137ccec7d7f281562817d81a1035c4aa6124 100644 (file)
@@ -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 52a05c20bbf4d6d70ac293e3960ead33454357fa..a65c2b8c37c2f7785d76ec1f4e3692ee52dab8e9 100644 (file)
@@ -101,7 +101,7 @@ static struct option builtin_commit_options[] = {
        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"),
+       OPT_BOOLEAN('u', "untracked-files", &untracked_files, "show all untracked files"),
        OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
        OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
 
@@ -175,9 +175,11 @@ static void add_remove_files(struct path_list *list)
 {
        int i;
        for (i = 0; i < list->nr; i++) {
+               struct stat st;
                struct path_list_item *p = &(list->items[i]);
-               if (file_exists(p->path))
-                       add_file_to_cache(p->path, 0);
+
+               if (!lstat(p->path, &st))
+                       add_to_cache(p->path, &st, 0);
                else
                        remove_file_from_cache(p->path);
        }
@@ -745,10 +747,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 +810,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 167f948036170fd2ec5a8e3bd3ca20359d8c9ee5..f6584ecea165704208059b632bb1a1ac2b6a6d1f 100644 (file)
@@ -360,12 +360,10 @@ static int store_updated_refs(const char *url, struct ref *ref_map)
 
                if (ref)
                        update_local_ref(ref, what, verbose, note);
-               else if (verbose)
+               else
                        sprintf(note, "* %-*s %-*s -> FETCH_HEAD",
                                SUMMARY_WIDTH, *kind ? kind : "branch",
                                 REFCOL_WIDTH, *what ? what : "HEAD");
-               else
-                       *note = '\0';
                if (*note) {
                        if (!shown_url) {
                                fprintf(stderr, "From %.*s\n",
@@ -510,10 +508,8 @@ static void find_non_local_tags(struct transport *transport,
                     will_fetch(head, ref->old_sha1))) {
                        path_list_insert(ref_name, &new_refs);
 
-                       rm = alloc_ref(strlen(ref_name) + 1);
-                       strcpy(rm->name, ref_name);
-                       rm->peer_ref = alloc_ref(strlen(ref_name) + 1);
-                       strcpy(rm->peer_ref->name, ref_name);
+                       rm = alloc_ref_from_str(ref_name);
+                       rm->peer_ref = alloc_ref_from_str(ref_name);
                        hashcpy(rm->old_sha1, ref_sha1);
 
                        **tail = rm;
index 7077d524776e748e0add4ff85478cd62512add15..b72cb59e6a6aab33e7170826242d82785e4fa1e4 100644 (file)
@@ -6,13 +6,18 @@
 #include "tag.h"
 
 static const char *fmt_merge_msg_usage =
-       "git-fmt-merge-msg [--summary] [--no-summary] [--file <file>]";
+       "git-fmt-merge-msg [--log] [--no-log] [--file <file>]";
 
 static int merge_summary;
 
 static int fmt_merge_msg_config(const char *key, const char *value)
 {
-       if (!strcmp("merge.summary", key))
+       static int found_merge_log = 0;
+       if (!strcmp("merge.log", key)) {
+               found_merge_log = 1;
+               merge_summary = git_config_bool(key, value);
+       }
+       if (!found_merge_log && !strcmp("merge.summary", key))
                merge_summary = git_config_bool(key, value);
        return 0;
 }
@@ -258,9 +263,10 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
        git_config(fmt_merge_msg_config);
 
        while (argc > 1) {
-               if (!strcmp(argv[1], "--summary"))
+               if (!strcmp(argv[1], "--log") || !strcmp(argv[1], "--summary"))
                        merge_summary = 1;
-               else if (!strcmp(argv[1], "--no-summary"))
+               else if (!strcmp(argv[1], "--no-log")
+                               || !strcmp(argv[1], "--no-summary"))
                        merge_summary = 0;
                else if (!strcmp(argv[1], "-F") || !strcmp(argv[1], "--file")) {
                        if (argc < 3)
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..80a01f8d44a40608e989b1cc1973a28c0b9040a5 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;
 
        /*
@@ -756,6 +757,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int thread = 0;
        int cover_letter = 0;
        int boundary_count = 0;
+       int no_binary_diff = 0;
        struct commit *origin = NULL, *head = NULL;
        const char *in_reply_to = NULL;
        struct patch_ids ids;
@@ -769,7 +771,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;
@@ -862,6 +863,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        fmt_patch_suffix = argv[i] + 9;
                else if (!strcmp(argv[i], "--cover-letter"))
                        cover_letter = 1;
+               else if (!strcmp(argv[i], "--no-binary"))
+                       no_binary_diff = 1;
                else
                        argv[j++] = argv[i];
        }
@@ -914,7 +917,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        if (!rev.diffopt.output_format)
                rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH;
 
-       if (!DIFF_OPT_TST(&rev.diffopt, TEXT))
+       if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
                DIFF_OPT_SET(&rev.diffopt, BINARY);
 
        if (!output_directory && !use_stdout)
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 e9cfd2bbc5539ee0c9c048798383b837ff63991b..7ac30883bc72c51b0227828bef5758751e524f65 100644 (file)
@@ -40,7 +40,7 @@ static int read_cache_unmerged(void)
        for (i = 0; i < active_nr; i++) {
                struct cache_entry *ce = active_cache[i];
                if (ce_stage(ce)) {
-                       remove_index_entry(ce);
+                       remove_name_hash(ce);
                        if (last && !strcmp(ce->name, last->name))
                                continue;
                        cache_tree_invalidate_path(active_cache_tree, ce->name);
index 4149f3b3ce4bed091200fdff539e459aa39fb06d..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);
@@ -116,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;
 
@@ -385,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..f8d8548e9cddd7d2d123fcf0fdee513754df86c1 100644 (file)
@@ -28,8 +28,6 @@ static int symbolic;
 static int abbrev;
 static int output_sq;
 
-static int revs_count;
-
 /*
  * Some arguments are relevant "revision" arguments,
  * others are about output format or other details.
@@ -102,7 +100,6 @@ static void show_rev(int type, const unsigned char *sha1, const char *name)
        if (!(filter & DO_REVS))
                return;
        def = NULL;
-       revs_count++;
 
        if (type != show_type)
                putchar('^');
@@ -150,7 +147,7 @@ static int show_flag(const char *arg)
        return 0;
 }
 
-static void show_default(void)
+static int show_default(void)
 {
        const char *s = def;
 
@@ -160,9 +157,10 @@ static void show_default(void)
                def = NULL;
                if (!get_sha1(s, sha1)) {
                        show_rev(NORMAL, sha1, s);
-                       return;
+                       return 1;
                }
        }
+       return 0;
 }
 
 static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
@@ -365,10 +363,19 @@ 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, revs_count = 0, type = 0;
        unsigned char sha1[20];
+       const char *name = NULL;
 
        if (argc > 1 && !strcmp("--parseopt", argv[1]))
                return cmd_parseopt(argc - 1, argv + 1, prefix);
@@ -432,6 +439,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,30 +560,41 @@ 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;
                }
 
                /* Not a flag argument */
                if (try_difference(arg))
                        continue;
-               if (!get_sha1(arg, sha1)) {
-                       show_rev(NORMAL, sha1, arg);
-                       continue;
+               name = arg;
+               type = NORMAL;
+               if (*arg == '^') {
+                       name++;
+                       type = REVERSED;
                }
-               if (*arg == '^' && !get_sha1(arg+1, sha1)) {
-                       show_rev(REVERSED, sha1, arg+1);
+               if (!get_sha1(name, sha1)) {
+                       if (verify)
+                               revs_count++;
+                       else
+                               show_rev(type, sha1, name);
                        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");
+       if (verify) {
+               if (revs_count == 1) {
+                       show_rev(type, sha1, name);
+                       return 0;
+               } else if (revs_count == 0 && show_default())
+                       return 0;
+               die_no_single_rev(quiet);
+       } else
+               show_default();
        return 0;
 }
index 607a2f0337c3d3f1fb8bdac7443e3a7f56e92305..2b57525d7234086bdf2022d13fa405e5007627fb 100644 (file)
@@ -33,7 +33,7 @@ static const char * const cherry_pick_usage[] = {
        NULL
 };
 
-static int edit, no_replay, no_commit, mainline;
+static int edit, no_replay, no_commit, mainline, signoff;
 static enum { REVERT, CHERRY_PICK } action;
 static struct commit *commit;
 
@@ -53,6 +53,7 @@ static void parse_args(int argc, const char **argv)
                OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"),
                OPT_BOOLEAN('x', NULL, &no_replay, "append commit name when cherry-picking"),
                OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"),
+               OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
                OPT_INTEGER('m', "mainline", &mainline, "parent number"),
                OPT_END(),
        };
@@ -404,10 +405,19 @@ static int revert_or_cherry_pick(int argc, const char **argv)
         */
 
        if (!no_commit) {
-               if (edit)
-                       return execl_git_cmd("commit", "-n", NULL);
-               else
-                       return execl_git_cmd("commit", "-n", "-F", defmsg, NULL);
+               /* 6 is max possible length of our args array including NULL */
+               const char *args[6];
+               int i = 0;
+               args[i++] = "commit";
+               args[i++] = "-n";
+               if (signoff)
+                       args[i++] = "-s";
+               if (!edit) {
+                       args[i++] = "-F";
+                       args[i++] = defmsg;
+               }
+               args[i] = NULL;
+               return execv_git_cmd(args);
        }
        free(reencoded_message);
 
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);
diff --git a/cache.h b/cache.h
index 2a1e7ec6b2bf712af80813936b5aed434d02090e..9cee9a5f3fb2e51f617dcf9f92defcd596b97c10 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -133,6 +133,7 @@ struct cache_entry {
 #define CE_UPDATE    (0x10000)
 #define CE_REMOVE    (0x20000)
 #define CE_UPTODATE  (0x40000)
+#define CE_ADDED     (0x80000)
 
 #define CE_HASHED    (0x100000)
 #define CE_UNHASHED  (0x200000)
@@ -153,20 +154,6 @@ static inline void copy_cache_entry(struct cache_entry *dst, struct cache_entry
        dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
 }
 
-/*
- * We don't actually *remove* it, we can just mark it invalid so that
- * we won't find it in lookups.
- *
- * Not only would we have to search the lists (simple enough), but
- * we'd also have to rehash other hash buckets in case this makes the
- * hash bucket empty (common). So it's much better to just mark
- * it.
- */
-static inline void remove_index_entry(struct cache_entry *ce)
-{
-       ce->ce_flags |= CE_UNHASHED;
-}
-
 static inline unsigned create_ce_flags(size_t len, unsigned stage)
 {
        if (len >= CE_NAMEMASK)
@@ -241,6 +228,23 @@ struct index_state {
 
 extern struct index_state the_index;
 
+/* Name hashing */
+extern void add_name_hash(struct index_state *istate, struct cache_entry *ce);
+/*
+ * We don't actually *remove* it, we can just mark it invalid so that
+ * we won't find it in lookups.
+ *
+ * Not only would we have to search the lists (simple enough), but
+ * we'd also have to rehash other hash buckets in case this makes the
+ * hash bucket empty (common). So it's much better to just mark
+ * it.
+ */
+static inline void remove_name_hash(struct cache_entry *ce)
+{
+       ce->ce_flags |= CE_UNHASHED;
+}
+
+
 #ifndef NO_THE_INDEX_COMPATIBILITY_MACROS
 #define active_cache (the_index.cache)
 #define active_nr (the_index.cache_nr)
@@ -257,11 +261,12 @@ extern struct index_state the_index;
 #define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option))
 #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
 #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
+#define add_to_cache(path, st, verbose) add_to_index(&the_index, (path), (st), (verbose))
 #define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose))
 #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
 #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
 #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
-#define cache_name_exists(name, namelen) index_name_exists(&the_index, (name), (namelen))
+#define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
 #endif
 
 enum object_type {
@@ -311,6 +316,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"
 
@@ -350,7 +356,7 @@ extern int write_index(const struct index_state *, int newfd);
 extern int discard_index(struct index_state *);
 extern int unmerged_index(const struct index_state *);
 extern int verify_path(const char *path);
-extern int index_name_exists(struct index_state *istate, const char *name, int namelen);
+extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase);
 extern int index_name_pos(const struct index_state *, const char *name, int namelen);
 #define ADD_CACHE_OK_TO_ADD 1          /* Ok to add */
 #define ADD_CACHE_OK_TO_REPLACE 2      /* Ok to replace file/directory */
@@ -360,6 +366,7 @@ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int opt
 extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
 extern int remove_index_entry_at(struct index_state *, int pos);
 extern int remove_file_from_index(struct index_state *, const char *path);
+extern int add_to_index(struct index_state *, const char *path, struct stat *, int verbose);
 extern int add_file_to_index(struct index_state *, const char *path, int verbose);
 extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
 extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
@@ -404,6 +411,7 @@ extern int delete_ref(const char *, const unsigned char *sha1);
 extern int trust_executable_bit;
 extern int quote_path_fully;
 extern int has_symlinks;
+extern int ignore_case;
 extern int assume_unchanged;
 extern int prefer_symlink_refs;
 extern int log_all_ref_updates;
@@ -433,7 +441,15 @@ enum branch_track {
        BRANCH_TRACK_EXPLICIT,
 };
 
+enum rebase_setup_type {
+       AUTOREBASE_NEVER = 0,
+       AUTOREBASE_LOCAL,
+       AUTOREBASE_REMOTE,
+       AUTOREBASE_ALWAYS,
+};
+
 extern enum branch_track git_branch_track;
+extern enum rebase_setup_type autorebase;
 
 #define GIT_REPO_VERSION 0
 extern int repository_format_version;
@@ -474,10 +490,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);
@@ -580,7 +606,7 @@ struct checkout {
 };
 
 extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
-extern int has_symlink_leading_path(const char *name, char *last_symlink);
+extern int has_symlink_leading_path(int len, const char *name);
 
 extern struct alternate_object_database {
        struct alternate_object_database *next;
@@ -624,6 +650,7 @@ struct ref {
        struct ref *next;
        unsigned char old_sha1[20];
        unsigned char new_sha1[20];
+       char *symref;
        unsigned int force:1,
                merge:1,
                nonfastforward:1,
@@ -692,6 +719,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 +743,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 ccb9e89fa46144744fa530930d8db7644ab2da74..b5ca142fedf2ac0e0cedde1011ab385f65010fdf 100644 (file)
@@ -1,5 +1,16 @@
+/*
+ *  The order of the following two lines is important.
+ *
+ *  FREAD_READS_DIRECTORIES is undefined before including git-compat-util.h
+ *  to avoid the redefinition of fopen within git-compat-util.h. This is
+ *  necessary since fopen is a macro on some platforms which may be set
+ *  based on compiler options. For example, on AIX fopen is set to fopen64
+ *  when _LARGE_FILES is defined. The previous technique of merely undefining
+ *  fopen after including git-compat-util.h is inadequate in this case.
+ */
+#undef FREAD_READS_DIRECTORIES
 #include "../git-compat-util.h"
-#undef fopen
+
 FILE *git_fopen(const char *path, const char *mode)
 {
        FILE *fp;
index 062449459e1a4cfc2a605c065ed281669e0e7452..4d00ebc7aac7726d8317760aa993ac0854933fda 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)
@@ -342,6 +350,11 @@ int git_default_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.ignorecase")) {
+               ignore_case = git_config_bool(var, value);
+               return 0;
+       }
+
        if (!strcmp(var, "core.bare")) {
                is_bare_repository_cfg = git_config_bool(var, value);
                return 0;
@@ -479,6 +492,21 @@ int git_default_config(const char *var, const char *value)
                git_branch_track = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "branch.autosetuprebase")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               else if (!strcmp(value, "never"))
+                       autorebase = AUTOREBASE_NEVER;
+               else if (!strcmp(value, "local"))
+                       autorebase = AUTOREBASE_LOCAL;
+               else if (!strcmp(value, "remote"))
+                       autorebase = AUTOREBASE_REMOTE;
+               else if (!strcmp(value, "always"))
+                       autorebase = AUTOREBASE_ALWAYS;
+               else
+                       return error("Malformed value for %s", var);
+               return 0;
+       }
 
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
index 6949cac45d0342ee6aa57bbdee5f6fe142d72c5d..16984632d984006e2f2867a26714086bf3045ca3 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
@@ -780,7 +780,7 @@ _git_merge ()
                ;;
        --*)
                __gitcomp "
-                       --no-commit --no-summary --squash --strategy
+                       --no-commit --no-stat --log --no-log --squash --strategy
                        "
                return
        esac
@@ -1053,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
 
@@ -1345,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)))))
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
index 069e4507ae7caa70f79d5369bc61dfefd0f174e2..fe2ccec7e6b3230715cbdf04591d1c0efb6a6389 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;
@@ -334,22 +337,41 @@ int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)
        }
        return run_diff_files(revs, options);
 }
+
 /*
- * See if work tree has an entity that can be staged.  Return 0 if so,
- * return 1 if not and return -1 if error.
+ * Has the work tree entity been removed?
+ *
+ * Return 1 if it was removed from the work tree, 0 if an entity to be
+ * compared with the cache entry ce still exists (the latter includes
+ * the case where a directory that is not a submodule repository
+ * exists for ce that is a submodule -- it is a submodule that is not
+ * checked out).  Return negative for an error.
  */
-static int check_work_tree_entity(const struct cache_entry *ce, struct stat *st, char *symcache)
+static int check_removed(const struct cache_entry *ce, struct stat *st)
 {
        if (lstat(ce->name, st) < 0) {
                if (errno != ENOENT && errno != ENOTDIR)
                        return -1;
                return 1;
        }
-       if (has_symlink_leading_path(ce->name, symcache))
+       if (has_symlink_leading_path(ce_namelen(ce), ce->name))
                return 1;
        if (S_ISDIR(st->st_mode)) {
                unsigned char sub[20];
-               if (resolve_gitlink_ref(ce->name, "HEAD", sub))
+
+               /*
+                * If ce is already a gitlink, we can have a plain
+                * directory (i.e. the submodule is not checked out),
+                * or a checked out submodule.  Either case this is not
+                * a case where something was removed from the work tree,
+                * so we will return 0.
+                *
+                * Otherwise, if the directory is not a submodule
+                * repository, that means ce which was a blob turned into
+                * a directory --- the blob was removed!
+                */
+               if (!S_ISGITLINK(ce->ce_mode) &&
+                   resolve_gitlink_ref(ce->name, "HEAD", sub))
                        return 1;
        }
        return 0;
@@ -399,7 +421,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                        memset(&(dpath->parent[0]), 0,
                               sizeof(struct combine_diff_parent)*5);
 
-                       changed = check_work_tree_entity(ce, &st, symcache);
+                       changed = check_removed(ce, &st);
                        if (!changed)
                                dpath->mode = ce_mode_from_stat(ce, st.st_mode);
                        else {
@@ -463,7 +485,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                if (ce_uptodate(ce))
                        continue;
 
-               changed = check_work_tree_entity(ce, &st, symcache);
+               changed = check_removed(ce, &st);
                if (changed) {
                        if (changed < 0) {
                                perror(ce->name);
@@ -476,8 +498,11 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                        continue;
                }
                changed = ce_match_stat(ce, &st, ce_option);
-               if (!changed && !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
-                       continue;
+               if (!changed) {
+                       ce_mark_uptodate(ce);
+                       if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
+                               continue;
+               }
                oldmode = ce->ce_mode;
                newmode = ce_mode_from_stat(ce, st.st_mode);
                diff_change(&revs->diffopt, oldmode, newmode,
@@ -521,7 +546,7 @@ static int get_stat_data(struct cache_entry *ce,
        if (!cached) {
                int changed;
                struct stat st;
-               changed = check_work_tree_entity(ce, &st, cbdata->symcache);
+               changed = check_removed(ce, &st);
                if (changed < 0)
                        return -1;
                else if (changed) {
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 d79762c7c0bc9e762ed8dc5f00fb2fd3ce01ad57..29d1d5ba31def46ba8b55905dc60773cc6cc167e 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -52,6 +52,11 @@ int common_prefix(const char **pathspec)
        return prefix;
 }
 
+static inline int special_char(unsigned char c1)
+{
+       return !c1 || c1 == '*' || c1 == '[' || c1 == '?';
+}
+
 /*
  * Does 'match' matches the given name?
  * A match is found if
@@ -69,14 +74,27 @@ static int match_one(const char *match, const char *name, int namelen)
        int matchlen;
 
        /* If the match was just the prefix, we matched */
-       matchlen = strlen(match);
-       if (!matchlen)
+       if (!*match)
                return MATCHED_RECURSIVELY;
 
+       for (;;) {
+               unsigned char c1 = *match;
+               unsigned char c2 = *name;
+               if (special_char(c1))
+                       break;
+               if (c1 != c2)
+                       return 0;
+               match++;
+               name++;
+               namelen--;
+       }
+
+
        /*
         * If we don't match the matchstring exactly,
         * we need to match by fnmatch
         */
+       matchlen = strlen(match);
        if (strncmp(match, name, matchlen))
                return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0;
 
@@ -371,7 +389,7 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len)
 
 struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
 {
-       if (cache_name_exists(pathname, len))
+       if (cache_name_exists(pathname, len, ignore_case))
                return NULL;
 
        ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);
index 6739a3f41745fe1c01dc767e09af8746d6f2815f..55c252706317818db64af8ff3fbfde6cd23e3488 100644 (file)
@@ -14,6 +14,7 @@ char git_default_name[MAX_GITNAME];
 int trust_executable_bit = 1;
 int quote_path_fully = 1;
 int has_symlinks = 1;
+int ignore_case;
 int assume_unchanged;
 int prefer_symlink_refs;
 int is_bare_repository_cfg = -1; /* unspecified */
@@ -38,6 +39,7 @@ int auto_crlf = 0;    /* 1: both ways, -1: only when adding git objects */
 enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
 unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
 enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
+enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
 
 /* This is set by setup_git_dir_gently() and/or git_default_config() */
 char *git_work_tree_cfg;
@@ -49,6 +51,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 b1800edaf713f972131431fcd26b5e7f64f089fc..164e8ed81fc3ecb5d33dafb9af0992761bfe513d 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
@@ -487,6 +491,8 @@ case "$#" in
     cmd="$1"
     shift
     case "$cmd" in
+    help)
+        git bisect -h ;;
     start)
         bisect_start "$@" ;;
     bad|good|skip)
index 8c7fc7f6317113fcef923dcc625fb94e13fb77a0..9d88d1ce60d9c34bd16976d403f210fb450940a5 100755 (executable)
@@ -334,7 +334,10 @@ yes)
                        fi
                fi &&
                cd "$repo" &&
-               find objects -depth -print | cpio $cpio_quiet_flag -pumd$l "$GIT_DIR/" || \
+               # Create dirs using umask and permissions and destination
+               find objects -type d -print | (cd "$GIT_DIR" && xargs mkdir -p) &&
+               # Copy existing 0444 permissions on content
+               find objects ! -type d -print | cpio $cpio_quiet_flag -pumd$l "$GIT_DIR/" || \
                        exit 1
        fi
        git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1
index a18235e6d053322a85805d1b07b631c6739deb93..01c4045e89a2e156062255193ed5d865fdf8a922 100644 (file)
@@ -206,6 +206,9 @@ void *gitmemmem(const void *haystack, size_t haystacklen,
 #endif
 
 #ifdef FREAD_READS_DIRECTORIES
+#ifdef fopen
+#undef fopen
+#endif
 #define fopen(a,b) git_fopen(a,b)
 extern FILE *git_fopen(const char*, const char*);
 #endif
@@ -268,6 +271,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 +338,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 +354,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 ea59015baa2507fdc8fe77d1c77ebdb2d5db2fa7..333f6a8f3b783d46a0ce3b2f59bae152936cd11c 100755 (executable)
@@ -406,8 +406,22 @@ if [ "$filter_tag_name" ]; then
                echo "$ref -> $new_ref ($sha1 -> $new_sha1)"
 
                if [ "$type" = "tag" ]; then
-                       # Warn that we are not rewriting the tag object itself.
-                       warn "unreferencing tag object $sha1t"
+                       new_sha1=$(git cat-file tag "$ref" |
+                               sed -n \
+                                   -e "1,/^$/{
+                                         s/^object .*/object $new_sha1/
+                                         s/^type .*/type commit/
+                                         s/^tag .*/tag $new_ref/
+                                       }" \
+                                   -e '/^-----BEGIN PGP SIGNATURE-----/q' \
+                                   -e 'p' |
+                               git mktag) ||
+                               die "Could not create new tag object for $ref"
+                       if git cat-file tag "$ref" | \
+                          grep '^-----BEGIN PGP SIGNATURE-----' >/dev/null 2>&1
+                       then
+                               warn "gpg signature stripped from tag object $sha1t"
+                       fi
                fi
 
                git update-ref "refs/tags/$new_ref" "$new_sha1" ||
index 7dbbb1d79dc1d66db3a662045a13554f8d574004..69b35d87e62b628d45f8e934f163b7ef64e2dda6 100755 (executable)
@@ -8,8 +8,12 @@ OPTIONS_SPEC="\
 git-merge [options] <remote>...
 git-merge [options] <msg> HEAD <remote>
 --
-summary              show a diffstat at the end of the merge
-n,no-summary         don't show a diffstat at the end of the merge
+stat                 show a diffstat at the end of the merge
+n,no-stat            don't show a diffstat at the end of the merge
+summary              (synonym to --stat)
+no-summary           (synonym to --no-stat)
+log                  add list of one-line log to merge commit message
+no-log               don't add list of one-line log to merge commit message
 squash               create a single commit instead of doing a merge
 commit               perform a commit if the merge sucesses (default)
 ff                   allow fast forward (default)
@@ -37,7 +41,7 @@ use_strategies=
 
 allow_fast_forward=t
 allow_trivial_merge=t
-squash= no_commit=
+squash= no_commit= log_arg=
 
 dropsave() {
        rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \
@@ -148,10 +152,12 @@ merge_name () {
 parse_config () {
        while test $# != 0; do
                case "$1" in
-               -n|--no-summary)
+               -n|--no-stat|--no-summary)
                        show_diffstat=false ;;
-               --summary)
+               --stat|--summary)
                        show_diffstat=t ;;
+               --log|--no-log)
+                       log_arg=$1 ;;
                --squash)
                        test "$allow_fast_forward" = t ||
                                die "You cannot combine --squash with --no-ff."
@@ -210,6 +216,7 @@ while test $args_left -lt $#; do shift; done
 
 if test -z "$show_diffstat"; then
     test "$(git config --bool merge.diffstat)" = false && show_diffstat=false
+    test "$(git config --bool merge.stat)" = false && show_diffstat=false
     test -z "$show_diffstat" && show_diffstat=t
 fi
 
@@ -258,7 +265,7 @@ else
        merge_name=$(for remote
                do
                        merge_name "$remote"
-               done | git fmt-merge-msg
+               done | git fmt-merge-msg $log_arg
        )
        merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name"
 fi
index 5c86f69229042c16704d11ce404e43297cc7b72c..fcdec4a504acd5681e0695fb9657d6a1cd513ab0 100755 (executable)
@@ -237,9 +237,9 @@ merge_file () {
        ecmerge)
            touch "$BACKUP"
            if base_present; then
-               "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --mode=merge3 --to="$MERGED"
+               "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --default --mode=merge3 --to="$MERGED"
            else
-               "$merge_tool_path" "$LOCAL" "$REMOTE" --mode=merge2 --to="$MERGED"
+               "$merge_tool_path" "$LOCAL" "$REMOTE" --default --mode=merge2 --to="$MERGED"
            fi
            check_unchanged
            ;;
index 3ce32b5f211bd20f1d154c860eb5bf7f9005ca3c..bf0c2985af875cdb7b2c64998390dbee908ff14c 100755 (executable)
@@ -4,7 +4,7 @@
 #
 # Fetch one or more remote refs and merge it/them into the current HEAD.
 
-USAGE='[-n | --no-summary] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s strategy]... [<fetch-options>] <repo> <head>...'
+USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s strategy]... [<fetch-options>] <repo> <head>...'
 LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEAD.'
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
@@ -16,19 +16,19 @@ cd_to_toplevel
 test -z "$(git ls-files -u)" ||
        die "You are in the middle of a conflicted merge."
 
-strategy_args= no_summary= no_commit= squash= no_ff=
+strategy_args= no_stat= no_commit= squash= no_ff= log_arg=
 curr_branch=$(git symbolic-ref -q HEAD)
 curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
 rebase=$(git config --bool branch.$curr_branch_short.rebase)
 while :
 do
        case "$1" in
-       -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\
-               --no-summa|--no-summar|--no-summary)
-               no_summary=-n ;;
-       --summary)
-               no_summary=$1
-               ;;
+       -n|--no-stat|--no-summary)
+               no_stat=-n ;;
+       --stat|--summary)
+               no_stat=$1 ;;
+       --log|--no-log)
+               log_arg=$1 ;;
        --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)
                no_commit=--no-commit ;;
        --c|--co|--com|--comm|--commi|--commit)
@@ -172,9 +172,9 @@ then
        exit
 fi
 
-merge_name=$(git fmt-merge-msg <"$GIT_DIR/FETCH_HEAD") || exit
+merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
 test true = "$rebase" &&
        exec git-rebase $strategy_args --onto $merge_head \
        ${oldremoteref:-$merge_head}
-exec git-merge $no_summary $no_commit $squash $no_ff $strategy_args \
+exec git-merge $no_stat $no_commit $squash $no_ff $log_arg $strategy_args \
        "$merge_name" HEAD $merge_head
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 e18eb3f5dcf42abfbd125594877ececf92c3d9b6..501519ab6897c2463c054e3f7310efc6721c432f 100755 (executable)
@@ -11,6 +11,7 @@ a               pack everything in a single pack
 A               same as -a, and keep unreachable objects too
 d               remove redundant packs, and run git-prune-packed
 f               pass --no-reuse-delta to git-pack-objects
+n               do not run git-update-server-info
 q,quiet         be quiet
 l               pass --local to git-pack-objects
  Packing constraints
index be4a20d7cd562e9b00d93fe71313aed70afe50af..9e568bf9c012b610fd8db7e4444cb7ff8c6e0a0f 100755 (executable)
@@ -168,7 +168,8 @@ my $envelope_sender;
 # 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 @@ if (0) {
        }
 }
 
-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 @@ if (@files) {
 
 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 @@ GIT: for the patch you are writing.
 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 a745e42bf7b19d6b813ff8e1e60e8b0ba41aaa14..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=
@@ -343,6 +343,7 @@ set_name_rev () {
 #
 cmd_summary() {
        summary_limit=-1
+       for_status=
 
        # parse $args after "submodule ... summary".
        while test $# -ne 0
@@ -351,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
@@ -398,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- |
@@ -500,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:
@@ -543,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 b1510495a7f003991a37d0810c551404c6c1252c..2c53f39aefa0131cb15ab679e6b2633622c000a1 100755 (executable)
@@ -65,7 +65,8 @@ my ($_stdin, $_help, $_edit,
        $_template, $_shared,
        $_version, $_fetch_all, $_no_rebase,
        $_merge, $_strategy, $_dry_run, $_local,
-       $_prefix, $_no_checkout, $_url, $_verbose);
+       $_prefix, $_no_checkout, $_url, $_verbose,
+       $_git_format);
 $Git::SVN::_follow_parent = 1;
 my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username,
                     'config-dir=s' => \$Git::SVN::Ra::config_dir,
@@ -188,7 +189,7 @@ my %cmd = (
                    { 'url' => \$_url, } ],
        'blame' => [ \&Git::SVN::Log::cmd_blame,
                    "Show what revision and author last modified each line of a file",
-                   {} ],
+                   { 'git-format' => \$_git_format } ],
 );
 
 my $cmd;
@@ -225,7 +226,7 @@ unless ($cmd && $cmd =~ /(?:clone|init|multi-init)$/) {
 my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
 
 read_repo_config(\%opts);
-Getopt::Long::Configure('pass_through') if ($cmd && $cmd eq 'log');
+Getopt::Long::Configure('pass_through') if ($cmd && ($cmd eq 'log' || $cmd eq 'blame'));
 my $rv = GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version,
                     'minimize-connections' => \$Git::SVN::Migration::_minimize,
                     'id|i=s' => \$Git::SVN::default_ref_id,
@@ -410,10 +411,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 +615,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);
        });
 }
 
@@ -2426,13 +2429,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) {
@@ -3669,7 +3674,7 @@ sub escape_uri_only {
        my ($uri) = @_;
        my @tmp;
        foreach (split m{/}, $uri) {
-               s/([^\w.%-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
+               s/([^\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
                push @tmp, $_;
        }
        join('/', @tmp);
@@ -4464,19 +4469,51 @@ out:
 }
 
 sub cmd_blame {
-       my $path = shift;
+       my $path = pop;
 
        config_pager();
        run_pager();
 
-       my ($fh, $ctx) = command_output_pipe('blame', @_, $path);
-       while (my $line = <$fh>) {
-               if ($line =~ /^\^?([[:xdigit:]]+)\s/) {
-                       my (undef, $rev, undef) = ::cmt_metadata($1);
-                       $rev = sprintf('%-10s', $rev);
-                       $line =~ s/^\^?[[:xdigit:]]+(\s)/$rev$1/;
+       my ($fh, $ctx, $rev);
+
+       if ($_git_format) {
+               ($fh, $ctx) = command_output_pipe('blame', @_, $path);
+               while (my $line = <$fh>) {
+                       if ($line =~ /^\^?([[:xdigit:]]+)\s/) {
+                               # Uncommitted edits show up as a rev ID of
+                               # all zeros, which we can't look up with
+                               # cmt_metadata
+                               if ($1 !~ /^0+$/) {
+                                       (undef, $rev, undef) =
+                                               ::cmt_metadata($1);
+                                       $rev = '0' if (!$rev);
+                               } else {
+                                       $rev = '0';
+                               }
+                               $rev = sprintf('%-10s', $rev);
+                               $line =~ s/^\^?[[:xdigit:]]+(\s)/$rev$1/;
+                       }
+                       print $line;
+               }
+       } else {
+               ($fh, $ctx) = command_output_pipe('blame', '-p', @_, 'HEAD',
+                                                 '--', $path);
+               my ($sha1);
+               my %authors;
+               while (my $line = <$fh>) {
+                       if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) {
+                               $sha1 = $1;
+                               (undef, $rev, undef) = ::cmt_metadata($1);
+                               $rev = '0' if (!$rev);
+                       }
+                       elsif ($line =~ /^author (.*)/) {
+                               $authors{$rev} = $1;
+                               $authors{$rev} =~ s/\s/_/g;
+                       }
+                       elsif ($line =~ /^\t(.*)$/) {
+                               printf("%6s %10s %s\n", $rev, $authors{$rev}, $1);
+                       }
                }
-               print $line;
        }
        command_close_pipe($fh, $ctx);
 }
diff --git a/git.c b/git.c
index c4e4644b30e184e6496fb5a4877f3141488f8f44..89b431fa28162adbd0f64d7a036a71b5bfce9eee 100644 (file)
--- a/git.c
+++ b/git.c
@@ -347,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 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 446a1c333bd55bb25db5e53794277e9e6d2c51da..aa0eeca24786dbd5143354fc3bb5e8cdb3ef831f 100644 (file)
@@ -464,6 +464,14 @@ a.rss_logo:hover {
        background-color: #ee5500;
 }
 
+a.rss_logo.generic {
+       background-color: #ff8800;
+}
+
+a.rss_logo.generic:hover {
+       background-color: #ee7700;
+}
+
 span.refs span {
        padding: 0px 4px;
        font-size: 70%;
index 12843a4846563842aef8f0311096794ad70011c3..2facf2db7a9cd034476fa65496ef575f3073c6df 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";
@@ -587,7 +592,7 @@ exit;
 ## ======================================================================
 ## action links
 
-sub href(%) {
+sub href (%) {
        my %params = @_;
        # default is to use -absolute url() i.e. $my_uri
        my $href = $params{-full} ? $my_url : $my_uri;
@@ -1443,6 +1448,46 @@ sub format_snapshot_links {
        }
 }
 
+## ......................................................................
+## functions returning values to be passed, perhaps after some
+## transformation, to other functions; e.g. returning arguments to href()
+
+# returns hash to be passed to href to generate gitweb URL
+# in -title key it returns description of link
+sub get_feed_info {
+       my $format = shift || 'Atom';
+       my %res = (action => lc($format));
+
+       # feed links are possible only for project views
+       return unless (defined $project);
+       # some views should link to OPML, or to generic project feed,
+       # or don't have specific feed yet (so they should use generic)
+       return if ($action =~ /^(?:tags|heads|forks|tag|search)$/x);
+
+       my $branch;
+       # branches refs uses 'refs/heads/' prefix (fullname) to differentiate
+       # from tag links; this also makes possible to detect branch links
+       if ((defined $hash_base && $hash_base =~ m!^refs/heads/(.*)$!) ||
+           (defined $hash      && $hash      =~ m!^refs/heads/(.*)$!)) {
+               $branch = $1;
+       }
+       # find log type for feed description (title)
+       my $type = 'log';
+       if (defined $file_name) {
+               $type  = "history of $file_name";
+               $type .= "/" if ($action eq 'tree');
+               $type .= " on '$branch'" if (defined $branch);
+       } else {
+               $type = "log of $branch" if (defined $branch);
+       }
+
+       $res{-title} = $type;
+       $res{'hash'} = (defined $branch ? "refs/heads/$branch" : undef);
+       $res{'file_name'} = $file_name;
+
+       return %res;
+}
+
 ## ----------------------------------------------------------------------
 ## git utility subroutines, invoking git commands
 
@@ -2505,30 +2550,49 @@ EOF
                }
        }
        if (defined $project) {
-               printf('<link rel="alternate" title="%s log RSS feed" '.
-                      'href="%s" type="application/rss+xml" />'."\n",
-                      esc_param($project), href(action=>"rss"));
-               printf('<link rel="alternate" title="%s log RSS feed (no merges)" '.
-                      'href="%s" type="application/rss+xml" />'."\n",
-                      esc_param($project), href(action=>"rss",
-                                                extra_options=>"--no-merges"));
-               printf('<link rel="alternate" title="%s log Atom feed" '.
-                      'href="%s" type="application/atom+xml" />'."\n",
-                      esc_param($project), href(action=>"atom"));
-               printf('<link rel="alternate" title="%s log Atom feed (no merges)" '.
-                      'href="%s" type="application/atom+xml" />'."\n",
-                      esc_param($project), href(action=>"atom",
-                                                extra_options=>"--no-merges"));
+               my %href_params = get_feed_info();
+               if (!exists $href_params{'-title'}) {
+                       $href_params{'-title'} = 'log';
+               }
+
+               foreach my $format qw(RSS Atom) {
+                       my $type = lc($format);
+                       my %link_attr = (
+                               '-rel' => 'alternate',
+                               '-title' => "$project - $href_params{'-title'} - $format feed",
+                               '-type' => "application/$type+xml"
+                       );
+
+                       $href_params{'action'} = $type;
+                       $link_attr{'-href'} = href(%href_params);
+                       print "<link ".
+                             "rel=\"$link_attr{'-rel'}\" ".
+                             "title=\"$link_attr{'-title'}\" ".
+                             "href=\"$link_attr{'-href'}\" ".
+                             "type=\"$link_attr{'-type'}\" ".
+                             "/>\n";
+
+                       $href_params{'extra_options'} = '--no-merges';
+                       $link_attr{'-href'} = href(%href_params);
+                       $link_attr{'-title'} .= ' (no merges)';
+                       print "<link ".
+                             "rel=\"$link_attr{'-rel'}\" ".
+                             "title=\"$link_attr{'-title'}\" ".
+                             "href=\"$link_attr{'-href'}\" ".
+                             "type=\"$link_attr{'-type'}\" ".
+                             "/>\n";
+               }
+
        } else {
                printf('<link rel="alternate" title="%s projects list" '.
-                      'href="%s" type="text/plain; charset=utf-8"/>'."\n",
+                      'href="%s" type="text/plain; charset=utf-8" />'."\n",
                       $site_name, href(project=>undef, action=>"project_index"));
                printf('<link rel="alternate" title="%s projects feeds" '.
-                      'href="%s" type="text/x-opml"/>'."\n",
+                      'href="%s" type="text/x-opml" />'."\n",
                       $site_name, href(project=>undef, action=>"opml"));
        }
        if (defined $favicon) {
-               print qq(<link rel="shortcut icon" href="$favicon" type="image/png"/>\n);
+               print qq(<link rel="shortcut icon" href="$favicon" type="image/png" />\n);
        }
 
        print "</head>\n" .
@@ -2596,23 +2660,35 @@ EOF
 }
 
 sub git_footer_html {
+       my $feed_class = 'rss_logo';
+
        print "<div class=\"page_footer\">\n";
        if (defined $project) {
                my $descr = git_get_project_description($project);
                if (defined $descr) {
                        print "<div class=\"page_footer_text\">" . esc_html($descr) . "</div>\n";
                }
-               print $cgi->a({-href => href(action=>"rss"),
-                             -class => "rss_logo"}, "RSS") . " ";
-               print $cgi->a({-href => href(action=>"atom"),
-                             -class => "rss_logo"}, "Atom") . "\n";
+
+               my %href_params = get_feed_info();
+               if (!%href_params) {
+                       $feed_class .= ' generic';
+               }
+               $href_params{'-title'} ||= 'log';
+
+               foreach my $format qw(RSS Atom) {
+                       $href_params{'action'} = lc($format);
+                       print $cgi->a({-href => href(%href_params),
+                                     -title => "$href_params{'-title'} $format feed",
+                                     -class => $feed_class}, $format)."\n";
+               }
+
        } else {
                print $cgi->a({-href => href(project=>undef, action=>"opml"),
-                             -class => "rss_logo"}, "OPML") . " ";
+                             -class => $feed_class}, "OPML") . " ";
                print $cgi->a({-href => href(project=>undef, action=>"project_index"),
-                             -class => "rss_logo"}, "TXT") . "\n";
+                             -class => $feed_class}, "TXT") . "\n";
        }
-       print "</div>\n" ;
+       print "</div>\n"; # class="page_footer"
 
        if (-f $site_footer) {
                open (my $fd, $site_footer);
diff --git a/help.c b/help.c
index 10298fb0a197f18008783214de11e424fbadb77d..af80979fcb177faace150a7cfa7cb66daeb59f49 100644 (file)
--- a/help.c
+++ b/help.c
 #include "run-command.h"
 
 static struct man_viewer_list {
-       void (*exec)(const char *);
        struct man_viewer_list *next;
+       char name[FLEX_ARRAY];
 } *man_viewer_list;
 
+static struct man_viewer_info_list {
+       struct man_viewer_info_list *next;
+       const char *info;
+       char name[FLEX_ARRAY];
+} *man_viewer_info_list;
+
 enum help_format {
        HELP_FORMAT_MAN,
        HELP_FORMAT_INFO,
@@ -49,6 +55,18 @@ static enum help_format parse_help_format(const char *format)
        die("unrecognized help format '%s'", format);
 }
 
+static const char *get_man_viewer_info(const char *name)
+{
+       struct man_viewer_info_list *viewer;
+
+       for (viewer = man_viewer_info_list; viewer; viewer = viewer->next)
+       {
+               if (!strcasecmp(name, viewer->name))
+                       return viewer->info;
+       }
+       return NULL;
+}
+
 static int check_emacsclient_version(void)
 {
        struct strbuf buffer = STRBUF_INIT;
@@ -95,56 +113,145 @@ static int check_emacsclient_version(void)
        return 0;
 }
 
-static void exec_woman_emacs(const char *page)
+static void exec_woman_emacs(const char* path, const char *page)
 {
        if (!check_emacsclient_version()) {
                /* This works only with emacsclient version >= 22. */
                struct strbuf man_page = STRBUF_INIT;
+
+               if (!path)
+                       path = "emacsclient";
                strbuf_addf(&man_page, "(woman \"%s\")", page);
-               execlp("emacsclient", "emacsclient", "-e", man_page.buf, NULL);
+               execlp(path, "emacsclient", "-e", man_page.buf, NULL);
+               warning("failed to exec '%s': %s", path, strerror(errno));
        }
 }
 
-static void exec_man_konqueror(const char *page)
+static void exec_man_konqueror(const char* path, const char *page)
 {
        const char *display = getenv("DISPLAY");
        if (display && *display) {
                struct strbuf man_page = STRBUF_INIT;
+               const char *filename = "kfmclient";
+
+               /* It's simpler to launch konqueror using kfmclient. */
+               if (path) {
+                       const char *file = strrchr(path, '/');
+                       if (file && !strcmp(file + 1, "konqueror")) {
+                               char *new = xstrdup(path);
+                               char *dest = strrchr(new, '/');
+
+                               /* strlen("konqueror") == strlen("kfmclient") */
+                               strcpy(dest + 1, "kfmclient");
+                               path = new;
+                       }
+                       if (file)
+                               filename = file;
+               } else
+                       path = "kfmclient";
                strbuf_addf(&man_page, "man:%s(1)", page);
-               execlp("kfmclient", "kfmclient", "newTab", man_page.buf, NULL);
+               execlp(path, filename, "newTab", man_page.buf, NULL);
+               warning("failed to exec '%s': %s", path, strerror(errno));
        }
 }
 
-static void exec_man_man(const char *page)
+static void exec_man_man(const char* path, const char *page)
+{
+       if (!path)
+               path = "man";
+       execlp(path, "man", page, NULL);
+       warning("failed to exec '%s': %s", path, strerror(errno));
+}
+
+static void exec_man_cmd(const char *cmd, const char *page)
 {
-       execlp("man", "man", page, NULL);
+       struct strbuf shell_cmd = STRBUF_INIT;
+       strbuf_addf(&shell_cmd, "%s %s", cmd, page);
+       execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
+       warning("failed to exec '%s': %s", cmd, strerror(errno));
 }
 
-static void do_add_man_viewer(void (*exec)(const char *))
+static void add_man_viewer(const char *name)
 {
        struct man_viewer_list **p = &man_viewer_list;
+       size_t len = strlen(name);
 
        while (*p)
                p = &((*p)->next);
-       *p = xmalloc(sizeof(**p));
-       (*p)->next = NULL;
-       (*p)->exec = exec;
+       *p = xcalloc(1, (sizeof(**p) + len + 1));
+       strncpy((*p)->name, name, len);
 }
 
-static int add_man_viewer(const char *value)
+static int supported_man_viewer(const char *name, size_t len)
 {
-       if (!strcasecmp(value, "man"))
-               do_add_man_viewer(exec_man_man);
-       else if (!strcasecmp(value, "woman"))
-               do_add_man_viewer(exec_woman_emacs);
-       else if (!strcasecmp(value, "konqueror"))
-               do_add_man_viewer(exec_man_konqueror);
+       return (!strncasecmp("man", name, len) ||
+               !strncasecmp("woman", name, len) ||
+               !strncasecmp("konqueror", name, len));
+}
+
+static void do_add_man_viewer_info(const char *name,
+                                  size_t len,
+                                  const char *value)
+{
+       struct man_viewer_info_list *new = xcalloc(1, sizeof(*new) + len + 1);
+
+       strncpy(new->name, name, len);
+       new->info = xstrdup(value);
+       new->next = man_viewer_info_list;
+       man_viewer_info_list = new;
+}
+
+static int add_man_viewer_path(const char *name,
+                              size_t len,
+                              const char *value)
+{
+       if (supported_man_viewer(name, len))
+               do_add_man_viewer_info(name, len, value);
        else
-               warning("'%s': unsupported man viewer.", value);
+               warning("'%s': path for unsupported man viewer.\n"
+                       "Please consider using 'man.<tool>.cmd' instead.",
+                       name);
 
        return 0;
 }
 
+static int add_man_viewer_cmd(const char *name,
+                             size_t len,
+                             const char *value)
+{
+       if (supported_man_viewer(name, len))
+               warning("'%s': cmd for supported man viewer.\n"
+                       "Please consider using 'man.<tool>.path' instead.",
+                       name);
+       else
+               do_add_man_viewer_info(name, len, value);
+
+       return 0;
+}
+
+static int add_man_viewer_info(const char *var, const char *value)
+{
+       const char *name = var + 4;
+       const char *subkey = strrchr(name, '.');
+
+       if (!subkey)
+               return error("Config with no key for man viewer: %s", name);
+
+       if (!strcmp(subkey, ".path")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               return add_man_viewer_path(name, subkey - name, value);
+       }
+       if (!strcmp(subkey, ".cmd")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               return add_man_viewer_cmd(name, subkey - name, value);
+       }
+
+       warning("'%s': unsupported man viewer sub key.", subkey);
+       return 0;
+}
+
 static int git_help_config(const char *var, const char *value)
 {
        if (!strcmp(var, "help.format")) {
@@ -156,8 +263,12 @@ static int git_help_config(const char *var, const char *value)
        if (!strcmp(var, "man.viewer")) {
                if (!value)
                        return config_error_nonbool(var);
-               return add_man_viewer(value);
+               add_man_viewer(value);
+               return 0;
        }
+       if (!prefixcmp(var, "man."))
+               return add_man_viewer_info(var, value);
+
        return git_default_config(var, value);
 }
 
@@ -453,6 +564,22 @@ static void setup_man_path(void)
        strbuf_release(&new_path);
 }
 
+static void exec_viewer(const char *name, const char *page)
+{
+       const char *info = get_man_viewer_info(name);
+
+       if (!strcasecmp(name, "man"))
+               exec_man_man(info, page);
+       else if (!strcasecmp(name, "woman"))
+               exec_woman_emacs(info, page);
+       else if (!strcasecmp(name, "konqueror"))
+               exec_man_konqueror(info, page);
+       else if (info)
+               exec_man_cmd(info, page);
+       else
+               warning("'%s': unknown man viewer.", name);
+}
+
 static void show_man_page(const char *git_cmd)
 {
        struct man_viewer_list *viewer;
@@ -461,9 +588,9 @@ static void show_man_page(const char *git_cmd)
        setup_man_path();
        for (viewer = man_viewer_list; viewer; viewer = viewer->next)
        {
-               viewer->exec(page); /* will return when unable */
+               exec_viewer(viewer->name, page); /* will return when unable */
        }
-       exec_man_man(page);
+       exec_viewer("man", page);
        die("no man viewer handled the request");
 }
 
index 5b230380ccd33d8b04f8dfce24050237ae9206ad..42727c8a45c7b50e8e6b2b04a0799c9ba85a0114 100644 (file)
@@ -1759,15 +1759,15 @@ static int one_local_ref(const char *refname, const unsigned char *sha1, int fla
 static void one_remote_ref(char *refname)
 {
        struct ref *ref;
-       unsigned char remote_sha1[20];
        struct object *obj;
-       int len = strlen(refname) + 1;
 
-       if (http_fetch_ref(remote->url, refname + 5 /* "refs/" */,
-                          remote_sha1) != 0) {
+       ref = alloc_ref_from_str(refname);
+
+       if (http_fetch_ref(remote->url, ref) != 0) {
                fprintf(stderr,
                        "Unable to fetch ref %s from %s\n",
                        refname, remote->url);
+               free(ref);
                return;
        }
 
@@ -1775,18 +1775,15 @@ static void one_remote_ref(char *refname)
         * Fetch a copy of the object if it doesn't exist locally - it
         * may be required for updating server info later.
         */
-       if (remote->can_update_info_refs && !has_sha1_file(remote_sha1)) {
-               obj = lookup_unknown_object(remote_sha1);
+       if (remote->can_update_info_refs && !has_sha1_file(ref->old_sha1)) {
+               obj = lookup_unknown_object(ref->old_sha1);
                if (obj) {
                        fprintf(stderr, "  fetch %s for %s\n",
-                               sha1_to_hex(remote_sha1), refname);
+                               sha1_to_hex(ref->old_sha1), refname);
                        add_fetch_request(obj);
                }
        }
 
-       ref = xcalloc(1, sizeof(*ref) + len);
-       hashcpy(ref->old_sha1, remote_sha1);
-       memcpy(ref->name, refname, len);
        *remote_tail = ref;
        remote_tail = &ref->next;
 }
@@ -1891,33 +1888,36 @@ static void mark_edges_uninteresting(struct commit_list *list)
 static void add_remote_info_ref(struct remote_ls_ctx *ls)
 {
        struct strbuf *buf = (struct strbuf *)ls->userData;
-       unsigned char remote_sha1[20];
        struct object *o;
        int len;
        char *ref_info;
+       struct ref *ref;
+
+       ref = alloc_ref_from_str(ls->dentry_name);
 
-       if (http_fetch_ref(remote->url, ls->dentry_name + 5 /* "refs/" */,
-                          remote_sha1) != 0) {
+       if (http_fetch_ref(remote->url, ref) != 0) {
                fprintf(stderr,
                        "Unable to fetch ref %s from %s\n",
                        ls->dentry_name, remote->url);
                aborted = 1;
+               free(ref);
                return;
        }
 
-       o = parse_object(remote_sha1);
+       o = parse_object(ref->old_sha1);
        if (!o) {
                fprintf(stderr,
                        "Unable to parse object %s for remote ref %s\n",
-                       sha1_to_hex(remote_sha1), ls->dentry_name);
+                       sha1_to_hex(ref->old_sha1), ls->dentry_name);
                aborted = 1;
+               free(ref);
                return;
        }
 
        len = strlen(ls->dentry_name) + 42;
        ref_info = xcalloc(len + 1, 1);
        sprintf(ref_info, "%s   %s\n",
-               sha1_to_hex(remote_sha1), ls->dentry_name);
+               sha1_to_hex(ref->old_sha1), ls->dentry_name);
        fwrite_buffer(ref_info, 1, len, buf);
        free(ref_info);
 
@@ -1932,6 +1932,7 @@ static void add_remote_info_ref(struct remote_ls_ctx *ls)
                        free(ref_info);
                }
        }
+       free(ref);
 }
 
 static void update_remote_info_refs(struct remote_lock *lock)
index 7bda34d91498c3959569327ea2132851230999e7..99f397e32b673e289c2f9d298b1287a97cb7a49e 100644 (file)
@@ -888,10 +888,10 @@ static int fetch(struct walker *walker, unsigned char *sha1)
                     data->alt->base);
 }
 
-static int fetch_ref(struct walker *walker, char *ref, unsigned char *sha1)
+static int fetch_ref(struct walker *walker, struct ref *ref)
 {
        struct walker_data *data = walker->data;
-       return http_fetch_ref(data->alt->base, ref, sha1);
+       return http_fetch_ref(data->alt->base, ref);
 }
 
 static void cleanup(struct walker *walker)
diff --git a/http.c b/http.c
index 256a5f15f40a8d9389560e1fb08e34a56e9f7140..acf746a12da0f0b5e3fe3f097a6626e17da8852c 100644 (file)
--- a/http.c
+++ b/http.c
@@ -589,8 +589,9 @@ static char *quote_ref_url(const char *base, const char *ref)
                        len += 2; /* extra two hex plus replacement % */
        qref = xmalloc(len);
        memcpy(qref, base, baselen);
-       memcpy(qref + baselen, "/refs/", 6);
-       for (cp = ref, dp = qref + baselen + 6; (ch = *cp) != 0; cp++) {
+       dp = qref + baselen;
+       *(dp++) = '/';
+       for (cp = ref; (ch = *cp) != 0; cp++) {
                if (needs_quote(ch)) {
                        *dp++ = '%';
                        *dp++ = hex((ch >> 4) & 0xF);
@@ -604,7 +605,7 @@ static char *quote_ref_url(const char *base, const char *ref)
        return qref;
 }
 
-int http_fetch_ref(const char *base, const char *ref, unsigned char *sha1)
+int http_fetch_ref(const char *base, struct ref *ref)
 {
        char *url;
        struct strbuf buffer = STRBUF_INIT;
@@ -612,7 +613,7 @@ int http_fetch_ref(const char *base, const char *ref, unsigned char *sha1)
        struct slot_results results;
        int ret;
 
-       url = quote_ref_url(base, ref);
+       url = quote_ref_url(base, ref->name);
        slot = get_active_slot();
        slot->results = &results;
        curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
@@ -624,12 +625,15 @@ int http_fetch_ref(const char *base, const char *ref, unsigned char *sha1)
                if (results.curl_result == CURLE_OK) {
                        strbuf_rtrim(&buffer);
                        if (buffer.len == 40)
-                               ret = get_sha1_hex(buffer.buf, sha1);
-                       else
+                               ret = get_sha1_hex(buffer.buf, ref->old_sha1);
+                       else if (!prefixcmp(buffer.buf, "ref: ")) {
+                               ref->symref = xstrdup(buffer.buf + 5);
+                               ret = 0;
+                       } else
                                ret = 1;
                } else {
                        ret = error("Couldn't get %s for %s\n%s",
-                                   url, ref, curl_errorstr);
+                                   url, ref->name, curl_errorstr);
                }
        } else {
                ret = error("Unable to start request");
diff --git a/http.h b/http.h
index 04169d5f9c8fa4cb82ad720b9e6d371f02be83d3..a04fc6a9277945a7084e718753c133ed47d13691 100644 (file)
--- a/http.h
+++ b/http.h
@@ -105,6 +105,6 @@ static inline int missing__target(int code, int result)
 
 #define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
 
-extern int http_fetch_ref(const char *base, const char *ref, unsigned char *sha1);
+extern int http_fetch_ref(const char *base, struct ref *ref);
 
 #endif /* HTTP_H */
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/name-hash.c b/name-hash.c
new file mode 100644 (file)
index 0000000..0031d78
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * name-hash.c
+ *
+ * Hashing names in the index state
+ *
+ * Copyright (C) 2008 Linus Torvalds
+ */
+#define NO_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+
+/*
+ * This removes bit 5 if bit 6 is set.
+ *
+ * That will make US-ASCII characters hash to their upper-case
+ * equivalent. We could easily do this one whole word at a time,
+ * but that's for future worries.
+ */
+static inline unsigned char icase_hash(unsigned char c)
+{
+       return c & ~((c & 0x40) >> 1);
+}
+
+static unsigned int hash_name(const char *name, int namelen)
+{
+       unsigned int hash = 0x123;
+
+       do {
+               unsigned char c = *name++;
+               c = icase_hash(c);
+               hash = hash*101 + c;
+       } while (--namelen);
+       return hash;
+}
+
+static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
+{
+       void **pos;
+       unsigned int hash;
+
+       if (ce->ce_flags & CE_HASHED)
+               return;
+       ce->ce_flags |= CE_HASHED;
+       ce->next = NULL;
+       hash = hash_name(ce->name, ce_namelen(ce));
+       pos = insert_hash(hash, ce, &istate->name_hash);
+       if (pos) {
+               ce->next = *pos;
+               *pos = ce;
+       }
+}
+
+static void lazy_init_name_hash(struct index_state *istate)
+{
+       int nr;
+
+       if (istate->name_hash_initialized)
+               return;
+       for (nr = 0; nr < istate->cache_nr; nr++)
+               hash_index_entry(istate, istate->cache[nr]);
+       istate->name_hash_initialized = 1;
+}
+
+void add_name_hash(struct index_state *istate, struct cache_entry *ce)
+{
+       ce->ce_flags &= ~CE_UNHASHED;
+       if (istate->name_hash_initialized)
+               hash_index_entry(istate, ce);
+}
+
+static int slow_same_name(const char *name1, int len1, const char *name2, int len2)
+{
+       if (len1 != len2)
+               return 0;
+
+       while (len1) {
+               unsigned char c1 = *name1++;
+               unsigned char c2 = *name2++;
+               len1--;
+               if (c1 != c2) {
+                       c1 = toupper(c1);
+                       c2 = toupper(c2);
+                       if (c1 != c2)
+                               return 0;
+               }
+       }
+       return 1;
+}
+
+static int same_name(const struct cache_entry *ce, const char *name, int namelen, int icase)
+{
+       int len = ce_namelen(ce);
+
+       /*
+        * Always do exact compare, even if we want a case-ignoring comparison;
+        * we do the quick exact one first, because it will be the common case.
+        */
+       if (len == namelen && !cache_name_compare(name, namelen, ce->name, len))
+               return 1;
+
+       return icase && slow_same_name(name, namelen, ce->name, len);
+}
+
+struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int icase)
+{
+       unsigned int hash = hash_name(name, namelen);
+       struct cache_entry *ce;
+
+       lazy_init_name_hash(istate);
+       ce = lookup_hash(hash, &istate->name_hash);
+
+       while (ce) {
+               if (!(ce->ce_flags & CE_UNHASHED)) {
+                       if (same_name(ce, name, namelen, icase))
+                               return ce;
+               }
+               ce = ce->next;
+       }
+       return NULL;
+}
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",
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 @@ does. In scalar context requires the variable to be set only one time
 (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 @@ Retrieve the bool configuration C<VARIABLE>. The return value
 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 @@ or 'g' in the config file will cause the value to be multiplied
 by 1024, 1048576 (1024^2), or 1073741824 (1024^3) prior to output.
 It would return C<undef> if configuration variable is not defined,
 
-Must be called on a repository instance.
-
 This currently wraps command('config') so it is not so fast.
 
 =cut
 
 sub config_int {
-       my ($self, $var) = @_;
-       $self->repo_path()
-               or throw Error::Simple("not a repository");
+       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 @@ The synopsis is like:
        "$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 a92b25b59bf0e096942bca126542a1ea411b525b..0382804e7694e9a0e87cc2dc8340187ddeea2d64 100644 (file)
 
 struct index_state the_index;
 
-static unsigned int hash_name(const char *name, int namelen)
-{
-       unsigned int hash = 0x123;
-
-       do {
-               unsigned char c = *name++;
-               hash = hash*101 + c;
-       } while (--namelen);
-       return hash;
-}
-
-static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
-{
-       void **pos;
-       unsigned int hash;
-
-       if (ce->ce_flags & CE_HASHED)
-               return;
-       ce->ce_flags |= CE_HASHED;
-       ce->next = NULL;
-       hash = hash_name(ce->name, ce_namelen(ce));
-       pos = insert_hash(hash, ce, &istate->name_hash);
-       if (pos) {
-               ce->next = *pos;
-               *pos = ce;
-       }
-}
-
-static void lazy_init_name_hash(struct index_state *istate)
-{
-       int nr;
-
-       if (istate->name_hash_initialized)
-               return;
-       for (nr = 0; nr < istate->cache_nr; nr++)
-               hash_index_entry(istate, istate->cache[nr]);
-       istate->name_hash_initialized = 1;
-}
-
 static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
 {
-       ce->ce_flags &= ~CE_UNHASHED;
        istate->cache[nr] = ce;
-       if (istate->name_hash_initialized)
-               hash_index_entry(istate, ce);
+       add_name_hash(istate, ce);
 }
 
 static void replace_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
 {
        struct cache_entry *old = istate->cache[nr];
 
-       remove_index_entry(old);
+       remove_name_hash(old);
        set_index_entry(istate, nr, ce);
        istate->cache_changed = 1;
 }
 
-int index_name_exists(struct index_state *istate, const char *name, int namelen)
-{
-       unsigned int hash = hash_name(name, namelen);
-       struct cache_entry *ce;
-
-       lazy_init_name_hash(istate);
-       ce = lookup_hash(hash, &istate->name_hash);
-
-       while (ce) {
-               if (!(ce->ce_flags & CE_UNHASHED)) {
-                       if (!cache_name_compare(name, namelen, ce->name, ce->ce_flags))
-                               return 1;
-               }
-               ce = ce->next;
-       }
-       return 0;
-}
-
 /*
  * This only updates the "non-critical" parts of the directory
  * cache, ie the parts that aren't tracked by GIT, and only used
@@ -257,7 +198,8 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
 
 static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce)
 {
-       return (istate->timestamp &&
+       return (!S_ISGITLINK(ce->ce_mode) &&
+               istate->timestamp &&
                ((unsigned int)istate->timestamp) <= ce->ce_mtime);
 }
 
@@ -438,7 +380,7 @@ int remove_index_entry_at(struct index_state *istate, int pos)
 {
        struct cache_entry *ce = istate->cache[pos];
 
-       remove_index_entry(ce);
+       remove_name_hash(ce);
        istate->cache_changed = 1;
        istate->cache_nr--;
        if (pos >= istate->cache_nr)
@@ -488,21 +430,50 @@ static int index_name_pos_also_unmerged(struct index_state *istate,
        return pos;
 }
 
-int add_file_to_index(struct index_state *istate, const char *path, int verbose)
+static int different_name(struct cache_entry *ce, struct cache_entry *alias)
 {
-       int size, namelen, pos;
-       struct stat st;
-       struct cache_entry *ce;
-       unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;
+       int len = ce_namelen(ce);
+       return ce_namelen(alias) != len || memcmp(ce->name, alias->name, len);
+}
 
-       if (lstat(path, &st))
-               die("%s: unable to stat (%s)", path, strerror(errno));
+/*
+ * If we add a filename that aliases in the cache, we will use the
+ * name that we already have - but we don't want to update the same
+ * alias twice, because that implies that there were actually two
+ * different files with aliasing names!
+ *
+ * So we use the CE_ADDED flag to verify that the alias was an old
+ * one before we accept it as
+ */
+static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_entry *alias)
+{
+       int len;
+       struct cache_entry *new;
+
+       if (alias->ce_flags & CE_ADDED)
+               die("Will not add file alias '%s' ('%s' already exists in index)", ce->name, alias->name);
+
+       /* Ok, create the new entry using the name of the existing alias */
+       len = ce_namelen(alias);
+       new = xcalloc(1, cache_entry_size(len));
+       memcpy(new->name, alias->name, len);
+       copy_cache_entry(new, ce);
+       free(ce);
+       return new;
+}
+
+int add_to_index(struct index_state *istate, const char *path, struct stat *st, int verbose)
+{
+       int size, namelen;
+       mode_t st_mode = st->st_mode;
+       struct cache_entry *ce, *alias;
+       unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;
 
-       if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
+       if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))
                die("%s: can only add regular files, symbolic links or git-directories", path);
 
        namelen = strlen(path);
-       if (S_ISDIR(st.st_mode)) {
+       if (S_ISDIR(st_mode)) {
                while (namelen && path[namelen-1] == '/')
                        namelen--;
        }
@@ -510,10 +481,10 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)
        ce = xcalloc(1, size);
        memcpy(ce->name, path, namelen);
        ce->ce_flags = namelen;
-       fill_stat_cache_info(ce, &st);
+       fill_stat_cache_info(ce, st);
 
        if (trust_executable_bit && has_symlinks)
-               ce->ce_mode = create_ce_mode(st.st_mode);
+               ce->ce_mode = create_ce_mode(st_mode);
        else {
                /* If there is an existing entry, pick the mode bits and type
                 * from it, otherwise assume unexecutable regular file.
@@ -522,21 +493,22 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)
                int pos = index_name_pos_also_unmerged(istate, path, namelen);
 
                ent = (0 <= pos) ? istate->cache[pos] : NULL;
-               ce->ce_mode = ce_mode_from_stat(ent, st.st_mode);
+               ce->ce_mode = ce_mode_from_stat(ent, st_mode);
        }
 
-       pos = index_name_pos(istate, ce->name, namelen);
-       if (0 <= pos &&
-           !ce_stage(istate->cache[pos]) &&
-           !ie_match_stat(istate, istate->cache[pos], &st, ce_option)) {
+       alias = index_name_exists(istate, ce->name, ce_namelen(ce), ignore_case);
+       if (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, st, ce_option)) {
                /* Nothing changed, really */
                free(ce);
-               ce_mark_uptodate(istate->cache[pos]);
+               ce_mark_uptodate(alias);
+               alias->ce_flags |= CE_ADDED;
                return 0;
        }
-
-       if (index_path(ce->sha1, path, &st, 1))
+       if (index_path(ce->sha1, path, st, 1))
                die("unable to index file %s", path);
+       if (ignore_case && alias && different_name(ce, alias))
+               ce = create_alias_ce(ce, alias);
+       ce->ce_flags |= CE_ADDED;
        if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
                die("unable to add %s to index",path);
        if (verbose)
@@ -544,6 +516,14 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)
        return 0;
 }
 
+int add_file_to_index(struct index_state *istate, const char *path, int verbose)
+{
+       struct stat st;
+       if (lstat(path, &st))
+               die("%s: unable to stat (%s)", path, strerror(errno));
+       return add_to_index(istate, path, &st, verbose);
+}
+
 struct cache_entry *make_cache_entry(unsigned int mode,
                const unsigned char *sha1, const char *path, int stage,
                int refresh)
@@ -1370,7 +1350,7 @@ int write_index(const struct index_state *istate, int newfd)
                struct cache_entry *ce = cache[i];
                if (ce->ce_flags & CE_REMOVE)
                        continue;
-               if (is_racy_timestamp(istate, ce))
+               if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
                        ce_smudge_racily_clean_entry(ce);
                if (ce_write_entry(&c, newfd, ce) < 0)
                        return -1;
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 2d9af4023eba6f8b2fe528ccbf03569fcaa265ee..91cbb72ddeba1c60dab7aad2105e1c004ea6e198 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -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;
 }
 
@@ -686,6 +691,13 @@ struct ref *alloc_ref(unsigned namelen)
        return ret;
 }
 
+struct ref *alloc_ref_from_str(const char* str)
+{
+       struct ref *ret = alloc_ref(strlen(str) + 1);
+       strcpy(ret->name, str);
+       return ret;
+}
+
 static struct ref *copy_ref(const struct ref *ref)
 {
        struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1);
@@ -706,13 +718,22 @@ struct ref *copy_ref_list(const struct ref *ref)
        return ret;
 }
 
+void free_ref(struct ref *ref)
+{
+       if (!ref)
+               return;
+       free(ref->remote_status);
+       free(ref->symref);
+       free(ref);
+}
+
 void free_refs(struct ref *ref)
 {
        struct ref *next;
        while (ref) {
                next = ref->next;
                free(ref->peer_ref);
-               free(ref);
+               free_ref(ref);
                ref = next;
        }
 }
@@ -783,7 +804,6 @@ static struct ref *try_explicit_object_name(const char *name)
 {
        unsigned char sha1[20];
        struct ref *ref;
-       int len;
 
        if (!*name) {
                ref = alloc_ref(20);
@@ -793,21 +813,14 @@ static struct ref *try_explicit_object_name(const char *name)
        }
        if (get_sha1(name, sha1))
                return NULL;
-       len = strlen(name) + 1;
-       ref = alloc_ref(len);
-       memcpy(ref->name, name, len);
+       ref = alloc_ref_from_str(name);
        hashcpy(ref->new_sha1, sha1);
        return ref;
 }
 
 static struct ref *make_linked_ref(const char *name, struct ref ***tail)
 {
-       struct ref *ret;
-       size_t len;
-
-       len = strlen(name) + 1;
-       ret = alloc_ref(len);
-       memcpy(ret->name, name, len);
+       struct ref *ret = alloc_ref_from_str(name);
        tail_link_ref(ret, tail);
        return ret;
 }
@@ -1111,9 +1124,7 @@ static struct ref *get_local_ref(const char *name)
                return NULL;
 
        if (!prefixcmp(name, "refs/")) {
-               ret = alloc_ref(strlen(name) + 1);
-               strcpy(ret->name, name);
-               return ret;
+               return alloc_ref_from_str(name);
        }
 
        if (!prefixcmp(name, "heads/") ||
@@ -1172,3 +1183,15 @@ int get_fetch_map(const struct ref *remote_refs,
 
        return 0;
 }
+
+int resolve_remote_symref(struct ref *ref, struct ref *list)
+{
+       if (!ref->symref)
+               return 0;
+       for (; list; list = list->next)
+               if (!strcmp(ref->symref, list->name)) {
+                       hashcpy(ref->old_sha1, list->old_sha1);
+                       return 0;
+               }
+       return 1;
+}
index a38774bbdc5acfb5ed9360ac92e1049fa79b26e1..2ee83a33b3bb7ddaa5bb33493dacfbeca4ae5dbe 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;
@@ -53,6 +54,8 @@ struct refspec {
 
 struct ref *alloc_ref(unsigned namelen);
 
+struct ref *alloc_ref_from_str(const char* str);
+
 struct ref *copy_ref_list(const struct ref *ref);
 
 int check_ref_type(const struct ref *ref, int flags);
@@ -62,6 +65,8 @@ int check_ref_type(const struct ref *ref, int flags);
  */
 void free_refs(struct ref *ref);
 
+int resolve_remote_symref(struct ref *ref, struct ref *list);
+
 /*
  * Removes and frees any duplicate refs in the map.
  */
index ffbed3fbf22c22b8862c49bdddd10cab201f86a2..4231ea2cce57c74a4110c9c69459a4caf67dc15c 100644 (file)
@@ -1199,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 be9ace6c04ce1fe7d53d85f30adab2218ec1ec65..5a5e781a15d7d9cb60797958433eca896b31ec85 100644 (file)
@@ -1,48 +1,64 @@
 #include "cache.h"
 
-int has_symlink_leading_path(const char *name, char *last_symlink)
-{
+struct pathname {
+       int len;
        char path[PATH_MAX];
-       const char *sp, *ep;
-       char *dp;
-
-       sp = name;
-       dp = path;
-
-       if (last_symlink && *last_symlink) {
-               size_t last_len = strlen(last_symlink);
-               size_t len = strlen(name);
-               if (last_len < len &&
-                   !strncmp(name, last_symlink, last_len) &&
-                   name[last_len] == '/')
-                       return 1;
-               *last_symlink = '\0';
+};
+
+/* Return matching pathname prefix length, or zero if not matching */
+static inline int match_pathname(int len, const char *name, struct pathname *match)
+{
+       int match_len = match->len;
+       return (len > match_len &&
+               name[match_len] == '/' &&
+               !memcmp(name, match->path, match_len)) ? match_len : 0;
+}
+
+static inline void set_pathname(int len, const char *name, struct pathname *match)
+{
+       if (len < PATH_MAX) {
+               match->len = len;
+               memcpy(match->path, name, len);
+               match->path[len] = 0;
        }
+}
+
+int has_symlink_leading_path(int len, const char *name)
+{
+       static struct pathname link, nonlink;
+       char path[PATH_MAX];
+       struct stat st;
+       char *sp;
+       int known_dir;
 
-       while (1) {
-               size_t len;
-               struct stat st;
+       /*
+        * See if the last known symlink cache matches.
+        */
+       if (match_pathname(len, name, &link))
+               return 1;
 
-               ep = strchr(sp, '/');
-               if (!ep)
-                       break;
-               len = ep - sp;
-               if (PATH_MAX <= dp + len - path + 2)
-                       return 0; /* new name is longer than that??? */
-               memcpy(dp, sp, len);
-               dp[len] = 0;
+       /*
+        * Get rid of the last known directory part
+        */
+       known_dir = match_pathname(len, name, &nonlink);
+
+       while ((sp = strchr(name + known_dir + 1, '/')) != NULL) {
+               int thislen = sp - name ;
+               memcpy(path, name, thislen);
+               path[thislen] = 0;
 
                if (lstat(path, &st))
                        return 0;
+               if (S_ISDIR(st.st_mode)) {
+                       set_pathname(thislen, path, &nonlink);
+                       known_dir = thislen;
+                       continue;
+               }
                if (S_ISLNK(st.st_mode)) {
-                       if (last_symlink)
-                               strcpy(last_symlink, path);
+                       set_pathname(thislen, path, &link);
                        return 1;
                }
-
-               dp[len++] = '/';
-               dp = dp + len;
-               sp = ep + 1;
+               break;
        }
        return 0;
 }
index 9decd2e1e81cfcba9c2b407abb176eea73d37ed9..d8f33557911389ab0e179b70e27d85264ef5ca0a 100644 (file)
@@ -73,11 +73,16 @@ for d in \
 done
 
 start_httpd () {
+       repo_base_path="$1"
        if test -z "$SVN_HTTPD_PORT"
        then
                echo >&2 'SVN_HTTPD_PORT is not defined!'
                return
        fi
+       if test -z "$repo_base_path"
+       then
+               repo_base_path=svn
+       fi
 
        mkdir "$GIT_DIR"/logs
 
@@ -90,13 +95,13 @@ LockFile logs/accept.lock
 Listen 127.0.0.1:$SVN_HTTPD_PORT
 LoadModule dav_module $SVN_HTTPD_MODULE_PATH/mod_dav.so
 LoadModule dav_svn_module $SVN_HTTPD_MODULE_PATH/mod_dav_svn.so
-<Location /svn>
+<Location /$repo_base_path>
        DAV svn
        SVNPath $rawsvnrepo
 </Location>
 EOF
        "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k start
-       svnrepo=http://127.0.0.1:$SVN_HTTPD_PORT/svn
+       svnrepo="http://127.0.0.1:$SVN_HTTPD_PORT/$repo_base_path"
 }
 
 stop_httpd () {
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 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
diff --git a/t/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh
new file mode 100755 (executable)
index 0000000..95244c9
--- /dev/null
@@ -0,0 +1,107 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Christian Couder
+#
+test_description='test git rev-parse --verify'
+
+exec </dev/null
+
+. ./test-lib.sh
+
+add_line_into_file()
+{
+    _line=$1
+    _file=$2
+
+    if [ -f "$_file" ]; then
+        echo "$_line" >> $_file || return $?
+        MSG="Add <$_line> into <$_file>."
+    else
+        echo "$_line" > $_file || return $?
+        git add $_file || return $?
+        MSG="Create file <$_file> with <$_line> inside."
+    fi
+
+    test_tick
+    git-commit --quiet -m "$MSG" $_file
+}
+
+HASH1=
+HASH2=
+HASH3=
+HASH4=
+
+test_expect_success 'set up basic repo with 1 file (hello) and 4 commits' '
+       add_line_into_file "1: Hello World" hello &&
+       HASH1=$(git rev-parse --verify HEAD) &&
+       add_line_into_file "2: A new day for git" hello &&
+       HASH2=$(git rev-parse --verify HEAD) &&
+       add_line_into_file "3: Another new day for git" hello &&
+       HASH3=$(git rev-parse --verify HEAD) &&
+       add_line_into_file "4: Ciao for now" hello &&
+       HASH4=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'works with one good rev' '
+       rev_hash1=$(git rev-parse --verify $HASH1) &&
+       test "$rev_hash1" = "$HASH1" &&
+       rev_hash2=$(git rev-parse --verify $HASH2) &&
+       test "$rev_hash2" = "$HASH2" &&
+       rev_hash3=$(git rev-parse --verify $HASH3) &&
+       test "$rev_hash3" = "$HASH3" &&
+       rev_hash4=$(git rev-parse --verify $HASH4) &&
+       test "$rev_hash4" = "$HASH4" &&
+       rev_master=$(git rev-parse --verify master) &&
+       test "$rev_master" = "$HASH4" &&
+       rev_head=$(git rev-parse --verify HEAD) &&
+       test "$rev_head" = "$HASH4"
+'
+
+test_expect_success 'fails with any bad rev or many good revs' '
+       test_must_fail git rev-parse --verify 2>error &&
+       grep "single revision" error &&
+       test_must_fail git rev-parse --verify foo 2>error &&
+       grep "single revision" error &&
+       test_must_fail git rev-parse --verify HEAD bar 2>error &&
+       grep "single revision" error &&
+       test_must_fail git rev-parse --verify baz HEAD 2>error &&
+       grep "single revision" error &&
+       test_must_fail git rev-parse --verify $HASH2 HEAD 2>error &&
+       grep "single revision" error
+'
+
+test_expect_success 'fails silently when using -q' '
+       test_must_fail git rev-parse --verify --quiet 2>error &&
+       test -z "$(cat error)" &&
+       test_must_fail git rev-parse -q --verify foo 2>error &&
+       test -z "$(cat error)" &&
+       test_must_fail git rev-parse --verify -q HEAD bar 2>error &&
+       test -z "$(cat error)" &&
+       test_must_fail git rev-parse --quiet --verify baz HEAD 2>error &&
+       test -z "$(cat error)" &&
+       test_must_fail git rev-parse -q --verify $HASH2 HEAD 2>error &&
+       test -z "$(cat error)"
+'
+
+test_expect_success 'no stdout output on error' '
+       test -z "$(git rev-parse --verify)" &&
+       test -z "$(git rev-parse --verify foo)" &&
+       test -z "$(git rev-parse --verify baz HEAD)" &&
+       test -z "$(git rev-parse --verify HEAD bar)" &&
+       test -z "$(git rev-parse --verify $HASH2 HEAD)"
+'
+
+test_expect_success 'use --default' '
+       git rev-parse --verify --default master &&
+       git rev-parse --verify --default master HEAD &&
+       git rev-parse --default master --verify &&
+       git rev-parse --default master --verify HEAD &&
+       git rev-parse --verify HEAD --default master &&
+       test_must_fail git rev-parse --verify foo --default master &&
+       test_must_fail git rev-parse --default HEAD --verify bar &&
+       test_must_fail git rev-parse --verify --default HEAD baz &&
+       test_must_fail git rev-parse --default foo --verify &&
+       test_must_fail git rev-parse --verify --default bar
+'
+
+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 cb5f7a444175938c8a9cc4c029b41b7325b91618..8d8768688d8dde3637a50ece28fb8720b7612ddd 100755 (executable)
@@ -224,4 +224,238 @@ test_expect_success 'avoid ambiguous track' '
        test -z "$(git config branch.all1.merge)"
 '
 
+test_expect_success 'autosetuprebase local on a tracked local branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       git config branch.autosetuprebase local &&
+       (git show-ref -q refs/remotes/local/o || git-fetch local) &&
+       git branch mybase &&
+       git branch --track myr1 mybase &&
+       test "$(git config branch.myr1.remote)" = . &&
+       test "$(git config branch.myr1.merge)" = refs/heads/mybase &&
+       test "$(git config branch.myr1.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase always on a tracked local branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       git config branch.autosetuprebase always &&
+       (git show-ref -q refs/remotes/local/o || git-fetch local) &&
+       git branch mybase2 &&
+       git branch --track myr2 mybase &&
+       test "$(git config branch.myr2.remote)" = . &&
+       test "$(git config branch.myr2.merge)" = refs/heads/mybase &&
+       test "$(git config branch.myr2.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase remote on a tracked local branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       git config branch.autosetuprebase remote &&
+       (git show-ref -q refs/remotes/local/o || git-fetch local) &&
+       git branch mybase3 &&
+       git branch --track myr3 mybase2 &&
+       test "$(git config branch.myr3.remote)" = . &&
+       test "$(git config branch.myr3.merge)" = refs/heads/mybase2 &&
+       ! test "$(git config branch.myr3.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase never on a tracked local branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       git config branch.autosetuprebase never &&
+       (git show-ref -q refs/remotes/local/o || git-fetch local) &&
+       git branch mybase4 &&
+       git branch --track myr4 mybase2 &&
+       test "$(git config branch.myr4.remote)" = . &&
+       test "$(git config branch.myr4.merge)" = refs/heads/mybase2 &&
+       ! test "$(git config branch.myr4.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase local on a tracked remote branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       git config branch.autosetuprebase local &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --track myr5 local/master &&
+       test "$(git config branch.myr5.remote)" = local &&
+       test "$(git config branch.myr5.merge)" = refs/heads/master &&
+       ! test "$(git config branch.myr5.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase never on a tracked remote branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       git config branch.autosetuprebase never &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --track myr6 local/master &&
+       test "$(git config branch.myr6.remote)" = local &&
+       test "$(git config branch.myr6.merge)" = refs/heads/master &&
+       ! test "$(git config branch.myr6.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase remote on a tracked remote branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       git config branch.autosetuprebase remote &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --track myr7 local/master &&
+       test "$(git config branch.myr7.remote)" = local &&
+       test "$(git config branch.myr7.merge)" = refs/heads/master &&
+       test "$(git config branch.myr7.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase always on a tracked remote branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       git config branch.autosetuprebase remote &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --track myr8 local/master &&
+       test "$(git config branch.myr8.remote)" = local &&
+       test "$(git config branch.myr8.merge)" = refs/heads/master &&
+       test "$(git config branch.myr8.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase unconfigured on a tracked remote branch' '
+       git config --unset branch.autosetuprebase &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --track myr9 local/master &&
+       test "$(git config branch.myr9.remote)" = local &&
+       test "$(git config branch.myr9.merge)" = refs/heads/master &&
+       test "z$(git config branch.myr9.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase unconfigured on a tracked local branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/o || git-fetch local) &&
+       git branch mybase10 &&
+       git branch --track myr10 mybase2 &&
+       test "$(git config branch.myr10.remote)" = . &&
+       test "$(git config branch.myr10.merge)" = refs/heads/mybase2 &&
+       test "z$(git config branch.myr10.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase unconfigured on untracked local branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr11 mybase2 &&
+       test "z$(git config branch.myr11.remote)" = z &&
+       test "z$(git config branch.myr11.merge)" = z &&
+       test "z$(git config branch.myr11.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase unconfigured on untracked remote branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr12 local/master &&
+       test "z$(git config branch.myr12.remote)" = z &&
+       test "z$(git config branch.myr12.merge)" = z &&
+       test "z$(git config branch.myr12.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase never on an untracked local branch' '
+       git config branch.autosetuprebase never &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr13 mybase2 &&
+       test "z$(git config branch.myr13.remote)" = z &&
+       test "z$(git config branch.myr13.merge)" = z &&
+       test "z$(git config branch.myr13.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase local on an untracked local branch' '
+       git config branch.autosetuprebase local &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr14 mybase2 &&
+       test "z$(git config branch.myr14.remote)" = z &&
+       test "z$(git config branch.myr14.merge)" = z &&
+       test "z$(git config branch.myr14.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase remote on an untracked local branch' '
+       git config branch.autosetuprebase remote &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr15 mybase2 &&
+       test "z$(git config branch.myr15.remote)" = z &&
+       test "z$(git config branch.myr15.merge)" = z &&
+       test "z$(git config branch.myr15.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase always on an untracked local branch' '
+       git config branch.autosetuprebase always &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr16 mybase2 &&
+       test "z$(git config branch.myr16.remote)" = z &&
+       test "z$(git config branch.myr16.merge)" = z &&
+       test "z$(git config branch.myr16.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase never on an untracked remote branch' '
+       git config branch.autosetuprebase never &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr17 local/master &&
+       test "z$(git config branch.myr17.remote)" = z &&
+       test "z$(git config branch.myr17.merge)" = z &&
+       test "z$(git config branch.myr17.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase local on an untracked remote branch' '
+       git config branch.autosetuprebase local &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr18 local/master &&
+       test "z$(git config branch.myr18.remote)" = z &&
+       test "z$(git config branch.myr18.merge)" = z &&
+       test "z$(git config branch.myr18.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase remote on an untracked remote branch' '
+       git config branch.autosetuprebase remote &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr19 local/master &&
+       test "z$(git config branch.myr19.remote)" = z &&
+       test "z$(git config branch.myr19.merge)" = z &&
+       test "z$(git config branch.myr19.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase always on an untracked remote branch' '
+       git config branch.autosetuprebase always &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr20 local/master &&
+       test "z$(git config branch.myr20.remote)" = z &&
+       test "z$(git config branch.myr20.merge)" = z &&
+       test "z$(git config branch.myr20.rebase)" = z
+'
+
+test_expect_success 'detect misconfigured autosetuprebase (bad value)' '
+       git config branch.autosetuprebase garbage &&
+       test_must_fail git branch
+'
+
+test_expect_success 'detect misconfigured autosetuprebase (no value)' '
+       git config --unset branch.autosetuprebase &&
+       echo "[branch] autosetuprebase" >> .git/config &&
+       test_must_fail git branch &&
+       git config --unset branch.autosetuprebase
+'
+
 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
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 1fd3fb74d763026d4e20a38fe9d2ed48dda74fed..ba6679c6e4032bb12e4206226f95770946ece8cc 100755 (executable)
@@ -50,4 +50,11 @@ test_expect_success 'git diff-files --raw' '
        test_cmp expect actual.files
 '
 
+test_expect_success 'git diff (empty submodule dir)' '
+       : >empty &&
+       rm -rf sub/* sub/.git &&
+       git diff > actual.empty &&
+       test_cmp empty actual.empty
+'
+
 test_done
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 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
index 526d7d1c4422e342c7257e260626dac3dda36a3a..bd4e49bf1e979a9b260a2aa23be60d15c911397a 100755 (executable)
@@ -106,8 +106,24 @@ Merge branch 'left'
   Common #1
 EOF
 
-test_expect_success 'merge-msg test #3' '
+test_expect_success 'merge-msg test #3-1' '
 
+       git config --unset-all merge.log
+       git config --unset-all merge.summary
+       git config merge.log true &&
+
+       git checkout master &&
+       setdate &&
+       git fetch . left &&
+
+       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       git diff actual expected
+'
+
+test_expect_success 'merge-msg test #3-2' '
+
+       git config --unset-all merge.log
+       git config --unset-all merge.summary
        git config merge.summary true &&
 
        git checkout master &&
@@ -136,8 +152,24 @@ Merge branches 'left' and 'right'
   Common #1
 EOF
 
-test_expect_success 'merge-msg test #4' '
+test_expect_success 'merge-msg test #4-1' '
+
+       git config --unset-all merge.log
+       git config --unset-all merge.summary
+       git config merge.log true &&
+
+       git checkout master &&
+       setdate &&
+       git fetch . left right &&
+
+       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       git diff actual expected
+'
+
+test_expect_success 'merge-msg test #4-2' '
 
+       git config --unset-all merge.log
+       git config --unset-all merge.summary
        git config merge.summary true &&
 
        git checkout master &&
@@ -148,8 +180,24 @@ test_expect_success 'merge-msg test #4' '
        git diff actual expected
 '
 
-test_expect_success 'merge-msg test #5' '
+test_expect_success 'merge-msg test #5-1' '
+
+       git config --unset-all merge.log
+       git config --unset-all merge.summary
+       git config merge.log yes &&
+
+       git checkout master &&
+       setdate &&
+       git fetch . left right &&
+
+       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       git diff actual expected
+'
+
+test_expect_success 'merge-msg test #5-2' '
 
+       git config --unset-all merge.log
+       git config --unset-all merge.summary
        git config merge.summary yes &&
 
        git checkout master &&
index efd658adb6d6327863f430b10ab65aa3f112f996..16df3d4adb1cc6bc970dd79829adbc46492bc66c 100755 (executable)
@@ -219,4 +219,36 @@ test_expect_success 'Subdirectory filter with disappearing trees' '
        test $(git rev-list master | wc -l) = 3
 '
 
+test_expect_success 'Tag name filtering retains tag message' '
+       git tag -m atag T &&
+       git cat-file tag T > expect &&
+       git filter-branch -f --tag-name-filter cat &&
+       git cat-file tag T > actual &&
+       git diff expect actual
+'
+
+faux_gpg_tag='object XXXXXX
+type commit
+tag S
+tagger T A Gger <tagger@example.com> 1206026339 -0500
+
+This is a faux gpg signed tag.
+-----BEGIN PGP SIGNATURE-----
+Version: FauxGPG v0.0.0 (FAUX/Linux)
+
+gdsfoewhxu/6l06f1kxyxhKdZkrcbaiOMtkJUA9ITAc1mlamh0ooasxkH1XwMbYQ
+acmwXaWET20H0GeAGP+7vow=
+=agpO
+-----END PGP SIGNATURE-----
+'
+test_expect_success 'Tag name filtering strips gpg signature' '
+       sha1=$(git rev-parse HEAD) &&
+       sha1t=$(echo "$faux_gpg_tag" | sed -e s/XXXXXX/$sha1/ | git mktag) &&
+       git update-ref "refs/tags/S" "$sha1t" &&
+       echo "$faux_gpg_tag" | sed -e s/XXXXXX/$sha1/ | head -n 6 > expect &&
+       git filter-branch -f --tag-name-filter cat &&
+       git cat-file tag S > actual &&
+       git diff expect actual
+'
+
 test_done
index b9a22190e86a0b8718b215771352ecefbdf86edc..bf12dbdeef6e307850a91eb6be5ebe537b2de0c8 100755 (executable)
@@ -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/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
new file mode 100755 (executable)
index 0000000..a75130c
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='git-status for submodule'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_create_repo sub
+       cd sub &&
+       : >bar &&
+       git add bar &&
+       git commit -m " Add bar" &&
+       cd .. &&
+       git add sub &&
+       git commit -m "Add submodule sub"
+'
+
+test_expect_success 'status clean' '
+       git status |
+       grep "nothing to commit"
+'
+test_expect_success 'status -a clean' '
+       git status -a |
+       grep "nothing to commit"
+'
+test_expect_success 'rm submodule contents' '
+       rm -rf sub/* sub/.git
+'
+test_expect_success 'status clean (empty submodule dir)' '
+       git status |
+       grep "nothing to commit"
+'
+test_expect_success 'status -a clean (empty submodule dir)' '
+       git status -a |
+       grep "nothing to commit"
+'
+
+test_done
index 56869aceeda90281a9fed992d0b202ec03fcb9f9..d21cd290d3d8e3d912b94045f3d3d9db8bdd7e7d 100755 (executable)
@@ -104,7 +104,11 @@ create_merge_msgs() {
        git log --no-merges ^HEAD c2 >>squash.1-5 &&
        echo "Squashed commit of the following:" >squash.1-5-9 &&
        echo >>squash.1-5-9 &&
-       git log --no-merges ^HEAD c2 c3 >>squash.1-5-9
+       git log --no-merges ^HEAD c2 c3 >>squash.1-5-9 &&
+       echo > msg.nolog &&
+       echo "* commit 'c3':" >msg.log &&
+       echo "  commit 3" >>msg.log &&
+       echo >>msg.log
 }
 
 verify_diff() {
@@ -364,7 +368,7 @@ test_expect_success 'merge c1 with c2 (squash in config)' '
 
 test_debug 'gitk --all'
 
-test_expect_success 'override config option -n' '
+test_expect_success 'override config option -n with --summary' '
        git reset --hard c1 &&
        git config branch.master.mergeoptions "-n" &&
        test_tick &&
@@ -373,15 +377,30 @@ test_expect_success 'override config option -n' '
        verify_parents $c1 $c2 &&
        if ! grep "^ file |  *2 +-$" diffstat.txt
        then
-               echo "[OOPS] diffstat was not generated"
+               echo "[OOPS] diffstat was not generated with --summary"
+               false
+       fi
+'
+
+test_expect_success 'override config option -n with --stat' '
+       git reset --hard c1 &&
+       git config branch.master.mergeoptions "-n" &&
+       test_tick &&
+       git merge --stat c2 >diffstat.txt &&
+       verify_merge file result.1-5 msg.1-5 &&
+       verify_parents $c1 $c2 &&
+       if ! grep "^ file |  *2 +-$" diffstat.txt
+       then
+               echo "[OOPS] diffstat was not generated with --stat"
+               false
        fi
 '
 
 test_debug 'gitk --all'
 
-test_expect_success 'override config option --summary' '
+test_expect_success 'override config option --stat' '
        git reset --hard c1 &&
-       git config branch.master.mergeoptions "--summary" &&
+       git config branch.master.mergeoptions "--stat" &&
        test_tick &&
        git merge -n c2 >diffstat.txt &&
        verify_merge file result.1-5 msg.1-5 &&
@@ -441,6 +460,16 @@ test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
        verify_head $c1
 '
 
+test_expect_success 'merge log message' '
+       git reset --hard c0 &&
+       git merge --no-log c2 &&
+       git show -s --pretty=format:%b HEAD >msg.act &&
+       verify_diff msg.nolog msg.act "[OOPS] bad merge log message" &&
+       git merge --log c3 &&
+       git show -s --pretty=format:%b HEAD >msg.act &&
+       verify_diff msg.log msg.act "[OOPS] bad merge log message"
+'
+
 test_debug 'gitk --all'
 
 test_done
index 182299cbb53a8bae9e3cd51f9aff9073e08559c1..4acbcb0acdce7852ee1f416250b5939132a93ca1 100755 (executable)
@@ -9,7 +9,7 @@ test_description='git-svn dcommit can commit renames of files with ugly names'
 
 test_expect_success 'load repository with strange names' "
        svnadmin load -q $rawsvnrepo < ../t9115/funky-names.dump &&
-       start_httpd
+       start_httpd gtk+
        "
 
 test_expect_success 'init and fetch repository' "
@@ -49,6 +49,39 @@ test_expect_success 'rename pretty file into ugly one' '
        git svn dcommit
        '
 
+test_expect_success 'add a file with plus signs' '
+       echo .. > +_+ &&
+       git update-index --add +_+ &&
+       git commit -m plus &&
+       mkdir gtk+ &&
+       git mv +_+ gtk+/_+_ &&
+       git commit -m plus_dir &&
+       git svn dcommit
+       '
+
+test_expect_success 'clone the repository to test rebase' "
+       git svn clone $svnrepo test-rebase &&
+       cd test-rebase &&
+               echo test-rebase > test-rebase &&
+               git add test-rebase &&
+               git commit -m test-rebase &&
+               cd ..
+       "
+
+test_expect_success 'make a commit to test rebase' "
+               echo test-rebase-main > test-rebase-main &&
+               git add test-rebase-main &&
+               git commit -m test-rebase-main &&
+               git svn dcommit
+       "
+
+test_expect_success 'git-svn rebase works inside a fresh-cloned repository' "
+       cd test-rebase &&
+               git svn rebase &&
+               test -e test-rebase-main &&
+               test -e test-rebase
+       "
+
 stop_httpd
 
 test_done
index 393e0e8fe233ca51dc4c5db94c55e81ee53baa52..1bc16f2b65f46650c53227758df8431cf5a77598 100644 (file)
@@ -441,10 +441,14 @@ static struct ref *get_refs_via_curl(struct transport *transport)
        struct ref *ref = NULL;
        struct ref *last_ref = NULL;
 
+       struct walker *walker;
+
        if (!transport->data)
                transport->data = get_http_walker(transport->url,
                                                transport->remote);
 
+       walker = transport->data;
+
        refs_url = xmalloc(strlen(transport->url) + 11);
        sprintf(refs_url, "%s/info/refs", transport->url);
 
@@ -500,6 +504,15 @@ static struct ref *get_refs_via_curl(struct transport *transport)
 
        strbuf_release(&buffer);
 
+       ref = alloc_ref_from_str("HEAD");
+       if (!walker->fetch_ref(walker, ref) &&
+           !resolve_remote_symref(ref, refs)) {
+               ref->next = refs;
+               refs = ref;
+       } else {
+               free(ref);
+       }
+
        return refs;
 }
 
@@ -532,9 +545,8 @@ static struct ref *get_refs_from_bundle(struct transport *transport)
                die ("Could not read bundle '%s'.", transport->url);
        for (i = 0; i < data->header.references.nr; i++) {
                struct ref_list_entry *e = data->header.references.list + i;
-               struct ref *ref = alloc_ref(strlen(e->name) + 1);
+               struct ref *ref = alloc_ref_from_str(e->name);
                hashcpy(ref->old_sha1, e->sha1);
-               strcpy(ref->name, e->name);
                ref->next = result;
                result = ref;
        }
index a59f47557a2b3760c27b93fa678697c35211f952..1ab28fda45be940b300b504ae029f3fba2c28fa9 100644 (file)
@@ -26,11 +26,12 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
  * directories, in case this unlink is the removal of the
  * last entry in the directory -- empty directories are removed.
  */
-static void unlink_entry(char *name, char *last_symlink)
+static void unlink_entry(struct cache_entry *ce)
 {
        char *cp, *prev;
+       char *name = ce->name;
 
-       if (has_symlink_leading_path(name, last_symlink))
+       if (has_symlink_leading_path(ce_namelen(ce), ce->name))
                return;
        if (unlink(name))
                return;
@@ -58,7 +59,6 @@ static int check_updates(struct unpack_trees_options *o)
 {
        unsigned cnt = 0, total = 0;
        struct progress *progress = NULL;
-       char last_symlink[PATH_MAX];
        struct index_state *index = &o->result;
        int i;
        int errs = 0;
@@ -75,24 +75,27 @@ static int check_updates(struct unpack_trees_options *o)
                cnt = 0;
        }
 
-       *last_symlink = '\0';
        for (i = 0; i < index->cache_nr; i++) {
                struct cache_entry *ce = index->cache[i];
 
-               if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
-                       display_progress(progress, ++cnt);
                if (ce->ce_flags & CE_REMOVE) {
+                       display_progress(progress, ++cnt);
                        if (o->update)
-                               unlink_entry(ce->name, last_symlink);
+                               unlink_entry(ce);
                        remove_index_entry_at(&o->result, i);
                        i--;
                        continue;
                }
+       }
+
+       for (i = 0; i < index->cache_nr; i++) {
+               struct cache_entry *ce = index->cache[i];
+
                if (ce->ce_flags & CE_UPDATE) {
+                       display_progress(progress, ++cnt);
                        ce->ce_flags &= ~CE_UPDATE;
                        if (o->update) {
                                errs |= checkout_entry(ce, &state, NULL);
-                               *last_symlink = '\0';
                        }
                }
        }
@@ -520,6 +523,22 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
        return cnt;
 }
 
+/*
+ * This gets called when there was no index entry for the tree entry 'dst',
+ * but we found a file in the working tree that 'lstat()' said was fine,
+ * and we're on a case-insensitive filesystem.
+ *
+ * See if we can find a case-insensitive match in the index that also
+ * matches the stat information, and assume it's that other file!
+ */
+static int icase_exists(struct unpack_trees_options *o, struct cache_entry *dst, struct stat *st)
+{
+       struct cache_entry *src;
+
+       src = index_name_exists(o->src_index, dst->name, ce_namelen(dst), 1);
+       return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID);
+}
+
 /*
  * We do not want to remove or overwrite a working tree file that
  * is not tracked, unless it is ignored.
@@ -532,12 +551,23 @@ static int verify_absent(struct cache_entry *ce, const char *action,
        if (o->index_only || o->reset || !o->update)
                return 0;
 
-       if (has_symlink_leading_path(ce->name, NULL))
+       if (has_symlink_leading_path(ce_namelen(ce), ce->name))
                return 0;
 
        if (!lstat(ce->name, &st)) {
                int cnt;
                int dtype = ce_to_dtype(ce);
+               struct cache_entry *result;
+
+               /*
+                * It may be that the 'lstat()' succeeded even though
+                * target 'ce' was absent, because there is an old
+                * entry that is different only in case..
+                *
+                * Ignore that lstat() if it matches.
+                */
+               if (ignore_case && icase_exists(o, ce, &st))
+                       return 0;
 
                if (o->dir && excluded(o->dir, ce->name, &dtype))
                        /*
@@ -581,10 +611,9 @@ static int verify_absent(struct cache_entry *ce, const char *action,
                 * delete this path, which is in a subdirectory that
                 * is being replaced with a blob.
                 */
-               cnt = index_name_pos(&o->result, ce->name, strlen(ce->name));
-               if (0 <= cnt) {
-                       struct cache_entry *ce = o->result.cache[cnt];
-                       if (ce->ce_flags & CE_REMOVE)
+               result = index_name_exists(&o->result, ce->name, ce_namelen(ce), 0);
+               if (result) {
+                       if (result->ce_flags & CE_REMOVE)
                                return 0;
                }
 
index 50453ed20f755fea2e7138d7f01300b318f28dce..d436d6ced9939beeb4599dc8fddebe0890e55db8 100644 (file)
@@ -9,16 +9,16 @@ typedef int (*merge_fn_t)(struct cache_entry **src,
                struct unpack_trees_options *options);
 
 struct unpack_trees_options {
-       int reset;
-       int merge;
-       int update;
-       int index_only;
-       int nontrivial_merge;
-       int trivial_merges_only;
-       int verbose_update;
-       int aggressive;
-       int skip_unmerged;
-       int gently;
+       unsigned int reset:1,
+                    merge:1,
+                    update:1,
+                    index_only:1,
+                    nontrivial_merge:1,
+                    trivial_merges_only:1,
+                    verbose_update:1,
+                    aggressive:1,
+                    skip_unmerged:1,
+                    gently:1;
        const char *prefix;
        int pos;
        struct dir_struct *dir;
@@ -31,7 +31,7 @@ struct unpack_trees_options {
        void *unpack_data;
 
        struct index_state *dst_index;
-       const struct index_state *src_index;
+       struct index_state *src_index;
        struct index_state result;
 };
 
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 c10eca88261eed6b0e5c3fc2fdc19b8332c4ac6b..31de6c16bd021003bdd4fc1475bfe500f28773f4 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -190,9 +190,13 @@ static int interpret_target(struct walker *walker, char *target, unsigned char *
        if (!get_sha1_hex(target, sha1))
                return 0;
        if (!check_ref_format(target)) {
-               if (!walker->fetch_ref(walker, target, sha1)) {
+               struct ref *ref = alloc_ref_from_str(target);
+               if (!walker->fetch_ref(walker, ref)) {
+                       hashcpy(sha1, ref->old_sha1);
+                       free(ref);
                        return 0;
                }
+               free(ref);
        }
        return -1;
 }
index e1d40deaffa5965b1edd381a610ab225e027e3b2..8a149e11084eeec4501b5b2c5d22e5266f4852e7 100644 (file)
--- a/walker.h
+++ b/walker.h
@@ -5,7 +5,7 @@
 
 struct walker {
        void *data;
-       int (*fetch_ref)(struct walker *, char *ref, unsigned char *sha1);
+       int (*fetch_ref)(struct walker *, struct ref *ref);
        void (*prefetch)(struct walker *, unsigned char *sha1);
        int (*fetch)(struct walker *, unsigned char *sha1);
        void (*cleanup)(struct walker *);
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;
index 02afaa60eee74018e074a4dcb3bec97c2e0ce9dc..7d61410b1733825e3bb8042781a75de3ee62c41d 100644 (file)
@@ -28,8 +28,8 @@ struct wt_status {
 };
 
 int git_status_config(const char *var, const char *value);
-int wt_status_use_color;
-int wt_status_relative_paths;
+extern int wt_status_use_color;
+extern int wt_status_relative_paths;
 void wt_status_prepare(struct wt_status *s);
 void wt_status_print(struct wt_status *s);