Merge branch 'jc/blame-boundary'
authorJunio C Hamano <junkio@cox.net>
Mon, 18 Dec 2006 02:34:51 +0000 (18:34 -0800)
committerJunio C Hamano <junkio@cox.net>
Mon, 18 Dec 2006 02:34:51 +0000 (18:34 -0800)
* jc/blame-boundary:
git-blame: show lines attributed to boundary commits differently.

48 files changed:
Documentation/config.txt
Documentation/core-tutorial.txt
Documentation/diff-options.txt
Documentation/git-clone.txt
Documentation/git-commit.txt
Documentation/git-diff.txt
Documentation/git-merge-file.txt [new file with mode: 0644]
Documentation/git-repo-config.txt
Documentation/git-reset.txt
Documentation/git-show.txt
Documentation/git-svnimport.txt
Documentation/git.txt
Documentation/tutorial-2.txt
Documentation/tutorial.txt
INSTALL
Makefile
builtin-commit-tree.c
builtin-diff.c
builtin-init-db.c
builtin-log.c
builtin-repo-config.c
builtin-show-branch.c
builtin-show-ref.c
cache.h
config.c
contrib/completion/git-completion.bash
diff.c
diff.h
diffcore-rename.c
environment.c
generate-cmdlist.sh
git-commit.sh
git-fetch.sh
git-merge.sh
git-repack.sh
git-reset.sh
git-svn.perl
gitweb/gitweb.perl
ident.c
read-cache.c
receive-pack.c
refs.c
t/t0000-basic.sh
t/t1200-tutorial.sh
t/t1300-repo-config.sh
t/t1400-update-ref.sh
t/t3200-branch.sh
wt-status.c
index f5a552ee8722dc0e18265164fd6a6b344d691d57..ceac54b024bd407f3c3d3079f6164b3d61401da3 100644 (file)
@@ -79,8 +79,11 @@ core.logAllRefUpdates::
        file is automatically created for branch heads.
 
        This information can be used to determine what commit
-       was the tip of a branch "2 days ago".  This value is
-       false by default (no automated creation of log files).
+       was the tip of a branch "2 days ago".
+
+       This value is true by default in a repository that has
+       a working directory associated with it, and false by
+       default in a bare repository.
 
 core.repositoryFormatVersion::
        Internal variable identifying the repository format and layout
@@ -137,10 +140,6 @@ branch.<name>.merge::
        this option, `git pull` defaults to merge the first refspec fetched.
        Specify multiple values to get an octopus merge.
 
-color.pager::
-       A boolean to enable/disable colored output when the pager is in
-       use (default is true).
-
 color.diff::
        When true (or `always`), always use colors in patch.
        When false (or `never`), never.  When set to `auto`, use
@@ -157,6 +156,24 @@ color.diff.<slot>::
        `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, or
        `white`.
 
+color.pager::
+       A boolean to enable/disable colored output when the pager is in
+       use (default is true).
+
+color.status::
+       A boolean to enable/disable color in the output of
+       gitlink:git-status[1]. May be set to `true` (or `always`),
+       `false` (or `never`) or `auto`, in which case colors are used
+       only when the output is to a terminal. Defaults to false.
+
+color.status.<slot>::
+       Use customized color for status colorization. `<slot>` is
+       one of `header` (the header text of the status message),
+       `added` or `updated` (files which are added but not committed),
+       `changed` (files which are changed but not added in the index),
+       or `untracked` (files which are not tracked by git). The values of
+       these variables may be specified as in color.diff.<slot>.
+
 diff.renameLimit::
        The number of files to consider when performing the copy/rename
        detection; equivalent to the git diff option '-l'.
@@ -271,20 +288,6 @@ showbranch.default::
        The default set of branches for gitlink:git-show-branch[1].
        See gitlink:git-show-branch[1].
 
-color.status::
-       A boolean to enable/disable color in the output of
-       gitlink:git-status[1]. May be set to `true` (or `always`),
-       `false` (or `never`) or `auto`, in which case colors are used
-       only when the output is to a terminal. Defaults to false.
-
-color.status.<slot>::
-       Use customized color for status colorization. `<slot>` is
-       one of `header` (the header text of the status message),
-       `updated` (files which are updated but not committed),
-       `changed` (files which are changed but not updated in the index),
-       or `untracked` (files which are not tracked by git). The values of
-       these variables may be specified as in color.diff.<slot>.
-
 tar.umask::
        By default, gitlink:git-tar-tree[1] sets file and directories modes
        to 0666 or 0777. While this is both useful and acceptable for projects
index 47505aa20a56b4dc29fad86d66e8c6638edf03e0..5ea611748c6da80ee7df0387c5c5021a7095f45e 100644 (file)
@@ -57,7 +57,7 @@ $ git-init-db
 to which git will reply
 
 ----------------
-defaulting to local storage area
+Initialized empty Git repository in .git/
 ----------------
 
 which is just git's way of saying that you haven't been doing anything
@@ -336,17 +336,9 @@ $ commit=$(echo 'Initial commit' | git-commit-tree $tree)
 $ git-update-ref HEAD $commit
 ------------------------------------------------
 
-which will say:
-
-----------------
-Committing initial tree 8988da15d077d4829fc51d8544c097def6644dbb
-----------------
-
-just to warn you about the fact that it created a totally new commit
-that is not related to anything else. Normally you do this only *once*
-for a project ever, and all later commits will be parented on top of an
-earlier commit, and you'll never see this "Committing initial tree"
-message ever again.
+In this case this creates a totally new commit that is not related to
+anything else. Normally you do this only *once* for a project ever, and
+all later commits will be parented on top of an earlier commit.
 
 Again, normally you'd never actually do this by hand. There is a
 helpful script called `git commit` that will do all of this for you. So
index 9cdd171af74013340144ab60b703498001809502..f12082e134871c37c92a06c68430de2ef2a0413b 100644 (file)
        deleted lines in decimal notation and pathname without
        abbreviation, to make it more machine friendly.
 
+--shortstat::
+       Output only the last line of the --stat format containing total
+       number of modified files, as well as number of added and deleted
+       lines.
+
 --summary::
        Output a condensed summary of extended header information
        such as creations, renames and mode changes.
index 985043facab96a29e7cf19df213539312e7b5642..bfddb21fee0a73641dbbbb7efb1863f162851653 100644 (file)
@@ -100,7 +100,7 @@ OPTIONS
        defined default, typically `/usr/share/git-core/templates`.
 
 --use-separate-remote::
-       Save remotes heads under `$GIT_DIR/remotes/origin/` instead
+       Save remotes heads under `$GIT_DIR/refs/remotes/origin/` instead
        of `$GIT_DIR/refs/heads/`.  Only the local master branch is
        saved in the latter. This is the default.
 
index 97d66ef4d2c982706be98eaaea056107694e8d8c..0b74cd708ecaa63ccc19d2db40790b315539ce8b 100644 (file)
@@ -113,6 +113,9 @@ but can be used to amend a merge commit.
        as well.  This is usually not what you want unless you
        are concluding a conflicted merge.
 
+-q|--quiet::
+       Supress commit summary message.
+
 \--::
        Do not interpret any more arguments as options.
 
index 3144864d85009824996e3067e78adeadedc63c3d..10fdf88dce296f9fd7aea849b974cfbe709fa1b4 100644 (file)
@@ -8,38 +8,54 @@ git-diff - Show changes between commits, commit and working tree, etc
 
 SYNOPSIS
 --------
-'git-diff' [ --diff-options ] <tree-ish>{0,2} [<path>...]
+'git-diff' [ --diff-options ] <tree-ish>{0,2} [--] [<path>...]
 
 DESCRIPTION
 -----------
 Show changes between two trees, a tree and the working tree, a
 tree and the index file, or the index file and the working tree.
-The combination of what is compared with what is determined by
-the number of trees given to the command.
 
-* When no <tree-ish> is given, the working tree and the index
-  file are compared, using `git-diff-files`.
+'git-diff' [--options] [--] [<path>...]::
 
-* When one <tree-ish> is given, the working tree and the named
-  tree are compared, using `git-diff-index`.  The option
-  `--index` can be given to compare the index file and
-  the named tree.
-  `--cached` is a deprecated alias for `--index`. It's use is
-  discouraged.
+       This form is to view the changes you made relative to
+       the index (staging area for the next commit).  In other
+       words, the differences are what you _could_ tell git to
+       further add to the index but you still haven't.  You can
+       stage these changes by using gitlink:git-add[1].
+
+'git-diff' [--options] --cached [<commit>] [--] [<path>...]::
+
+       This form is to view the changes you staged for the next
+       commit relative to the named <tree-ish>.  Typically you
+       would want comparison with the latest commit, so if you
+       do not give <commit>, it defaults to HEAD.
+
+'git-diff' [--options] <commit> [--] [<path>...]::
+
+       This form is to view the changes you have in your
+       working tree relative to the named <commit>.  You can
+       use HEAD to compare it with the latest commit, or a
+       branch name to compare with the tip of a different
+       branch.
+
+'git-diff' [--options] <commit> <commit> [--] [<path>...]::
+
+       This form is to view the changes between two <commit>,
+       for example, tips of two branches.
+
+Just in case if you are doing something exotic, it should be
+noted that all of the <commit> in the above description can be
+any <tree-ish>.
 
-* When two <tree-ish>s are given, these two trees are compared
-  using `git-diff-tree`.
 
 OPTIONS
 -------
---diff-options::
-       '--diff-options' are passed to the `git-diff-files`,
-       `git-diff-index`, and `git-diff-tree` commands.  See the
-       documentation for these commands for description.
+include::diff-options.txt[]
 
 <path>...::
-       The <path> arguments are also passed to `git-diff-\*`
-       commands.
+       The <paths> parameters, when given, are used to limit
+       the diff to the named paths (you can give directory
+       names and get diff for all files under them).
 
 
 EXAMPLES
@@ -49,11 +65,11 @@ Various ways to check your working tree::
 +
 ------------
 $ git diff            <1>
-$ git diff --index    <2>
+$ git diff --cached   <2>
 $ git diff HEAD       <3>
 ------------
 +
-<1> changes in the working tree since your last git-update-index.
+<1> changes in the working tree not yet staged for the next commit.
 <2> changes between the index and your last commit; what you
 would be committing if you run "git commit" without "-a" option.
 <3> changes in the working tree since your last commit; what you
diff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt
new file mode 100644 (file)
index 0000000..29d3faa
--- /dev/null
@@ -0,0 +1,92 @@
+git-merge-file(1)
+=================
+
+NAME
+----
+git-merge-file - three-way file merge
+
+
+SYNOPSIS
+--------
+[verse]
+'git-merge-file' [-L <current-name> [-L <base-name> [-L <other-name>]]]
+       [-p|--stdout] [-q|--quiet] <current-file> <base-file> <other-file>
+
+
+DESCRIPTION
+-----------
+git-file-merge incorporates all changes that lead from the `<base-file>`
+to `<other-file>` into `<current-file>`. The result ordinarily goes into
+`<current-file>`. git-merge-file is useful for combining separate changes
+to an original. Suppose `<base-file>` is the original, and both
+`<current-file>` and `<other-file>` are modifications of `<base-file>`.
+Then git-merge-file combines both changes.
+
+A conflict occurs if both `<current-file>` and `<other-file>` have changes
+in a common segment of lines. If a conflict is found, git-merge-file
+normally outputs a warning and brackets the conflict with <<<<<<< and
+>>>>>>> lines. A typical conflict will look like this:
+
+       <<<<<<< A
+       lines in file A
+       =======
+       lines in file B
+       >>>>>>> B
+
+If there are conflicts, the user should edit the result and delete one of
+the alternatives.
+
+The exit value of this program is negative on error, and the number of
+conflicts otherwise. If the merge was clean, the exit value is 0.
+
+git-merge-file is designed to be a minimal clone of RCS merge, that is, it
+implements all of RCS merge's functionality which is needed by
+gitlink:git[1].
+
+
+OPTIONS
+-------
+
+-L <label>::
+       This option may be given up to three times, and
+       specifies labels to be used in place of the
+       corresponding file names in conflict reports. That is,
+       `git-merge-file -L x -L y -L z a b c` generates output that
+       looks like it came from files x, y and z instead of
+       from files a, b and c.
+
+-p::
+       Send results to standard output instead of overwriting
+       `<current-file>`.
+
+-q::
+       Quiet;  do  not  warn about conflicts.
+
+
+EXAMPLES
+--------
+
+git merge-file README.my README README.upstream::
+
+       combines the changes of README.my and README.upstream since README,
+       tries to merge them and writes the result into README.my.
+
+git merge-file -L a -L b -L c tmp/a123 tmp/b234 tmp/c345::
+
+       merges tmp/a123 and tmp/c345 with the base tmp/b234, but uses labels
+       `a` and `c` instead of `tmp/a123` and `tmp/c345`.
+
+
+Author
+------
+Written by Johannes Schindelin <johannes.schindelin@gmx.de>
+
+
+Documentation
+--------------
+Documentation by Johannes Schindelin and the git-list <git@vger.kernel.org>,
+with parts copied from the original documentation of RCS merge.
+
+GIT
+---
+Part of the gitlink:git[7] suite
index 5bede9ac22d260d703c5632f44ffc404013650d0..b379ec5075981347e75d0795e44e57620d89399b 100644 (file)
@@ -10,6 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git-repo-config' [--global] [type] name [value [value_regex]]
+'git-repo-config' [--global] [type] --add name value
 'git-repo-config' [--global] [type] --replace-all name [value [value_regex]]
 'git-repo-config' [--global] [type] --get name [value_regex]
 'git-repo-config' [--global] [type] --get-all name [value_regex]
@@ -23,7 +24,8 @@ You can query/set/replace/unset options with this command. The name is
 actually the section and the key separated by a dot, and the value will be
 escaped.
 
-If you want to set/unset an option which can occur on multiple
+Multiple lines can be added to an option by using the '--add' option.
+If you want to update or unset an option which can occur on multiple
 lines, a POSIX regexp `value_regex` needs to be given.  Only the
 existing values that match the regexp are updated or unset.  If
 you want to handle the lines that do *not* match the regex, just
@@ -53,6 +55,10 @@ OPTIONS
        Default behavior is to replace at most one line. This replaces
        all lines matching the key (and optionally the value_regex).
 
+--add::
+       Adds a new line to the option without altering any existing
+       values.  This is the same as providing '^$' as the value_regex.
+
 --get::
        Get the value for a given key (optionally filtered by a regex
        matching the value). Returns error code 1 if the key was not
@@ -194,6 +200,12 @@ To actually match only values with an exclamation mark, you have to
 % git repo-config section.key value '[!]'
 ------------
 
+To add a new proxy, without altering any of the existing ones, use
+
+------------
+% git repo-config core.gitproxy '"proxy" for example.com'
+------------
+
 
 include::config.txt[]
 
index 73a0ffc41085e87fbc996fab4baa25ace1951460..4a4ceb62012d7dbd56085a87d62aa4dec928d800 100644 (file)
@@ -31,7 +31,7 @@ OPTIONS
 --soft::
        Does not touch the index file nor the working tree at all, but
        requires them to be in a good order. This leaves all your changed
-       files "Updated but not checked in", as gitlink:git-status[1] would
+       files "Added but not yet committed", as gitlink:git-status[1] would
        put it.
 
 --hard::
index 4c880a871792a64355ac94537af934aca14879ec..98dea6125da78fa74800925b57dad2f002b1d6f6 100644 (file)
@@ -3,20 +3,27 @@ git-show(1)
 
 NAME
 ----
-git-show - Show one commit with difference it introduces
+git-show - Show various types of objects
 
 
 SYNOPSIS
 --------
-'git-show' <option>...
+'git-show' [options] <object>...
 
 DESCRIPTION
 -----------
-Shows commit log and textual diff for a single commit.  The
-command internally invokes 'git-rev-list' piped to
-'git-diff-tree', and takes command line options for both of
-these commands. It also presents the merge commit in a special
-format as produced by 'git-diff-tree --cc'.
+Shows one or more objects (blobs, trees, tags and commits).
+
+For commits it shows the log message and textual diff. It also
+presents the merge commit in a special format as produced by
+'git-diff-tree --cc'.
+
+For tags, it shows the tag message and the referenced objects.
+
+For trees, it shows the names (equivalent to gitlink:git-ls-tree[1]
+with \--name-only).
+
+For plain blobs, it shows the plain contents.
 
 This manual page describes only the most frequently used options.
 
@@ -28,6 +35,25 @@ OPTIONS
 
 include::pretty-formats.txt[]
 
+
+EXAMPLES
+--------
+
+git show v1.0.0::
+       Shows the tag `v1.0.0`.
+
+git show v1.0.0^{tree}::
+       Shows the tree pointed to by the tag `v1.0.0`.
+
+git show next~10:Documentation/README
+       Shows the contents of the file `Documentation/README` as
+       they were current in the 10th last commit of the branch
+       `next`.
+
+git show master:Makefile master:t/Makefile
+       Concatenates the contents of said Makefiles in the head
+       of the branch `master`.
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org> and
index b1b87c2fcd9b60aef6fb937bd4d28572f1cb8463..2c7c7dad54b5d2c773194b494d98ea521bc54d2f 100644 (file)
@@ -15,6 +15,7 @@ SYNOPSIS
                [ -b branch_subdir ] [ -T trunk_subdir ] [ -t tag_subdir ]
                [ -s start_chg ] [ -m ] [ -r ] [ -M regex ]
                [ -I <ignorefile_name> ] [ -A <author_file> ]
+               [ -P <path_from_trunk> ]
                <SVN_repository_URL> [ <path> ]
 
 
@@ -103,9 +104,17 @@ repository without -A.
 
 -l <max_rev>::
        Specify a maximum revision number to pull.
++
+Formerly, this option controlled how many revisions to pull,
+due to SVN memory leaks. (These have been worked around.)
 
-       Formerly, this option controlled how many revisions to pull,
-       due to SVN memory leaks. (These have been worked around.)
+-P <path_from_trunk>::
+       Partial import of the SVN tree.
++
+By default, the whole tree on the SVN trunk (/trunk) is imported.
+'-P my/proj' will import starting only from '/trunk/my/proj'.
+This option is useful when you want to import one project from a
+svn repo which hosts multiple projects under the same trunk.
 
 -v::
        Verbosity: let 'svnimport' report what it is doing.
index 6382ef0a02f9c40d5a46e10ea517a64bfa9ba42d..2a9e97dac1e2c21a8a4aa7a6501a054fcefbef35 100644 (file)
@@ -351,6 +351,9 @@ gitlink:git-init-db[1]::
        Creates an empty git object database, or reinitialize an
        existing one.
 
+gitlink:git-merge-file[1]::
+       Runs a threeway merge.
+
 gitlink:git-merge-index[1]::
        Runs a merge for files needing merging.
 
index 6389de5ef7e221d84bb9c5443af09fb62960de4c..60e54777dc6cef259a045512f42a50527f430fb9 100644 (file)
@@ -18,18 +18,18 @@ Let's start a new project and create a small amount of history:
 $ mkdir test-project
 $ cd test-project
 $ git init-db
-defaulting to local storage area
+Initialized empty Git repository in .git/
 $ echo 'hello world' > file.txt
 $ git add .
 $ git commit -a -m "initial commit"
-Committing initial tree 92b8b694ffb1675e5975148e1121810081dbdffe
+Created initial commit 54196cc2703dc165cbd373a65a4dcf22d50ae7f7
  create mode 100644 file.txt
 $ echo 'hello world!' >file.txt
 $ git commit -a -m "add emphasis"
+Created commit c4d59f390b9cfd4318117afde11d601c1085f241
 ------------------------------------------------
 
-What are the 40 digits of hex that git responded to the first commit
-with?
+What are the 40 digits of hex that git responded to the commit with?
 
 We saw in part one of the tutorial that commits have names like this.
 It turns out that every object in the git history is stored under
@@ -39,13 +39,25 @@ the same data twice (since identical data is given an identical SHA1
 name), and that the contents of a git object will never change (since
 that would change the object's name as well).
 
+It is expected that the content of the commit object you created while
+following the example above generates a different SHA1 hash than
+the one shown above because the commit object records the time when
+it was created and the name of the person performing the commit.
+
 We can ask git about this particular object with the cat-file
-command--just cut-and-paste from the reply to the initial commit, to
-save yourself typing all 40 hex digits:
+command. Don't copy the 40 hex digits from this example but use those
+from your own version. Note that you can shorten it to only a few
+characters to save yourself typing all 40 hex digits:
 
 ------------------------------------------------
-$ git cat-file -t 92b8b694ffb1675e5975148e1121810081dbdffe
-tree
+$ git-cat-file -t 54196cc2
+commit
+$ git-cat-file commit 54196cc2
+tree 92b8b694ffb1675e5975148e1121810081dbdffe
+author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500
+committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500
+
+initial commit
 ------------------------------------------------
 
 A tree can refer to one or more "blob" objects, each corresponding to
@@ -102,8 +114,7 @@ $ find .git/objects/
 
 and the contents of these files is just the compressed data plus a
 header identifying their length and their type.  The type is either a
-blob, a tree, a commit, or a tag.  We've seen a blob and a tree now,
-so next we should look at a commit.
+blob, a tree, a commit, or a tag.
 
 The simplest commit to find is the HEAD commit, which we can find
 from .git/HEAD:
@@ -342,23 +353,23 @@ situation:
 ------------------------------------------------
 $ git status
 #
-# Updated but not checked in:
+# Added but not yet committed:
 #   (will commit)
 #
 #       new file: closing.txt
 #
 #
-# Changed but not updated:
-#   (use git-update-index to mark for commit)
+# Changed but not added:
+#   (use "git add file1 file2" to include for commit)
 #
 #       modified: file.txt
 #
 ------------------------------------------------
 
 Since the current state of closing.txt is cached in the index file,
-it is listed as "updated but not checked in".  Since file.txt has
+it is listed as "added but not yet committed".  Since file.txt has
 changes in the working directory that aren't reflected in the index,
-it is marked "changed but not updated".  At this point, running "git
+it is marked "changed but not added".  At this point, running "git
 commit" would create a commit that added closing.txt (with its new
 contents), but that didn't modify file.txt.
 
index 02dede320ca9ddff4a9de0c5bb273bdf5eff9e74..cb808d924be51e111b3e57b1b6e007cdf9f451bb 100644 (file)
@@ -38,7 +38,7 @@ $ git init-db
 Git will reply
 
 ------------------------------------------------
-defaulting to local storage area
+Initialized empty Git repository in .git/
 ------------------------------------------------
 
 You've now initialized the working directory--you may notice a new
diff --git a/INSTALL b/INSTALL
index b5dd9f0abb2483ad986950785fa95b8d373b60b6..e7aea60e92eba80d63e9596d8d78b9447c292bcf 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -72,16 +72,6 @@ Issues of note:
        - expat library; git-http-push uses it for remote lock
          management over DAV.  Similar to "curl" above, this is optional.
 
-       - "GNU diff" to generate patches.  Of course, you don't _have_ to
-         generate patches if you don't want to, but let's face it, you'll
-         be wanting to. Or why did you get git in the first place?
-
-         Non-GNU versions of the diff/patch programs don't generally support
-         the unified patch format (which is the one git uses), so you
-         really do want to get the GNU one.  Trust me, you will want to
-         do that even if it wasn't for git.  There's no point in living
-         in the dark ages any more. 
-
         - "wish", the Tcl/Tk windowing shell is used in gitk to show the
           history graphically
 
index 2d17fa702795ca08a8ffc3aeaf6393b300401e33..05cfe45b104d6860c3918f1c80596403ae73e5f7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -69,6 +69,9 @@ all:
 #
 # Define NO_MMAP if you want to avoid mmap.
 #
+# Define NO_FAST_WORKING_DIRECTORY if accessing objects in pack files is
+# generally faster on your platform than accessing the working directory.
+#
 # Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
 #
 # Define NO_SOCKADDR_STORAGE if your platform does not have struct
@@ -225,6 +228,8 @@ ifndef PERL_PATH
        PERL_PATH = /usr/bin/perl
 endif
 
+export PERL_PATH
+
 LIB_FILE=libgit.a
 XDIFF_LIB=xdiff/lib.a
 
@@ -355,6 +360,7 @@ ifeq ($(uname_O),Cygwin)
        NO_SYMLINK_HEAD = YesPlease
        NEEDS_LIBICONV = YesPlease
        NO_C99_FORMAT = YesPlease
+       NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
        # There are conflicting reports about this.
        # On some boxes NO_MMAP is needed, and not so elsewhere.
        # Try uncommenting this if you see things break -- YMMV.
@@ -506,6 +512,9 @@ ifdef NO_MMAP
        COMPAT_CFLAGS += -DNO_MMAP
        COMPAT_OBJS += compat/mmap.o
 endif
+ifdef NO_FAST_WORKING_DIRECTORY
+       BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
+endif
 ifdef NO_IPV6
        BASIC_CFLAGS += -DNO_IPV6
 endif
index e2e690a1ae89b47f595288e452da10113f7b2e8f..856f3cd841818bdad4446b3f7b75d5ab22959788 100644 (file)
@@ -107,8 +107,6 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
                if (new_parent(parents))
                        parents++;
        }
-       if (!parents)
-               fprintf(stderr, "Committing initial tree %s\n", argv[1]);
 
        init_buffer(&buffer, &size);
        add_buffer(&buffer, &size, "tree %s\n", sha1_to_hex(tree_sha1));
index 1c535b1dd620ce40814da41bdddb8638cafeceb4..a6590205e8f746304f3d06d3e328a05bf2bf954e 100644 (file)
@@ -137,7 +137,7 @@ static int builtin_diff_index(struct rev_info *revs,
        int cached = 0;
        while (1 < argc) {
                const char *arg = argv[1];
-               if (!strcmp(arg, "--index") || !strcmp(arg, "--cached"))
+               if (!strcmp(arg, "--cached"))
                        cached = 1;
                else
                        usage(builtin_diff_usage);
index 235a0ee48f2c5ce09c63a949358eb16cde05332d..1d7d15e8d53a7a2817ceff6f5c5e056747688c9e 100644 (file)
@@ -164,13 +164,14 @@ static void copy_templates(const char *git_dir, int len, const char *template_di
        closedir(dir);
 }
 
-static void create_default_files(const char *git_dir, const char *template_path)
+static int create_default_files(const char *git_dir, const char *template_path)
 {
        unsigned len = strlen(git_dir);
        static char path[PATH_MAX];
        unsigned char sha1[20];
        struct stat st1;
        char repo_version_string[10];
+       int reinit;
 
        if (len > sizeof(path)-50)
                die("insane git directory %s", git_dir);
@@ -218,7 +219,8 @@ static void create_default_files(const char *git_dir, const char *template_path)
         * branch, if it does not exist yet.
         */
        strcpy(path + len, "HEAD");
-       if (read_ref("HEAD", sha1) < 0) {
+       reinit = !read_ref("HEAD", sha1);
+       if (!reinit) {
                if (create_symref("HEAD", "refs/heads/master") < 0)
                        exit(1);
        }
@@ -239,6 +241,11 @@ static void create_default_files(const char *git_dir, const char *template_path)
                git_config_set("core.filemode",
                               filemode ? "true" : "false");
        }
+
+       /* Enable logAllRefUpdates if a working tree is attached */
+       if (!is_bare_git_dir(git_dir))
+               git_config_set("core.logallrefupdates", "true");
+       return reinit;
 }
 
 static const char init_db_usage[] =
@@ -256,7 +263,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
        const char *sha1_dir;
        const char *template_dir = NULL;
        char *path;
-       int len, i;
+       int len, i, reinit;
 
        for (i = 1; i < argc; i++, argv++) {
                const char *arg = argv[1];
@@ -274,10 +281,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
         * Set up the default .git directory contents
         */
        git_dir = getenv(GIT_DIR_ENVIRONMENT);
-       if (!git_dir) {
+       if (!git_dir)
                git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
-               fprintf(stderr, "defaulting to local storage area\n");
-       }
        safe_create_dir(git_dir, 0);
 
        /* Check to see if the repository version is right.
@@ -287,7 +292,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
         */
        check_repository_format();
 
-       create_default_files(git_dir, template_dir);
+       reinit = create_default_files(git_dir, template_dir);
 
        /*
         * And set up the object store.
@@ -314,5 +319,10 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                git_config_set("receive.denyNonFastforwards", "true");
        }
 
+       printf("%s%s Git repository in %s/\n",
+               reinit ? "Reinitialized existing" : "Initialized empty",
+               shared_repository ? " shared" : "",
+               git_dir);
+
        return 0;
 }
index 6821a08442603ba28ee2190a9f602f3d8d0f43f3..17014f70a2896de46ff4714e77093b8adaa9fc8f 100644 (file)
@@ -10,6 +10,7 @@
 #include "revision.h"
 #include "log-tree.h"
 #include "builtin.h"
+#include "tag.h"
 #include <time.h>
 #include <sys/time.h>
 
@@ -71,9 +72,43 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
        return cmd_log_walk(&rev);
 }
 
+static int show_object(const unsigned char *sha1, int suppress_header)
+{
+       unsigned long size;
+       char type[20];
+       char *buf = read_sha1_file(sha1, type, &size);
+       int offset = 0;
+
+       if (!buf)
+               return error("Could not read object %s", sha1_to_hex(sha1));
+
+       if (suppress_header)
+               while (offset < size && buf[offset++] != '\n') {
+                       int new_offset = offset;
+                       while (new_offset < size && buf[new_offset++] != '\n')
+                               ; /* do nothing */
+                       offset = new_offset;
+               }
+
+       if (offset < size)
+               fwrite(buf + offset, size - offset, 1, stdout);
+       free(buf);
+       return 0;
+}
+
+static int show_tree_object(const unsigned char *sha1,
+               const char *base, int baselen,
+               const char *pathname, unsigned mode, int stage)
+{
+       printf("%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
+       return 0;
+}
+
 int cmd_show(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
+       struct object_array_entry *objects;
+       int i, count, ret = 0;
 
        git_config(git_log_config);
        init_revisions(&rev, prefix);
@@ -85,7 +120,52 @@ int cmd_show(int argc, const char **argv, const char *prefix)
        rev.ignore_merges = 0;
        rev.no_walk = 1;
        cmd_log_init(argc, argv, prefix, &rev);
-       return cmd_log_walk(&rev);
+
+       count = rev.pending.nr;
+       objects = rev.pending.objects;
+       for (i = 0; i < count && !ret; i++) {
+               struct object *o = objects[i].item;
+               const char *name = objects[i].name;
+               switch (o->type) {
+               case OBJ_BLOB:
+                       ret = show_object(o->sha1, 0);
+                       break;
+               case OBJ_TAG: {
+                       struct tag *t = (struct tag *)o;
+
+                       printf("%stag %s%s\n\n",
+                                       diff_get_color(rev.diffopt.color_diff,
+                                               DIFF_COMMIT),
+                                       t->tag,
+                                       diff_get_color(rev.diffopt.color_diff,
+                                               DIFF_RESET));
+                       ret = show_object(o->sha1, 1);
+                       objects[i].item = (struct object *)t->tagged;
+                       i--;
+                       break;
+               }
+               case OBJ_TREE:
+                       printf("%stree %s%s\n\n",
+                                       diff_get_color(rev.diffopt.color_diff,
+                                               DIFF_COMMIT),
+                                       name,
+                                       diff_get_color(rev.diffopt.color_diff,
+                                               DIFF_RESET));
+                       read_tree_recursive((struct tree *)o, "", 0, 0, NULL,
+                                       show_tree_object);
+                       break;
+               case OBJ_COMMIT:
+                       rev.pending.nr = rev.pending.alloc = 0;
+                       rev.pending.objects = NULL;
+                       add_object_array(o, name, &rev.pending);
+                       ret = cmd_log_walk(&rev);
+                       break;
+               default:
+                       ret = error("Unknown type: %d", o->type);
+               }
+       }
+       free(objects);
+       return ret;
 }
 
 int cmd_log(int argc, const char **argv, const char *prefix)
index 7b6e5725ae6d33350a9648e57139b646ab49696e..a38099a63d20d6f4a8187770c84e3a180ebeaa52 100644 (file)
@@ -3,7 +3,7 @@
 #include <regex.h>
 
 static const char git_config_set_usage[] =
-"git-repo-config [ --global ] [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --unset | --unset-all] name [value [value_regex]] | --list";
+"git-repo-config [ --global ] [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --list";
 
 static char *key;
 static regex_t *key_regexp;
@@ -148,6 +148,18 @@ int cmd_repo_config(int argc, const char **argv, const char *prefix)
                        } else {
                                die("$HOME not set");
                        }
+               } else if (!strcmp(argv[1], "--rename-section")) {
+                       int ret;
+                       if (argc != 4)
+                               usage(git_config_set_usage);
+                       ret = git_config_rename_section(argv[2], argv[3]);
+                       if (ret < 0)
+                               return ret;
+                       if (ret == 0) {
+                               fprintf(stderr, "No such section!\n");
+                               return 1;
+                       }
+                       return 0;
                } else
                        break;
                argc--;
@@ -190,7 +202,9 @@ int cmd_repo_config(int argc, const char **argv, const char *prefix)
                        use_key_regexp = 1;
                        do_all = 1;
                        return get_value(argv[2], argv[3]);
-               } else if (!strcmp(argv[1], "--replace-all"))
+               } else if (!strcmp(argv[1], "--add"))
+                       return git_config_set_multivar(argv[2], argv[3], "^$", 0);
+               else if (!strcmp(argv[1], "--replace-all"))
 
                        return git_config_set_multivar(argv[2], argv[3], NULL, 1);
                else
index fb1a4000d98e27389904578309b935cbda2a85fe..a38ac34efb8738fca2b415a47f782dd3745e9ff9 100644 (file)
@@ -6,7 +6,7 @@
 #include "builtin.h"
 
 static const char show_branch_usage[] =
-"git-show-branch [--sparse] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]";
+"git-show-branch [--sparse] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...] | --reflog[=n] <branch>";
 
 static int default_num;
 static int default_alloc;
@@ -17,6 +17,8 @@ static const char **default_arg;
 #define REV_SHIFT       2
 #define MAX_REVS       (FLAG_BITS - REV_SHIFT) /* should not exceed bits_per_int - REV_SHIFT */
 
+#define DEFAULT_REFLOG 4
+
 static struct commit *interesting(struct commit_list *list)
 {
        while (list) {
@@ -570,6 +572,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
        int head_at = -1;
        int topics = 0;
        int dense = 1;
+       int reflog = 0;
 
        git_config(git_show_branch_config);
 
@@ -615,6 +618,15 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                        dense = 0;
                else if (!strcmp(arg, "--date-order"))
                        lifo = 0;
+               else if (!strcmp(arg, "--reflog")) {
+                       reflog = DEFAULT_REFLOG;
+               }
+               else if (!strncmp(arg, "--reflog=", 9)) {
+                       char *end;
+                       reflog = strtoul(arg + 9, &end, 10);
+                       if (*end != '\0')
+                               die("unrecognized reflog count '%s'", arg + 9);
+               }
                else
                        usage(show_branch_usage);
                ac--; av++;
@@ -622,7 +634,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
        ac--; av++;
 
        /* Only one of these is allowed */
-       if (1 < independent + merge_base + (extra != 0))
+       if (1 < independent + merge_base + (extra != 0) + (!!reflog))
                usage(show_branch_usage);
 
        /* If nothing is specified, show all branches by default */
@@ -631,9 +643,22 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 
        if (all_heads + all_tags)
                snarf_refs(all_heads, all_tags);
-       while (0 < ac) {
-               append_one_rev(*av);
-               ac--; av++;
+       if (reflog) {
+               int reflen;
+               if (!ac)
+                       die("--reflog option needs one branch name");
+               reflen = strlen(*av);
+               for (i = 0; i < reflog; i++) {
+                       char *name = xmalloc(reflen + 20);
+                       sprintf(name, "%s@{%d}", *av, i);
+                       append_one_rev(name);
+               }
+       }
+       else {
+               while (0 < ac) {
+                       append_one_rev(*av);
+                       ac--; av++;
+               }
        }
 
        head_p = resolve_ref("HEAD", head_sha1, 1, NULL);
index 073979855b30b72363ed8116288b7bbbb2f0ff74..23e0ff8fbfea1ca29e4beb18c95475dd8bdae05b 100644 (file)
@@ -2,8 +2,9 @@
 #include "refs.h"
 #include "object.h"
 #include "tag.h"
+#include "path-list.h"
 
-static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<length>]] [--abbrev[=<length>]] [--tags] [--heads] [--] [pattern*]";
+static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<length>]] [--abbrev[=<length>]] [--tags] [--heads] [--] [pattern*] | --filter-invalid < ref-list";
 
 static int deref_tags = 0, show_head = 0, tags_only = 0, heads_only = 0,
        found_match = 0, verify = 0, quiet = 0, hash_only = 0, abbrev = 0;
@@ -86,6 +87,59 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
        return 0;
 }
 
+static int add_existing(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
+{
+       struct path_list *list = (struct path_list *)cbdata;
+       path_list_insert(refname, list);
+       return 0;
+}
+
+/*
+ * read "^(?:<anything>\s)?<refname>(?:\^\{\})?$" from the standard input,
+ * and
+ * (1) strip "^{}" at the end of line if any;
+ * (2) ignore if match is provided and does not head-match refname;
+ * (3) warn if refname is not a well-formed refname and skip;
+ * (4) ignore if refname is a ref that exists in the local repository;
+ * (5) otherwise output the line.
+ */
+static int exclude_existing(const char *match)
+{
+       static struct path_list existing_refs = { NULL, 0, 0, 0 };
+       char buf[1024];
+       int matchlen = match ? strlen(match) : 0;
+
+       for_each_ref(add_existing, &existing_refs);
+       while (fgets(buf, sizeof(buf), stdin)) {
+               int len = strlen(buf);
+               char *ref;
+               if (len > 0 && buf[len - 1] == '\n')
+                       buf[--len] = '\0';
+               if (!strcmp(buf + len - 3, "^{}")) {
+                       len -= 3;
+                       buf[len] = '\0';
+               }
+               for (ref = buf + len; buf < ref; ref--)
+                       if (isspace(ref[-1]))
+                               break;
+               if (match) {
+                       int reflen = buf + len - ref;
+                       if (reflen < matchlen)
+                               continue;
+                       if (strncmp(ref, match, matchlen))
+                               continue;
+               }
+               if (check_ref_format(ref)) {
+                       fprintf(stderr, "warning: ref '%s' ignored\n", ref);
+                       continue;
+               }
+               if (!path_list_has_path(&existing_refs, ref)) {
+                       printf("%s\n", buf);
+               }
+       }
+       return 0;
+}
+
 int cmd_show_ref(int argc, const char **argv, const char *prefix)
 {
        int i;
@@ -153,8 +207,29 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
                        heads_only = 1;
                        continue;
                }
+               if (!strcmp(arg, "--exclude-existing"))
+                       return exclude_existing(NULL);
+               if (!strncmp(arg, "--exclude-existing=", 19))
+                       return exclude_existing(arg + 19);
                usage(show_ref_usage);
        }
+
+       if (verify) {
+               unsigned char sha1[20];
+
+               while (*pattern) {
+                       if (resolve_ref(*pattern, sha1, 1, NULL))
+                               printf("%s %s\n", sha1_to_hex(sha1),
+                                      *pattern);
+                       else if (!quiet)
+                               die("'%s' - not a valid ref", *pattern);
+                       else
+                               return 1;
+                       pattern++;
+               }
+               return 0;
+       }
+
        if (show_head)
                head_ref(show_ref, NULL);
        for_each_ref(show_ref, NULL);
diff --git a/cache.h b/cache.h
index f2ec5c8c1416e3167d49813965212d414e6a5a7a..8ad5920d2be9593ad7810e3ccefa1c8b9f4e0750 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -123,6 +123,7 @@ extern int cache_errno;
 #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
 #define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
 
+extern int is_bare_git_dir(const char *dir);
 extern const char *get_git_dir(void);
 extern char *get_object_directory(void);
 extern char *get_refs_directory(void);
@@ -308,6 +309,7 @@ void datestamp(char *buf, int bufsize);
 unsigned long approxidate(const char *);
 
 extern int setup_ident(void);
+extern void ignore_missing_committer_name();
 extern const char *git_author_info(int);
 extern const char *git_committer_info(int);
 
@@ -403,6 +405,7 @@ extern int git_config_int(const char *, const char *);
 extern int git_config_bool(const char *, const char *);
 extern int git_config_set(const char *, const char *);
 extern int git_config_set_multivar(const char *, const char *, const char *, int);
+extern int git_config_rename_section(const char *, const char *);
 extern int check_repository_format_version(const char *var, const char *value);
 
 #define MAX_GITNAME (1000)
index 1bdef44a3ad315ecc4cdfa879e9c0881bd82722b..663993fefa9d8c347d2ff4c1b40edbf3b0d19c90 100644 (file)
--- a/config.c
+++ b/config.c
@@ -746,4 +746,68 @@ int git_config_set_multivar(const char* key, const char* value,
        return ret;
 }
 
+int git_config_rename_section(const char *old_name, const char *new_name)
+{
+       int ret = 0;
+       const char *config_filename;
+       struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1);
+       int out_fd;
+       char buf[1024];
+
+       config_filename = getenv("GIT_CONFIG");
+       if (!config_filename) {
+               config_filename = getenv("GIT_CONFIG_LOCAL");
+               if (!config_filename)
+                       config_filename  = git_path("config");
+       }
+       config_filename = xstrdup(config_filename);
+       out_fd = hold_lock_file_for_update(lock, config_filename, 0);
+       if (out_fd < 0)
+               return error("Could not lock config file!");
+
+       if (!(config_file = fopen(config_filename, "rb")))
+               return error("Could not open config file!");
+
+       while (fgets(buf, sizeof(buf), config_file)) {
+               int i;
+               for (i = 0; buf[i] && isspace(buf[i]); i++)
+                       ; /* do nothing */
+               if (buf[i] == '[') {
+                       /* it's a section */
+                       int j = 0, dot = 0;
+                       for (i++; buf[i] && buf[i] != ']'; i++) {
+                               if (!dot && isspace(buf[i])) {
+                                       dot = 1;
+                                       if (old_name[j++] != '.')
+                                               break;
+                                       for (i++; isspace(buf[i]); i++)
+                                               ; /* do nothing */
+                                       if (buf[i] != '"')
+                                               break;
+                                       continue;
+                               }
+                               if (buf[i] == '\\' && dot)
+                                       i++;
+                               else if (buf[i] == '"' && dot) {
+                                       for (i++; isspace(buf[i]); i++)
+                                               ; /* do_nothing */
+                                       break;
+                               }
+                               if (buf[i] != old_name[j++])
+                                       break;
+                       }
+                       if (buf[i] == ']') {
+                               /* old_name matches */
+                               ret++;
+                               store.baselen = strlen(new_name);
+                               store_write_section(out_fd, new_name);
+                               continue;
+                       }
+               }
+               write(out_fd, buf, strlen(buf));
+       }
+       if (close(out_fd) || commit_lock_file(lock) < 0)
+               return error("Cannot commit config file!");
+       return ret;
+}
 
index 9c4d23a23c899798613978c1766d08a80ced6f2e..234cd0954b888d814d8d4d86bb41983b80fddade 100755 (executable)
@@ -752,6 +752,24 @@ _git_reset ()
        COMPREPLY=($(compgen -W "$opt $(__git_refs)" -- "$cur"))
 }
 
+_git_show ()
+{
+       local cur="${COMP_WORDS[COMP_CWORD]}"
+       case "$cur" in
+       --pretty=*)
+               COMPREPLY=($(compgen -W "
+                       oneline short medium full fuller email raw
+                       " -- "${cur##--pretty=}"))
+               return
+               ;;
+       --*)
+               COMPREPLY=($(compgen -W "--pretty=" -- "$cur"))
+               return
+               ;;
+       esac
+       __git_complete_file
+}
+
 _git ()
 {
        local i c=1 command __git_dir
@@ -802,7 +820,7 @@ _git ()
        rebase)      _git_rebase ;;
        repo-config) _git_repo_config ;;
        reset)       _git_reset ;;
-       show)        _git_log ;;
+       show)        _git_show ;;
        show-branch) _git_log ;;
        whatchanged) _git_log ;;
        *)           COMPREPLY=() ;;
@@ -839,7 +857,7 @@ complete -o default -o nospace -F _git_push git-push
 complete -o default            -F _git_rebase git-rebase
 complete -o default            -F _git_repo_config git-repo-config
 complete -o default            -F _git_reset git-reset
-complete -o default            -F _git_log git-show
+complete -o default -o nospace -F _git_show git-show
 complete -o default -o nospace -F _git_log git-show-branch
 complete -o default -o nospace -F _git_log git-whatchanged
 
@@ -861,7 +879,7 @@ complete -o default            -F _git_merge_base git-merge-base.exe
 complete -o default            -F _git_name_rev git-name-rev.exe
 complete -o default -o nospace -F _git_push git-push.exe
 complete -o default            -F _git_repo_config git-repo-config
-complete -o default -o nospace -F _git_log git-show.exe
+complete -o default -o nospace -F _git_show git-show.exe
 complete -o default -o nospace -F _git_log git-show-branch.exe
 complete -o default -o nospace -F _git_log git-whatchanged.exe
 fi
diff --git a/diff.c b/diff.c
index 2df14b2469362bfd8a30a45457953d993baae66d..99744354a77418ab462b6b797c6ab334cc364960 100644 (file)
--- a/diff.c
+++ b/diff.c
 #include "xdiff-interface.h"
 #include "color.h"
 
+#ifdef NO_FAST_WORKING_DIRECTORY
+#define FAST_WORKING_DIRECTORY 0
+#else
+#define FAST_WORKING_DIRECTORY 1
+#endif
+
 static int use_size_cache;
 
 static int diff_detect_rename_default;
@@ -795,6 +801,35 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
               set, total_files, adds, dels, reset);
 }
 
+static void show_shortstats(struct diffstat_t* data)
+{
+       int i, adds = 0, dels = 0, total_files = data->nr;
+
+       if (data->nr == 0)
+               return;
+
+       for (i = 0; i < data->nr; i++) {
+               if (!data->files[i]->is_binary &&
+                   !data->files[i]->is_unmerged) {
+                       int added = data->files[i]->added;
+                       int deleted= data->files[i]->deleted;
+                       if (!data->files[i]->is_renamed &&
+                           (added + deleted == 0)) {
+                               total_files--;
+                       } else {
+                               adds += added;
+                               dels += deleted;
+                       }
+               }
+               free(data->files[i]->name);
+               free(data->files[i]);
+       }
+       free(data->files);
+
+       printf(" %d files changed, %d insertions(+), %d deletions(-)\n",
+              total_files, adds, dels);
+}
+
 static void show_numstat(struct diffstat_t* data, struct diff_options *options)
 {
        int i;
@@ -1158,7 +1193,7 @@ void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
  * the work tree has that object contents, return true, so that
  * prepare_temp_file() does not have to inflate and extract.
  */
-static int work_tree_matches(const char *name, const unsigned char *sha1)
+static int reuse_worktree_file(const char *name, const unsigned char *sha1, int want_file)
 {
        struct cache_entry *ce;
        struct stat st;
@@ -1179,6 +1214,18 @@ static int work_tree_matches(const char *name, const unsigned char *sha1)
        if (!active_cache)
                return 0;
 
+       /* We want to avoid the working directory if our caller
+        * doesn't need the data in a normal file, this system
+        * is rather slow with its stat/open/mmap/close syscalls,
+        * and the object is contained in a pack file.  The pack
+        * is probably already open and will be faster to obtain
+        * the data through than the working directory.  Loose
+        * objects however would tend to be slower as they need
+        * to be individually opened and inflated.
+        */
+       if (FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1, NULL))
+               return 0;
+
        len = strlen(name);
        pos = cache_name_pos(name, len);
        if (pos < 0)
@@ -1265,7 +1312,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
        if (s->data)
                return err;
        if (!s->sha1_valid ||
-           work_tree_matches(s->path, s->sha1)) {
+           reuse_worktree_file(s->path, s->sha1, 0)) {
                struct stat st;
                int fd;
                if (lstat(s->path, &st) < 0) {
@@ -1372,7 +1419,7 @@ static void prepare_temp_file(const char *name,
        }
 
        if (!one->sha1_valid ||
-           work_tree_matches(name, one->sha1)) {
+           reuse_worktree_file(name, one->sha1, 1)) {
                struct stat st;
                if (lstat(name, &st) < 0) {
                        if (errno == ENOENT)
@@ -1753,6 +1800,7 @@ int diff_setup_done(struct diff_options *options)
                options->output_format &= ~(DIFF_FORMAT_RAW |
                                            DIFF_FORMAT_NUMSTAT |
                                            DIFF_FORMAT_DIFFSTAT |
+                                           DIFF_FORMAT_SHORTSTAT |
                                            DIFF_FORMAT_SUMMARY |
                                            DIFF_FORMAT_PATCH);
 
@@ -1763,6 +1811,7 @@ int diff_setup_done(struct diff_options *options)
        if (options->output_format & (DIFF_FORMAT_PATCH |
                                      DIFF_FORMAT_NUMSTAT |
                                      DIFF_FORMAT_DIFFSTAT |
+                                     DIFF_FORMAT_SHORTSTAT |
                                      DIFF_FORMAT_SUMMARY |
                                      DIFF_FORMAT_CHECKDIFF))
                options->recursive = 1;
@@ -1854,6 +1903,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        else if (!strcmp(arg, "--numstat")) {
                options->output_format |= DIFF_FORMAT_NUMSTAT;
        }
+       else if (!strcmp(arg, "--shortstat")) {
+               options->output_format |= DIFF_FORMAT_SHORTSTAT;
+       }
        else if (!strncmp(arg, "--stat", 6)) {
                char *end;
                int width = options->stat_width;
@@ -2628,7 +2680,7 @@ void diff_flush(struct diff_options *options)
                separator++;
        }
 
-       if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_NUMSTAT)) {
+       if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {
                struct diffstat_t diffstat;
 
                memset(&diffstat, 0, sizeof(struct diffstat_t));
@@ -2642,6 +2694,8 @@ void diff_flush(struct diff_options *options)
                        show_numstat(&diffstat, options);
                if (output_format & DIFF_FORMAT_DIFFSTAT)
                        show_stats(&diffstat, options);
+               else if (output_format & DIFF_FORMAT_SHORTSTAT)
+                       show_shortstats(&diffstat);
                separator++;
        }
 
diff --git a/diff.h b/diff.h
index 101b2b505dcace41754687a427ad0523daaa50a0..eff445596d98e46d40dd37843e690de27c5fabf1 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -29,6 +29,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
 #define DIFF_FORMAT_NUMSTAT    0x0004
 #define DIFF_FORMAT_SUMMARY    0x0008
 #define DIFF_FORMAT_PATCH      0x0010
+#define DIFF_FORMAT_SHORTSTAT  0x0020
 
 /* These override all above */
 #define DIFF_FORMAT_NAME       0x0100
index 57a74b6bb83e14af5f7336f397f79fbfcdc8b5f7..91fa2bea519a39ee65dcf018b7a72885b9eeec70 100644 (file)
@@ -109,6 +109,8 @@ static int is_exact_match(struct diff_filespec *src,
                return 0;
        if (src->size != dst->size)
                return 0;
+       if (src->sha1_valid && dst->sha1_valid)
+           return !hashcmp(src->sha1, dst->sha1);
        if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
                return 0;
        if (src->size == dst->size &&
index 84d870ca4eca6e202bcbec388e9a299b887f9a12..f8c7dbceadf2190997816f9a21b211a6a97efe48 100644 (file)
@@ -48,6 +48,16 @@ static void setup_git_env(void)
        git_graft_file = getenv(GRAFT_ENVIRONMENT);
        if (!git_graft_file)
                git_graft_file = xstrdup(git_path("info/grafts"));
+       log_all_ref_updates = !is_bare_git_dir(git_dir);
+}
+
+int is_bare_git_dir (const char *dir)
+{
+       const char *s;
+       if (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT))
+               return 0;
+       s = strrchr(dir, '/');
+       return !s || strcmp(s + 1, DEFAULT_GIT_DIR_ENVIRONMENT);
 }
 
 const char *get_git_dir(void)
index 5450918be339bf101a6562d518c0f4cc7b37c0f2..06c42b042d999856aacb9e156cffc39eb8dbce3f 100755 (executable)
@@ -4,7 +4,7 @@ echo "/* Automatically generated by $0 */
 struct cmdname_help
 {
     char name[16];
-    char help[64];
+    char help[80];
 };
 
 struct cmdname_help common_cmds[] = {"
index 05828bb113d715a1067838ed2ac58ad8db06bf34..6bce41af4dc73d90028e76ba174e4e7795103d6c 100755 (executable)
@@ -80,6 +80,7 @@ no_edit=
 log_given=
 log_message=
 verify=t
+quiet=
 verbose=
 signoff=
 force_author=
@@ -241,6 +242,10 @@ $1"
                signoff=t
                shift
                ;;
+       -q|--q|--qu|--qui|--quie|--quiet)
+               quiet=t
+               shift
+               ;;
        -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
                verbose=t
                shift
@@ -515,7 +520,7 @@ then
        current="$(git-rev-parse --verify HEAD)"
 else
        if [ -z "$(git-ls-files)" ]; then
-               echo >&2 Nothing to commit
+               echo >&2 'nothing to commit (use "git add file1 file2" to include for commit)'
                exit 1
        fi
        PARENTS=""
@@ -615,11 +620,17 @@ then
        git-rerere
 fi
 
-if test -x "$GIT_DIR"/hooks/post-commit && test "$ret" = 0
+if test "$ret" = 0
 then
-       "$GIT_DIR"/hooks/post-commit
+       if test -x "$GIT_DIR"/hooks/post-commit
+       then
+               "$GIT_DIR"/hooks/post-commit
+       fi
+       if test -z "$quiet"
+       then
+               echo "Created${initial_commit:+ initial} commit $commit"
+               git-diff-tree --shortstat --summary --root --no-commit-id HEAD
+       fi
 fi
 
-test "$ret" = 0 && git-diff-tree --summary --root --no-commit-id HEAD
-
 exit "$ret"
index 4eecf148ea90c02bfe9f2aa018118ccbd26f8e97..38101a6ace2331707bb7a424d32b32f3699d3bab 100755 (executable)
@@ -2,7 +2,13 @@
 #
 
 USAGE='<fetch-options> <repository> <refspec>...'
+SUBDIRECTORY_OK=Yes
 . git-sh-setup
+TOP=$(git-rev-parse --show-cdup)
+if test ! -z "$TOP"
+then
+       cd "$TOP"
+fi
 . git-parse-remote
 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
@@ -236,7 +242,7 @@ esac
 reflist=$(get_remote_refs_for_fetch "$@")
 if test "$tags"
 then
-       taglist=`IFS="  " &&
+       taglist=`IFS='  ' &&
                  echo "$ls_remote_result" |
                  while read sha1 name
                  do
@@ -432,17 +438,11 @@ case "$no_tags$tags" in
        *:refs/*)
                # effective only when we are following remote branch
                # using local tracking branch.
-               taglist=$(IFS=" " &&
+               taglist=$(IFS=' ' &&
                echo "$ls_remote_result" |
-               sed -n  -e 's|^\('"$_x40"'\)    \(refs/tags/.*\)^{}$|\1 \2|p' \
-                       -e 's|^\('"$_x40"'\)    \(refs/tags/.*\)$|\1 \2|p' |
+               git-show-ref --exclude-existing=refs/tags/ |
                while read sha1 name
                do
-                       git-show-ref --verify --quiet -- "$name" && continue
-                       git-check-ref-format "$name" || {
-                               echo >&2 "warning: tag ${name} ignored"
-                               continue
-                       }
                        git-cat-file -t "$sha1" >/dev/null 2>&1 || continue
                        echo >&2 "Auto-following $name"
                        echo ".${name}:${name}"
index c895a04f6be0e09670af71532558abe994369a64..4ebfcf65d99f7743048df901b38969c28cc11edb 100755 (executable)
@@ -91,6 +91,22 @@ finish () {
        esac
 }
 
+merge_name () {
+       remote="$1"
+       rh=$(git-rev-parse --verify "$remote^0" 2>/dev/null) || return
+       bh=$(git-show-ref -s --verify "refs/heads/$remote" 2>/dev/null)
+       if test "$rh" = "$bh"
+       then
+               echo "$rh               branch '$remote' of ."
+       elif truname=$(expr "$remote" : '\(.*\)~[1-9][0-9]*$') &&
+               git-show-ref -q --verify "refs/heads/$truname" 2>/dev/null
+       then
+               echo "$rh               branch '$truname' (early part) of ."
+       else
+               echo "$rh               commit '$remote'"
+       fi
+}
+
 case "$#" in 0) usage ;; esac
 
 rloga= have_message=
@@ -188,15 +204,7 @@ else
        # in this loop.
        merge_name=$(for remote
                do
-                       rh=$(git-rev-parse --verify "$remote"^0 2>/dev/null) ||
-                       continue ;# not something we can merge
-                       bh=$(git show-ref -s --verify "refs/heads/$remote" 2>/dev/null)
-                       if test "$rh" = "$bh"
-                       then
-                               echo "$rh               branch '$remote' of ."
-                       else
-                               echo "$rh               commit '$remote'"
-                       fi
+                       merge_name "$remote"
                done | git-fmt-merge-msg
        )
        merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name"
index f150a558ca7a7a659f4eb88be3a275e1a4e87337..067898f1207736732c2c80eabbef5537f8dfef69 100755 (executable)
@@ -67,6 +67,8 @@ name=$(git-pack-objects --non-empty --all $args </dev/null "$PACKTMP") ||
 if [ -z "$name" ]; then
        echo Nothing new to pack.
 else
+       chmod a-w "$PACKTMP-$name.pack"
+       chmod a-w "$PACKTMP-$name.idx"
        if test "$quiet" != '-q'; then
            echo "Pack pack-$name created."
        fi
index c0feb4435d15da9cc9928800cbe270018f044152..8d95e3748d2cfdaa248dee385766dedde5ff4075 100755 (executable)
@@ -1,28 +1,60 @@
 #!/bin/sh
-
-USAGE='[--mixed | --soft | --hard]  [<commit-ish>]'
+#
+# Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
+#
+USAGE='[--mixed | --soft | --hard]  [<commit-ish>] [ [--] <paths>...]'
+SUBDIRECTORY_OK=Yes
 . git-sh-setup
 
-update=
-reset_type=--mixed
-case "$1" in
---mixed | --soft | --hard)
-       reset_type="$1"
+update= reset_type=--mixed
+unset rev
+
+while case $# in 0) break ;; esac
+do
+       case "$1" in
+       --mixed | --soft | --hard)
+               reset_type="$1"
+               ;;
+       --)
+               break
+               ;;
+       -*)
+               usage
+               ;;
+       *)
+               rev=$(git-rev-parse --verify "$1") || exit
+               shift
+               break
+               ;;
+       esac
        shift
-       ;;
--*)
-        usage ;;
-esac
+done
 
-case $# in
-0) rev=HEAD ;;
-1) rev=$(git-rev-parse --verify "$1") || exit ;;
-*) usage ;;
-esac
+: ${rev=HEAD}
 rev=$(git-rev-parse --verify $rev^0) || exit
 
-# We need to remember the set of paths that _could_ be left
-# behind before a hard reset, so that we can remove them.
+# Skip -- in "git reset HEAD -- foo" and "git reset -- foo".
+case "$1" in --) shift ;; esac
+
+# git reset --mixed tree [--] paths... can be used to
+# load chosen paths from the tree into the index without
+# affecting the working tree nor HEAD.
+if test $# != 0
+then
+       test "$reset_type" == "--mixed" ||
+               die "Cannot do partial $reset_type reset."
+       git ls-tree -r --full-name $rev -- "$@" |
+       git update-index --add --index-info || exit
+       git update-index --refresh
+       exit
+fi
+
+TOP=$(git-rev-parse --show-cdup)
+if test ! -z "$TOP"
+then
+       cd "$TOP"
+fi
+
 if test "$reset_type" = "--hard"
 then
        update=-u
index 15254e479595de047258188e0979bea322869696..73ab8d873fb33f4447c78b5eab4781e245697c10 100755 (executable)
@@ -925,19 +925,38 @@ sub cmt_showable {
 
 sub log_use_color {
        return 1 if $_color;
-       my $dc;
-       chomp($dc = `git-repo-config --get diff.color`);
+       my ($dc, $dcvar);
+       $dcvar = 'color.diff';
+       $dc = `git-repo-config --get $dcvar`;
+       if ($dc eq '') {
+               # nothing at all; fallback to "diff.color"
+               $dcvar = 'diff.color';
+               $dc = `git-repo-config --get $dcvar`;
+       }
+       chomp($dc);
        if ($dc eq 'auto') {
-               if (-t *STDOUT || (defined $_pager &&
-                   `git-repo-config --bool --get pager.color` !~ /^false/)) {
+               my $pc;
+               $pc = `git-repo-config --get color.pager`;
+               if ($pc eq '') {
+                       # does not have it -- fallback to pager.color
+                       $pc = `git-repo-config --bool --get pager.color`;
+               }
+               else {
+                       $pc = `git-repo-config --bool --get color.pager`;
+                       if ($?) {
+                               $pc = 'false';
+                       }
+               }
+               chomp($pc);
+               if (-t *STDOUT || (defined $_pager && $pc eq 'true')) {
                        return ($ENV{TERM} && $ENV{TERM} ne 'dumb');
                }
                return 0;
        }
        return 0 if $dc eq 'never';
        return 1 if $dc eq 'always';
-       chomp($dc = `git-repo-config --bool --get diff.color`);
-       $dc eq 'true';
+       chomp($dc = `git-repo-config --bool --get $dcvar`);
+       return ($dc eq 'true');
 }
 
 sub git_svn_log_cmd {
@@ -2981,7 +3000,8 @@ sub libsvn_log_entry {
        my ($Y,$m,$d,$H,$M,$S) = ($date =~ /^(\d{4})\-(\d\d)\-(\d\d)T
                                         (\d\d)\:(\d\d)\:(\d\d).\d+Z$/x)
                                or die "Unable to parse date: $date\n";
-       if (defined $_authors && ! defined $users{$author}) {
+       if (defined $author && length $author > 0 &&
+           defined $_authors && ! defined $users{$author}) {
                die "Author: $author not defined in $_authors file\n";
        }
        $msg = '' if ($rev == 0 && !defined $msg);
index 5ea3fda540f00e4b7ee2c11c380eac86168eca17..4059894e0b51d2c355193064f0cca8fb882a3055 100755 (executable)
@@ -434,6 +434,7 @@ sub evaluate_path_info {
        "tags" => \&git_tags,
        "tree" => \&git_tree,
        "snapshot" => \&git_snapshot,
+       "object" => \&git_object,
        # those below don't need $project
        "opml" => \&git_opml,
        "project_list" => \&git_project_list,
@@ -827,14 +828,12 @@ sub format_log_line_html {
        my $line = shift;
 
        $line = esc_html($line, -nbsp=>1);
-       if ($line =~ m/([0-9a-fA-F]{40})/) {
+       if ($line =~ m/([0-9a-fA-F]{8,40})/) {
                my $hash_text = $1;
-               if (git_get_type($hash_text) eq "commit") {
-                       my $link =
-                               $cgi->a({-href => href(action=>"commit", hash=>$hash_text),
-                                       -class => "text"}, $hash_text);
-                       $line =~ s/$hash_text/$link/;
-               }
+               my $link =
+                       $cgi->a({-href => href(action=>"object", hash=>$hash_text),
+                               -class => "text"}, $hash_text);
+               $line =~ s/$hash_text/$link/;
        }
        return $line;
 }
@@ -856,7 +855,8 @@ sub format_ref_marker {
                                $name = $ref;
                        }
 
-                       $markers .= " <span class=\"$type\">" . esc_html($name) . "</span>";
+                       $markers .= " <span class=\"$type\" title=\"$ref\">" .
+                                   esc_html($name) . "</span>";
                }
        }
 
@@ -1989,12 +1989,73 @@ ($;%)
        }
 }
 
+# return link target (what link points to)
+sub git_get_link_target {
+       my $hash = shift;
+       my $link_target;
+
+       # read link
+       open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
+               or return;
+       {
+               local $/;
+               $link_target = <$fd>;
+       }
+       close $fd
+               or return;
+
+       return $link_target;
+}
+
+# given link target, and the directory (basedir) the link is in,
+# return target of link relative to top directory (top tree);
+# return undef if it is not possible (including absolute links).
+sub normalize_link_target {
+       my ($link_target, $basedir, $hash_base) = @_;
+
+       # we can normalize symlink target only if $hash_base is provided
+       return unless $hash_base;
+
+       # absolute symlinks (beginning with '/') cannot be normalized
+       return if (substr($link_target, 0, 1) eq '/');
+
+       # normalize link target to path from top (root) tree (dir)
+       my $path;
+       if ($basedir) {
+               $path = $basedir . '/' . $link_target;
+       } else {
+               # we are in top (root) tree (dir)
+               $path = $link_target;
+       }
+
+       # remove //, /./, and /../
+       my @path_parts;
+       foreach my $part (split('/', $path)) {
+               # discard '.' and ''
+               next if (!$part || $part eq '.');
+               # handle '..'
+               if ($part eq '..') {
+                       if (@path_parts) {
+                               pop @path_parts;
+                       } else {
+                               # link leads outside repository (outside top dir)
+                               return;
+                       }
+               } else {
+                       push @path_parts, $part;
+               }
+       }
+       $path = join('/', @path_parts);
+
+       return $path;
+}
+
 # print tree entry (row of git_tree), but without encompassing <tr> element
 sub git_print_tree_entry {
        my ($t, $basedir, $hash_base, $have_blame) = @_;
 
        my %base_key = ();
-       $base_key{hash_base} = $hash_base if defined $hash_base;
+       $base_key{'hash_base'} = $hash_base if defined $hash_base;
 
        # The format of a table row is: mode list link.  Where mode is
        # the mode of the entry, list is the name of the entry, an href,
@@ -2005,16 +2066,31 @@ sub git_print_tree_entry {
                print "<td class=\"list\">" .
                        $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
                                               file_name=>"$basedir$t->{'name'}", %base_key),
-                               -class => "list"}, esc_path($t->{'name'})) . "</td>\n";
+                               -class => "list"}, esc_path($t->{'name'}));
+               if (S_ISLNK(oct $t->{'mode'})) {
+                       my $link_target = git_get_link_target($t->{'hash'});
+                       if ($link_target) {
+                               my $norm_target = normalize_link_target($link_target, $basedir, $hash_base);
+                               if (defined $norm_target) {
+                                       print " -> " .
+                                             $cgi->a({-href => href(action=>"object", hash_base=>$hash_base,
+                                                                    file_name=>$norm_target),
+                                                      -title => $norm_target}, esc_path($link_target));
+                               } else {
+                                       print " -> " . esc_path($link_target);
+                               }
+                       }
+               }
+               print "</td>\n";
                print "<td class=\"link\">";
                print $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
-                                            file_name=>"$basedir$t->{'name'}", %base_key)},
-                             "blob");
+                                            file_name=>"$basedir$t->{'name'}", %base_key)},
+                             "blob");
                if ($have_blame) {
                        print " | " .
                              $cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'},
-                                                          file_name=>"$basedir$t->{'name'}", %base_key)},
-                                           "blame");
+                                                    file_name=>"$basedir$t->{'name'}", %base_key)},
+                                     "blame");
                }
                if (defined $hash_base) {
                        print " | " .
@@ -2036,8 +2112,8 @@ sub git_print_tree_entry {
                print "</td>\n";
                print "<td class=\"link\">";
                print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
-                                            file_name=>"$basedir$t->{'name'}", %base_key)},
-                             "tree");
+                                            file_name=>"$basedir$t->{'name'}", %base_key)},
+                             "tree");
                if (defined $hash_base) {
                        print " | " .
                              $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
@@ -3414,8 +3490,7 @@ sub git_snapshot {
        my $filename = basename($project) . "-$hash.tar.$suffix";
 
        print $cgi->header(
-               -type => 'application/x-tar',
-               -content_encoding => $ctype,
+               -type => "application/$ctype",
                -content_disposition => 'inline; filename="' . "$filename" . '"',
                -status => '200 OK');
 
@@ -3497,15 +3572,46 @@ sub git_commit {
        my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'});
        my %cd = parse_date($co{'committer_epoch'}, $co{'committer_tz'});
 
-       my $parent = $co{'parent'};
+       my $parent  = $co{'parent'};
+       my $parents = $co{'parents'}; # listref
+
+       # we need to prepare $formats_nav before any parameter munging
+       my $formats_nav;
+       if (!defined $parent) {
+               # --root commitdiff
+               $formats_nav .= '(initial)';
+       } elsif (@$parents == 1) {
+               # single parent commit
+               $formats_nav .=
+                       '(parent: ' .
+                       $cgi->a({-href => href(action=>"commit",
+                                              hash=>$parent)},
+                               esc_html(substr($parent, 0, 7))) .
+                       ')';
+       } else {
+               # merge commit
+               $formats_nav .=
+                       '(merge: ' .
+                       join(' ', map {
+                               $cgi->a({-href => href(action=>"commitdiff",
+                                                      hash=>$_)},
+                                       esc_html(substr($_, 0, 7)));
+                       } @$parents ) .
+                       ')';
+       }
+
        if (!defined $parent) {
                $parent = "--root";
        }
-       open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id",
-               @diff_opts, $parent, $hash, "--"
-               or die_error(undef, "Open git-diff-tree failed");
-       my @difftree = map { chomp; $_ } <$fd>;
-       close $fd or die_error(undef, "Reading git-diff-tree failed");
+       my @difftree;
+       if (@$parents <= 1) {
+               # difftree output is not printed for merges
+               open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id",
+                       @diff_opts, $parent, $hash, "--"
+                               or die_error(undef, "Open git-diff-tree failed");
+               @difftree = map { chomp; $_ } <$fd>;
+               close $fd or die_error(undef, "Reading git-diff-tree failed");
+       }
 
        # non-textual hash id's can be cached
        my $expires;
@@ -3517,16 +3623,10 @@ sub git_commit {
 
        my $have_snapshot = gitweb_have_snapshot();
 
-       my @views_nav = ();
-       if (defined $file_name && defined $co{'parent'}) {
-               push @views_nav,
-                       $cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)},
-                               "blame");
-       }
        git_header_html(undef, $expires);
        git_print_page_nav('commit', '',
                           $hash, $co{'tree'}, $hash,
-                          join (' | ', @views_nav));
+                          $formats_nav);
 
        if (defined $co{'parent'}) {
                git_print_header_div('commitdiff', esc_html($co{'title'}) . $ref, $hash);
@@ -3567,7 +3667,7 @@ sub git_commit {
        }
        print "</td>" .
              "</tr>\n";
-       my $parents = $co{'parents'};
+
        foreach my $par (@$parents) {
                print "<tr>" .
                      "<td>parent</td>" .
@@ -3589,11 +3689,61 @@ sub git_commit {
        git_print_log($co{'comment'});
        print "</div>\n";
 
-       git_difftree_body(\@difftree, $hash, $parent);
+       if (@$parents <= 1) {
+               # do not output difftree/whatchanged for merges
+               git_difftree_body(\@difftree, $hash, $parent);
+       }
 
        git_footer_html();
 }
 
+sub git_object {
+       # object is defined by:
+       # - hash or hash_base alone
+       # - hash_base and file_name
+       my $type;
+
+       # - hash or hash_base alone
+       if ($hash || ($hash_base && !defined $file_name)) {
+               my $object_id = $hash || $hash_base;
+
+               my $git_command = git_cmd_str();
+               open my $fd, "-|", "$git_command cat-file -t $object_id 2>/dev/null"
+                       or die_error('404 Not Found', "Object does not exist");
+               $type = <$fd>;
+               chomp $type;
+               close $fd
+                       or die_error('404 Not Found', "Object does not exist");
+
+       # - hash_base and file_name
+       } elsif ($hash_base && defined $file_name) {
+               $file_name =~ s,/+$,,;
+
+               system(git_cmd(), "cat-file", '-e', $hash_base) == 0
+                       or die_error('404 Not Found', "Base object does not exist");
+
+               # here errors should not hapen
+               open my $fd, "-|", git_cmd(), "ls-tree", $hash_base, "--", $file_name
+                       or die_error(undef, "Open git-ls-tree failed");
+               my $line = <$fd>;
+               close $fd;
+
+               #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa  panic.c'
+               unless ($line && $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/) {
+                       die_error('404 Not Found', "File or directory for given base does not exist");
+               }
+               $type = $2;
+               $hash = $3;
+       } else {
+               die_error('404 Not Found', "Not enough information to find object");
+       }
+
+       print $cgi->redirect(-uri => href(action=>$type, -full=>1,
+                                         hash=>$hash, hash_base=>$hash_base,
+                                         file_name=>$file_name),
+                            -status => '302 Found');
+}
+
 sub git_blobdiff {
        my $format = shift || 'html';
 
diff --git a/ident.c b/ident.c
index e415fd35889b3c5547878221c4a113351715d8bc..d7faba6a70139020d9ee0a2f801ae2e6b567d4f4 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -221,3 +221,18 @@ const char *git_committer_info(int error_on_no_name)
                         getenv("GIT_COMMITTER_DATE"),
                         error_on_no_name);
 }
+
+void ignore_missing_committer_name()
+{
+       /* If we did not get a name from the user's gecos entry then
+        * git_default_name is empty; so instead load the username
+        * into it as a 'good enough for now' approximation of who
+        * this user is.
+        */
+       if (!*git_default_name) {
+               struct passwd *pw = getpwuid(getuid());
+               if (!pw)
+                       die("You don't exist. Go away!");
+               strlcpy(git_default_name, pw->pw_name, sizeof(git_default_name));
+       }
+}
index eae4745d284e00e279b5b8f4b032bdb9ca433984..b8d83ccd9f7985d60f69b7cd44db698d4e932612 100644 (file)
@@ -358,7 +358,7 @@ int add_file_to_index(const char *path, int verbose)
 
        if (index_path(ce->sha1, path, &st, 1))
                die("unable to index file %s", path);
-       if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD))
+       if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
                die("unable to add %s to index",path);
        if (verbose)
                printf("add '%s'\n", path);
@@ -517,7 +517,7 @@ static int has_dir_name(const struct cache_entry *ce, int pos, int ok_to_replace
                pos = cache_name_pos(name, ntohs(create_ce_flags(len, stage)));
                if (pos >= 0) {
                        retval = -1;
-                       if (ok_to_replace)
+                       if (!ok_to_replace)
                                break;
                        remove_cache_entry_at(pos);
                        continue;
@@ -609,7 +609,7 @@ int add_cache_entry(struct cache_entry *ce, int option)
        if (!skip_df_check &&
            check_file_directory_conflict(ce, pos, ok_to_replace)) {
                if (!ok_to_replace)
-                       return -1;
+                       return error("'%s' appears as both a file and as a directory", ce->name);
                pos = cache_name_pos(ce->name, ntohs(ce->ce_flags));
                pos = -pos-1;
        }
index e76d9aea31886ec9b0d287f02a12af9793fb44db..5e5510bc3dadff835324f46b0cd936ceba8f1c6e 100644 (file)
@@ -420,6 +420,8 @@ int main(int argc, char **argv)
                die("'%s': unable to chdir or not a git archive", dir);
 
        setup_ident();
+       /* don't die if gecos is empty */
+       ignore_missing_committer_name();
        git_config(receive_pack_config);
 
        write_head_info();
diff --git a/refs.c b/refs.c
index a02957c399ded94bb9a49c9dc3d8ab5d9411bbec..d911b9e86009aa3d81c6c4d310a26bf057920e8a 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -867,6 +867,16 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
                goto rollback;
        }
 
+       if (!strncmp(oldref, "refs/heads/", 11) &&
+                       !strncmp(newref, "refs/heads/", 11)) {
+               char oldsection[1024], newsection[1024];
+
+               snprintf(oldsection, 1024, "branch.%s", oldref + 11);
+               snprintf(newsection, 1024, "branch.%s", newref + 11);
+               if (git_config_rename_section(oldsection, newsection) < 0)
+                       return 1;
+       }
+
        return 0;
 
  rollback:
index 3260d1d7a746f6968aab0d3f374bd7e76bf33bd0..0cd1c41866c6ca344ed99fae3d71014dd9321e2d 100755 (executable)
@@ -272,4 +272,13 @@ test_expect_success \
         wc -l) &&
      test $numparent = 1'
 
+test_expect_success 'update-index D/F conflict' '
+       mv path0 tmp &&
+       mv path2 path0 &&
+       mv tmp path2 &&
+       git update-index --add --replace path2 path0/file2 &&
+       numpath0=$(git ls-files path0 | wc -l) &&
+       test $numpath0 = 1
+'
+
 test_done
index 0272dd429302534846e3a7f221a520bac2907ca8..eebe643bda9dd5180e8435a59c7510ba97e59144 100755 (executable)
@@ -37,8 +37,6 @@ test_expect_success 'tree' "test 8988da15d077d4829fc51d8544c097def6644dbb = $tre
 
 output="$(echo "Initial commit" | git-commit-tree $(git-write-tree) 2>&1 > .git/refs/heads/master)"
 
-test_expect_success 'commit' "test 'Committing initial tree 8988da15d077d4829fc51d8544c097def6644dbb' = \"$output\""
-
 git-diff-index -p HEAD > diff.output
 test_expect_success 'git-diff-index -p HEAD' 'cmp diff.expect diff.output'
 
index 0de2497746e31f3dce418dbd248ff5d3e7cb939a..e48a4ecdcf7129da1431928bdb942eae8c3e6515 100755 (executable)
@@ -265,6 +265,16 @@ EOF
 test_expect_success '--get-regexp' \
        'git-repo-config --get-regexp in > output && cmp output expect'
 
+git-repo-config --add nextsection.nonewline "wow4 for you"
+
+cat > expect << EOF
+wow2 for me
+wow4 for you
+EOF
+
+test_expect_success '--add' \
+       'git-repo-config --get-all nextsection.nonewline > output && cmp output expect'
+
 cat > .git/config << EOF
 [novalue]
        variable
@@ -333,5 +343,53 @@ EOF
 
 test_expect_success '--set in alternative GIT_CONFIG' 'cmp other-config expect'
 
+cat > .git/config << EOF
+# Hallo
+       #Bello
+[branch "eins"]
+       x = 1
+[branch.eins]
+       y = 1
+       [branch "1 234 blabl/a"]
+weird
+EOF
+
+test_expect_success "rename section" \
+       "git-repo-config --rename-section branch.eins branch.zwei"
+
+cat > expect << EOF
+# Hallo
+       #Bello
+[branch "zwei"]
+       x = 1
+[branch "zwei"]
+       y = 1
+       [branch "1 234 blabl/a"]
+weird
+EOF
+
+test_expect_success "rename succeeded" "diff -u expect .git/config"
+
+test_expect_failure "rename non-existing section" \
+       'git-repo-config --rename-section branch."world domination" branch.drei'
+
+test_expect_success "rename succeeded" "diff -u expect .git/config"
+
+test_expect_success "rename another section" \
+       'git-repo-config --rename-section branch."1 234 blabl/a" branch.drei'
+
+cat > expect << EOF
+# Hallo
+       #Bello
+[branch "zwei"]
+       x = 1
+[branch "zwei"]
+       y = 1
+[branch "drei"]
+weird
+EOF
+
+test_expect_success "rename succeeded" "diff -u expect .git/config"
+
 test_done
 
index 6a917f2ff48661a5d70fdcbcc700cbf659004818..5637cb5eac2fa77ec3882263418c363a4da8a69a 100755 (executable)
@@ -63,8 +63,8 @@ test_expect_failure \
        "test $B"' = $(cat .git/'"$m"')'
 rm -f .git/$m
 
-mkdir -p .git/logs/refs/heads
-touch .git/logs/refs/heads/master
+: a repository with working tree always has reflog these days...
+: >.git/logs/refs/heads/master
 test_expect_success \
        "create $m (logged by touch)" \
        'GIT_COMMITTER_DATE="2005-05-26 23:30" \
index 5782c30b03a7e7bb7d244d305e13e856eed40a89..a6ea0f6a196c5285c229021c96bb83f33a3aa748 100755 (executable)
@@ -94,6 +94,8 @@ test_expect_failure \
          git-branch r &&
          git-branch -m q r/q'
 
+git-repo-config branch.s/s.dummy Hello
+
 test_expect_success \
     'git branch -m s/s s should work when s/t is deleted' \
        'git-branch -l s/s &&
@@ -104,6 +106,10 @@ test_expect_success \
         git-branch -m s/s s &&
         test -f .git/logs/refs/heads/s'
 
+test_expect_success 'config information was renamed, too' \
+       "test $(git-repo-config branch.s.dummy) = Hello &&
+        ! git-repo-config branch.s/s/dummy"
+
 test_expect_failure \
     'git-branch -m u v should fail when the reflog for u is a symlink' \
     'git-branch -l u &&
index 6e9414dbb07f1457eb436907a77d14476a6e6b1f..cface6ca88666d5e1de116110cb52eea4d91a5f1 100644 (file)
@@ -15,12 +15,14 @@ static char wt_status_colors[][COLOR_MAXLEN] = {
        "\033[31m", /* WT_STATUS_CHANGED: red */
        "\033[31m", /* WT_STATUS_UNTRACKED: red */
 };
+static const char* use_add_msg = "use \"git add file1 file2\" to include for commit";
 
 static int parse_status_slot(const char *var, int offset)
 {
        if (!strcasecmp(var+offset, "header"))
                return WT_STATUS_HEADER;
-       if (!strcasecmp(var+offset, "updated"))
+       if (!strcasecmp(var+offset, "updated")
+               || !strcasecmp(var+offset, "added"))
                return WT_STATUS_UPDATED;
        if (!strcasecmp(var+offset, "changed"))
                return WT_STATUS_CHANGED;
@@ -145,7 +147,7 @@ static void wt_status_print_updated_cb(struct diff_queue_struct *q,
                if (q->queue[i]->status == 'U')
                        continue;
                if (!shown_header) {
-                       wt_status_print_header("Updated but not checked in",
+                       wt_status_print_header("Added but not yet committed",
                                        "will commit");
                        s->commitable = 1;
                        shown_header = 1;
@@ -162,8 +164,7 @@ static void wt_status_print_changed_cb(struct diff_queue_struct *q,
 {
        int i;
        if (q->nr)
-               wt_status_print_header("Changed but not updated",
-                               "use git-add on files to include for commit");
+               wt_status_print_header("Changed but not added", use_add_msg);
        for (i = 0; i < q->nr; i++)
                wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]);
        if (q->nr)
@@ -178,7 +179,7 @@ void wt_status_print_initial(struct wt_status *s)
        read_cache();
        if (active_nr) {
                s->commitable = 1;
-               wt_status_print_header("Updated but not checked in",
+               wt_status_print_header("Added but not yet committed",
                                "will commit");
        }
        for (i = 0; i < active_nr; i++) {
@@ -249,8 +250,7 @@ static void wt_status_print_untracked(const struct wt_status *s)
                                continue;
                }
                if (!shown_header) {
-                       wt_status_print_header("Untracked files",
-                               "use \"git add\" to add to commit");
+                       wt_status_print_header("Untracked files", use_add_msg);
                        shown_header = 1;
                }
                color_printf(color(WT_STATUS_HEADER), "#\t");
@@ -271,7 +271,7 @@ static void wt_status_print_verbose(struct wt_status *s)
 
 void wt_status_print(struct wt_status *s)
 {
-       if (s->branch && strcmp(s->branch, "refs/heads/master"))
+       if (s->branch)
                color_printf_ln(color(WT_STATUS_HEADER),
                        "# On branch %s", s->branch);
 
@@ -292,7 +292,9 @@ void wt_status_print(struct wt_status *s)
        if (s->verbose && !s->is_initial)
                wt_status_print_verbose(s);
        if (!s->commitable)
-               printf("%s\n", s->amend ? "# No changes" : "nothing to commit");
+               printf("%s (%s)\n",
+                       s->amend ? "# No changes" : "nothing to commit",
+                       use_add_msg);
 }
 
 int git_status_config(const char *k, const char *v)