Merge branch 'jc/quickfetch'
authorJunio C Hamano <junkio@cox.net>
Sun, 22 Apr 2007 00:19:25 +0000 (17:19 -0700)
committerJunio C Hamano <junkio@cox.net>
Sun, 22 Apr 2007 00:19:25 +0000 (17:19 -0700)
* jc/quickfetch:
Make sure quickfetch is not fooled with a previous, incomplete fetch.
git-fetch: use fetch--tool pick-rref to avoid local fetch from alternate
git-fetch--tool pick-rref

92 files changed:
Documentation/RelNotes-1.5.1.1.txt
Documentation/RelNotes-1.5.1.2.txt [new file with mode: 0644]
Documentation/RelNotes-1.5.2.txt
Documentation/SubmittingPatches
Documentation/blame-options.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/git-am.txt
Documentation/git-annotate.txt
Documentation/git-apply.txt
Documentation/git-applymbox.txt
Documentation/git-archive.txt
Documentation/git-blame.txt
Documentation/git-checkout.txt
Documentation/git-cherry-pick.txt
Documentation/git-config.txt
Documentation/git-cvsserver.txt
Documentation/git-format-patch.txt
Documentation/git-log.txt
Documentation/git-lost-found.txt
Documentation/git-rev-list.txt
Documentation/git-rm.txt
Documentation/git-shortlog.txt
Documentation/git-show-ref.txt
Documentation/git-tar-tree.txt
Documentation/pretty-formats.txt
Documentation/tutorial.txt
Documentation/user-manual.txt
GIT-VERSION-GEN
Makefile
base85.c
builtin-apply.c
builtin-blame.c
builtin-config.c
builtin-fsck.c
builtin-grep.c
builtin-log.c
builtin-rm.c
builtin-shortlog.c
builtin-update-index.c
cache.h
commit.c
commit.h
contrib/emacs/git.el
contrib/gitview/gitview
contrib/hooks/update-paranoid [new file with mode: 0644]
convert-objects.c
decorate.c [new file with mode: 0644]
decorate.h [new file with mode: 0644]
diff-lib.c
git-bisect.sh
git-clone.sh
git-commit.sh
git-compat-util.h
git-cvsexportcommit.perl
git-cvsserver.perl
git-gui/Makefile
git-gui/git-gui.sh
git-quiltimport.sh
git-rebase.sh
git-send-email.perl
git-svn.perl
git.c
gitk
gitweb/gitweb.perl
ident.c
log-tree.c
merge-recursive.c
object-refs.c
object.h
patch-ids.c [new file with mode: 0644]
patch-ids.h [new file with mode: 0644]
perl/Makefile.PL
refs.c
revision.c
revision.h
t/diff-lib.sh [changed mode: 0755->0644]
t/lib-read-tree-m-3way.sh [changed mode: 0755->0644]
t/t1000-read-tree-m-3way.sh
t/t3030-merge-recursive.sh [new file with mode: 0755]
t/t3600-rm.sh
t/t4013-diff-various.sh
t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master [new file with mode: 0644]
t/t4121-apply-diffs.sh [new file with mode: 0755]
t/t4201-shortlog.sh [new file with mode: 0755]
t/t6023-merge-file.sh [changed mode: 0644->0755]
t/t6024-recursive-merge.sh [changed mode: 0644->0755]
t/t6025-merge-symlinks.sh [changed mode: 0644->0755]
t/t6030-bisect-porcelain.sh [new file with mode: 0755]
t/t6030-bisect-run.sh [deleted file]
t/test-lib.sh [changed mode: 0755->0644]
templates/hooks--update
unpack-trees.c
index b48b4bc3e836ee520bf9338e3d1d92f68d69da12..91471213bdec8d95209db256f594b0e1b3f3ec2a 100644 (file)
@@ -1,4 +1,4 @@
-GIT v1.5.1.1 Release Notes (draft)
+GIT v1.5.1.1 Release Notes
 ==========================
 
 Fixes since v1.5.1
@@ -10,8 +10,23 @@ Fixes since v1.5.1
 
   - The documentation for cvsimport has been majorly improved.
 
+  - "git-show-ref --exclude-existing" was documented.
+
 * Bugfixes
 
+  - The implementation of -p option in "git cvsexportcommit" had
+    the meaning of -C (context reduction) option wrong, and
+    loosened the context requirements when it was told to be
+    strict.
+
+  - "git cvsserver" did not behave like the real cvsserver when
+    client side removed a file from the working tree without
+    doing anything else on the path.  In such a case, it should
+    restore it from the checked out revision.
+
+  - "git fsck" issued an alarming error message on detached
+    HEAD.  It is not an error since at least 1.5.0.
+
   - "git send-email" produced of References header of unbounded length;
     fixed this with line-folding.
 
@@ -37,10 +52,14 @@ Fixes since v1.5.1
   - gitweb did not show type-changing patch correctly in the
     blobdiff view.
 
-* Performance Tweaks
+  - git-svn did not error out with incorrect command line options.
+
+  - git-svn fell into an infinite loop when insanely long commit
+    message was found.
+
+  - git-svn dcommit and rebase was confused by patches that were
+    merged from another branch that is managed by git-svn.
 
---
-exec >/var/tmp/1
-O=v1.5.1-26-ge94a4f6
-echo O=`git describe refs/heads/maint`
-git shortlog --no-merges $O..refs/heads/maint
+  - git-svn used to get confused when globbing remote branch/tag
+    spec (e.g. "branches = proj/branches/*:refs/remotes/origin/*")
+    is used and there was a plain file that matched the glob.
diff --git a/Documentation/RelNotes-1.5.1.2.txt b/Documentation/RelNotes-1.5.1.2.txt
new file mode 100644 (file)
index 0000000..d884563
--- /dev/null
@@ -0,0 +1,50 @@
+GIT v1.5.1.2 Release Notes
+==========================
+
+Fixes since v1.5.1.1
+--------------------
+
+* Bugfixes
+
+  - "git clone" over http from a repository that has lost the
+    loose refs by running "git pack-refs" were broken (a code to
+    deal with this was added to "git fetch" in v1.5.0, but it
+    was missing from "git clone").
+
+  - "git diff a/ b/" incorrectly fell in "diff between two
+    filesystem objects" codepath, when the user most likely
+    wanted to limit the extent of output to two tracked
+    directories.
+
+  - git-quiltimport had the same bug as we fixed for
+    git-applymbox in v1.5.1.1 -- it gave an alarming "did not
+    have any patch" message (but did not actually fail and was
+    harmless).
+
+  - various git-svn fixes.
+
+  - Sample update hook incorrectly always refused requests to
+    delete branches through push.
+
+  - git-blame on a very long working tree path had buffer
+    overrun problem.
+
+  - git-apply did not like to be fed two patches in a row that created
+    and then modified the same file.
+
+  - git-svn was confused when a non-project was stored directly under
+    trunk/, branches/ and tags/.
+
+  - git-svn wants the Error.pm module that was at least as new
+    as what we ship as part of git; install ours in our private
+    installation location if the one on the system is older.
+
+  - An earlier update to command line integer parameter parser was
+    botched and made 'update-index --cacheinfo' completely useless.
+
+
+* Documentation updates
+
+  - Various documentation updates from J. Bruce Fields, Frank
+    Lichtenheld, Alex Riesen and others.  Andrew Ruder started a
+    war on undocumented options.
index 2e3c7bc4f1cd39833f741dd2359f1b226c75a699..d93da608c70c3544963ad7dfd50ca12d39bc7fa9 100644 (file)
@@ -9,6 +9,14 @@ Updates since v1.5.1
   - "git bisect start" can optionally take a single bad commit and
     zero or more good commits on the command line.
 
+  - "git shortlog" can optionally be told to wrap its output.
+
+  - "subtree" merge strategy allows another project to be merged in as
+    your subdirectory.
+
+  - "git format-patch" learned a new --subject-prefix=<string>
+    option, to override the built-in "[PATCH]".
+
 * Updated behavior of existing commands.
 
   - "git diff --stat" shows size of preimage and postimage blobs
@@ -27,6 +35,12 @@ Updates since v1.5.1
     the root commit).  We used to refuse to operate without a
     good and a bad commit.
 
+  - "git push", when pushing into more than one repository, does
+    not stop at the first error.
+
+  - "git archive" does not insist you to give --format parameter
+    anymore; it defaults to "tar".
+
 * Builds
 
   - git-p4import has never been installed; now there is an
@@ -55,8 +69,30 @@ The following are all in v1.5.1.x series, unless otherwise noted.
 
 * Documentation updates
 
+  - Various documentation updates from J. Bruce Fields, Frank
+    Lichtenheld, Alex Riesen and others.  Andrew Ruder started a
+    war on undocumented options.
+
 * Bugfixes
 
+  - "git diff a/ b/" incorrectly fell in "diff between two
+    filesystem objects" codepath, when the user most likely
+    wanted to limit the extent of output to two tracked
+    directories.
+
+  - git-quiltimport had the same bug as we fixed for
+    git-applymbox in v1.5.1.1 -- it gave an alarming "did not
+    have any patch" message (but did not actually fail and was
+    harmless).
+
+  - various git-svn fixes.
+
+  - Sample update hook incorrectly always refused requests to
+    delete branches through push.
+
+  - git-blame on a very long working tree path had buffer
+    overrun problem.
+
   - Switching branches with "git checkout" refused to work when
     a path changes from a file to a directory between the
     current branch and the new branch, in order not to lose
@@ -67,10 +103,17 @@ The following are all in v1.5.1.x series, unless otherwise noted.
     been backported to 1.5.1.x series, as it is rather an
     intrusive change.
 
+  - Merging branches that have a file in one and a directory in
+    another at the same path used to get quite confused.  We
+    handle such a case a bit more carefully, even though that is
+    still left as a conflict for the user to sort out.  This
+    will not be backported to 1.5.1.x series, as it is rather an
+    intrusive change.
+
 * Performance Tweaks
 
 --
 exec >/var/tmp/1
-O=v1.5.1-91-g640ee0d
+O=v1.5.1.1-158-g86da9de
 echo O=`git describe refs/heads/master`
 git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
index 131bcff9b2367d63d7c8960487893bfcbfcfaa40..2386f496ee9c6151730e84385bf6b17a3eb42e10 100644 (file)
@@ -22,6 +22,9 @@ Checklist (and a short version for the impatient):
        - provide additional information (which is unsuitable for
          the commit message) between the "---" and the diffstat
        - send the patch to the list _and_ the maintainer
+       - if you change, add, or remove a command line option or
+         make some other user interface change, the associated
+         documentation should be updated as well.
 
 Long version:
 
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
new file mode 100644 (file)
index 0000000..331f161
--- /dev/null
@@ -0,0 +1,67 @@
+-b::
+       Show blank SHA-1 for boundary commits.  This can also
+       be controlled via the `blame.blankboundary` config option.
+
+--root::
+       Do not treat root commits as boundaries.  This can also be
+       controlled via the `blame.showroot` config option.
+
+--show-stats::
+       Include additional statistics at the end of blame output.
+
+-L n,m::
+       Annotate only the specified line range (lines count from 1).
+
+-l::
+       Show long rev (Default: off).
+
+-t::
+       Show raw timestamp (Default: off).
+
+-S <revs-file>::
+       Use revs from revs-file instead of calling gitlink:git-rev-list[1].
+
+-p, --porcelain::
+       Show in a format designed for machine consumption.
+
+--incremental::
+       Show the result incrementally in a format designed for
+       machine consumption.
+
+--contents <file>::
+       When <rev> is not specified, the command annotates the
+       changes starting backwards from the working tree copy.
+       This flag makes the command pretend as if the working
+       tree copy has the contents of he named file (specify
+       `-` to make the command read from the standard input).
+
+-M|<num>|::
+       Detect moving lines in the file as well.  When a commit
+       moves a block of lines in a file (e.g. the original file
+       has A and then B, and the commit changes it to B and
+       then A), traditional 'blame' algorithm typically blames
+       the lines that were moved up (i.e. B) to the parent and
+       assigns blame to the lines that were moved down (i.e. A)
+       to the child commit.  With this option, both groups of lines
+       are blamed on the parent.
+
+       <num> is optional but it is the lower bound on the number of
+       alphanumeric characters that git must detect as moving
+       within a file for it to associate those lines with the parent
+       commit.
+
+-C|<num>|::
+       In addition to `-M`, detect lines copied from other
+       files that were modified in the same commit.  This is
+       useful when you reorganize your program and move code
+       around across files.  When this option is given twice,
+       the command looks for copies from all other files in the
+       parent for the commit that creates the file in addition.
+
+       <num> is optional but it is the lower bound on the number of
+       alphanumeric characters that git must detect as moving
+       between files for it to associate those lines with the parent
+       commit.
+
+-h, --help::
+       Show help message.
index cf1e040381a99ed458dc061fe103e3c2d2c9474c..2c0a66632329dae8ae1bf9412a57ab247cc8a603 100644 (file)
@@ -117,6 +117,16 @@ core.fileMode::
        the working copy are ignored; useful on broken filesystems like FAT.
        See gitlink:git-update-index[1]. True by default.
 
+core.autocrlf::
+       If true, makes git convert `CRLF` at the end of lines in text files to
+       `LF` when reading from the filesystem, and convert in reverse when
+       writing to the filesystem.  The variable can be set to
+       'input', in which case the conversion happens only while
+       reading from the filesystem but files are written out with
+       `LF` at the end of lines.  Currently, which paths to consider
+       "text" (i.e. be subjected to the autocrlf mechanism) is
+       decided purely based on the contents.
+
 core.symlinks::
        If false, symbolic links are checked out as small plain files that
        contain the link text. gitlink:git-update-index[1] and
@@ -401,13 +411,46 @@ gc.rerereunresolved::
        The default is 15 days.  See gitlink:git-rerere[1].
 
 gitcvs.enabled::
-       Whether the cvs pserver interface is enabled for this repository.
+       Whether the cvs server interface is enabled for this repository.
        See gitlink:git-cvsserver[1].
 
 gitcvs.logfile::
-       Path to a log file where the cvs pserver interface well... logs
+       Path to a log file where the cvs server interface well... logs
        various stuff. See gitlink:git-cvsserver[1].
 
+gitcvs.allbinary::
+       If true, all files are sent to the client in mode '-kb'. This
+       causes the client to treat all files as binary files which suppresses
+       any newline munging it otherwise might do. A work-around for the
+       fact that there is no way yet to set single files to mode '-kb'.
+
+gitcvs.dbname::
+       Database used by git-cvsserver to cache revision information
+       derived from the git repository. The exact meaning depends on the
+       used database driver, for SQLite (which is the default driver) this
+       is a filename. Supports variable substitution (see
+       gitlink:git-cvsserver[1] for details). May not contain semicolons (`;`).
+       Default: '%Ggitcvs.%m.sqlite'
+
+gitcvs.dbdriver::
+       Used Perl DBI driver. You can specify any available driver
+        for this here, but it might not work. git-cvsserver is tested
+       with 'DBD::SQLite', reported to work with 'DBD::Pg', and
+       reported *not* to work with 'DBD::mysql'. Experimental feature.
+       May not contain double colons (`:`). Default: 'SQLite'.
+       See gitlink:git-cvsserver[1].
+
+gitcvs.dbuser, gitcvs.dbpass::
+       Database user and password. Only useful if setting 'gitcvs.dbdriver',
+       since SQLite has no concept of database users and/or passwords.
+       'gitcvs.dbuser' supports variable substitution (see
+       gitlink:git-cvsserver[1] for details).
+
+All gitcvs variables except for 'gitcvs.allbinary' can also specifed
+as 'gitcvs.<access_method>.<varname>' (where 'access_method' is one
+of "ext" and "pserver") to make them apply only for the given access
+method.
+
 http.sslVerify::
        Whether to verify the SSL certificate when fetching or pushing
        over HTTPS. Can be overridden by the 'GIT_SSL_NO_VERIFY' environment
@@ -445,7 +488,7 @@ http.lowSpeedLimit, http.lowSpeedTime::
 
 http.noEPSV::
        A boolean which disables using of EPSV ftp command by curl.
-       This can helpful with some "poor" ftp servers which doesn't
+       This can helpful with some "poor" ftp servers which don't
        support EPSV mode. Can be overridden by the 'GIT_CURL_FTP_NO_EPSV'
        environment variable. Default is false (curl will use EPSV).
 
index 148ce405681cd4f5bd38575b6a427a7e5745a109..f0405a35e9aa34fe67b2ddf92052f35a2d706610 100644 (file)
@@ -26,18 +26,18 @@ OPTIONS
        The list of mailbox files to read patches from. If you do not
        supply this argument, reads from the standard input.
 
---signoff::
+-s, --signoff::
        Add `Signed-off-by:` line to the commit message, using
        the committer identity of yourself.
 
---dotest=<dir>::
+-d=<dir>, --dotest=<dir>::
        Instead of `.dotest` directory, use <dir> as a working
        area to store extracted patches.
 
---keep::
+-k, --keep::
        Pass `-k` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]).
 
---utf8::
+-u, --utf8::
        Pass `-u` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]).
        The proposed commit log message taken from the e-mail
        are re-coded into UTF-8 encoding (configuration variable
@@ -48,14 +48,14 @@ This was optional in prior versions of git, but now it is the
 default.   You could use `--no-utf8` to override this.
 
 --no-utf8::
-       Do not pass `-u` flag to `git-mailinfo` (see
+       Pass `-n` flag to `git-mailinfo` (see
        gitlink:git-mailinfo[1]).
 
---binary::
+-b, --binary::
        Pass `--allow-binary-replacement` flag to `git-apply`
        (see gitlink:git-apply[1]).
 
---3way::
+-3, --3way::
        When the patch does not apply cleanly, fall back on
        3-way merge, if the patch records the identity of blobs
        it is supposed to apply to, and we have those blobs
@@ -73,10 +73,10 @@ default.   You could use `--no-utf8` to override this.
        These flags are passed to the `git-apply` program that applies
        the patch.
 
---interactive::
+-i, --interactive::
        Run interactively, just like git-applymbox.
 
---resolved::
+-r, --resolved::
        After a patch failure (e.g. attempting to apply
        conflicting patch), the user has applied it by hand and
        the index file stores the result of the application.
@@ -84,6 +84,13 @@ default.   You could use `--no-utf8` to override this.
        extracted from the e-mail message and the current index
        file, and continue.
 
+--resolvemsg=<msg>::
+       When a patch failure occurs, <msg> will be printed
+       to the screen before exiting.  This overrides the
+       standard message informing you to use `--resolved`
+       or `--skip` to handle the failure.  This is solely
+       for internal use between `git-rebase` and `git-am`.
+
 DISCUSSION
 ----------
 
index 7baf73111bc6e4fa7668f8aec31b95f20698a6ae..02dc4740d0a5f129d46c116eb9aabeec85af02fa 100644 (file)
@@ -16,20 +16,7 @@ which introduced the line. Optionally annotate from a given revision.
 
 OPTIONS
 -------
--l, --long::
-       Show long rev (Defaults off).
-
--t, --time::
-       Show raw timestamp (Defaults off).
-
--r, --rename::
-       Follow renames (Defaults on).
-
--S, --rev-file <revs-file>::
-       Use revs from revs-file instead of calling git-rev-list.
-
--h, --help::
-       Show help message.
+include::blame-options.txt[]
 
 SEE ALSO
 --------
index 065ba1bf24828d8dfbb4495068621aae8e57e31a..3bd2c995da7d65977481837486dc15e5e96baf91 100644 (file)
@@ -9,11 +9,12 @@ git-apply - Apply a patch on a git index file and a working tree
 SYNOPSIS
 --------
 [verse]
-'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] [--apply]
-         [--no-add] [--index-info] [--allow-binary-replacement | --binary]
-         [-R | --reverse] [--reject] [-z] [-pNUM] [-CNUM] [--inaccurate-eof]
-         [--whitespace=<nowarn|warn|error|error-all|strip>] [--exclude=PATH]
-         [--cached] [--verbose] [<patch>...]
+'git-apply' [--stat] [--numstat] [--summary] [--check] [--index]
+         [--apply] [--no-add] [--index-info] [-R | --reverse]
+         [--allow-binary-replacement | --binary] [--reject] [-z]
+         [-pNUM] [-CNUM] [--inaccurate-eof] [--cached]
+         [--whitespace=<nowarn|warn|error|error-all|strip>]
+         [--exclude=PATH] [--verbose] [<patch>...]
 
 DESCRIPTION
 -----------
@@ -158,7 +159,7 @@ discouraged.
        correctly. This option adds support for applying such patches by
        working around this bug.
 
---verbose::
+-v, --verbose::
        Report progress to stderr. By default, only a message about the
        current patch being applied will be printed. This option will cause
        additional information to be reported.
index 95dc65a583552d4f7e2d1fa34c448832c66a3353..3bc92d8cf17297e7b4a88bd2aeb9339b83d9d283 100644 (file)
@@ -42,14 +42,20 @@ OPTIONS
        and the current tree.
 
 -u::
-       The commit log message, author name and author email are
-       taken from the e-mail, and after minimally decoding MIME
-       transfer encoding, re-coded in UTF-8 by transliterating
-       them.  This used to be optional but now it is the default.
+       Pass `-u` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]).
+       The proposed commit log message taken from the e-mail
+       are re-coded into UTF-8 encoding (configuration variable
+       `i18n.commitencoding` can be used to specify project's
+       preferred encoding if it is not UTF-8).  This used to be
+       optional but now it is the default.
 +
 Note that the patch is always used as-is without charset
 conversion, even with this flag.
 
+-n::
+       Pass `-n` flag to `git-mailinfo` (see
+       gitlink:git-mailinfo[1]).
+
 -c .dotest/<num>::
        When the patch contained in an e-mail does not cleanly
        apply, the command exits with an error message. The
index 8d1041598e566a3ffc87cbe294c7800be1ab0c60..d3ca9a90cee97622d2a717d618da23d20aa248e4 100644 (file)
@@ -33,9 +33,12 @@ OPTIONS
        Format of the resulting archive: 'tar', 'zip'...  The default
        is 'tar'.
 
---list::
+--list, -l::
        Show all available formats.
 
+--verbose, -v::
+       Report progress to stderr.
+
 --prefix=<prefix>/::
        Prepend <prefix>/ to each filename in the archive.
 
index 5c9888d0143ec8e6c8f7c6782139cde38a25ff17..8f9439a6ddfa505c0f3cd0d1f0ba826fc12cc1ba 100644 (file)
@@ -8,8 +8,9 @@ git-blame - Show what revision and author last modified each line of a file
 SYNOPSIS
 --------
 [verse]
-'git-blame' [-c] [-l] [-t] [-f] [-n] [-p] [--incremental] [-L n,m] [-S <revs-file>]
-            [-M] [-C] [-C] [--since=<date>] [<rev> | --contents <file>] [--] <file>
+'git-blame' [-c] [-l] [-t] [-f] [-n] [-p] [--incremental] [-L n,m]
+            [-S <revs-file>] [-M] [-C] [-C] [--since=<date>]
+            [<rev> | --contents <file>] [--] <file>
 
 DESCRIPTION
 -----------
@@ -37,20 +38,19 @@ ea4c7f9bf69e781dd0cd88d2bccb2bf5cc15c9a7 git-blame: Make the output
 
 OPTIONS
 -------
--c, --compatibility::
-       Use the same output mode as gitlink:git-annotate[1] (Default: off).
-
--L n,m::
-       Annotate only the specified line range (lines count from 1).
-
--l, --long::
-       Show long rev (Default: off).
+include::blame-options.txt[]
 
--t, --time::
-       Show raw timestamp (Default: off).
+-c::
+       Use the same output mode as gitlink:git-annotate[1] (Default: off).
 
--S, --rev-file <revs-file>::
-       Use revs from revs-file instead of calling gitlink:git-rev-list[1].
+--score-debug::
+       Include debugging information related to the movement of
+       lines between files (see `-C`) and lines moved within a
+       file (see `-M`).  The first number listed is the score.
+       This is the number of alphanumeric characters detected
+       to be moved between or within files.  This must be above
+       a certain threshold for git-blame to consider those lines
+       of code to have been moved.
 
 -f, --show-name::
        Show filename in the original commit.  By default
@@ -60,42 +60,6 @@ OPTIONS
 -n, --show-number::
        Show line number in the original commit (Default: off).
 
--p, --porcelain::
-       Show in a format designed for machine consumption.
-
---incremental::
-       Show the result incrementally in a format designed for
-       machine consumption.
-
---contents <file>::
-       When <rev> is not specified, the command annotates the
-       changes starting backwards from the working tree copy.
-       This flag makes the command pretend as if the working
-       tree copy has the contents of he named file (specify
-       `-` to make the command read from the standard input).
-
--M::
-       Detect moving lines in the file as well.  When a commit
-       moves a block of lines in a file (e.g. the original file
-       has A and then B, and the commit changes it to B and
-       then A), traditional 'blame' algorithm typically blames
-       the lines that were moved up (i.e. B) to the parent and
-       assigns blame to the lines that were moved down (i.e. A)
-       to the child commit.  With this option, both groups of
-       lines are blamed on the parent.
-
--C::
-       In addition to `-M`, detect lines copied from other
-       files that were modified in the same commit.  This is
-       useful when you reorganize your program and move code
-       around across files.  When this option is given twice,
-       the command looks for copies from all other files in the
-       parent for the commit that creates the file in addition.
-
--h, --help::
-       Show help message.
-
-
 THE PORCELAIN FORMAT
 --------------------
 
index f5b2d5017b5710a08c89a7cb150d8ef45527bf99..4f2e847dc3fe44f503a578fc79496179c21ac11a 100644 (file)
@@ -23,9 +23,9 @@ options, which will be passed to `git branch`.
 
 When <paths> are given, this command does *not* switch
 branches.  It updates the named paths in the working tree from
-the index file (i.e. it runs `git-checkout-index -f -u`), or a
-named commit.  In
-this case, `-f` and `-b` options are meaningless and giving
+the index file (i.e. it runs `git-checkout-index -f -u`), or
+from a named commit.  In
+this case, the `-f` and `-b` options are meaningless and giving
 either of them results in an error.  <tree-ish> argument can be
 used to specify a specific tree-ish (i.e. commit, tag or tree)
 to update the index for the given paths before updating the
@@ -38,7 +38,8 @@ OPTIONS
        Quiet, supress feedback messages.
 
 -f::
-       Force a re-read of everything.
+       Proceed even if the index or the working tree differs
+       from HEAD.  This is used to throw away local changes.
 
 -b::
        Create a new branch named <new_branch> and start it at
@@ -48,13 +49,17 @@ OPTIONS
 
 --track::
        When -b is given and a branch is created off a remote branch,
-       setup so that git-pull will automatically retrieve data from
-       the remote branch.
+       set up configuration so that git-pull will automatically
+       retrieve data from the remote branch.  Set the
+       branch.autosetupmerge configuration variable to true if you
+       want git-checkout and git-branch to always behave as if
+       '--track' were given.
 
 --no-track::
        When -b is given and a branch is created off a remote branch,
-       force that git-pull will automatically retrieve data from
-       the remote branch independent of the configuration settings.
+       set up configuration so that git-pull will not retrieve data
+       from the remote branch, ignoring the branch.autosetupmerge
+       configuration variable.
 
 -l::
        Create the new branch's ref log.  This activates recording of
index 3149d08da83f5cd7b92f13da47298791ce9a3737..68bba982606b5e17b730f0e5d4d201923231c235 100644 (file)
@@ -38,7 +38,7 @@ OPTIONS
        development branch), adding this information can be
        useful.
 
--r|--replay::
+-r::
        It used to be that the command defaulted to do `-x`
        described above, and `-r` was to disable it.  Now the
        default is not to do `-x` so this option is a no-op.
index c759efb7fc6ebb69d9c6b59e23e31f33e0596675..280ef2058ca95e601243ae57946b80695234c41c 100644 (file)
@@ -9,16 +9,16 @@ git-config - Get and set repository or global options
 SYNOPSIS
 --------
 [verse]
-'git-config' [--global] [type] name [value [value_regex]]
-'git-config' [--global] [type] --add name value
-'git-config' [--global] [type] --replace-all name [value [value_regex]]
-'git-config' [--global] [type] --get name [value_regex]
-'git-config' [--global] [type] --get-all name [value_regex]
-'git-config' [--global] [type] --unset name [value_regex]
-'git-config' [--global] [type] --unset-all name [value_regex]
-'git-config' [--global] [type] --rename-section old_name new_name
-'git-config' [--global] [type] --remove-section name
-'git-config' [--global] -l | --list
+'git-config' [--system | --global] [type] name [value [value_regex]]
+'git-config' [--system | --global] [type] --add name value
+'git-config' [--system | --global] [type] --replace-all name [value [value_regex]]
+'git-config' [--system | --global] [type] --get name [value_regex]
+'git-config' [--system | --global] [type] --get-all name [value_regex]
+'git-config' [--system | --global] [type] --unset name [value_regex]
+'git-config' [--system | --global] [type] --unset-all name [value_regex]
+'git-config' [--system | --global] [type] --rename-section old_name new_name
+'git-config' [--system | --global] [type] --remove-section name
+'git-config' [--system | --global] -l | --list
 
 DESCRIPTION
 -----------
@@ -76,6 +76,10 @@ OPTIONS
 --global::
        Use global ~/.gitconfig file rather than the repository .git/config.
 
+--system::
+       Use system-wide $(prefix)/etc/gitconfig rather than the repository
+       .git/config.
+
 --remove-section::
        Remove the given section from the configuration file.
 
index f9e0c7737952891633a1f5503f8dc5ad46fbf53f..d22844ba49859b9a189317744e0f14431267e60a 100644 (file)
@@ -31,6 +31,10 @@ over pserver for anonymous CVS access.
 
 CVS clients cannot tag, branch or perform GIT merges.
 
+git-cvsserver maps GIT branches to CVS modules. This is very different
+from what most CVS users would expect since in CVS modules usually represent
+one or more directories.
+
 INSTALLATION
 ------------
 
@@ -65,9 +69,22 @@ env variable, you can rename git-cvsserver to cvs.
 
 ------
 Note: you need to ensure each user that is going to invoke git-cvsserver has
-write access to the log file and to the git repository. When offering anon
-access via pserver, this means that the nobody user should have write access
-to at least the sqlite database at the root of the repository.
+write access to the log file and to the database (see
+<<dbbackend,Database Backend>>. If you want to offer write access over
+SSH, the users of course also need write access to the git repository itself.
+
+[[configaccessmethod]]
+All configuration variables can also be overriden for a specific method of
+access. Valid method names are "ext" (for SSH access) and "pserver". The
+following example configuration would disable pserver access while still
+allowing access over SSH.
+------
+   [gitcvs]
+        enabled=0
+
+   [gitcvs "ext"]
+        enabled=1
+------
 --
 3. On the client machine you need to set the following variables.
    CVSROOT should be set as per normal, but the directory should point at the
@@ -93,6 +110,90 @@ Example:
      cvs co -d project-master master
 ------
 
+[[dbbackend]]
+Database Backend
+----------------
+
+git-cvsserver uses one database per git head (i.e. CVS module) to
+store information about the repository for faster access. The
+database doesn't contain any persitent data and can be completly
+regenerated from the git repository at any time. The database
+needs to be updated (i.e. written to) after every commit.
+
+If the commit is done directly by using git (as opposed to
+using git-cvsserver) the update will need to happen on the
+next repository access by git-cvsserver, independent of
+access method and requested operation.
+
+That means that even if you offer only read access (e.g. by using
+the pserver method), git-cvsserver should have write access to
+the database to work reliably (otherwise you need to make sure
+that the database if up-to-date all the time git-cvsserver is run).
+
+By default it uses SQLite databases in the git directory, named
+`gitcvs.<module_name>.sqlite`. Note that the SQLite backend creates
+temporary files in the same directory as the database file on
+write so it might not be enough to grant the users using
+git-cvsserver write access to the database file without granting
+them write access to the directory, too.
+
+You can configure the database backend with the following
+configuration variables:
+
+Configuring database backend
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+git-cvsserver uses the Perl DBI module. Please also read
+its documentation if changing these variables, especially
+about `DBI->connect()`.
+
+gitcvs.dbname::
+       Database name. The exact meaning depends on the
+       used database driver, for SQLite this is a filename.
+       Supports variable substitution (see below). May
+       not contain semicolons (`;`).
+       Default: '%Ggitcvs.%m.sqlite'
+
+gitcvs.dbdriver::
+       Used DBI driver. You can specify any available driver
+       for this here, but it might not work. cvsserver is tested
+       with 'DBD::SQLite', reported to work with
+       'DBD::Pg', and reported *not* to work with 'DBD::mysql'.
+       Please regard this as an experimental feature. May not
+       contain double colons (`:`).
+       Default: 'SQLite'
+
+gitcvs.dbuser::
+       Database user. Only useful if setting `dbdriver`, since
+       SQLite has no concept of database users. Supports variable
+       substitution (see below).
+
+gitcvs.dbpass::
+       Database password.  Only useful if setting `dbdriver`, since
+       SQLite has no concept of database passwords.
+
+All variables can also be set per access method, see <<configaccessmethod,above>>.
+
+Variable substitution
+^^^^^^^^^^^^^^^^^^^^^
+In `dbdriver` and `dbuser` you can use the following variables:
+
+%G::
+       git directory name
+%g::
+       git directory name, where all characters except for
+       alpha-numeric ones, `.`, and `-` are replaced with
+       `_` (this should make it easier to use the directory
+       name in a filename if wanted)
+%m::
+       CVS module/git head name
+%a::
+       access method (one of "ext" or "pserver")
+%u::
+       Name of the user running git-cvsserver.
+       If no name can be determined, the
+       numeric uid is used.
+
 Eclipse CVS Client Notes
 ------------------------
 
index 111d7c60bf1832bbfc27f8b819da77b8761236da..a33d157b970740aa7d056ebb459350de89513a8b 100644 (file)
@@ -10,11 +10,12 @@ SYNOPSIS
 --------
 [verse]
 'git-format-patch' [-n | -k] [-o <dir> | --stdout] [--thread]
-                  [--attach[=<boundary>] | --inline[=<boundary>]]
-                  [-s | --signoff] [<common diff options>] [--start-number <n>]
-                  [--in-reply-to=Message-Id] [--suffix=.<sfx>]
-                  [--ignore-if-in-upstream]
-                  <since>[..<until>]
+                   [--attach[=<boundary>] | --inline[=<boundary>]]
+                   [-s | --signoff] [<common diff options>] [--start-number <n>]
+                   [--in-reply-to=Message-Id] [--suffix=.<sfx>]
+                   [--ignore-if-in-upstream]
+                   [--subject-prefix=Subject-Prefix]
+                   <since>[..<until>]
 
 DESCRIPTION
 -----------
@@ -98,6 +99,12 @@ include::diff-options.txt[]
        patches being generated, and any patch that matches is
        ignored.
 
+--subject-prefix=<Subject-Prefix>::
+       Instead of the standard '[PATCH]' prefix in the subject
+       line, instead use '[<Subject-Prefix>]'. This
+       allows for useful naming of a patch series, and can be
+       combined with the --numbered option.
+
 --suffix=.<sfx>::
        Instead of using `.patch` as the suffix for generated
        filenames, use specifed suffix.  A common alternative is
index 030edaf305a620935840b3ffdcc0ca546d4e43da..49bb539dea6ad86fa8844cf6bbe27a74af41b4e4 100644 (file)
@@ -46,6 +46,11 @@ include::pretty-formats.txt[]
 -p::
        Show the change the commit introduces in a patch form.
 
+-g, \--walk-reflogs::
+       Show commits as they were recorded in the reflog. The log contains
+       a record about how the tip of a reference was changed.
+       See also gitlink:git-reflog[1].
+
 <paths>...::
        Show only commits that affect the specified paths.
 
index f52a9d7f680e3c581fe555f5a167b68c70c0a057..e48607f008395e365800b82d52eda9854519a6ce 100644 (file)
@@ -12,23 +12,22 @@ SYNOPSIS
 DESCRIPTION
 -----------
 Finds dangling commits and tags from the object database, and
-creates refs to them in .git/lost-found/ directory.  Commits and
-tags that dereference to commits go to .git/lost-found/commit
-and others are stored in .git/lost-found/other directory.
+creates refs to them in the .git/lost-found/ directory.  Commits and
+tags that dereference to commits are stored in .git/lost-found/commit,
+and other objects are stored in .git/lost-found/other.
 
 
 OUTPUT
 ------
-One line description from the commit and tag found along with
-their object name are printed on the standard output.
-
+Prints to standard output the object names and one-line descriptions
+of any commits or tags found.
 
 EXAMPLE
 -------
 
-Suppose you run 'git tag -f' and mistyped the tag to overwrite.
+Suppose you run 'git tag -f' and mistype the tag to overwrite.
 The ref to your tag is overwritten, but until you run 'git
-prune', it is still there.
+prune', the tag itself is still there.
 
 ------------
 $ git lost-found
@@ -36,15 +35,15 @@ $ git lost-found
 ...
 ------------
 
-Also you can use gitk to browse how they relate to each other
-and existing (probably old) tags.
+Also you can use gitk to browse how any tags found relate to each
+other.
 
 ------------
 $ gitk $(cd .git/lost-found/commit && echo ??*)
 ------------
 
-After making sure that it is the object you are looking for, you
-can reconnect it to your regular .git/refs hierarchy.
+After making sure you know which the object is the tag you are looking
+for, you can reconnect it to your regular .git/refs hierarchy.
 
 ------------
 $ git cat-file -t 1ef2b196
index 12b71ed0bbae2516cb67c98c96a56e6278a6f5c2..77e068b15fb02da40a8c4c9f35d076d62f379587 100644 (file)
@@ -22,6 +22,7 @@ SYNOPSIS
             [ \--topo-order ]
             [ \--parents ]
             [ \--left-right ]
+            [ \--cherry-pick ]
             [ \--encoding[=<encoding>] ]
             [ \--(author|committer|grep)=<pattern> ]
             [ [\--objects | \--objects-edge] [ \--unpacked ] ]
@@ -224,6 +225,20 @@ limiting may be applied.
        In addition to the '<commit>' listed on the command
        line, read them from the standard input.
 
+--cherry-pick::
+
+       Omit any commit that introduces the same change as
+       another commit on the "other side" when the set of
+       commits are limited with symmetric difference.
++
+For example, if you have two branches, `A` and `B`, a usual way
+to list all commits on only one side of them is with
+`--left-right`, like the example above in the description of
+that option.  It however shows the commits that were cherry-picked
+from the other branch (for example, "3rd on b" may be cherry-picked
+from branch A).  With this option, such pairs of commits are
+excluded from the output.
+
 -g, --walk-reflogs::
 
        Instead of walking the commit ancestry chain, walk
index 6feebc0400b86407a118840d9a6cc994a94715d6..a65f24a0f698eb801ac43bba5aa7dc746e96edc0 100644 (file)
@@ -7,7 +7,7 @@ git-rm - Remove files from the working tree and from the index
 
 SYNOPSIS
 --------
-'git-rm' [-f] [-n] [-r] [--cached] [--] <file>...
+'git-rm' [-f] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...
 
 DESCRIPTION
 -----------
@@ -47,6 +47,13 @@ OPTIONS
        the paths only from the index, leaving working tree
        files.
 
+\--ignore-unmatch::
+       Exit with a zero status even if no files matched.
+
+\--quiet::
+       git-rm normally outputs one line (in the form of an "rm" command)
+       for each file removed. This option suppresses that output.
+
 
 DISCUSSION
 ----------
index b0df92e8192bc494e5d4db1707c233790560afb8..1c8c55ef6eac57d5bcc1cad7afbecd354af9d3ab 100644 (file)
@@ -7,6 +7,7 @@ git-shortlog - Summarize 'git log' output
 
 SYNOPSIS
 --------
+[verse]
 git-log --pretty=short | 'git-shortlog' [-h] [-n] [-s]
 git-shortlog [-n|--number] [-s|--summary] [<committish>...]
 
@@ -33,7 +34,8 @@ OPTIONS
 
 FILES
 -----
-'.mailmap'::
+
+.mailmap::
        If this file exists, it will be used for mapping author email
        addresses to a real author name. One mapping per line, first
        the author name followed by the email address enclosed by
index 5973a82517f2b64e5575b891a2fcee8ad0664b8a..2355aa5e86d21766bbde61e3188e1c9539738150 100644 (file)
@@ -10,6 +10,7 @@ SYNOPSIS
 [verse]
 'git-show-ref' [-q|--quiet] [--verify] [-h|--head] [-d|--dereference]
             [-s|--hash] [--abbrev] [--tags] [--heads] [--] <pattern>...
+'git-show-ref' --exclude-existing[=pattern]
 
 DESCRIPTION
 -----------
@@ -19,6 +20,9 @@ commit IDs. Results can be filtered using a pattern and tags can be
 dereferenced into object IDs. Additionally, it can be used to test whether a
 particular ref exists.
 
+The --exclude-existing form is a filter that does the inverse, it shows the
+refs from stdin that don't exist in the local repository.
+
 Use of this utility is encouraged in favor of directly accessing files under
 in the `.git` directory.
 
@@ -61,6 +65,18 @@ OPTIONS
        Do not print any results to stdout. When combined with '--verify' this
        can be used to silently check if a reference exists.
 
+--exclude-existing, --exclude-existing=pattern::
+
+       Make git-show-ref act as a filter that reads refs from stdin of the
+       form "^(?:<anything>\s)?<refname>(?:\^\{\})?$" and performs the
+       following actions on each:
+       (1) strip "^{}" at the end of line if any;
+       (2) ignore if pattern 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.
+
+
 <pattern>::
 
        Show references matching one or more patterns.
index 595940524e7d7f1112700899cf74e24e1c2e1e87..7bde73b1b85df03893fa15b99b17c5aff09ae574 100644 (file)
@@ -13,7 +13,7 @@ SYNOPSIS
 DESCRIPTION
 -----------
 THIS COMMAND IS DEPRECATED.  Use `git-archive` with `--format=tar`
-option instead.
+option instead (and move the <base> argument to `--prefix=base/`).
 
 Creates a tar archive containing the tree structure for the named tree.
 When <base> is specified it is added as a leading path to the files in the
index 2fe6c319675926afe1609ad7c221dceb42c82310..d7ffc21ddf1e1241d8c485920e6ce56fe38c3f35 100644 (file)
@@ -117,6 +117,7 @@ The placeholders are:
 - '%Cgreen': switch color to green
 - '%Cblue': switch color to blue
 - '%Creset': reset color
+- '%m': left, right or boundary mark
 - '%n': newline
 
 
index 129c5c5f5b8657175db7efed2c06078affd05ab6..e978562d6e6b9685eb0835ff7e8bc131226a581d 100644 (file)
@@ -111,7 +111,7 @@ make it real.
 Note: don't forget to 'add' a file again if you modified it after the
 first 'add' and before 'commit'. Otherwise only the previous added
 state of that file will be committed. This is because git tracks
-content, so what you're really 'add'ing to the commit is the *content*
+content, so what you're really 'adding' to the commit is the *content*
 of the file in the state it is in when you 'add' it.
 
 2) By using 'git commit -a' directly
index d43d2377ec51e88116dbfed90bb1064d8fed05a1..9c4c41df5af03e53f140cab6f9e680e0b3b2a91f 100644 (file)
@@ -298,9 +298,9 @@ $ git branch
 * master
 ------------------------------------------------
 
-A freshly cloned repository contains a single branch head, named
-"master", and working directory is initialized to the state of
-the project referred to by "master".
+A freshly cloned repository contains a single branch head, by default
+named "master", with the working directory initialized to the state of
+the project referred to by that branch head.
 
 Most projects also use <<def_tag,tags>>.  Tags, like heads, are
 references into the project's history, and can be listed using the
@@ -495,8 +495,49 @@ git checkout -b <new> <start-point>::
        create a new branch <new> referencing <start-point>, and
        check it out.
 
-It is also useful to know that the special symbol "HEAD" can always
-be used to refer to the current branch.
+The special symbol "HEAD" can always be used to refer to the current
+branch.  In fact, git uses a file named "HEAD" in the .git directory to
+remember which branch is current:
+
+------------------------------------------------
+$ cat .git/HEAD
+ref: refs/heads/master
+------------------------------------------------
+
+[[detached-head]]
+Examining an old version without creating a new branch
+------------------------------------------------------
+
+The git-checkout command normally expects a branch head, but will also
+accept an arbitrary commit; for example, you can check out the commit
+referenced by a tag:
+
+------------------------------------------------
+$ git checkout v2.6.17
+Note: moving to "v2.6.17" which isn't a local branch
+If you want to create a new branch from this checkout, you may do so
+(now or later) by using -b with the checkout command again. Example:
+  git checkout -b <new_branch_name>
+HEAD is now at 427abfa... Linux v2.6.17
+------------------------------------------------
+
+The HEAD then refers to the SHA1 of the commit instead of to a branch,
+and git branch shows that you are no longer on a branch:
+
+------------------------------------------------
+$ cat .git/HEAD
+427abfa28afedffadfca9dd8b067eb6d36bac53f
+git branch
+* (no branch)
+  master
+------------------------------------------------
+
+In this case we say that the HEAD is "detached".
+
+This can be an easy way to check out a particular version without having
+to make up a name for a new branch.  However, keep in mind that when you
+switch away from the (for example, by checking out something else), you
+can lose track of what the HEAD used to point to.
 
 Examining branches from a remote repository
 -------------------------------------------
@@ -2015,22 +2056,22 @@ $ git tag bad mywork~5
 
 (Either gitk or git-log may be useful for finding the commit.)
 
-Then check out a new branch at that commit, edit it, and rebase the rest of
-the series on top of it:
+Then check out that commit, edit it, and rebase the rest of the series
+on top of it (note that we could check out the commit on a temporary
+branch, but instead we're using a <<detached-head,detached head>>):
 
 -------------------------------------------------
-$ git checkout -b TMP bad
+$ git checkout bad
 $ # make changes here and update the index
 $ git commit --amend
-$ git rebase --onto TMP bad mywork
+$ git rebase --onto HEAD bad mywork
 -------------------------------------------------
 
-When you're done, you'll be left with mywork checked out, with the top patches
-on mywork reapplied on top of the modified commit you created in TMP.  You can
+When you're done, you'll be left with mywork checked out, with the top
+patches on mywork reapplied on top of your modified commit.  You can
 then clean up with
 
 -------------------------------------------------
-$ git branch -d TMP
 $ git tag -d bad
 -------------------------------------------------
 
@@ -2275,8 +2316,8 @@ options mentioned above.
 Git internals
 =============
 
-There are two object abstractions: the "object database", and the
-"current directory cache" aka "index".
+Git depends on two fundamental abstractions: the "object database", and
+the "current directory cache" aka "index".
 
 The Object Database
 -------------------
@@ -2291,22 +2332,23 @@ All objects have a statically determined "type" aka "tag", which is
 determined at object creation time, and which identifies the format of
 the object (i.e. how it is used, and how it can refer to other
 objects).  There are currently four different object types: "blob",
-"tree", "commit" and "tag".
+"tree", "commit", and "tag".
 
-A "blob" object cannot refer to any other object, and is, like the type
-implies, a pure storage object containing some user data.  It is used to
-actually store the file data, i.e. a blob object is associated with some
-particular version of some file. 
+A <<def_blob_object,"blob" object>> cannot refer to any other object,
+and is, as the name implies, a pure storage object containing some
+user data.  It is used to actually store the file data, i.e. a blob
+object is associated with some particular version of some file.
 
-A "tree" object is an object that ties one or more "blob" objects into a
-directory structure. In addition, a tree object can refer to other tree
-objects, thus creating a directory hierarchy. 
+A <<def_tree_object,"tree" object>> is an object that ties one or more
+"blob" objects into a directory structure. In addition, a tree object
+can refer to other tree objects, thus creating a directory hierarchy.
 
-A "commit" object ties such directory hierarchies together into
-a DAG of revisions - each "commit" is associated with exactly one tree
-(the directory hierarchy at the time of the commit). In addition, a
-"commit" refers to one or more "parent" commit objects that describe the
-history of how we arrived at that directory hierarchy.
+A <<def_commit_object,"commit" object>> ties such directory hierarchies
+together into a <<def_DAG,directed acyclic graph>> of revisions - each
+"commit" is associated with exactly one tree (the directory hierarchy at
+the time of the commit). In addition, a "commit" refers to one or more
+"parent" commit objects that describe the history of how we arrived at
+that directory hierarchy.
 
 As a special case, a commit object with no parents is called the "root"
 object, and is the point of an initial project commit.  Each project
@@ -2316,9 +2358,10 @@ has two or more separate roots as its ultimate parents, that's probably
 just going to confuse people.  So aim for the notion of "one root object
 per project", even if git itself does not enforce that. 
 
-A "tag" object symbolically identifies and can be used to sign other
-objects. It contains the identifier and type of another object, a
-symbolic name (of course!) and, optionally, a signature.
+A <<def_tag_object,"tag" object>> symbolically identifies and can be
+used to sign other objects. It contains the identifier and type of
+another object, a symbolic name (of course!) and, optionally, a
+signature.
 
 Regardless of object type, all objects share the following
 characteristics: they are all deflated with zlib, and have a header
index 48715012bee68c5df07a49e9bce83240b8631746..41ee8b4ea2af9e25f7ac2ad56c083108080de49e 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.5.1.GIT
+DEF_VER=v1.5.1.2.GIT
 
 LF='
 '
index a77d31de989f6de63bb2fbbd5ccef3c1c83352a8..251fc31fc05035bf85265d3ea56c8b3ab24c895f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -283,7 +283,7 @@ LIB_H = \
        diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \
        run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
        tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
-       utf8.h reflog-walk.h
+       utf8.h reflog-walk.h patch-ids.h decorate.h
 
 DIFF_OBJS = \
        diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@ -295,6 +295,7 @@ LIB_OBJS = \
        date.o diff-delta.o entry.o exec_cmd.o ident.o \
        interpolate.o \
        lockfile.o \
+       patch-ids.o \
        object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \
        reachable.o reflog-walk.o \
        quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \
@@ -304,7 +305,7 @@ LIB_OBJS = \
        write_or_die.o trace.o list-objects.o grep.o match-trees.o \
        alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
        color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
-       convert.o
+       convert.o decorate.o
 
 BUILTIN_OBJS = \
        builtin-add.o \
@@ -728,7 +729,7 @@ git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS) GIT-CFLAGS
 help.o: common-cmds.h
 
 git-merge-subtree$X: git-merge-recursive$X
-       rm -f $@ && ln git-merge-recursive$X $@
+       $(QUIET_BUILT_IN)rm -f $@ && ln git-merge-recursive$X $@
 
 $(BUILT_INS): git$X
        $(QUIET_BUILT_IN)rm -f $@ && ln git$X $@
index a9e97f89d9beb51ffd7c8f0be828b8c85358a1ef..a6c41d597ab8eccb5eb2ccf2b3b543398c360b44 100644 (file)
--- a/base85.c
+++ b/base85.c
@@ -37,7 +37,7 @@ static void prep_base85(void)
        }
 }
 
-int decode_85(char *dst, char *buffer, int len)
+int decode_85(char *dst, const char *buffer, int len)
 {
        prep_base85();
 
@@ -82,7 +82,7 @@ int decode_85(char *dst, char *buffer, int len)
        return 0;
 }
 
-void encode_85(char *buf, unsigned char *data, int bytes)
+void encode_85(char *buf, const unsigned char *data, int bytes)
 {
        prep_base85();
 
index fd92ef7174dae1586921ebd34233c93b381106de..94311e7af47f856c0ecfad08bebfde83a80938a2 100644 (file)
@@ -2416,8 +2416,7 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
                 * used to be.
                 */
                struct stat st;
-               errno = 0;
-               if (!lstat(path, &st) && S_ISDIR(st.st_mode) && !rmdir(path))
+               if (!lstat(path, &st) && (!S_ISDIR(st.st_mode) || !rmdir(path)))
                        errno = EEXIST;
        }
 
index 60ec5354f11c61c49829d41e8c07d22573f16bc7..8919b028e620d573e30203c5bffafce9b5776406 100644 (file)
 
 static char blame_usage[] =
 "git-blame [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [--contents <filename>] [--incremental] [commit] [--] file\n"
-"  -c, --compatibility Use the same output mode as git-annotate (Default: off)\n"
+"  -c                  Use the same output mode as git-annotate (Default: off)\n"
 "  -b                  Show blank SHA-1 for boundary commits (Default: off)\n"
-"  -l, --long          Show long commit SHA1 (Default: off)\n"
+"  -l                  Show long commit SHA1 (Default: off)\n"
 "  --root              Do not treat root commits as boundaries (Default: off)\n"
-"  -t, --time          Show raw timestamp (Default: off)\n"
+"  -t                  Show raw timestamp (Default: off)\n"
 "  -f, --show-name     Show original filename (Default: auto)\n"
 "  -n, --show-number   Show original linenumber (Default: off)\n"
 "  -p, --porcelain     Show in a format designed for machine consumption\n"
@@ -2041,7 +2041,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
 
        commit->buffer = xmalloc(400);
        ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
-       sprintf(commit->buffer,
+       snprintf(commit->buffer, 400,
                "tree 0000000000000000000000000000000000000000\n"
                "parent %s\n"
                "author %s\n"
index dfa403b94baea97c655ab707d75e80f58f318c51..b2515f7e65ed05a5352639686b5163f4c74bc1c2 100644 (file)
@@ -2,7 +2,7 @@
 #include "cache.h"
 
 static const char git_config_set_usage[] =
-"git-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 | --remove-section name | --list";
+"git-config [ --global | --system ] [ --bool | --int ] [--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";
 
 static char *key;
 static regex_t *key_regexp;
index 4d8b66c344422dd60c72c72bbfbc5aa87be7907e..05d98d2cfc0c8ba5e38482a181fee8115e9bd640 100644 (file)
@@ -534,7 +534,7 @@ static void get_default_heads(void)
         * "show_unreachable" flag.
         */
        if (!default_refs) {
-               error("No default references");
+               fprintf(stderr, "notice: No default references\n");
                show_unreachable = 0;
        }
 }
@@ -554,15 +554,23 @@ static int fsck_head_link(void)
 {
        unsigned char sha1[20];
        int flag;
-       const char *head_points_at = resolve_ref("HEAD", sha1, 1, &flag);
-
-       if (!head_points_at || !(flag & REF_ISSYMREF))
-               return error("HEAD is not a symbolic ref");
-       if (prefixcmp(head_points_at, "refs/heads/"))
+       int null_is_error = 0;
+       const char *head_points_at = resolve_ref("HEAD", sha1, 0, &flag);
+
+       if (!head_points_at)
+               return error("Invalid HEAD");
+       if (!strcmp(head_points_at, "HEAD"))
+               /* detached HEAD */
+               null_is_error = 1;
+       else if (prefixcmp(head_points_at, "refs/heads/"))
                return error("HEAD points to something strange (%s)",
                             head_points_at);
-       if (is_null_sha1(sha1))
-               return error("HEAD: not a valid git pointer");
+       if (is_null_sha1(sha1)) {
+               if (null_is_error)
+                       return error("HEAD: detached HEAD points at nothing");
+               fprintf(stderr, "notice: HEAD points to an unborn branch (%s)\n",
+                       head_points_at + 11);
+       }
        return 0;
 }
 
index 981f3d4d8eb079f5985beaaf94014d5745c5aacc..e13cb31f2b9dd63d335437fb5b1c9b0b06fbf188 100644 (file)
@@ -434,19 +434,6 @@ static const char emsg_missing_context_len[] =
 static const char emsg_missing_argument[] =
 "option requires an argument -%s";
 
-static int strtoul_ui(char const *s, unsigned int *result)
-{
-       unsigned long ul;
-       char *p;
-
-       errno = 0;
-       ul = strtoul(s, &p, 10);
-       if (errno || *p || p == s || (unsigned int) ul != ul)
-               return -1;
-       *result = ul;
-       return 0;
-}
-
 int cmd_grep(int argc, const char **argv, const char *prefix)
 {
        int hit = 0;
@@ -569,7 +556,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                                scan = arg + 1;
                                break;
                        }
-                       if (strtoul_ui(scan, &num))
+                       if (strtoul_ui(scan, 10, &num))
                                die(emsg_invalid_context_len, scan);
                        switch (arg[1]) {
                        case 'A':
index 71df957eaa0b85bd841fe3758b6efbfd51243881..38bf52f1006fd991175621734e11e4d23f4b23de 100644 (file)
 #include "builtin.h"
 #include "tag.h"
 #include "reflog-walk.h"
+#include "patch-ids.h"
+#include "refs.h"
 
 static int default_show_root = 1;
 
 /* this is in builtin-diff.c */
 void add_head(struct rev_info *revs);
 
+static void add_name_decoration(const char *prefix, const char *name, struct object *obj)
+{
+       int plen = strlen(prefix);
+       int nlen = strlen(name);
+       struct name_decoration *res = xmalloc(sizeof(struct name_decoration) + plen + nlen);
+       memcpy(res->name, prefix, plen);
+       memcpy(res->name + plen, name, nlen + 1);
+       res->next = add_decoration(&name_decoration, obj, res);
+}
+
+static int add_ref_decoration(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+{
+       struct object *obj = parse_object(sha1);
+       if (!obj)
+               return 0;
+       add_name_decoration("", refname, obj);
+       while (obj->type == OBJ_TAG) {
+               obj = ((struct tag *)obj)->tagged;
+               if (!obj)
+                       break;
+               add_name_decoration("tag: ", refname, obj);
+       }
+       return 0;
+}
+
 static void cmd_log_init(int argc, const char **argv, const char *prefix,
                      struct rev_info *rev)
 {
        int i;
+       int decorate = 0;
 
        rev->abbrev = DEFAULT_ABBREV;
        rev->commit_format = CMIT_FMT_DEFAULT;
@@ -38,8 +66,11 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
                                git_log_output_encoding = xstrdup(arg);
                        else
                                git_log_output_encoding = "";
-               }
-               else
+               } else if (!strcmp(arg, "--decorate")) {
+                       if (!decorate)
+                               for_each_ref(add_ref_decoration, NULL);
+                       decorate = 1;
+               } else
                        die("unrecognized argument: %s", arg);
        }
 }
@@ -333,25 +364,12 @@ static int reopen_stdout(struct commit *commit, int nr, int keep_subject)
 
 }
 
-static int get_patch_id(struct commit *commit, struct diff_options *options,
-               unsigned char *sha1)
-{
-       if (commit->parents)
-               diff_tree_sha1(commit->parents->item->object.sha1,
-                              commit->object.sha1, "", options);
-       else
-               diff_root_tree_sha1(commit->object.sha1, "", options);
-       diffcore_std(options);
-       return diff_flush_patch_id(options, sha1);
-}
-
-static void get_patch_ids(struct rev_info *rev, struct diff_options *options, const char *prefix)
+static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const char *prefix)
 {
        struct rev_info check_rev;
        struct commit *commit;
        struct object *o1, *o2;
        unsigned flags1, flags2;
-       unsigned char sha1[20];
 
        if (rev->pending.nr != 2)
                die("Need exactly one range.");
@@ -364,10 +382,7 @@ static void get_patch_ids(struct rev_info *rev, struct diff_options *options, co
        if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
                die("Not a range.");
 
-       diff_setup(options);
-       options->recursive = 1;
-       if (diff_setup_done(options) < 0)
-               die("diff_setup_done failed");
+       init_patch_ids(ids);
 
        /* given a range a..b get all patch ids for b..a */
        init_revisions(&check_rev, prefix);
@@ -382,8 +397,7 @@ static void get_patch_ids(struct rev_info *rev, struct diff_options *options, co
                if (commit->parents && commit->parents->next)
                        continue;
 
-               if (!get_patch_id(commit, options, sha1))
-                       created_object(sha1, xcalloc(1, sizeof(struct object)));
+               add_commit_patch_id(commit, ids);
        }
 
        /* reset for next revision walk */
@@ -417,10 +431,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int numbered = 0;
        int start_number = -1;
        int keep_subject = 0;
+       int subject_prefix = 0;
        int ignore_if_in_upstream = 0;
        int thread = 0;
        const char *in_reply_to = NULL;
-       struct diff_options patch_id_opts;
+       struct patch_ids ids;
        char *add_signoff = NULL;
        char message_id[1024];
        char ref_message_id[1024];
@@ -509,8 +524,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        if (i == argc)
                                die("Need a Message-Id for --in-reply-to");
                        in_reply_to = argv[i];
-               }
-               else if (!prefixcmp(argv[i], "--suffix="))
+               } else if (!prefixcmp(argv[i], "--subject-prefix=")) {
+                       subject_prefix = 1;
+                       rev.subject_prefix = argv[i] + 17;
+               } else if (!prefixcmp(argv[i], "--suffix="))
                        fmt_patch_suffix = argv[i] + 9;
                else
                        argv[j++] = argv[i];
@@ -521,6 +538,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                start_number = 1;
        if (numbered && keep_subject)
                die ("-n and -k are mutually exclusive.");
+       if (keep_subject && subject_prefix)
+               die ("--subject-prefix and -k are mutually exclusive.");
 
        argc = setup_revisions(argc, argv, &rev, "HEAD");
        if (argc > 1)
@@ -554,22 +573,19 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        }
 
        if (ignore_if_in_upstream)
-               get_patch_ids(&rev, &patch_id_opts, prefix);
+               get_patch_ids(&rev, &ids, prefix);
 
        if (!use_stdout)
                realstdout = fdopen(dup(1), "w");
 
        prepare_revision_walk(&rev);
        while ((commit = get_revision(&rev)) != NULL) {
-               unsigned char sha1[20];
-
                /* ignore merges */
                if (commit->parents && commit->parents->next)
                        continue;
 
                if (ignore_if_in_upstream &&
-                               !get_patch_id(commit, &patch_id_opts, sha1) &&
-                               lookup_object(sha1))
+                               has_commit_patch_id(commit, &ids))
                        continue;
 
                nr++;
@@ -624,6 +640,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        fclose(stdout);
        }
        free(list);
+       if (ignore_if_in_upstream)
+               free_patch_ids(&ids);
        return 0;
 }
 
@@ -646,7 +664,7 @@ static const char cherry_usage[] =
 int cmd_cherry(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
-       struct diff_options patch_id_opts;
+       struct patch_ids ids;
        struct commit *commit;
        struct commit_list *list = NULL;
        const char *upstream;
@@ -692,7 +710,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
                        return 0;
        }
 
-       get_patch_ids(&revs, &patch_id_opts, prefix);
+       get_patch_ids(&revs, &ids, prefix);
 
        if (limit && add_pending_commit(limit, &revs, UNINTERESTING))
                die("Unknown commit %s", limit);
@@ -708,12 +726,10 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
        }
 
        while (list) {
-               unsigned char sha1[20];
                char sign = '+';
 
                commit = list->item;
-               if (!get_patch_id(commit, &patch_id_opts, sha1) &&
-                   lookup_object(sha1))
+               if (has_commit_patch_id(commit, &ids))
                        sign = '-';
 
                if (verbose) {
@@ -731,5 +747,6 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
                list = list->next;
        }
 
+       free_patch_ids(&ids);
        return 0;
 }
index 8a0738f83dd31a847bf486ddb3aefef94af5e049..4a0bd93c8b3b644fb86ce05686b09d79b180bffc 100644 (file)
@@ -10,7 +10,7 @@
 #include "tree-walk.h"
 
 static const char builtin_rm_usage[] =
-"git-rm [-f] [-n] [-r] [--cached] [--] <file>...";
+"git-rm [-f] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...";
 
 static struct {
        int nr, alloc;
@@ -104,7 +104,8 @@ static struct lock_file lock_file;
 int cmd_rm(int argc, const char **argv, const char *prefix)
 {
        int i, newfd;
-       int show_only = 0, force = 0, index_only = 0, recursive = 0;
+       int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
+       int ignore_unmatch = 0;
        const char **pathspec;
        char *seen;
 
@@ -132,6 +133,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                        force = 1;
                else if (!strcmp(arg, "-r"))
                        recursive = 1;
+               else if (!strcmp(arg, "--quiet"))
+                       quiet = 1;
+               else if (!strcmp(arg, "--ignore-unmatch"))
+                       ignore_unmatch = 1;
                else
                        usage(builtin_rm_usage);
        }
@@ -153,14 +158,24 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 
        if (pathspec) {
                const char *match;
+               int seen_any = 0;
                for (i = 0; (match = pathspec[i]) != NULL ; i++) {
-                       if (!seen[i])
-                               die("pathspec '%s' did not match any files",
-                                   match);
+                       if (!seen[i]) {
+                               if (!ignore_unmatch) {
+                                       die("pathspec '%s' did not match any files",
+                                           match);
+                               }
+                       }
+                       else {
+                               seen_any = 1;
+                       }
                        if (!recursive && seen[i] == MATCHED_RECURSIVELY)
                                die("not removing '%s' recursively without -r",
                                    *match ? match : ".");
                }
+
+               if (! seen_any)
+                       exit(0);
        }
 
        /*
@@ -168,7 +183,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
         * must match; but the file can already been removed, since
         * this sequence is a natural "novice" way:
         *
-        *      rm F; git fm F
+        *      rm F; git rm F
         *
         * Further, if HEAD commit exists, "diff-index --cached" must
         * report no changes unless forced.
@@ -187,7 +202,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
         */
        for (i = 0; i < list.nr; i++) {
                const char *path = list.name[i];
-               printf("rm '%s'\n", path);
+               if (!quiet)
+                       printf("rm '%s'\n", path);
 
                if (remove_file_from_cache(path))
                        die("git-rm: unable to remove %s", path);
index 29343aefc843c4dd22095f559262bc6b5e381440..3f93498bb711ccaf1f8a1ad5ed9ac8e02958d357 100644 (file)
@@ -4,6 +4,7 @@
 #include "diff.h"
 #include "path-list.h"
 #include "revision.h"
+#include "utf8.h"
 
 static const char shortlog_usage[] =
 "git-shortlog [-n] [-s] [<commit-id>... ]";
@@ -276,11 +277,64 @@ static void get_from_rev(struct rev_info *rev, struct path_list *list)
 
 }
 
+static int parse_uint(char const **arg, int comma)
+{
+       unsigned long ul;
+       int ret;
+       char *endp;
+
+       ul = strtoul(*arg, &endp, 10);
+       if (endp != *arg && *endp && *endp != comma)
+               return -1;
+       ret = (int) ul;
+       if (ret != ul)
+               return -1;
+       *arg = endp;
+       if (**arg)
+               (*arg)++;
+       return ret;
+}
+
+static const char wrap_arg_usage[] = "-w[<width>[,<indent1>[,<indent2>]]]";
+#define DEFAULT_WRAPLEN 76
+#define DEFAULT_INDENT1 6
+#define DEFAULT_INDENT2 9
+
+static void parse_wrap_args(const char *arg, int *in1, int *in2, int *wrap)
+{
+       arg += 2; /* skip -w */
+
+       *wrap = parse_uint(&arg, ',');
+       if (*wrap < 0)
+               die(wrap_arg_usage);
+       *in1 = parse_uint(&arg, ',');
+       if (*in1 < 0)
+               die(wrap_arg_usage);
+       *in2 = parse_uint(&arg, '\0');
+       if (*in2 < 0)
+               die(wrap_arg_usage);
+
+       if (!*wrap)
+               *wrap = DEFAULT_WRAPLEN;
+       if (!*in1)
+               *in1 = DEFAULT_INDENT1;
+       if (!*in2)
+               *in2 = DEFAULT_INDENT2;
+       if (*wrap &&
+           ((*in1 && *wrap <= *in1) ||
+            (*in2 && *wrap <= *in2)))
+               die(wrap_arg_usage);
+}
+
 int cmd_shortlog(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
        struct path_list list = { NULL, 0, 0, 1 };
        int i, j, sort_by_number = 0, summary = 0;
+       int wrap_lines = 0;
+       int wrap = DEFAULT_WRAPLEN;
+       int in1 = DEFAULT_INDENT1;
+       int in2 = DEFAULT_INDENT2;
 
        /* since -n is a shadowed rev argument, parse our args first */
        while (argc > 1) {
@@ -289,6 +343,10 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
                else if (!strcmp(argv[1], "-s") ||
                                !strcmp(argv[1], "--summary"))
                        summary = 1;
+               else if (!prefixcmp(argv[1], "-w")) {
+                       wrap_lines = 1;
+                       parse_wrap_args(argv[1], &in1, &in2, &wrap);
+               }
                else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
                        usage(shortlog_usage);
                else
@@ -323,9 +381,18 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
                        printf("%s: %d\n", list.items[i].path, onelines->nr);
                } else {
                        printf("%s (%d):\n", list.items[i].path, onelines->nr);
-                       for (j = onelines->nr - 1; j >= 0; j--)
-                               printf("      %s\n", onelines->items[j].path);
-                       printf("\n");
+                       for (j = onelines->nr - 1; j >= 0; j--) {
+                               const char *msg = onelines->items[j].path;
+
+                               if (wrap_lines) {
+                                       int col = print_wrapped_text(msg, in1, in2, wrap);
+                                       if (col != wrap)
+                                               putchar('\n');
+                               }
+                               else
+                                       printf("      %s\n", msg);
+                       }
+                       putchar('\n');
                }
 
                onelines->strdup_paths = 1;
index 47d42ed6450695b0502b58e6aca9dabeb96e5bf5..e5541df28423c4297187f5b9d42c5362fba5de29 100644 (file)
@@ -227,6 +227,7 @@ static void read_index_info(int line_termination)
                char *path_name;
                unsigned char sha1[20];
                unsigned int mode;
+               unsigned long ul;
                int stage;
 
                /* This reads lines formatted in one of three formats:
@@ -249,9 +250,12 @@ static void read_index_info(int line_termination)
                if (buf.eof)
                        break;
 
-               mode = strtoul(buf.buf, &ptr, 8);
-               if (ptr == buf.buf || *ptr != ' ')
+               errno = 0;
+               ul = strtoul(buf.buf, &ptr, 8);
+               if (ptr == buf.buf || *ptr != ' '
+                   || errno || (unsigned int) ul != ul)
                        goto bad_line;
+               mode = ul;
 
                tab = strchr(ptr, '\t');
                if (!tab || tab - ptr < 41)
@@ -547,7 +551,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                                if (i+3 >= argc)
                                        die("git-update-index: --cacheinfo <mode> <sha1> <path>");
 
-                               if ((sscanf(argv[i+1], "%o", &mode) != 1) ||
+                               if (strtoul_ui(argv[i+1], 8, &mode) ||
                                    get_sha1_hex(argv[i+2], sha1) ||
                                    add_cacheinfo(mode, sha1, argv[i+3], 0))
                                        die("git-update-index: --cacheinfo"
diff --git a/cache.h b/cache.h
index eb57507b804019f7d6b27a27a2b262d4b051e43b..53c23413e4ba0b6c43632be821c69c8eb80db857 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -217,7 +217,7 @@ extern int commit_locked_index(struct lock_file *);
 extern void set_alternate_index_output(const char *);
 
 extern void rollback_lock_file(struct lock_file *);
-extern int delete_ref(const char *, unsigned char *sha1);
+extern int delete_ref(const char *, const unsigned char *sha1);
 
 /* Environment bits from configuration mechanism */
 extern int use_legacy_headers;
@@ -472,8 +472,8 @@ extern int pager_in_use;
 extern int pager_use_color;
 
 /* base85 */
-int decode_85(char *dst, char *line, int linelen);
-void encode_85(char *buf, unsigned char *data, int bytes);
+int decode_85(char *dst, const char *line, int linelen);
+void encode_85(char *buf, const unsigned char *data, int bytes);
 
 /* alloc.c */
 struct blob;
index 754d1b8a0b8282fd3d1d6bd8f6ccb21b407504a5..952095faa70dd8f5166f106a83382e50e131897a 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -4,6 +4,8 @@
 #include "pkt-line.h"
 #include "utf8.h"
 #include "interpolate.h"
+#include "diff.h"
+#include "revision.h"
 
 int save_commit_buffer = 1;
 
@@ -808,7 +810,8 @@ static long format_commit_message(const struct commit *commit,
                { "%Cgreen" },  /* green */
                { "%Cblue" },   /* blue */
                { "%Creset" },  /* reset color */
-               { "%n" }        /* newline */
+               { "%n" },       /* newline */
+               { "%m" },       /* left/right/bottom */
        };
        enum interp_index {
                IHASH = 0, IHASH_ABBREV,
@@ -824,14 +827,15 @@ static long format_commit_message(const struct commit *commit,
                ISUBJECT,
                IBODY,
                IRED, IGREEN, IBLUE, IRESET_COLOR,
-               INEWLINE
+               INEWLINE,
+               ILEFT_RIGHT,
        };
        struct commit_list *p;
        char parents[1024];
        int i;
        enum { HEADER, SUBJECT, BODY } state;
 
-       if (INEWLINE + 1 != ARRAY_SIZE(table))
+       if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))
                die("invalid interp table!");
 
        /* these are independent of the commit */
@@ -852,6 +856,12 @@ static long format_commit_message(const struct commit *commit,
        interp_set_entry(table, ITREE_ABBREV,
                        find_unique_abbrev(commit->tree->object.sha1,
                                DEFAULT_ABBREV));
+       interp_set_entry(table, ILEFT_RIGHT,
+                        (commit->object.flags & BOUNDARY)
+                        ? "-"
+                        : (commit->object.flags & SYMMETRIC_LEFT)
+                        ? "<"
+                        : ">");
 
        parents[1] = 0;
        for (i = 0, p = commit->parents;
index 83507a07e4cbaab81f130891a64b78539d1beede..59de17eff81dc6987350baab086935de7c9dcd1f 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -3,6 +3,7 @@
 
 #include "object.h"
 #include "tree.h"
+#include "decorate.h"
 
 struct commit_list {
        struct commit *item;
@@ -21,6 +22,13 @@ struct commit {
 extern int save_commit_buffer;
 extern const char *commit_type;
 
+/* While we can decorate any object with a name, it's only used for commits.. */
+extern struct decoration name_decoration;
+struct name_decoration {
+       struct name_decoration *next;
+       char name[1];
+};
+
 struct commit *lookup_commit(const unsigned char *sha1);
 struct commit *lookup_commit_reference(const unsigned char *sha1);
 struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
index 2f9995ea3979350c7cd3883dfc6364e90b8d9bb2..f60017948f4eb45f6aa7fe0fc60ca1507cec98f2 100644 (file)
@@ -345,9 +345,15 @@ and returns the process output as a string."
   (let ((str (git-call-process-env-string nil "symbolic-ref" ref)))
     (and str (car (split-string str "\n")))))
 
-(defun git-update-ref (ref val &optional oldval)
+(defun git-update-ref (ref newval &optional oldval reason)
   "Update a reference by calling git-update-ref."
-  (apply #'git-call-process-env nil nil "update-ref" ref val (if oldval (list oldval))))
+  (let ((args (and oldval (list oldval))))
+    (push newval args)
+    (push ref args)
+    (when reason
+     (push reason args)
+     (push "-m" args))
+    (eq 0 (apply #'git-call-process-env nil nil "update-ref" args))))
 
 (defun git-read-tree (tree &optional index-file)
   "Read a tree into the index file."
@@ -364,8 +370,10 @@ and returns the process output as a string."
   "Call git-commit-tree with buffer as input and return the resulting commit SHA1."
   (let ((author-name (git-get-committer-name))
         (author-email (git-get-committer-email))
+        (subject "commit (initial): ")
         author-date log-start log-end args coding-system-for-write)
     (when head
+      (setq subject "commit: ")
       (push "-p" args)
       (push head args))
     (with-current-buffer buffer
@@ -384,22 +392,29 @@ and returns the process output as a string."
             (goto-char (point-min))
             (while (re-search-forward "^Parent: +\\([0-9a-f]+\\)" nil t)
               (unless (string-equal head (match-string 1))
+                (setq subject "commit (merge): ")
                 (push "-p" args)
                 (push (match-string 1) args))))
         (setq log-start (point-min)))
       (setq log-end (point-max))
+      (goto-char log-start)
+      (when (re-search-forward ".*$" nil t)
+        (setq subject (concat subject (match-string 0))))
       (setq coding-system-for-write buffer-file-coding-system))
-    (git-get-string-sha1
-     (with-output-to-string
-       (with-current-buffer standard-output
-         (let ((env `(("GIT_AUTHOR_NAME" . ,author-name)
-                      ("GIT_AUTHOR_EMAIL" . ,author-email)
-                      ("GIT_COMMITTER_NAME" . ,(git-get-committer-name))
-                      ("GIT_COMMITTER_EMAIL" . ,(git-get-committer-email)))))
-           (when author-date (push `("GIT_AUTHOR_DATE" . ,author-date) env))
-           (apply #'git-run-command-region
-                  buffer log-start log-end env
-                  "commit-tree" tree (nreverse args))))))))
+    (let ((commit
+           (git-get-string-sha1
+            (with-output-to-string
+              (with-current-buffer standard-output
+                (let ((env `(("GIT_AUTHOR_NAME" . ,author-name)
+                             ("GIT_AUTHOR_EMAIL" . ,author-email)
+                             ("GIT_COMMITTER_NAME" . ,(git-get-committer-name))
+                             ("GIT_COMMITTER_EMAIL" . ,(git-get-committer-email)))))
+                  (when author-date (push `("GIT_AUTHOR_DATE" . ,author-date) env))
+                  (apply #'git-run-command-region
+                         buffer log-start log-end env
+                         "commit-tree" tree (nreverse args))))))))
+      (and (git-update-ref "HEAD" commit head subject)
+           commit))))
 
 (defun git-empty-db-p ()
   "Check if the git db is empty (no commit done yet)."
@@ -662,7 +677,6 @@ and returns the process output as a string."
                       (if (or (not (string-equal tree head-tree))
                               (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? "))
                           (let ((commit (git-commit-tree buffer tree head)))
-                            (git-update-ref "HEAD" commit head)
                             (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
                             (condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
                             (with-current-buffer buffer (erase-buffer))
index 521b2fcd32da4103d0c916af0ae44fbe53ed282a..2d80e2bad2e6f322d7ff7e9f03a6897a11f74231 100755 (executable)
@@ -10,7 +10,8 @@ GUI browser for git repository
 This program is based on bzrk by Scott James Remnant <scott@ubuntu.com>
 """
 __copyright__ = "Copyright (C) 2006 Hewlett-Packard Development Company, L.P."
-__author__    = "Aneesh Kumar K.V <aneesh.kumar@hp.com>"
+__copyright__ = "Copyright (C) 2007 Aneesh Kumar K.V <aneesh.kumar@gmail.com"
+__author__    = "Aneesh Kumar K.V <aneesh.kumar@gmail.com>"
 
 
 import sys
@@ -24,6 +25,7 @@ import gobject
 import cairo
 import math
 import string
+import fcntl
 
 try:
     import gtksourceview
@@ -337,6 +339,186 @@ class Commit:
                fp.close()
                return diff
 
+class AnnotateWindow:
+       """Annotate window.
+       This object represents and manages a single window containing the
+       annotate information of the file
+       """
+
+       def __init__(self):
+               self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+               self.window.set_border_width(0)
+               self.window.set_title("Git repository browser annotation window")
+
+               # Use two thirds of the screen by default
+               screen = self.window.get_screen()
+               monitor = screen.get_monitor_geometry(0)
+               width = int(monitor.width * 0.66)
+               height = int(monitor.height * 0.66)
+               self.window.set_default_size(width, height)
+
+       def add_file_data(self, filename, commit_sha1, line_num):
+               fp = os.popen("git cat-file blob " + commit_sha1 +":"+filename)
+               i = 1;
+               for line in fp.readlines():
+                       line = string.rstrip(line)
+                       self.model.append(None, ["HEAD", filename, line, i])
+                       i = i+1
+               fp.close()
+
+               # now set the cursor position
+               self.treeview.set_cursor(line_num-1)
+               self.treeview.grab_focus()
+
+       def _treeview_cursor_cb(self, *args):
+               """Callback for when the treeview cursor changes."""
+               (path, col) = self.treeview.get_cursor()
+               commit_sha1 = self.model[path][0]
+               commit_msg = ""
+               fp = os.popen("git cat-file commit " + commit_sha1)
+               for line in fp.readlines():
+                       commit_msg =  commit_msg + line
+               fp.close()
+
+               self.commit_buffer.set_text(commit_msg)
+
+       def _treeview_row_activated(self, *args):
+               """Callback for when the treeview row gets selected."""
+               (path, col) = self.treeview.get_cursor()
+               commit_sha1 = self.model[path][0]
+               filename    = self.model[path][1]
+               line_num    = self.model[path][3]
+
+               window = AnnotateWindow();
+               fp = os.popen("git rev-parse "+ commit_sha1 + "~1")
+               commit_sha1 = string.strip(fp.readline())
+               fp.close()
+               window.annotate(filename, commit_sha1, line_num)
+
+       def data_ready(self, source, condition):
+               while (1):
+                       try :
+                               buffer = source.read(8192)
+                       except:
+                               # resource temporary not available
+                               return True
+
+                       if (len(buffer) == 0):
+                               gobject.source_remove(self.io_watch_tag)
+                               source.close()
+                               return False
+
+                       for buff in buffer.split("\n"):
+                               annotate_line = re.compile('^([0-9a-f]{40}) (.+) (.+) (.+)$')
+                               m = annotate_line.match(buff)
+                               if not m:
+                                       annotate_line = re.compile('^(filename) (.+)$')
+                                       m = annotate_line.match(buff)
+                                       if not m:
+                                               continue
+                                       filename = m.group(2)
+                               else:
+                                       self.commit_sha1 = m.group(1)
+                                       self.source_line = int(m.group(2))
+                                       self.result_line = int(m.group(3))
+                                       self.count          = int(m.group(4))
+                                       #set the details only when we have the file name
+                                       continue
+
+                               while (self.count > 0):
+                                       # set at result_line + count-1 the sha1 as commit_sha1
+                                       self.count = self.count - 1
+                                       iter = self.model.iter_nth_child(None, self.result_line + self.count-1)
+                                       self.model.set(iter, 0, self.commit_sha1, 1, filename, 3, self.source_line)
+
+
+       def annotate(self, filename, commit_sha1, line_num):
+               # verify the commit_sha1 specified has this filename
+
+               fp = os.popen("git ls-tree "+ commit_sha1 + " -- " + filename)
+               line = string.strip(fp.readline())
+               if line == '':
+                       # pop up the message the file is not there as a part of the commit
+                       fp.close()
+                       dialog = gtk.MessageDialog(parent=None, flags=0,
+                                       type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE,
+                                       message_format=None)
+                       dialog.set_markup("The file %s is not present in the parent commit %s" % (filename, commit_sha1))
+                       dialog.run()
+                       dialog.destroy()
+                       return
+
+               fp.close()
+
+               vpan = gtk.VPaned();
+               self.window.add(vpan);
+               vpan.show()
+
+               scrollwin = gtk.ScrolledWindow()
+               scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+               scrollwin.set_shadow_type(gtk.SHADOW_IN)
+               vpan.pack1(scrollwin, True, True);
+               scrollwin.show()
+
+               self.model = gtk.TreeStore(str, str, str, int)
+               self.treeview = gtk.TreeView(self.model)
+               self.treeview.set_rules_hint(True)
+               self.treeview.set_search_column(0)
+               self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
+               self.treeview.connect("row-activated", self._treeview_row_activated)
+               scrollwin.add(self.treeview)
+               self.treeview.show()
+
+               cell = gtk.CellRendererText()
+               cell.set_property("width-chars", 10)
+               cell.set_property("ellipsize", pango.ELLIPSIZE_END)
+               column = gtk.TreeViewColumn("Commit")
+               column.set_resizable(True)
+               column.pack_start(cell, expand=True)
+               column.add_attribute(cell, "text", 0)
+               self.treeview.append_column(column)
+
+               cell = gtk.CellRendererText()
+               cell.set_property("width-chars", 20)
+               cell.set_property("ellipsize", pango.ELLIPSIZE_END)
+               column = gtk.TreeViewColumn("File Name")
+               column.set_resizable(True)
+               column.pack_start(cell, expand=True)
+               column.add_attribute(cell, "text", 1)
+               self.treeview.append_column(column)
+
+               cell = gtk.CellRendererText()
+               cell.set_property("width-chars", 20)
+               cell.set_property("ellipsize", pango.ELLIPSIZE_END)
+               column = gtk.TreeViewColumn("Data")
+               column.set_resizable(True)
+               column.pack_start(cell, expand=True)
+               column.add_attribute(cell, "text", 2)
+               self.treeview.append_column(column)
+
+               # The commit message window
+               scrollwin = gtk.ScrolledWindow()
+               scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+               scrollwin.set_shadow_type(gtk.SHADOW_IN)
+               vpan.pack2(scrollwin, True, True);
+               scrollwin.show()
+
+               commit_text = gtk.TextView()
+               self.commit_buffer = gtk.TextBuffer()
+               commit_text.set_buffer(self.commit_buffer)
+               scrollwin.add(commit_text)
+               commit_text.show()
+
+               self.window.show()
+
+               self.add_file_data(filename, commit_sha1, line_num)
+
+               fp = os.popen("git blame --incremental -- " + filename + " " + commit_sha1)
+               flags = fcntl.fcntl(fp.fileno(), fcntl.F_GETFL)
+               fcntl.fcntl(fp.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK)
+               self.io_watch_tag = gobject.io_add_watch(fp, gobject.IO_IN, self.data_ready)
+
+
 class DiffWindow:
        """Diff window.
        This object represents and manages a single window containing the
@@ -355,6 +537,7 @@ class DiffWindow:
                height = int(monitor.height * 0.66)
                self.window.set_default_size(width, height)
 
+
                self.construct()
 
        def construct(self):
@@ -371,10 +554,12 @@ class DiffWindow:
                vbox.pack_start(menu_bar, expand=False, fill=True)
                menu_bar.show()
 
+               hpan = gtk.HPaned()
+
                scrollwin = gtk.ScrolledWindow()
                scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
                scrollwin.set_shadow_type(gtk.SHADOW_IN)
-               vbox.pack_start(scrollwin, expand=True, fill=True)
+               hpan.pack1(scrollwin, True, True)
                scrollwin.show()
 
                if have_gtksourceview:
@@ -388,11 +573,77 @@ class DiffWindow:
                        self.buffer = gtk.TextBuffer()
                        sourceview = gtk.TextView(self.buffer)
 
+
                sourceview.set_editable(False)
                sourceview.modify_font(pango.FontDescription("Monospace"))
                scrollwin.add(sourceview)
                sourceview.show()
 
+               # The file hierarchy: a scrollable treeview
+               scrollwin = gtk.ScrolledWindow()
+               scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+               scrollwin.set_shadow_type(gtk.SHADOW_IN)
+               scrollwin.set_size_request(20, -1)
+               hpan.pack2(scrollwin, True, True)
+               scrollwin.show()
+
+               self.model = gtk.TreeStore(str, str, str)
+               self.treeview = gtk.TreeView(self.model)
+               self.treeview.set_search_column(1)
+               self.treeview.connect("cursor-changed", self._treeview_clicked)
+               scrollwin.add(self.treeview)
+               self.treeview.show()
+
+               cell = gtk.CellRendererText()
+               cell.set_property("width-chars", 20)
+               column = gtk.TreeViewColumn("Select to annotate")
+               column.pack_start(cell, expand=True)
+               column.add_attribute(cell, "text", 0)
+               self.treeview.append_column(column)
+
+               vbox.pack_start(hpan, expand=True, fill=True)
+               hpan.show()
+
+       def _treeview_clicked(self, *args):
+               """Callback for when the treeview cursor changes."""
+               (path, col) = self.treeview.get_cursor()
+               specific_file = self.model[path][1]
+               commit_sha1 =  self.model[path][2]
+               if specific_file ==  None :
+                       return
+               elif specific_file ==  "" :
+                       specific_file =  None
+
+               window = AnnotateWindow();
+               window.annotate(specific_file, commit_sha1, 1)
+
+
+       def commit_files(self, commit_sha1, parent_sha1):
+               self.model.clear()
+               add  = self.model.append(None, [ "Added", None, None])
+               dele = self.model.append(None, [ "Deleted", None, None])
+               mod  = self.model.append(None, [ "Modified", None, None])
+               diff_tree = re.compile('^(:.{6}) (.{6}) (.{40}) (.{40}) (A|D|M)\s(.+)$')
+               fp = os.popen("git diff-tree -r --no-commit-id " + parent_sha1 + " " + commit_sha1)
+               while 1:
+                       line = string.strip(fp.readline())
+                       if line == '':
+                               break
+                       m = diff_tree.match(line)
+                       if not m:
+                               continue
+
+                       attr = m.group(5)
+                       filename = m.group(6)
+                       if attr == "A":
+                               self.model.append(add,  [filename, filename, commit_sha1])
+                       elif attr == "D":
+                               self.model.append(dele, [filename, filename, commit_sha1])
+                       elif attr == "M":
+                               self.model.append(mod,  [filename, filename, commit_sha1])
+               fp.close()
+
+               self.treeview.expand_all()
 
        def set_diff(self, commit_sha1, parent_sha1, encoding):
                """Set the differences showed by this window.
@@ -406,6 +657,7 @@ class DiffWindow:
                fp = os.popen("git diff-tree -p " + parent_sha1 + " " + commit_sha1)
                self.buffer.set_text(unicode(fp.read(), encoding).encode('utf-8'))
                fp.close()
+               self.commit_files(commit_sha1, parent_sha1)
                self.window.show()
 
        def save_menu_response(self, widget, string):
@@ -425,7 +677,7 @@ class DiffWindow:
 class GitView:
        """ This is the main class
        """
-       version = "0.8"
+       version = "0.9"
 
        def __init__(self, with_diff=0):
                self.with_diff = with_diff
@@ -590,7 +842,7 @@ class GitView:
                dialog = gtk.AboutDialog()
                dialog.set_name("Gitview")
                dialog.set_version(GitView.version)
-               dialog.set_authors(["Aneesh Kumar K.V <aneesh.kumar@hp.com>"])
+               dialog.set_authors(["Aneesh Kumar K.V <aneesh.kumar@gmail.com>"])
                dialog.set_website("http://www.kernel.org/pub/software/scm/git/")
                dialog.set_copyright("Use and distribute under the terms of the GNU General Public License")
                dialog.set_wrap_license(True)
diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid
new file mode 100644 (file)
index 0000000..5ee1835
--- /dev/null
@@ -0,0 +1,284 @@
+#!/usr/bin/perl
+
+use strict;
+use File::Spec;
+
+$ENV{PATH}     = '/opt/git/bin';
+my $acl_git    = '/vcs/acls.git';
+my $acl_branch = 'refs/heads/master';
+my $debug      = 0;
+
+=doc
+Invoked as: update refname old-sha1 new-sha1
+
+This script is run by git-receive-pack once for each ref that the
+client is trying to modify.  If we exit with a non-zero exit value
+then the update for that particular ref is denied, but updates for
+other refs in the same run of receive-pack may still be allowed.
+
+We are run after the objects have been uploaded, but before the
+ref is actually modified.  We take advantage of that fact when we
+look for "new" commits and tags (the new objects won't show up in
+`rev-list --all`).
+
+This script loads and parses the content of the config file
+"users/$this_user.acl" from the $acl_branch commit of $acl_git ODB.
+The acl file is a git-config style file, but uses a slightly more
+restricted syntax as the Perl parser contained within this script
+is not nearly as permissive as git-config.
+
+Example:
+
+  [user]
+    committer = John Doe <john.doe@example.com>
+    committer = John R. Doe <john.doe@example.com>
+
+  [repository "acls"]
+    allow = heads/master
+    allow = CDUR for heads/jd/
+    allow = C    for ^tags/v\\d+$
+
+For all new commit or tag objects the committer (or tagger) line
+within the object must exactly match one of the user.committer
+values listed in the acl file ("HEAD:users/$this_user.acl").
+
+For a branch to be modified an allow line within the matching
+repository section must be matched for both the refname and the
+opcode.
+
+Repository sections are matched on the basename of the repository
+(after removing the .git suffix).
+
+The opcode abbrevations are:
+
+  C: create new ref
+  D: delete existing ref
+  U: fast-forward existing ref (no commit loss)
+  R: rewind/rebase existing ref (commit loss)
+
+if no opcodes are listed before the "for" keyword then "U" (for
+fast-forward update only) is assumed as this is the most common
+usage.
+
+Refnames are matched by always assuming a prefix of "refs/".
+This hook forbids pushing or deleting anything not under "refs/".
+
+Refnames that start with ^ are Perl regular expressions, and the ^
+is kept as part of the regexp.  \\ is needed to get just one \, so
+\\d expands to \d in Perl.  The 3rd allow line above is an example.
+
+Refnames that don't start with ^ but that end with / are prefix
+matches (2nd allow line above); all other refnames are strict
+equality matches (1st allow line).
+
+Anything pushed to "heads/" (ok, really "refs/heads/") must be
+a commit.  Tags are not permitted here.
+
+Anything pushed to "tags/" (err, really "refs/tags/") must be an
+annotated tag.  Commits, blobs, trees, etc. are not permitted here.
+Annotated tag signatures aren't checked, nor are they required.
+
+The special subrepository of 'info/new-commit-check' can
+be created and used to allow users to push new commits and
+tags from another local repository to this one, even if they
+aren't the committer/tagger of those objects.  In a nut shell
+the info/new-commit-check directory is a Git repository whose
+objects/info/alternates file lists this repository and all other
+possible sources, and whose refs subdirectory contains symlinks
+to this repository's refs subdirectory, and to all other possible
+sources refs subdirectories.  Yes, this means that you cannot
+use packed-refs in those repositories as they won't be resolved
+correctly.
+
+=cut
+
+my $git_dir = $ENV{GIT_DIR};
+my $new_commit_check = "$git_dir/info/new-commit-check";
+my $ref = $ARGV[0];
+my $old = $ARGV[1];
+my $new = $ARGV[2];
+my $new_type;
+my ($this_user) = getpwuid $<; # REAL_USER_ID
+my $repository_name;
+my %user_committer;
+my @allow_rules;
+
+sub deny ($) {
+       print STDERR "-Deny-    $_[0]\n" if $debug;
+       print STDERR "\ndenied: $_[0]\n\n";
+       exit 1;
+}
+
+sub grant ($) {
+       print STDERR "-Grant-   $_[0]\n" if $debug;
+       exit 0;
+}
+
+sub info ($) {
+       print STDERR "-Info-    $_[0]\n" if $debug;
+}
+
+sub parse_config ($$) {
+       my ($data, $fn) = @_;
+       info "Loading $fn";
+       open(I,'-|','git',"--git-dir=$acl_git",'cat-file','blob',$fn);
+       my $section = '';
+       while (<I>) {
+               chomp;
+               if (/^\s*$/ || /^\s*#/) {
+               } elsif (/^\[([a-z]+)\]$/i) {
+                       $section = $1;
+               } elsif (/^\[([a-z]+)\s+"(.*)"\]$/i) {
+                       $section = "$1.$2";
+               } elsif (/^\s*([a-z][a-z0-9]+)\s*=\s*(.*?)\s*$/i) {
+                       push @{$data->{"$section.$1"}}, $2;
+               } else {
+                       deny "bad config file line $. in $fn";
+               }
+       }
+       close I;
+}
+
+sub all_new_committers () {
+       local $ENV{GIT_DIR} = $git_dir;
+       $ENV{GIT_DIR} = $new_commit_check if -d $new_commit_check;
+
+       info "Getting committers of new commits.";
+       my %used;
+       open(T,'-|','git','rev-list','--pretty=raw',$new,'--not','--all');
+       while (<T>) {
+               next unless s/^committer //;
+               chop;
+               s/>.*$/>/;
+               info "Found $_." unless $used{$_}++;
+       }
+       close T;
+       info "No new commits." unless %used;
+       keys %used;
+}
+
+sub all_new_taggers () {
+       my %exists;
+       open(T,'-|','git','for-each-ref','--format=%(objectname)','refs/tags');
+       while (<T>) {
+               chop;
+               $exists{$_} = 1;
+       }
+       close T;
+
+       info "Getting taggers of new tags.";
+       my %used;
+       my $obj = $new;
+       my $obj_type = $new_type;
+       while ($obj_type eq 'tag') {
+               last if $exists{$obj};
+               $obj_type = '';
+               open(T,'-|','git','cat-file','tag',$obj);
+               while (<T>) {
+                       chop;
+                       if (/^object ([a-z0-9]{40})$/) {
+                               $obj = $1;
+                       } elsif (/^type (.+)$/) {
+                               $obj_type = $1;
+                       } elsif (s/^tagger //) {
+                               s/>.*$/>/;
+                               info "Found $_." unless $used{$_}++;
+                               last;
+                       }
+               }
+               close T;
+       }
+       info "No new tags." unless %used;
+       keys %used;
+}
+
+sub check_committers (@) {
+       my @bad;
+       foreach (@_) { push @bad, $_ unless $user_committer{$_}; }
+       if (@bad) {
+               print STDERR "\n";
+               print STDERR "You are not $_.\n" foreach (sort @bad);
+               deny "You cannot push changes not committed by you.";
+       }
+}
+
+sub git_value (@) {
+       open(T,'-|','git',@_); local $_ = <T>; chop; close T;
+       $_;
+}
+
+deny "No GIT_DIR inherited from caller" unless $git_dir;
+deny "Need a ref name" unless $ref;
+deny "Refusing funny ref $ref" unless $ref =~ s,^refs/,,;
+deny "Bad old value $old" unless $old =~ /^[a-z0-9]{40}$/;
+deny "Bad new value $new" unless $new =~ /^[a-z0-9]{40}$/;
+deny "Cannot determine who you are." unless $this_user;
+
+$repository_name = File::Spec->rel2abs($git_dir);
+$repository_name =~ m,/([^/]+)(?:\.git|/\.git)$,;
+$repository_name = $1;
+info "Updating in '$repository_name'.";
+
+my $op;
+if    ($old =~ /^0{40}$/) { $op = 'C'; }
+elsif ($new =~ /^0{40}$/) { $op = 'D'; }
+else                      { $op = 'R'; }
+
+# This is really an update (fast-forward) if the
+# merge base of $old and $new is $old.
+#
+$op = 'U' if ($op eq 'R'
+       && $ref =~ m,^heads/,
+       && $old eq git_value('merge-base',$old,$new));
+
+# Load the user's ACL file.
+{
+       my %data = ('user.committer' => []);
+       parse_config(\%data, "$acl_branch:users/$this_user.acl");
+       %user_committer = map {$_ => $_} @{$data{'user.committer'}};
+       my $rules = $data{"repository.$repository_name.allow"} || [];
+       foreach (@$rules) {
+               if (/^([CDRU ]+)\s+for\s+([^\s]+)$/) {
+                       my $ops = $1;
+                       my $ref = $2;
+                       $ops =~ s/ //g;
+                       $ref =~ s/\\\\/\\/g;
+                       push @allow_rules, [$ops, $ref];
+               } elsif (/^for\s+([^\s]+)$/) {
+                       # Mentioned, but nothing granted?
+               } elsif (/^[^\s]+$/) {
+                       s/\\\\/\\/g;
+                       push @allow_rules, ['U', $_];
+               }
+       }
+}
+
+if ($op ne 'D') {
+       $new_type = git_value('cat-file','-t',$new);
+
+       if ($ref =~ m,^heads/,) {
+               deny "$ref must be a commit." unless $new_type eq 'commit';
+       } elsif ($ref =~ m,^tags/,) {
+               deny "$ref must be an annotated tag." unless $new_type eq 'tag';
+       }
+
+       check_committers (all_new_committers);
+       check_committers (all_new_taggers) if $new_type eq 'tag';
+}
+
+info "$this_user wants $op for $ref";
+foreach my $acl_entry (@allow_rules) {
+       my ($acl_ops, $acl_n) = @$acl_entry;
+       next unless $acl_ops =~ /^[CDRU]+$/; # Uhh.... shouldn't happen.
+       next unless $acl_n;
+       next unless $op =~ /^[$acl_ops]$/;
+
+       grant "Allowed by: $acl_ops for $acl_n"
+       if (
+          ($acl_n eq $ref)
+       || ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n)
+       || ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:)
+       );
+}
+close A;
+deny "You are not permitted to $op $ref";
index 4809f9199fa21dcd95ab508e26196080d49e8e88..cefbcebdcaa0a085745c81b2ed1103e9575cb635 100644 (file)
@@ -88,7 +88,7 @@ static int write_subdirectory(void *buffer, unsigned long size, const char *base
                unsigned int mode;
                char *slash, *origpath;
 
-               if (!path || sscanf(buffer, "%o", &mode) != 1)
+               if (!path || strtoul_ui(buffer, 8, &mode))
                        die("bad tree conversion");
                mode = convert_mode(mode);
                path++;
diff --git a/decorate.c b/decorate.c
new file mode 100644 (file)
index 0000000..23f6b00
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * decorate.c - decorate a git object with some arbitrary
+ * data.
+ */
+#include "cache.h"
+#include "object.h"
+#include "decorate.h"
+
+static unsigned int hash_obj(struct object *obj, unsigned int n)
+{
+       unsigned int hash = *(unsigned int *)obj->sha1;
+       return hash % n;
+}
+
+static void *insert_decoration(struct decoration *n, struct object *base, void *decoration)
+{
+       int size = n->size;
+       struct object_decoration *hash = n->hash;
+       int j = hash_obj(base, size);
+
+       while (hash[j].base) {
+               if (hash[j].base == base) {
+                       void *old = hash[j].decoration;
+                       hash[j].decoration = decoration;
+                       return old;
+               }
+               if (++j >= size)
+                       j = 0;
+       }
+       hash[j].base = base;
+       hash[j].decoration = decoration;
+       n->nr++;
+       return NULL;
+}
+
+static void grow_decoration(struct decoration *n)
+{
+       int i;
+       int old_size = n->size;
+       struct object_decoration *old_hash;
+
+       old_size = n->size;
+       old_hash = n->hash;
+
+       n->size = (old_size + 1000) * 3 / 2;
+       n->hash = xcalloc(n->size, sizeof(struct object_decoration));
+       n->nr = 0;
+
+       for (i = 0; i < old_size; i++) {
+               struct object *base = old_hash[i].base;
+               void *decoration = old_hash[i].decoration;
+
+               if (!base)
+                       continue;
+               insert_decoration(n, base, decoration);
+       }
+       free(old_hash);
+}
+
+/* Add a decoration pointer, return any old one */
+void *add_decoration(struct decoration *n, struct object *obj, void *decoration)
+{
+       int nr = n->nr + 1;
+
+       if (nr > n->size * 2 / 3)
+               grow_decoration(n);
+       return insert_decoration(n, obj, decoration);
+}
+
+/* Lookup a decoration pointer */
+void *lookup_decoration(struct decoration *n, struct object *obj)
+{
+       int j;
+
+       /* nothing to lookup */
+       if (!n->size)
+               return NULL;
+       j = hash_obj(obj, n->size);
+       for (;;) {
+               struct object_decoration *ref = n->hash + j;
+               if (ref->base == obj)
+                       return ref->decoration;
+               if (!ref->base)
+                       return NULL;
+               if (++j == n->size)
+                       j = 0;
+       }
+}
diff --git a/decorate.h b/decorate.h
new file mode 100644 (file)
index 0000000..1fa4ad9
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef DECORATE_H
+#define DECORATE_H
+
+struct object_decoration {
+       struct object *base;
+       void *decoration;
+};
+
+struct decoration {
+       const char *name;
+       unsigned int size, nr;
+       struct object_decoration *hash;
+};
+
+extern void *add_decoration(struct decoration *n, struct object *obj, void *decoration);
+extern void *lookup_decoration(struct decoration *n, struct object *obj);
+
+#endif
index 5c5b05bfe32bc90484b5bd6a9c171e0f9b04fbd6..7531e20c784c44c0b5d3ecb2057638874a09ce6c 100644 (file)
@@ -142,18 +142,34 @@ static int queue_diff(struct diff_options *o,
        }
 }
 
+/*
+ * Does the path name a blob in the working tree, or a directory
+ * in the working tree?
+ */
 static int is_in_index(const char *path)
 {
-       int len = strlen(path);
-       int pos = cache_name_pos(path, len);
-       char c;
-
-       if (pos < 0)
-               return 0;
-       if (strncmp(active_cache[pos]->name, path, len))
-               return 0;
-       c = active_cache[pos]->name[len];
-       return c == '\0' || c == '/';
+       int len, pos;
+       struct cache_entry *ce;
+
+       len = strlen(path);
+       while (path[len-1] == '/')
+               len--;
+       if (!len)
+               return 1; /* "." */
+       pos = cache_name_pos(path, len);
+       if (0 <= pos)
+               return 1;
+       pos = -1 - pos;
+       while (pos < active_nr) {
+               ce = active_cache[pos++];
+               if (ce_namelen(ce) <= len ||
+                   strncmp(ce->name, path, len) ||
+                   (ce->name[len] > '/'))
+                       break; /* path cannot be a prefix */
+               if (ce->name[len] == '/')
+                       return 1;
+       }
+       return 0;
 }
 
 static int handle_diff_files_args(struct rev_info *revs,
index 85c374e21e7076cae8f186afe899437bda1ac770..1cd456173dc386528cbbbca35e327badf97a25e2 100755 (executable)
@@ -116,10 +116,7 @@ bisect_start() {
         done
 
        sq "$@" >"$GIT_DIR/BISECT_NAMES"
-       {
-           printf "git-bisect start"
-           echo "$orig_args"
-       } >>"$GIT_DIR/BISECT_LOG"
+       echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG"
        bisect_auto_next
 }
 
index 513b574d13858f1a81e6f66251890d81bf0e55ce..cad5c0c088fa997c6f8f36c44e8888a5cb19515e 100755 (executable)
@@ -60,7 +60,7 @@ Perhaps git-update-server-info needs to be run there?"
                else
                        tname=$name
                fi
-               git-http-fetch $v -a -w "$tname" "$name" "$1" || exit 1
+               git-http-fetch $v -a -w "$tname" "$sha1" "$1" || exit 1
        done <"$clone_tmp/refs"
        rm -fr "$clone_tmp"
        http_fetch "$1/HEAD" "$GIT_DIR/REMOTE_HEAD" ||
index 9e0959aec06c4d4d7b4f14660c26bc9dd941f111..f28fc242241d3f0f5c88b287da0c59417667a013 100755 (executable)
@@ -649,8 +649,9 @@ then
        fi
        if test -z "$quiet"
        then
+               commit=`git-diff-tree --always --shortstat --pretty="format:%h: %s"\
+                      --summary --root HEAD --`
                echo "Created${initial_commit:+ initial} commit $commit"
-               git-diff-tree --shortstat --summary --root --no-commit-id HEAD --
        fi
 fi
 
index 139fc19108cab51eb23659868ef8600ff6b25795..5f6a281b78245bbb4e1e480d81cfb8c1b4cfa6ac 100644 (file)
@@ -301,4 +301,17 @@ static inline int prefixcmp(const char *str, const char *prefix)
        return strncmp(str, prefix, strlen(prefix));
 }
 
+static inline int strtoul_ui(char const *s, int base, unsigned int *result)
+{
+       unsigned long ul;
+       char *p;
+
+       errno = 0;
+       ul = strtoul(s, &p, base);
+       if (errno || *p || p == s || (unsigned int) ul != ul)
+               return -1;
+       *result = ul;
+       return 0;
+}
+
 #endif
index 67224b44497715edc07df9d7df469339caa48e51..6ed471918df25006314f3f2c6177bdd018757068 100755 (executable)
 `git-diff-tree --binary -p $parent $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
 
 ## apply non-binary changes
-my $fuzz = $opt_p ? 0 : 2;
+
+# In pedantic mode require all lines of context to match.  In normal
+# mode, be compatible with diff/patch: assume 3 lines of context and
+# require at least one line match, i.e. ignore at most 2 lines of
+# context, like diff/patch do by default.
+my $context = $opt_p ? '' : '-C1';
 
 print "Checking if patch will apply\n";
 
 my @stat;
-open APPLY, "GIT_DIR= git-apply -C$fuzz --binary --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch";
+open APPLY, "GIT_DIR= git-apply $context --binary --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch";
 @stat=<APPLY>;
 close APPLY || die "Cannot patch";
 my (@bfiles,@files,@afiles,@dfiles);
 }
 
 print "Applying\n";
-`GIT_DIR= git-apply -C$fuzz --binary --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
+`GIT_DIR= git-apply $context --binary --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
 
 print "Patch applied successfully. Adding new files and directories to CVS\n";
 my $dirtypatch = 0;
index 68aa75255e21ff8231fe2f54e5772f8cbb026fce..087e3abaefd54c8a950ce11e889757e48c428808 100755 (executable)
@@ -91,7 +91,9 @@
 # if we are called with a pserver argument,
 # deal with the authentication cat before entering the
 # main loop
+$state->{method} = 'ext';
 if (@ARGV && $ARGV[0] eq 'pserver') {
+    $state->{method} = 'pserver';
     my $line = <STDIN>; chomp $line;
     unless( $line eq 'BEGIN AUTH REQUEST') {
        die "E Do not understand $line - expecting BEGIN AUTH REQUEST\n";
@@ -181,11 +183,18 @@ sub req_Root
     }
     foreach my $line ( @gitvars )
     {
-        next unless ( $line =~ /^(.*?)\.(.*?)=(.*)$/ );
-        $cfg->{$1}{$2} = $3;
+        next unless ( $line =~ /^(.*?)\.(.*?)(?:\.(.*?))?=(.*)$/ );
+        unless ($3) {
+            $cfg->{$1}{$2} = $4;
+        } else {
+            $cfg->{$1}{$2}{$3} = $4;
+        }
     }
 
-    unless ( defined ( $cfg->{gitcvs}{enabled} ) and $cfg->{gitcvs}{enabled} =~ /^\s*(1|true|yes)\s*$/i )
+    unless ( ($cfg->{gitcvs}{$state->{method}}{enabled}
+             and $cfg->{gitcvs}{$state->{method}}{enabled} =~ /^\s*(1|true|yes)\s*$/i)
+            or ($cfg->{gitcvs}{enabled}
+             and $cfg->{gitcvs}{enabled} =~ /^\s*(1|true|yes)\s*$/i) )
     {
         print "E GITCVS emulation needs to be enabled on this repo\n";
         print "E the repo config file needs a [gitcvs] section added, and the parameter 'enabled' set to 1\n";
@@ -194,9 +203,10 @@ sub req_Root
         return 0;
     }
 
-    if ( defined ( $cfg->{gitcvs}{logfile} ) )
+    my $logfile = $cfg->{gitcvs}{$state->{method}}{logfile} || $cfg->{gitcvs}{logfile};
+    if ( $logfile )
     {
-        $log->setfile($cfg->{gitcvs}{logfile});
+        $log->setfile($logfile);
     } else {
         $log->nofile();
     }
@@ -350,12 +360,52 @@ sub req_add
 
     argsplit("add");
 
+    my $updater = GITCVS::updater->new($state->{CVSROOT}, $state->{module}, $log);
+    $updater->update();
+
+    argsfromdir($updater);
+
     my $addcount = 0;
 
     foreach my $filename ( @{$state->{args}} )
     {
         $filename = filecleanup($filename);
 
+        my $meta = $updater->getmeta($filename);
+        my $wrev = revparse($filename);
+
+        if ($wrev && $meta && ($wrev < 0))
+        {
+            # previously removed file, add back
+            $log->info("added file $filename was previously removed, send 1.$meta->{revision}");
+
+            print "MT +updated\n";
+            print "MT text U \n";
+            print "MT fname $filename\n";
+            print "MT newline\n";
+            print "MT -updated\n";
+
+            unless ( $state->{globaloptions}{-n} )
+            {
+                my ( $filepart, $dirpart ) = filenamesplit($filename,1);
+
+                print "Created $dirpart\n";
+                print $state->{CVSROOT} . "/$state->{module}/$filename\n";
+
+                # this is an "entries" line
+                my $kopts = kopts_from_path($filepart);
+                $log->debug("/$filepart/1.$meta->{revision}//$kopts/");
+                print "/$filepart/1.$meta->{revision}//$kopts/\n";
+                # permissions
+                $log->debug("SEND : u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}");
+                print "u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}\n";
+                # transmit file
+                transmitfile($meta->{filehash});
+            }
+
+            next;
+        }
+
         unless ( defined ( $state->{entries}{$filename}{modified_filename} ) )
         {
             print "E cvs add: nothing known about `$filename'\n";
@@ -843,6 +893,7 @@ sub req_update
         if ( defined ( $wrev )
              and defined($meta->{revision})
              and $wrev == $meta->{revision}
+             and defined($state->{entries}{$filename}{modified_hash})
              and not exists ( $state->{opt}{C} ) )
         {
             $log->info("Tell the client the file is modified");
@@ -1026,7 +1077,7 @@ sub req_ci
 
     $log->info("req_ci : " . ( defined($data) ? $data : "[NULL]" ));
 
-    if ( @ARGV && $ARGV[0] eq 'pserver')
+    if ( $state->{method} eq 'pserver')
     {
         print "error 1 pserver access cannot commit\n";
         exit;
@@ -2131,25 +2182,40 @@ sub new
 
     bless $self, $class;
 
-    $self->{dbdir} = $config . "/";
-    die "Database dir '$self->{dbdir}' isn't a directory" unless ( defined($self->{dbdir}) and -d $self->{dbdir} );
-
     $self->{module} = $module;
-    $self->{file} = $self->{dbdir} . "/gitcvs.$module.sqlite";
-
     $self->{git_path} = $config . "/";
 
     $self->{log} = $log;
 
     die "Git repo '$self->{git_path}' doesn't exist" unless ( -d $self->{git_path} );
 
-    $self->{dbh} = DBI->connect("dbi:SQLite:dbname=" . $self->{file},"","");
+    $self->{dbdriver} = $cfg->{gitcvs}{$state->{method}}{dbdriver} ||
+        $cfg->{gitcvs}{dbdriver} || "SQLite";
+    $self->{dbname} = $cfg->{gitcvs}{$state->{method}}{dbname} ||
+        $cfg->{gitcvs}{dbname} || "%Ggitcvs.%m.sqlite";
+    $self->{dbuser} = $cfg->{gitcvs}{$state->{method}}{dbuser} ||
+        $cfg->{gitcvs}{dbuser} || "";
+    $self->{dbpass} = $cfg->{gitcvs}{$state->{method}}{dbpass} ||
+        $cfg->{gitcvs}{dbpass} || "";
+    my %mapping = ( m => $module,
+                    a => $state->{method},
+                    u => getlogin || getpwuid($<) || $<,
+                    G => $self->{git_path},
+                    g => mangle_dirname($self->{git_path}),
+                    );
+    $self->{dbname} =~ s/%([mauGg])/$mapping{$1}/eg;
+    $self->{dbuser} =~ s/%([mauGg])/$mapping{$1}/eg;
+
+    die "Invalid char ':' in dbdriver" if $self->{dbdriver} =~ /:/;
+    die "Invalid char ';' in dbname" if $self->{dbname} =~ /;/;
+    $self->{dbh} = DBI->connect("dbi:$self->{dbdriver}:dbname=$self->{dbname}",
+                                $self->{dbuser},
+                                $self->{dbpass});
+    die "Error connecting to database\n" unless defined $self->{dbh};
 
     $self->{tables} = {};
-    foreach my $table ( $self->{dbh}->tables )
+    foreach my $table ( keys %{$self->{dbh}->table_info(undef,undef,undef,'TABLE')->fetchall_hashref('TABLE_NAME')} )
     {
-        $table =~ s/^"//;
-        $table =~ s/"$//;
         $self->{tables}{$table} = 1;
     }
 
@@ -2847,5 +2913,19 @@ sub safe_pipe_capture {
     return wantarray ? @output : join('',@output);
 }
 
+=head2 mangle_dirname
+
+create a string from a directory name that is suitable to use as
+part of a filename, mainly by converting all chars except \w.- to _
+
+=cut
+sub mangle_dirname {
+    my $dirname = shift;
+    return unless defined $dirname;
+
+    $dirname =~ s/[^\w.-]/_/g;
+
+    return $dirname;
+}
 
 1;
index b82789ead6255b33be0f1ed2029a91611cea3072..b29d7d1e68d2489fd07612c8d0062d3491f04719 100644 (file)
@@ -28,6 +28,8 @@ ifndef V
        QUIET_BUILT_IN = @echo '   ' BUILTIN $@;
 endif
 
+TCLTK_PATH ?= wish
+
 ifeq ($(findstring $(MAKEFLAGS),s),s)
 QUIET_GEN =
 QUIET_BUILT_IN =
@@ -36,10 +38,12 @@ endif
 DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
 gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
 
 $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
        $(QUIET_GEN)rm -f $@ $@+ && \
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+               -e 's|^exec wish "$$0"|exec $(subst |,'\|',$(TCLTK_PATH_SQ)) "$$0"|' \
                -e 's/@@GITGUI_VERSION@@/$(GITGUI_VERSION)/g' \
                $@.sh >$@+ && \
        chmod +x $@+ && \
index 60e79ca1b01bc8b057abe17ddab484699a7f5fdb..94067cc5f73388f33722d52ae02f44692bc07490 100755 (executable)
@@ -242,6 +242,8 @@ proc error_popup {msg} {
        if {[reponame] ne {}} {
                append title " ([reponame])"
        }
+       option add *Dialog.msg.font font_ui
+       option add *Button.font font_ui
        set cmd [list tk_messageBox \
                -icon error \
                -type ok \
@@ -258,6 +260,8 @@ proc warn_popup {msg} {
        if {[reponame] ne {}} {
                append title " ([reponame])"
        }
+       option add *Dialog.msg.font font_ui
+       option add *Button.font font_ui
        set cmd [list tk_messageBox \
                -icon warning \
                -type ok \
@@ -274,6 +278,8 @@ proc info_popup {msg {parent .}} {
        if {[reponame] ne {}} {
                append title " ([reponame])"
        }
+       option add *Dialog.msg.font font_ui
+       option add *Button.font font_ui
        tk_messageBox \
                -parent $parent \
                -icon info \
@@ -287,6 +293,8 @@ proc ask_popup {msg} {
        if {[reponame] ne {}} {
                append title " ([reponame])"
        }
+       option add *Dialog.msg.font font_ui
+       option add *Button.font font_ui
        return [tk_messageBox \
                -parent . \
                -icon question \
@@ -727,12 +735,9 @@ proc handle_empty_diff {} {
 
 [short_path $path] has no changes.
 
-The modification date of this file was updated
-by another application, but the content within
-the file was not changed.
+The modification date of this file was updated by another application, but the content within the file was not changed.
 
-A rescan will be automatically started to find
-other files which may have the same state."
+A rescan will be automatically started to find other files which may have the same state."
 
        clear_diff
        display_file $path __
@@ -1033,8 +1038,7 @@ proc load_last_commit {} {
        if {[llength $PARENT] == 0} {
                error_popup {There is nothing to amend.
 
-You are about to create the initial commit.
-There is no commit before this to amend.
+You are about to create the initial commit.  There is no commit before this to amend.
 }
                return
        }
@@ -1043,10 +1047,7 @@ There is no commit before this to amend.
        if {$curType eq {merge}} {
                error_popup {Cannot amend while merging.
 
-You are currently in the middle of a merge that
-has not been fully completed.  You cannot amend
-the prior commit unless you first abort the
-current merge activity.
+You are currently in the middle of a merge that has not been fully completed.  You cannot amend the prior commit unless you first abort the current merge activity.
 }
                return
        }
@@ -1136,9 +1137,7 @@ proc commit_tree {} {
        } elseif {$commit_type ne $curType || $HEAD ne $curHEAD} {
                info_popup {Last scanned state does not match repository state.
 
-Another Git program has modified this repository
-since the last scan.  A rescan must be performed
-before another commit can be created.
+Another Git program has modified this repository since the last scan.  A rescan must be performed before another commit can be created.
 
 The rescan will be automatically started now.
 }
@@ -1159,8 +1158,7 @@ The rescan will be automatically started now.
                U? {
                        error_popup "Unmerged files cannot be committed.
 
-File [short_path $path] has merge conflicts.
-You must resolve them and add the file before committing.
+File [short_path $path] has merge conflicts.  You must resolve them and add the file before committing.
 "
                        unlock_index
                        return
@@ -1276,8 +1274,7 @@ proc commit_committree {fd_wt curHEAD msg} {
                if {$tree_id eq $old_tree} {
                        info_popup {No changes to commit.
 
-No files were modified by this commit and it
-was not a merge commit.
+No files were modified by this commit and it was not a merge commit.
 
 A rescan will be automatically started now.
 }
@@ -2116,7 +2113,10 @@ proc do_create_branch {} {
                -value head \
                -variable create_branch_revtype \
                -font font_ui
-       eval tk_optionMenu $w.from.head_m create_branch_head $all_heads
+       set lbranchm [eval tk_optionMenu $w.from.head_m create_branch_head \
+               $all_heads]
+       $lbranchm configure -font font_ui
+       $w.from.head_m configure -font font_ui
        grid $w.from.head_r $w.from.head_m -sticky w
        set all_trackings [all_tracking_branches]
        if {$all_trackings ne {}} {
@@ -2126,9 +2126,11 @@ proc do_create_branch {} {
                        -value tracking \
                        -variable create_branch_revtype \
                        -font font_ui
-               eval tk_optionMenu $w.from.tracking_m \
+               set tbranchm [eval tk_optionMenu $w.from.tracking_m \
                        create_branch_trackinghead \
-                       $all_trackings
+                       $all_trackings]
+               $tbranchm configure -font font_ui
+               $w.from.tracking_m configure -font font_ui
                grid $w.from.tracking_r $w.from.tracking_m -sticky w
        }
        set all_tags [load_all_tags]
@@ -2139,9 +2141,11 @@ proc do_create_branch {} {
                        -value tag \
                        -variable create_branch_revtype \
                        -font font_ui
-               eval tk_optionMenu $w.from.tag_m \
+               set tagsm [eval tk_optionMenu $w.from.tag_m \
                        create_branch_tag \
-                       $all_tags
+                       $all_tags]
+               $tagsm configure -font font_ui
+               $w.from.tag_m configure -font font_ui
                grid $w.from.tag_r $w.from.tag_m -sticky w
        }
        radiobutton $w.from.exp_r \
@@ -2335,7 +2339,11 @@ proc do_delete_branch {} {
                -value head \
                -variable delete_branch_checktype \
                -font font_ui
-       eval tk_optionMenu $w.validate.head_m delete_branch_head $all_heads
+       set mergedlocalm [eval tk_optionMenu $w.validate.head_m \
+               delete_branch_head \
+               $all_heads]
+       $mergedlocalm configure -font font_ui
+       $w.validate.head_m configure -font font_ui
        grid $w.validate.head_r $w.validate.head_m -sticky w
        set all_trackings [all_tracking_branches]
        if {$all_trackings ne {}} {
@@ -2345,9 +2353,11 @@ proc do_delete_branch {} {
                        -value tracking \
                        -variable delete_branch_checktype \
                        -font font_ui
-               eval tk_optionMenu $w.validate.tracking_m \
+               set mergedtrackm [eval tk_optionMenu $w.validate.tracking_m \
                        delete_branch_trackinghead \
-                       $all_trackings
+                       $all_trackings]
+               $mergedtrackm configure -font font_ui
+               $w.validate.tracking_m configure -font font_ui
                grid $w.validate.tracking_r $w.validate.tracking_m -sticky w
        }
        radiobutton $w.validate.always_r \
@@ -2382,9 +2392,7 @@ proc switch_branch {new_branch} {
        } elseif {$commit_type ne $curType || $HEAD ne $curHEAD} {
                info_popup {Last scanned state does not match repository state.
 
-Another Git program has modified this repository
-since the last scan.  A rescan must be performed
-before the current branch can be changed.
+Another Git program has modified this repository since the last scan.  A rescan must be performed before the current branch can be changed.
 
 The rescan will be automatically started now.
 }
@@ -2475,12 +2483,9 @@ Staying on branch '$current_branch'."
        if {[catch {git symbolic-ref HEAD "refs/heads/$new_branch"} err]} {
                error_popup "Failed to set current branch.
 
-This working directory is only partially switched.
-We successfully updated your files, but failed to
-update an internal Git file.
+This working directory is only partially switched.  We successfully updated your files, but failed to update an internal Git file.
 
-This should not have occurred.  [appname] will now
-close and give up.
+This should not have occurred.  [appname] will now close and give up.
 
 $err"
                do_quit
@@ -2684,10 +2689,12 @@ proc do_push_anywhere {} {
        frame $w.buttons
        button $w.buttons.create -text Push \
                -font font_ui \
+               -default active \
                -command [list start_push_anywhere_action $w]
        pack $w.buttons.create -side right
        button $w.buttons.cancel -text {Cancel} \
                -font font_ui \
+               -default normal \
                -command [list destroy $w]
        pack $w.buttons.cancel -side right -padx 5
        pack $w.buttons -side bottom -fill x -pady 10 -padx 10
@@ -2721,7 +2728,10 @@ proc do_push_anywhere {} {
                        -value remote \
                        -variable push_urltype \
                        -font font_ui
-               eval tk_optionMenu $w.dest.remote_m push_remote $all_remotes
+               set remmenu [eval tk_optionMenu $w.dest.remote_m push_remote \
+                       $all_remotes]
+               $remmenu configure -font font_ui
+               $w.dest.remote_m configure -font font_ui
                grid $w.dest.remote_r $w.dest.remote_m -sticky w
                if {[lsearch -sorted -exact $all_remotes origin] != -1} {
                        set push_remote origin
@@ -2775,8 +2785,9 @@ proc do_push_anywhere {} {
        set push_thin 0
        set push_tags 0
 
-       bind $w <Visibility> "grab $w"
+       bind $w <Visibility> "grab $w; focus $w.buttons.create"
        bind $w <Key-Escape> "destroy $w"
+       bind $w <Key-Return> [list start_push_anywhere_action $w]
        wm title $w "[appname] ([reponame]): Push"
        tkwait window $w
 }
@@ -2791,8 +2802,7 @@ proc can_merge {} {
        if {[string match amend* $commit_type]} {
                info_popup {Cannot merge while amending.
 
-You must finish amending this commit before
-starting any type of merge.
+You must finish amending this commit before starting any type of merge.
 }
                return 0
        }
@@ -2806,9 +2816,7 @@ starting any type of merge.
        if {$commit_type ne $curType || $HEAD ne $curHEAD} {
                info_popup {Last scanned state does not match repository state.
 
-Another Git program has modified this repository
-since the last scan.  A rescan must be performed
-before a merge can be performed.
+Another Git program has modified this repository since the last scan.  A rescan must be performed before a merge can be performed.
 
 The rescan will be automatically started now.
 }
@@ -2827,9 +2835,7 @@ The rescan will be automatically started now.
 
 File [short_path $path] has merge conflicts.
 
-You must resolve them, add the file, and commit to
-complete the current merge.  Only then can you
-begin another merge.
+You must resolve them, add the file, and commit to complete the current merge.  Only then can you begin another merge.
 "
                        unlock_index
                        return 0
@@ -2839,9 +2845,7 @@ begin another merge.
 
 File [short_path $path] is modified.
 
-You should complete the current commit before
-starting a merge.  Doing so will help you abort
-a failed merge, should the need arise.
+You should complete the current commit before starting a merge.  Doing so will help you abort a failed merge, should the need arise.
 "
                        unlock_index
                        return 0
@@ -2917,13 +2921,11 @@ proc finish_merge {revcnt w ok} {
 
 Your merge of $revcnt branches has failed.
 
-There are file-level conflicts between the
-branches which must be resolved manually.
+There are file-level conflicts between the branches which must be resolved manually.
 
 The working directory will now be reset.
 
-You can attempt this merge again
-by merging only one branch at a time." $w
+You can attempt this merge again by merging only one branch at a time." $w
 
                        set fd [open "| git read-tree --reset -u HEAD" r]
                        fconfigure $fd -blocking 0 -translation binary
@@ -3036,8 +3038,7 @@ You must finish amending this commit.
 
        if {[ask_popup "Abort $op?
 
-Aborting the current $op will cause
-*ALL* uncommitted changes to be lost.
+Aborting the current $op will cause *ALL* uncommitted changes to be lost.
 
 Continue with aborting the current $op?"] eq {yes}} {
                set fd [open "| git read-tree --reset -u HEAD" r]
@@ -3604,12 +3605,14 @@ proc read_blame_incremental {fd w w_load w_cmit w_line w_file} {
 proc blame_incremental_status {w} {
        global blame_status blame_data
 
+       set have  $blame_data($w,blame_lines)
+       set total $blame_data($w,total_lines)
+       set pdone 0
+       if {$total} {set pdone [expr {100 * $have / $total}]}
+
        set blame_status($w) [format \
                "Loading annotations... %i of %i lines annotated (%2i%%)" \
-               $blame_data($w,blame_lines) \
-               $blame_data($w,total_lines) \
-               [expr {100 * $blame_data($w,blame_lines)
-                       / $blame_data($w,total_lines)}]]
+               $have $total $pdone]
 }
 
 proc blame_click {w w_cmit w_line w_file cur_w pos} {
@@ -4107,6 +4110,7 @@ proc console_done {args} {
                if {[winfo exists $w]} {
                        $w.m.s conf -background green -text {Success}
                        $w.ok conf -state normal
+                       focus $w.ok
                }
        } else {
                if {![winfo exists $w]} {
@@ -4114,6 +4118,7 @@ proc console_done {args} {
                }
                $w.m.s conf -background red -text {Error: Command Failed}
                $w.ok conf -state normal
+               focus $w.ok
        }
 
        array unset console_cr $w
@@ -4181,9 +4186,11 @@ proc do_stats {} {
        frame $w.buttons -border 1
        button $w.buttons.close -text Close \
                -font font_ui \
+               -default active \
                -command [list destroy $w]
        button $w.buttons.gc -text {Compress Database} \
                -font font_ui \
+               -default normal \
                -command "destroy $w;do_gc"
        pack $w.buttons.close -side right
        pack $w.buttons.gc -side left
@@ -4212,7 +4219,7 @@ proc do_stats {} {
        }
        pack $w.stat -pady 10 -padx 10
 
-       bind $w <Visibility> "grab $w; focus $w"
+       bind $w <Visibility> "grab $w; focus $w.buttons.close"
        bind $w <Key-Escape> [list destroy $w]
        bind $w <Key-Return> [list destroy $w]
        wm title $w "[appname] ([reponame]): Database Statistics"
@@ -4509,6 +4516,7 @@ proc do_about {} {
        frame $w.buttons
        button $w.buttons.close -text {Close} \
                -font font_ui \
+               -default active \
                -command [list destroy $w]
        pack $w.buttons.close -side right
        pack $w.buttons -side bottom -fill x -pady 10 -padx 10
@@ -4554,8 +4562,9 @@ $copyright" \
                clipboard append -format STRING -type STRING -- \[$w.vers cget -text\]
        "
 
-       bind $w <Visibility> "grab $w; focus $w"
+       bind $w <Visibility> "grab $w; focus $w.buttons.close"
        bind $w <Key-Escape> "destroy $w"
+       bind $w <Key-Return> "destroy $w"
        bind_button3 $w.vers "tk_popup $w.ctxm %X %Y; grab $w; focus $w"
        wm title $w "About [appname]"
        tkwait window $w
@@ -4592,14 +4601,17 @@ proc do_options {} {
        frame $w.buttons
        button $w.buttons.restore -text {Restore Defaults} \
                -font font_ui \
+               -default normal \
                -command do_restore_defaults
        pack $w.buttons.restore -side left
        button $w.buttons.save -text Save \
                -font font_ui \
+               -default active \
                -command [list do_save_config $w]
        pack $w.buttons.save -side right
        button $w.buttons.cancel -text {Cancel} \
                -font font_ui \
+               -default normal \
                -command [list destroy $w]
        pack $w.buttons.cancel -side right -padx 5
        pack $w.buttons -side bottom -fill x -pady 10 -padx 10
@@ -4686,9 +4698,11 @@ proc do_options {} {
                frame $w.global.$name
                label $w.global.$name.l -text "$text:" -font font_ui
                pack $w.global.$name.l -side left -anchor w -fill x
-               eval tk_optionMenu $w.global.$name.family \
+               set fontmenu [eval tk_optionMenu $w.global.$name.family \
                        global_config_new(gui.$font^^family) \
-                       $all_fonts
+                       $all_fonts]
+               $w.global.$name.family configure -font font_ui
+               $fontmenu configure -font font_ui
                spinbox $w.global.$name.size \
                        -textvariable global_config_new(gui.$font^^size) \
                        -from 2 -to 80 -increment 1 \
@@ -4700,8 +4714,9 @@ proc do_options {} {
                pack $w.global.$name -side top -anchor w -fill x
        }
 
-       bind $w <Visibility> "grab $w; focus $w"
+       bind $w <Visibility> "grab $w; focus $w.buttons.save"
        bind $w <Key-Escape> "destroy $w"
+       bind $w <Key-Return> [list do_save_config $w]
        wm title $w "[appname] ([reponame]): Options"
        tkwait window $w
 }
@@ -5083,18 +5098,18 @@ set ui_comm {}
 # -- Menu Bar
 #
 menu .mbar -tearoff 0
-.mbar add cascade -label Repository -menu .mbar.repository
-.mbar add cascade -label Edit -menu .mbar.edit
+.mbar add cascade -label Repository -menu .mbar.repository -font font_ui
+.mbar add cascade -label Edit -menu .mbar.edit -font font_ui
 if {[is_enabled branch]} {
-       .mbar add cascade -label Branch -menu .mbar.branch
+       .mbar add cascade -label Branch -menu .mbar.branch -font font_ui
 }
 if {[is_enabled multicommit] || [is_enabled singlecommit]} {
-       .mbar add cascade -label Commit -menu .mbar.commit
+       .mbar add cascade -label Commit -menu .mbar.commit -font font_ui
 }
 if {[is_enabled transport]} {
-       .mbar add cascade -label Merge -menu .mbar.merge
-       .mbar add cascade -label Fetch -menu .mbar.fetch
-       .mbar add cascade -label Push -menu .mbar.push
+       .mbar add cascade -label Merge -menu .mbar.merge -font font_ui
+       .mbar add cascade -label Fetch -menu .mbar.fetch -font font_ui
+       .mbar add cascade -label Push -menu .mbar.push -font font_ui
 }
 . configure -menu .mbar
 
@@ -5370,7 +5385,7 @@ if {[is_MacOSX]} {
 
 # -- Help Menu
 #
-.mbar add cascade -label Help -menu .mbar.help
+.mbar add cascade -label Help -menu .mbar.help -font font_ui
 menu .mbar.help
 
 if {![is_MacOSX]} {
@@ -5953,7 +5968,7 @@ unset i
 set file_lists($ui_index) [list]
 set file_lists($ui_workdir) [list]
 
-wm title . "[appname] ([file normalize [file dirname [gitdir]]])"
+wm title . "[appname] ([reponame]) [file normalize [file dirname [gitdir]]]"
 focus -force $ui_comm
 
 # -- Warn the user about environmental problems.  Cygwin's Tcl
@@ -6032,9 +6047,7 @@ if {[is_enabled multicommit]} {
                if {[ask_popup \
                        "This repository currently has $objects_current loose objects.
 
-To maintain optimal performance it is strongly
-recommended that you compress the database
-when more than $object_limit loose objects exist.
+To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
 
 Compress the database now?"] eq yes} {
                        do_gc
index edccd827553d04dde89f210bbb5128ccc09eff3d..018cc75bd032fd337b559c14b85f51f0418447ff 100755 (executable)
@@ -73,9 +73,9 @@ mkdir $tmp_dir || exit 2
 for patch_name in $(cat "$QUILT_PATCHES/series" | grep -v '^#'); do
        echo $patch_name
        (cat $QUILT_PATCHES/$patch_name | git-mailinfo "$tmp_msg" "$tmp_patch" > "$tmp_info") || exit 3
-       test -s $dotest/patch || {
+       test -s .dotest/patch || {
                echo "Patch is empty.  Was is split wrong?"
-               stop_here $this
+               exit 1
        }
 
        # Parse the author information
index 1d96f32685cdc51c89c63516519e8b544ab818af..2dc2c4fe9b4d4a7ff10398938efbed28fa6f43f7 100755 (executable)
@@ -339,8 +339,7 @@ prev_head=`git-rev-parse HEAD^0`
 echo "$prev_head" > "$dotest/prev_head"
 
 msgnum=0
-for cmt in `git-rev-list --no-merges "$upstream"..ORIG_HEAD \
-                       | @@PERL@@ -e 'print reverse <>'`
+for cmt in `git-rev-list --reverse --no-merges "$upstream"..ORIG_HEAD`
 do
        msgnum=$(($msgnum + 1))
        echo "$cmt" > "$dotest/cmt.$msgnum"
index 1278fcba462f632a3687742f74cc15c0498874e2..d6b15480dc1e211f0653ff54963889464dad3573 100755 (executable)
@@ -446,9 +446,12 @@ sub send_message
                my ($name, $addr) = ($from =~ /^(.*?)(\s+<.*)/);
                $from = "\"$name\"$addr";
        }
+       my $ccline = "";
+       if ($cc ne '') {
+               $ccline = "\nCc: $cc";
+       }
        my $header = "From: $from
-To: $to
-Cc: $cc
+To: $to${ccline}
 Subject: $subject
 Date: $date
 Message-Id: $message_id
index ac44f60b81412753248c78fbe73fe7f6a212b6df..efc4c88a4ea158bd630a6f53a8346e344b360ed6 100755 (executable)
@@ -168,14 +168,14 @@ BEGIN
 my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
 
 read_repo_config(\%opts);
-Getopt::Long::Configure('pass_through') if $cmd eq 'log';
+Getopt::Long::Configure('pass_through') if ($cmd && $cmd eq 'log');
 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,
                     'svn-remote|remote|R=s' => sub {
                        $Git::SVN::no_reuse_existing = 1;
                        $Git::SVN::default_repo_id = $_[1] });
-exit 1 if (!$rv && $cmd ne 'log');
+exit 1 if (!$rv && $cmd && $cmd ne 'log');
 
 usage(0) if $_help;
 version() if $_version;
@@ -1682,7 +1682,10 @@ sub find_parent_branch {
        }
        my ($r0, $parent) = $gs->find_rev_before($r, 1);
        if (!defined $r0 || !defined $parent) {
-               $gs->fetch(0, $r);
+               my ($base, $head) = parse_revision_argument(0, $r);
+               if ($base <= $r) {
+                       $gs->fetch($base, $r);
+               }
                ($r0, $parent) = $gs->last_rev_commit;
        }
        if (defined $r0 && defined $parent) {
@@ -3159,6 +3162,8 @@ sub match_globs {
                        my $p = $1;
                        my $pathname = $g->{path}->full_path($p);
                        next if $exists->{$pathname};
+                       next if ($self->check_path($pathname, $r) !=
+                                $SVN::Node::dir);
                        $exists->{$pathname} = Git::SVN->init(
                                              $self->{url}, $pathname, undef,
                                              $g->{ref}->full_path($p), 1);
diff --git a/git.c b/git.c
index 33dd4d39d907a229679a41f9712ee99007a34f3b..7def319e609454cd2389d2fc60caeaa5831d339e 100644 (file)
--- a/git.c
+++ b/git.c
@@ -66,6 +66,7 @@ static int handle_options(const char*** argv, int* argc)
                        setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
                        (*argv)++;
                        (*argc)--;
+                       handled++;
                } else if (!prefixcmp(cmd, "--git-dir=")) {
                        setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
                } else if (!strcmp(cmd, "--bare")) {
diff --git a/gitk b/gitk
index db28d745dc005722ff3d7c071aeb37f9fd4fdc21..b1c65d76806d1c26da0759569e6b83982a414bf1 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -648,8 +648,10 @@ proc makewindow {} {
     frame .bright.mode
     radiobutton .bright.mode.patch -text "Patch" \
        -command reselectline -variable cmitmode -value "patch"
+    .bright.mode.patch configure -font $uifont
     radiobutton .bright.mode.tree -text "Tree" \
        -command reselectline -variable cmitmode -value "tree"
+    .bright.mode.tree configure -font $uifont
     grid .bright.mode.patch .bright.mode.tree -sticky ew
     pack .bright.mode -side top -fill x
     set cflist .bright.cfiles
@@ -922,6 +924,7 @@ proc bindall {event action} {
 }
 
 proc about {} {
+    global uifont
     set w .about
     if {[winfo exists $w]} {
        raise $w
@@ -935,13 +938,19 @@ Gitk - a commit viewer for git
 Copyright © 2005-2006 Paul Mackerras
 
 Use and redistribute under the terms of the GNU General Public License} \
-           -justify center -aspect 400
-    pack $w.m -side top -fill x -padx 20 -pady 20
-    button $w.ok -text Close -command "destroy $w"
+           -justify center -aspect 400 -border 2 -bg white -relief groove
+    pack $w.m -side top -fill x -padx 2 -pady 2
+    $w.m configure -font $uifont
+    button $w.ok -text Close -command "destroy $w" -default active
     pack $w.ok -side bottom
+    $w.ok configure -font $uifont
+    bind $w <Visibility> "focus $w.ok"
+    bind $w <Key-Escape> "destroy $w"
+    bind $w <Key-Return> "destroy $w"
 }
 
 proc keys {} {
+    global uifont
     set w .keys
     if {[winfo exists $w]} {
        raise $w
@@ -988,10 +997,15 @@ f         Scroll diff view to next file
 <Ctrl-minus>   Decrease font size
 <F5>           Update
 } \
-           -justify left -bg white -border 2 -relief sunken
-    pack $w.m -side top -fill both
-    button $w.ok -text Close -command "destroy $w"
+           -justify left -bg white -border 2 -relief groove
+    pack $w.m -side top -fill both -padx 2 -pady 2
+    $w.m configure -font $uifont
+    button $w.ok -text Close -command "destroy $w" -default active
     pack $w.ok -side bottom
+    $w.ok configure -font $uifont
+    bind $w <Visibility> "focus $w.ok"
+    bind $w <Key-Escape> "destroy $w"
+    bind $w <Key-Return> "destroy $w"
 }
 
 # Procedures for manipulating the file list window at the
@@ -1457,20 +1471,21 @@ proc vieweditor {top n title} {
     toplevel $top
     wm title $top $title
     label $top.nl -text "Name" -font $uifont
-    entry $top.name -width 20 -textvariable newviewname($n)
+    entry $top.name -width 20 -textvariable newviewname($n) -font $uifont
     grid $top.nl $top.name -sticky w -pady 5
-    checkbutton $top.perm -text "Remember this view" -variable newviewperm($n)
+    checkbutton $top.perm -text "Remember this view" -variable newviewperm($n) \
+       -font $uifont
     grid $top.perm - -pady 5 -sticky w
     message $top.al -aspect 1000 -font $uifont \
        -text "Commits to include (arguments to git rev-list):"
     grid $top.al - -sticky w -pady 5
     entry $top.args -width 50 -textvariable newviewargs($n) \
-       -background white
+       -background white -font $uifont
     grid $top.args - -sticky ew -padx 5
     message $top.l -aspect 1000 -font $uifont \
        -text "Enter files and directories to include, one per line:"
     grid $top.l - -sticky w
-    text $top.t -width 40 -height 10 -background white
+    text $top.t -width 40 -height 10 -background white -font $uifont
     if {[info exists viewfiles($n)]} {
        foreach f $viewfiles($n) {
            $top.t insert end $f
@@ -1481,8 +1496,10 @@ proc vieweditor {top n title} {
     }
     grid $top.t - -sticky ew -padx 5
     frame $top.buts
-    button $top.buts.ok -text "OK" -command [list newviewok $top $n]
-    button $top.buts.can -text "Cancel" -command [list destroy $top]
+    button $top.buts.ok -text "OK" -command [list newviewok $top $n] \
+       -font $uifont
+    button $top.buts.can -text "Cancel" -command [list destroy $top] \
+       -font $uifont
     grid $top.buts.ok $top.buts.can
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
     grid columnconfigure $top.buts 1 -weight 1 -uniform a
@@ -5813,6 +5830,7 @@ proc doprefs {} {
     global maxwidth maxgraphpct diffopts
     global oldprefs prefstop showneartags
     global bgcolor fgcolor ctext diffcolors
+    global uifont
 
     set top .gitkprefs
     set prefstop $top
@@ -5826,6 +5844,7 @@ proc doprefs {} {
     toplevel $top
     wm title $top "Gitk preferences"
     label $top.ldisp -text "Commit list display options"
+    $top.ldisp configure -font $uifont
     grid $top.ldisp - -sticky w -pady 10
     label $top.spacer -text " "
     label $top.maxwidthl -text "Maximum graph width (lines)" \
@@ -5838,6 +5857,7 @@ proc doprefs {} {
     grid x $top.maxpctl $top.maxpct -sticky w
 
     label $top.ddisp -text "Diff display options"
+    $top.ddisp configure -font $uifont
     grid $top.ddisp - -sticky w -pady 10
     label $top.diffoptl -text "Options for diff program" \
        -font optionfont
@@ -5850,6 +5870,7 @@ proc doprefs {} {
     grid x $top.ntag -sticky w
 
     label $top.cdisp -text "Colors: press to choose"
+    $top.cdisp configure -font $uifont
     grid $top.cdisp - -sticky w -pady 10
     label $top.bg -padx 40 -relief sunk -background $bgcolor
     button $top.bgbut -text "Background" -font optionfont \
@@ -5877,12 +5898,15 @@ proc doprefs {} {
     grid x $top.hunksepbut $top.hunksep -sticky w
 
     frame $top.buts
-    button $top.buts.ok -text "OK" -command prefsok
-    button $top.buts.can -text "Cancel" -command prefscan
+    button $top.buts.ok -text "OK" -command prefsok -default active
+    $top.buts.ok configure -font $uifont
+    button $top.buts.can -text "Cancel" -command prefscan -default normal
+    $top.buts.can configure -font $uifont
     grid $top.buts.ok $top.buts.can
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
     grid columnconfigure $top.buts 1 -weight 1 -uniform a
     grid $top.buts - - -pady 10 -sticky ew
+    bind $top <Visibility> "focus $top.buts.ok"
 }
 
 proc choosecolor {v vi w x cmd} {
index e49eb91d69c2344163ab9477883777476c789bc7..c48b35aa39fd13c14baac33b8df66b9c9bfc1735 100755 (executable)
@@ -71,6 +71,10 @@ BEGIN
 # source of projects list
 our $projects_list = "++GITWEB_LIST++";
 
+# default order of projects list
+# valid values are none, project, descr, owner, and age
+our $default_projects_order = "project";
+
 # show repository only if this file exists
 # (only effective if this variable evaluates to true)
 our $export_ok = "++GITWEB_EXPORT_OK++";
@@ -176,8 +180,8 @@ BEGIN
        # projects matching $projname/*.git will not be shown in the main
        # projects list, instead a '+' mark will be added to $projname
        # there and a 'forks' view will be enabled for the project, listing
-       # all the forks. This feature is supported only if project list
-       # is taken from a directory, not file.
+       # all the forks. If project list is taken from a file, forks have
+       # to be listed after the main project.
 
        # To enable system wide have in $GITWEB_CONFIG
        # $feature{'forks'}{'default'} = [1];
@@ -1047,6 +1051,8 @@ sub git_get_projects_list {
        $filter ||= '';
        $filter =~ s/\.git$//;
 
+       my ($check_forks) = gitweb_check_feature('forks');
+
        if (-d $projects_list) {
                # search in directory
                my $dir = $projects_list . ($filter ? "/$filter" : '');
@@ -1054,8 +1060,6 @@ sub git_get_projects_list {
                $dir =~ s!/+$!!;
                my $pfxlen = length("$dir");
 
-               my ($check_forks) = gitweb_check_feature('forks');
-
                File::Find::find({
                        follow_fast => 1, # follow symbolic links
                        dangling_symlinks => 0, # ignore dangling symlinks, silently
@@ -1081,7 +1085,9 @@ sub git_get_projects_list {
                # 'git%2Fgit.git Linus+Torvalds'
                # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
                # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman'
+               my %paths;
                open my ($fd), $projects_list or return;
+       PROJECT:
                while (my $line = <$fd>) {
                        chomp $line;
                        my ($path, $owner) = split ' ', $line;
@@ -1094,11 +1100,27 @@ sub git_get_projects_list {
                                # looking for forks;
                                my $pfx = substr($path, 0, length($filter));
                                if ($pfx ne $filter) {
-                                       next;
+                                       next PROJECT;
                                }
                                my $sfx = substr($path, length($filter));
                                if ($sfx !~ /^\/.*\.git$/) {
-                                       next;
+                                       next PROJECT;
+                               }
+                       } elsif ($check_forks) {
+                       PATH:
+                               foreach my $filter (keys %paths) {
+                                       # looking for forks;
+                                       my $pfx = substr($path, 0, length($filter));
+                                       if ($pfx ne $filter) {
+                                               next PATH;
+                                       }
+                                       my $sfx = substr($path, length($filter));
+                                       if ($sfx !~ /^\/.*\.git$/) {
+                                               next PATH;
+                                       }
+                                       # is a fork, don't include it in
+                                       # the list
+                                       next PROJECT;
                                }
                        }
                        if (check_export_ok("$projectroot/$path")) {
@@ -1106,12 +1128,13 @@ sub git_get_projects_list {
                                        path => $path,
                                        owner => to_utf8($owner),
                                };
-                               push @list, $pr
+                               push @list, $pr;
+                               (my $forks_path = $path) =~ s/\.git$//;
+                               $paths{$forks_path}++;
                        }
                }
                close $fd;
        }
-       @list = sort {$a->{'path'} cmp $b->{'path'}} @list;
        return @list;
 }
 
@@ -2598,7 +2621,7 @@ sub git_project_list_body {
                push @projects, $pr;
        }
 
-       $order ||= "project";
+       $order ||= $default_projects_order;
        $from = 0 unless defined $from;
        $to = $#projects if (!defined $to || $#projects < $to);
 
@@ -2957,7 +2980,7 @@ sub git_search_grep_body {
 
 sub git_project_list {
        my $order = $cgi->param('o');
-       if (defined $order && $order !~ m/project|descr|owner|age/) {
+       if (defined $order && $order !~ m/none|project|descr|owner|age/) {
                die_error(undef, "Unknown order parameter");
        }
 
@@ -2980,7 +3003,7 @@ sub git_project_list {
 
 sub git_forks {
        my $order = $cgi->param('o');
-       if (defined $order && $order !~ m/project|descr|owner|age/) {
+       if (defined $order && $order !~ m/none|project|descr|owner|age/) {
                die_error(undef, "Unknown order parameter");
        }
 
diff --git a/ident.c b/ident.c
index bb03bddd34f2471ed130c499affc369758d4bfd9..88e7f74e888f1c7b13182bf07afee01408e8e525 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -9,10 +9,10 @@
 
 static char git_default_date[50];
 
-static void copy_gecos(struct passwd *w, char *name, int sz)
+static void copy_gecos(const struct passwd *w, char *name, size_t sz)
 {
        char *src, *dst;
-       int len, nlen;
+       size_t len, nlen;
 
        nlen = strlen(w->pw_name);
 
@@ -43,13 +43,13 @@ static void copy_gecos(struct passwd *w, char *name, int sz)
 
 }
 
-static void copy_email(struct passwd *pw)
+static void copy_email(const struct passwd *pw)
 {
        /*
         * Make up a fake email address
         * (name + '@' + hostname [+ '.' + domainname])
         */
-       int len = strlen(pw->pw_name);
+       size_t len = strlen(pw->pw_name);
        if (len > sizeof(git_default_email)/2)
                die("Your sysadmin must hate you!");
        memcpy(git_default_email, pw->pw_name, len);
@@ -95,9 +95,9 @@ static void setup_ident(void)
                datestamp(git_default_date, sizeof(git_default_date));
 }
 
-static int add_raw(char *buf, int size, int offset, const char *str)
+static int add_raw(char *buf, size_t size, int offset, const char *str)
 {
-       int len = strlen(str);
+       size_t len = strlen(str);
        if (offset + len > size)
                return size;
        memcpy(buf + offset, str, len);
@@ -131,9 +131,9 @@ static int crud(unsigned char c)
  * Copy over a string to the destination, but avoid special
  * characters ('\n', '<' and '>') and remove crud at the end
  */
-static int copy(char *buf, int size, int offset, const char *src)
+static int copy(char *buf, size_t size, int offset, const char *src)
 {
-       int i, len;
+       size_t i, len;
        unsigned char c;
 
        /* Remove crud from the beginning.. */
index 8797aa14c43e693c4cb64bc93dac4fe78716558d..300b73356054ffddb304c2a5cfffae680c0b5dd6 100644 (file)
@@ -4,6 +4,8 @@
 #include "log-tree.h"
 #include "reflog-walk.h"
 
+struct decoration name_decoration = { "object names" };
+
 static void show_parents(struct commit *commit, int abbrev)
 {
        struct commit_list *p;
@@ -13,6 +15,23 @@ static void show_parents(struct commit *commit, int abbrev)
        }
 }
 
+static void show_decorations(struct commit *commit)
+{
+       const char *prefix;
+       struct name_decoration *decoration;
+
+       decoration = lookup_decoration(&name_decoration, &commit->object);
+       if (!decoration)
+               return;
+       prefix = " (";
+       while (decoration) {
+               printf("%s%s", prefix, decoration->name);
+               prefix = ", ";
+               decoration = decoration->next;
+       }
+       putchar(')');
+}
+
 /*
  * Search for "^[-A-Za-z]+: [^@]+@" pattern. It usually matches
  * Signed-off-by: and Acked-by: lines.
@@ -136,6 +155,7 @@ void show_log(struct rev_info *opt, const char *sep)
                fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
                if (opt->parents)
                        show_parents(commit, abbrev_commit);
+               show_decorations(commit);
                putchar(opt->diffopt.line_termination);
                return;
        }
@@ -165,14 +185,20 @@ void show_log(struct rev_info *opt, const char *sep)
                if (opt->total > 0) {
                        static char buffer[64];
                        snprintf(buffer, sizeof(buffer),
-                                       "Subject: [PATCH %0*d/%d] ",
+                                       "Subject: [%s %0*d/%d] ",
+                                       opt->subject_prefix,
                                        digits_in_number(opt->total),
                                        opt->nr, opt->total);
                        subject = buffer;
-               } else if (opt->total == 0)
-                       subject = "Subject: [PATCH] ";
-               else
+               } else if (opt->total == 0) {
+                       static char buffer[256];
+                       snprintf(buffer, sizeof(buffer),
+                                       "Subject: [%s] ",
+                                       opt->subject_prefix);
+                       subject = buffer;
+               } else {
                        subject = "Subject: ";
+               }
 
                printf("From %s Mon Sep 17 00:00:00 2001\n", sha1);
                if (opt->message_id)
@@ -234,6 +260,7 @@ void show_log(struct rev_info *opt, const char *sep)
                        printf(" (from %s)",
                               diff_unique_abbrev(parent->object.sha1,
                                                  abbrev_commit));
+               show_decorations(commit);
                printf("%s",
                       diff_get_color(opt->diffopt.color_diff, DIFF_RESET));
                putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
index 3096594b3e9f07785229236700c6039efd61c305..31e66e5ca3811d6356a635ef7580e86ce9c83863 100644 (file)
@@ -95,11 +95,6 @@ static struct path_list current_directory_set = {NULL, 0, 0, 1};
 static int call_depth = 0;
 static int verbosity = 2;
 static int buffer_output = 1;
-static int do_progress = 1;
-static unsigned last_percent;
-static unsigned merged_cnt;
-static unsigned total_cnt;
-static volatile sig_atomic_t progress_update;
 static struct output_buffer *output_list, *output_end;
 
 static int show (int v)
@@ -174,39 +169,6 @@ static void output_commit_title(struct commit *commit)
        }
 }
 
-static void progress_interval(int signum)
-{
-       progress_update = 1;
-}
-
-static void setup_progress_signal(void)
-{
-       struct sigaction sa;
-       struct itimerval v;
-
-       memset(&sa, 0, sizeof(sa));
-       sa.sa_handler = progress_interval;
-       sigemptyset(&sa.sa_mask);
-       sa.sa_flags = SA_RESTART;
-       sigaction(SIGALRM, &sa, NULL);
-
-       v.it_interval.tv_sec = 1;
-       v.it_interval.tv_usec = 0;
-       v.it_value = v.it_interval;
-       setitimer(ITIMER_REAL, &v, NULL);
-}
-
-static void display_progress()
-{
-       unsigned percent = total_cnt ? merged_cnt * 100 / total_cnt : 0;
-       if (progress_update || percent != last_percent) {
-               fprintf(stderr, "%4u%% (%u/%u) done\r",
-                       percent, merged_cnt, total_cnt);
-               progress_update = 0;
-               last_percent = percent;
-       }
-}
-
 static struct cache_entry *make_cache_entry(unsigned int mode,
                const unsigned char *sha1, const char *path, int stage, int refresh)
 {
@@ -377,14 +339,11 @@ static struct path_list *get_unmerged(void)
        int i;
 
        unmerged->strdup_paths = 1;
-       total_cnt += active_nr;
 
-       for (i = 0; i < active_nr; i++, merged_cnt++) {
+       for (i = 0; i < active_nr; i++) {
                struct path_list_item *item;
                struct stage_data *e;
                struct cache_entry *ce = active_cache[i];
-               if (do_progress)
-                       display_progress();
                if (!ce_stage(ce))
                        continue;
 
@@ -574,6 +533,31 @@ static void flush_buffer(int fd, const char *buf, unsigned long size)
        }
 }
 
+static int make_room_for_path(const char *path)
+{
+       int status;
+       const char *msg = "failed to create path '%s'%s";
+
+       status = mkdir_p(path, 0777);
+       if (status) {
+               if (status == -3) {
+                       /* something else exists */
+                       error(msg, path, ": perhaps a D/F conflict?");
+                       return -1;
+               }
+               die(msg, path, "");
+       }
+
+       /* Successful unlink is good.. */
+       if (!unlink(path))
+               return 0;
+       /* .. and so is no existing file */
+       if (errno == ENOENT)
+               return 0;
+       /* .. but not some other error (who really cares what?) */
+       return error(msg, path, ": perhaps a D/F conflict?");
+}
+
 static void update_file_flags(const unsigned char *sha,
                              unsigned mode,
                              const char *path,
@@ -594,11 +578,12 @@ static void update_file_flags(const unsigned char *sha,
                if (type != OBJ_BLOB)
                        die("blob expected for %s '%s'", sha1_to_hex(sha), path);
 
+               if (make_room_for_path(path) < 0) {
+                       update_wd = 0;
+                       goto update_index;
+               }
                if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {
                        int fd;
-                       if (mkdir_p(path, 0777))
-                               die("failed to create path %s: %s", path, strerror(errno));
-                       unlink(path);
                        if (mode & 0100)
                                mode = 0777;
                        else
@@ -620,6 +605,7 @@ static void update_file_flags(const unsigned char *sha,
                        die("do not know what to do with %06o %s '%s'",
                            mode, sha1_to_hex(sha), path);
        }
+ update_index:
        if (update_cache)
                add_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);
 }
@@ -1018,9 +1004,9 @@ static int process_renames(struct path_list *a_renames,
        return clean_merge;
 }
 
-static unsigned char *has_sha(const unsigned char *sha)
+static unsigned char *stage_sha(const unsigned char *sha, unsigned mode)
 {
-       return is_null_sha1(sha) ? NULL: (unsigned char *)sha;
+       return (is_null_sha1(sha) || mode == 0) ? NULL: (unsigned char *)sha;
 }
 
 /* Per entry merge function */
@@ -1033,12 +1019,12 @@ static int process_entry(const char *path, struct stage_data *entry,
        print_index_entry("\tpath: ", entry);
        */
        int clean_merge = 1;
-       unsigned char *o_sha = has_sha(entry->stages[1].sha);
-       unsigned char *a_sha = has_sha(entry->stages[2].sha);
-       unsigned char *b_sha = has_sha(entry->stages[3].sha);
        unsigned o_mode = entry->stages[1].mode;
        unsigned a_mode = entry->stages[2].mode;
        unsigned b_mode = entry->stages[3].mode;
+       unsigned char *o_sha = stage_sha(entry->stages[1].sha, o_mode);
+       unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode);
+       unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode);
 
        if (o_sha && (!a_sha || !b_sha)) {
                /* Case A: Deleted in one */
@@ -1139,6 +1125,12 @@ static int process_entry(const char *path, struct stage_data *entry,
                                update_file_flags(mfi.sha, mfi.mode, path,
                                              0 /* update_cache */, 1 /* update_working_directory */);
                }
+       } else if (!o_sha && !a_sha && !b_sha) {
+               /*
+                * this entry was deleted altogether. a_mode == 0 means
+                * we had that path and want to actively remove it.
+                */
+               remove_file(1, path, !a_mode);
        } else
                die("Fatal merge failure, shouldn't happen.");
 
@@ -1185,15 +1177,12 @@ static int merge_trees(struct tree *head,
                re_merge = get_renames(merge, common, head, merge, entries);
                clean = process_renames(re_head, re_merge,
                                branch1, branch2);
-               total_cnt += entries->nr;
-               for (i = 0; i < entries->nr; i++, merged_cnt++) {
+               for (i = 0; i < entries->nr; i++) {
                        const char *path = entries->items[i].path;
                        struct stage_data *e = entries->items[i].util;
                        if (!e->processed
                                && !process_entry(path, e, branch1, branch2))
                                clean = 0;
-                       if (do_progress)
-                               display_progress();
                }
 
                path_list_clear(re_merge, 0);
@@ -1301,15 +1290,6 @@ static int merge(struct commit *h1,
                commit_list_insert(h1, &(*result)->parents);
                commit_list_insert(h2, &(*result)->parents->next);
        }
-       if (!call_depth && do_progress) {
-               /* Make sure we end at 100% */
-               if (!total_cnt)
-                       total_cnt = 1;
-               merged_cnt = total_cnt;
-               progress_update = 1;
-               display_progress();
-               fputc('\n', stderr);
-       }
        flush_output();
        return clean;
 }
@@ -1386,12 +1366,8 @@ int main(int argc, char *argv[])
        }
        if (argc - i != 3) /* "--" "<head>" "<remote>" */
                die("Not handling anything other than two heads merge.");
-       if (verbosity >= 5) {
+       if (verbosity >= 5)
                buffer_output = 0;
-               do_progress = 0;
-       }
-       else
-               do_progress = isatty(1);
 
        branch1 = argv[++i];
        branch2 = argv[++i];
@@ -1402,8 +1378,6 @@ int main(int argc, char *argv[])
        branch1 = better_branch_name(branch1);
        branch2 = better_branch_name(branch2);
 
-       if (do_progress)
-               setup_progress_signal();
        if (show(3))
                printf("Merging %s with %s\n", branch1, branch2);
 
index 98ea10005a851c3879c6e65929e3e34068dc9e8b..022e8d841c7f5498b6f008647ed37c430826b443 100644 (file)
@@ -1,75 +1,20 @@
 #include "cache.h"
 #include "object.h"
+#include "decorate.h"
 
 int track_object_refs = 0;
 
-static unsigned int refs_hash_size, nr_object_refs;
-static struct object_refs **refs_hash;
+static struct decoration ref_decorate;
 
-static unsigned int hash_obj(struct object *obj, unsigned int n)
+struct object_refs *lookup_object_refs(struct object *base)
 {
-       unsigned int hash = *(unsigned int *)obj->sha1;
-       return hash % n;
+       return lookup_decoration(&ref_decorate, base);
 }
 
-static void insert_ref_hash(struct object_refs *ref, struct object_refs **hash, unsigned int size)
+static void add_object_refs(struct object *obj, struct object_refs *refs)
 {
-       int j = hash_obj(ref->base, size);
-
-       while (hash[j]) {
-               j++;
-               if (j >= size)
-                       j = 0;
-       }
-       hash[j] = ref;
-}
-
-static void grow_refs_hash(void)
-{
-       int i;
-       int new_hash_size = (refs_hash_size + 1000) * 3 / 2;
-       struct object_refs **new_hash;
-
-       new_hash = xcalloc(new_hash_size, sizeof(struct object_refs *));
-       for (i = 0; i < refs_hash_size; i++) {
-               struct object_refs *ref = refs_hash[i];
-               if (!ref)
-                       continue;
-               insert_ref_hash(ref, new_hash, new_hash_size);
-       }
-       free(refs_hash);
-       refs_hash = new_hash;
-       refs_hash_size = new_hash_size;
-}
-
-static void add_object_refs(struct object *obj, struct object_refs *ref)
-{
-       int nr = nr_object_refs + 1;
-
-       if (nr > refs_hash_size * 2 / 3)
-               grow_refs_hash();
-       ref->base = obj;
-       insert_ref_hash(ref, refs_hash, refs_hash_size);
-       nr_object_refs = nr;
-}
-
-struct object_refs *lookup_object_refs(struct object *obj)
-{
-       struct object_refs *ref;
-       int j;
-
-       /* nothing to lookup */
-       if (!refs_hash_size)
-               return NULL;
-       j = hash_obj(obj, refs_hash_size);
-       while ((ref = refs_hash[j]) != NULL) {
-               if (ref->base == obj)
-                       break;
-               j++;
-               if (j >= refs_hash_size)
-                       j = 0;
-       }
-       return ref;
+       if (add_decoration(&ref_decorate, obj, refs))
+               die("object %s tried to add refs twice!", sha1_to_hex(obj->sha1));
 }
 
 struct object_refs *alloc_object_refs(unsigned count)
index bdbf0facd47015b61a6fbf25eb358b6e256c86b6..bdbbc1889c0227519de0ba02439f0e94dfcd1651 100644 (file)
--- a/object.h
+++ b/object.h
@@ -8,7 +8,6 @@ struct object_list {
 
 struct object_refs {
        unsigned count;
-       struct object *base;
        struct object *ref[FLEX_ARRAY]; /* more */
 };
 
diff --git a/patch-ids.c b/patch-ids.c
new file mode 100644 (file)
index 0000000..a288fac
--- /dev/null
@@ -0,0 +1,192 @@
+#include "cache.h"
+#include "diff.h"
+#include "commit.h"
+#include "patch-ids.h"
+
+static int commit_patch_id(struct commit *commit, struct diff_options *options,
+                   unsigned char *sha1)
+{
+       if (commit->parents)
+               diff_tree_sha1(commit->parents->item->object.sha1,
+                              commit->object.sha1, "", options);
+       else
+               diff_root_tree_sha1(commit->object.sha1, "", options);
+       diffcore_std(options);
+       return diff_flush_patch_id(options, sha1);
+}
+
+static uint32_t take2(const unsigned char *id)
+{
+       return ((id[0] << 8) | id[1]);
+}
+
+/*
+ * Conventional binary search loop looks like this:
+ *
+ *      do {
+ *              int 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 the 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 update hi with
+ *      it.
+ *
+ *    - if it is strictly lower than the target, we update lo to be
+ *      one slot after it, because we allow lo to be at the target.
+ *
+ * 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.
+ */
+static int patch_pos(struct patch_id **table, int nr, const unsigned char *id)
+{
+       int hi = nr;
+       int lo = 0;
+       int mi = 0;
+
+       if (!nr)
+               return -1;
+
+       if (nr != 1) {
+               unsigned lov, hiv, miv, ofs;
+
+               for (ofs = 0; ofs < 18; ofs += 2) {
+                       lov = take2(table[0]->patch_id + ofs);
+                       hiv = take2(table[nr-1]->patch_id + ofs);
+                       miv = take2(id + ofs);
+                       if (miv < lov)
+                               return -1;
+                       if (hiv < miv)
+                               return -1 - nr;
+                       if (lov != hiv) {
+                               /*
+                                * At this point miv could be equal
+                                * to hiv (but id could still be higher);
+                                * the invariant of (mi < hi) should be
+                                * kept.
+                                */
+                               mi = (nr-1) * (miv - lov) / (hiv - lov);
+                               if (lo <= mi && mi < hi)
+                                       break;
+                               die("oops");
+                       }
+               }
+               if (18 <= ofs)
+                       die("cannot happen -- lo and hi are identical");
+       }
+
+       do {
+               int cmp;
+               cmp = hashcmp(table[mi]->patch_id, id);
+               if (!cmp)
+                       return mi;
+               if (cmp > 0)
+                       hi = mi;
+               else
+                       lo = mi + 1;
+               mi = (hi + lo) / 2;
+       } while (lo < hi);
+       return -lo-1;
+}
+
+#define BUCKET_SIZE 190 /* 190 * 21 = 3990, with slop close enough to 4K */
+struct patch_id_bucket {
+       struct patch_id_bucket *next;
+       int nr;
+       struct patch_id bucket[BUCKET_SIZE];
+};
+
+int init_patch_ids(struct patch_ids *ids)
+{
+       memset(ids, 0, sizeof(*ids));
+       diff_setup(&ids->diffopts);
+       ids->diffopts.recursive = 1;
+       if (diff_setup_done(&ids->diffopts) < 0)
+               return error("diff_setup_done failed");
+       return 0;
+}
+
+int free_patch_ids(struct patch_ids *ids)
+{
+       struct patch_id_bucket *next, *patches;
+
+       free(ids->table);
+       for (patches = ids->patches; patches; patches = next) {
+               next = patches->next;
+               free(patches);
+       }
+       return 0;
+}
+
+static struct patch_id *add_commit(struct commit *commit,
+                                  struct patch_ids *ids,
+                                  int no_add)
+{
+       struct patch_id_bucket *bucket;
+       struct patch_id *ent;
+       unsigned char sha1[20];
+       int pos;
+
+       if (commit_patch_id(commit, &ids->diffopts, sha1))
+               return NULL;
+       pos = patch_pos(ids->table, ids->nr, sha1);
+       if (0 <= pos)
+               return ids->table[pos];
+       if (no_add)
+               return NULL;
+
+       pos = -1 - pos;
+
+       bucket = ids->patches;
+       if (!bucket || (BUCKET_SIZE <= bucket->nr)) {
+               bucket = xcalloc(1, sizeof(*bucket));
+               bucket->next = ids->patches;
+               ids->patches = bucket;
+       }
+       ent = &bucket->bucket[bucket->nr++];
+       hashcpy(ent->patch_id, sha1);
+
+       if (ids->alloc <= ids->nr) {
+               ids->alloc = alloc_nr(ids->nr);
+               ids->table = xrealloc(ids->table, sizeof(ent) * ids->alloc);
+       }
+       if (pos < ids->nr)
+               memmove(ids->table + pos + 1, ids->table + pos,
+                       sizeof(ent) * (ids->nr - pos));
+       ids->nr++;
+       ids->table[pos] = ent;
+       return ids->table[pos];
+}
+
+struct patch_id *has_commit_patch_id(struct commit *commit,
+                                    struct patch_ids *ids)
+{
+       return add_commit(commit, ids, 1);
+}
+
+struct patch_id *add_commit_patch_id(struct commit *commit,
+                                    struct patch_ids *ids)
+{
+       return add_commit(commit, ids, 0);
+}
diff --git a/patch-ids.h b/patch-ids.h
new file mode 100644 (file)
index 0000000..c8c7ca1
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef PATCH_IDS_H
+#define PATCH_IDS_H
+
+struct patch_id {
+       unsigned char patch_id[20];
+       char seen;
+};
+
+struct patch_ids {
+       struct diff_options diffopts;
+       int nr, alloc;
+       struct patch_id **table;
+       struct patch_id_bucket *patches;
+};
+
+int init_patch_ids(struct patch_ids *);
+int free_patch_ids(struct patch_ids *);
+struct patch_id *add_commit_patch_id(struct commit *, struct patch_ids *);
+struct patch_id *has_commit_patch_id(struct commit *, struct patch_ids *);
+
+#endif /* PATCH_IDS_H */
index 9b117fd0d736615a8c4aeda742384956373d295a..437516142cb6c14f197dc5821635a6ff8bc91adf 100644 (file)
@@ -13,7 +13,7 @@ my %pm = ('Git.pm' => '$(INST_LIBDIR)/Git.pm');
 # We come with our own bundled Error.pm. It's not in the set of default
 # Perl modules so install it if it's not available on the system yet.
 eval { require Error };
-if ($@) {
+if ($@ || $Error::VERSION < 0.15009) {
        $pm{'private-Error.pm'} = '$(INST_LIBDIR)/Error.pm';
 }
 
diff --git a/refs.c b/refs.c
index d2b7b7fb56f76294bb48526496429968d86e49b2..d7be2841c5f2a5fbee8964051b86356af7bf9905 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -705,7 +705,7 @@ static int repack_without_ref(const char *refname)
        return commit_lock_file(&packlock);
 }
 
-int delete_ref(const char *refname, unsigned char *sha1)
+int delete_ref(const char *refname, const unsigned char *sha1)
 {
        struct ref_lock *lock;
        int err, i, ret = 0, flag = 0;
index 486393cb0835ce70f685d665b916e1b67974f184..ce70f48ce0880e8b43c3f62cd13bc185bfee8c4b 100644 (file)
@@ -8,6 +8,7 @@
 #include "revision.h"
 #include "grep.h"
 #include "reflog-walk.h"
+#include "patch-ids.h"
 
 static char *path_name(struct name_path *path, const char *name)
 {
@@ -422,6 +423,86 @@ static void add_parents_to_list(struct rev_info *revs, struct commit *commit, st
        }
 }
 
+static void cherry_pick_list(struct commit_list *list)
+{
+       struct commit_list *p;
+       int left_count = 0, right_count = 0;
+       int left_first;
+       struct patch_ids ids;
+
+       /* First count the commits on the left and on the right */
+       for (p = list; p; p = p->next) {
+               struct commit *commit = p->item;
+               unsigned flags = commit->object.flags;
+               if (flags & BOUNDARY)
+                       ;
+               else if (flags & SYMMETRIC_LEFT)
+                       left_count++;
+               else
+                       right_count++;
+       }
+
+       left_first = left_count < right_count;
+       init_patch_ids(&ids);
+
+       /* Compute patch-ids for one side */
+       for (p = list; p; p = p->next) {
+               struct commit *commit = p->item;
+               unsigned flags = commit->object.flags;
+
+               if (flags & BOUNDARY)
+                       continue;
+               /*
+                * If we have fewer left, left_first is set and we omit
+                * commits on the right branch in this loop.  If we have
+                * fewer right, we skip the left ones.
+                */
+               if (left_first != !!(flags & SYMMETRIC_LEFT))
+                       continue;
+               commit->util = add_commit_patch_id(commit, &ids);
+       }
+
+       /* Check the other side */
+       for (p = list; p; p = p->next) {
+               struct commit *commit = p->item;
+               struct patch_id *id;
+               unsigned flags = commit->object.flags;
+
+               if (flags & BOUNDARY)
+                       continue;
+               /*
+                * If we have fewer left, left_first is set and we omit
+                * commits on the left branch in this loop.
+                */
+               if (left_first == !!(flags & SYMMETRIC_LEFT))
+                       continue;
+
+               /*
+                * Have we seen the same patch id?
+                */
+               id = has_commit_patch_id(commit, &ids);
+               if (!id)
+                       continue;
+               id->seen = 1;
+               commit->object.flags |= SHOWN;
+       }
+
+       /* Now check the original side for seen ones */
+       for (p = list; p; p = p->next) {
+               struct commit *commit = p->item;
+               struct patch_id *ent;
+
+               ent = commit->util;
+               if (!ent)
+                       continue;
+               if (ent->seen)
+                       commit->object.flags |= SHOWN;
+               commit->util = NULL;
+       }
+
+       free_patch_ids(&ids);
+}
+
 static void limit_list(struct rev_info *revs)
 {
        struct commit_list *list = revs->commits;
@@ -449,6 +530,9 @@ static void limit_list(struct rev_info *revs)
                        continue;
                p = &commit_list_insert(commit, p)->next;
        }
+       if (revs->cherry_pick)
+               cherry_pick_list(newlist);
+
        revs->commits = newlist;
 }
 
@@ -567,6 +651,7 @@ void init_revisions(struct rev_info *revs, const char *prefix)
        revs->min_age = -1;
        revs->skip_count = -1;
        revs->max_count = -1;
+       revs->subject_prefix = "PATCH";
 
        revs->prune_fn = NULL;
        revs->prune_data = NULL;
@@ -913,6 +998,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->left_right = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "--cherry-pick")) {
+                               revs->cherry_pick = 1;
+                               continue;
+                       }
                        if (!strcmp(arg, "--objects")) {
                                revs->tag_objects = 1;
                                revs->tree_objects = 1;
index 55e6b531ce3e5838f988ca1896484333e795f192..8a026184287479272ececf5cf3d5000c52007553 100644 (file)
@@ -47,6 +47,7 @@ struct rev_info {
                        left_right:1,
                        parents:1,
                        reverse:1,
+                       cherry_pick:1,
                        first_parent_only:1;
 
        /* Diff flags */
@@ -78,6 +79,7 @@ struct rev_info {
        const char      *add_signoff;
        const char      *extra_headers;
        const char      *log_reencode;
+       const char      *subject_prefix;
        int             no_inline;
 
        /* Filter by commit log message */
old mode 100755 (executable)
new mode 100644 (file)
old mode 100755 (executable)
new mode 100644 (file)
index e26a36cf0f4c113d916c2d23daf036e4844be053..de4e5eb61f5cc197a51005c6cfbd3bf2b9428480 100755 (executable)
@@ -184,7 +184,7 @@ checked.
   9  exists  O!=A    missing   no merge    must match A and be
                                            up-to-date, if exists.
  ------------------------------------------------------------------
- 10  exists  O==A    missing   remove      ditto
+ 10  exists  O==A    missing   no merge    must match A
  ------------------------------------------------------------------
  11  exists  O!=A    O!=B      no merge    must match A and be
                      A!=B                  up-to-date, if exists.
diff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh
new file mode 100755 (executable)
index 0000000..86ee2b0
--- /dev/null
@@ -0,0 +1,528 @@
+#!/bin/sh
+
+test_description='merge-recursive backend test'
+
+. ./test-lib.sh
+
+test_expect_success 'setup 1' '
+
+       echo hello >a &&
+       o0=$(git hash-object a) &&
+       cp a b &&
+       cp a c &&
+       mkdir d &&
+       cp a d/e &&
+
+       test_tick &&
+       git add a b c d/e &&
+       git commit -m initial &&
+       c0=$(git rev-parse --verify HEAD) &&
+       git branch side &&
+       git branch df-1 &&
+       git branch df-2 &&
+       git branch df-3 &&
+       git branch remove &&
+
+       echo hello >>a &&
+       cp a d/e &&
+       o1=$(git hash-object a) &&
+
+       git add a d/e &&
+
+       test_tick &&
+       git commit -m "master modifies a and d/e" &&
+       c1=$(git rev-parse --verify HEAD) &&
+       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       (
+               echo "100644 blob $o1   a"
+               echo "100644 blob $o0   b"
+               echo "100644 blob $o0   c"
+               echo "100644 blob $o1   d/e"
+               echo "100644 $o1 0      a"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o1 0      d/e"
+       ) >expected &&
+       git diff -u expected actual
+'
+
+test_expect_success 'setup 2' '
+
+       rm -rf [abcd] &&
+       git checkout side &&
+       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       (
+               echo "100644 blob $o0   a"
+               echo "100644 blob $o0   b"
+               echo "100644 blob $o0   c"
+               echo "100644 blob $o0   d/e"
+               echo "100644 $o0 0      a"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o0 0      d/e"
+       ) >expected &&
+       git diff -u expected actual &&
+
+       echo goodbye >>a &&
+       o2=$(git hash-object a) &&
+
+       git add a &&
+
+       test_tick &&
+       git commit -m "side modifies a" &&
+       c2=$(git rev-parse --verify HEAD) &&
+       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       (
+               echo "100644 blob $o2   a"
+               echo "100644 blob $o0   b"
+               echo "100644 blob $o0   c"
+               echo "100644 blob $o0   d/e"
+               echo "100644 $o2 0      a"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o0 0      d/e"
+       ) >expected &&
+       git diff -u expected actual
+'
+
+test_expect_success 'setup 3' '
+
+       rm -rf [abcd] &&
+       git checkout df-1 &&
+       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       (
+               echo "100644 blob $o0   a"
+               echo "100644 blob $o0   b"
+               echo "100644 blob $o0   c"
+               echo "100644 blob $o0   d/e"
+               echo "100644 $o0 0      a"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o0 0      d/e"
+       ) >expected &&
+       git diff -u expected actual &&
+
+       rm -f b && mkdir b && echo df-1 >b/c && git add b/c &&
+       o3=$(git hash-object b/c) &&
+
+       test_tick &&
+       git commit -m "df-1 makes b/c" &&
+       c3=$(git rev-parse --verify HEAD) &&
+       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       (
+               echo "100644 blob $o0   a"
+               echo "100644 blob $o3   b/c"
+               echo "100644 blob $o0   c"
+               echo "100644 blob $o0   d/e"
+               echo "100644 $o0 0      a"
+               echo "100644 $o3 0      b/c"
+               echo "100644 $o0 0      c"
+               echo "100644 $o0 0      d/e"
+       ) >expected &&
+       git diff -u expected actual
+'
+
+test_expect_success 'setup 4' '
+
+       rm -rf [abcd] &&
+       git checkout df-2 &&
+       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       (
+               echo "100644 blob $o0   a"
+               echo "100644 blob $o0   b"
+               echo "100644 blob $o0   c"
+               echo "100644 blob $o0   d/e"
+               echo "100644 $o0 0      a"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o0 0      d/e"
+       ) >expected &&
+       git diff -u expected actual &&
+
+       rm -f a && mkdir a && echo df-2 >a/c && git add a/c &&
+       o4=$(git hash-object a/c) &&
+
+       test_tick &&
+       git commit -m "df-2 makes a/c" &&
+       c4=$(git rev-parse --verify HEAD) &&
+       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       (
+               echo "100644 blob $o4   a/c"
+               echo "100644 blob $o0   b"
+               echo "100644 blob $o0   c"
+               echo "100644 blob $o0   d/e"
+               echo "100644 $o4 0      a/c"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o0 0      d/e"
+       ) >expected &&
+       git diff -u expected actual
+'
+
+test_expect_success 'setup 5' '
+
+       rm -rf [abcd] &&
+       git checkout remove &&
+       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       (
+               echo "100644 blob $o0   a"
+               echo "100644 blob $o0   b"
+               echo "100644 blob $o0   c"
+               echo "100644 blob $o0   d/e"
+               echo "100644 $o0 0      a"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o0 0      d/e"
+       ) >expected &&
+       git diff -u expected actual &&
+
+       rm -f b &&
+       echo remove-conflict >a &&
+
+       git add a &&
+       git rm b &&
+       o5=$(git hash-object a) &&
+
+       test_tick &&
+       git commit -m "remove removes b and modifies a" &&
+       c5=$(git rev-parse --verify HEAD) &&
+       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       (
+               echo "100644 blob $o5   a"
+               echo "100644 blob $o0   c"
+               echo "100644 blob $o0   d/e"
+               echo "100644 $o5 0      a"
+               echo "100644 $o0 0      c"
+               echo "100644 $o0 0      d/e"
+       ) >expected &&
+       git diff -u expected actual
+
+'
+
+test_expect_success 'setup 6' '
+
+       rm -rf [abcd] &&
+       git checkout df-3 &&
+       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       (
+               echo "100644 blob $o0   a"
+               echo "100644 blob $o0   b"
+               echo "100644 blob $o0   c"
+               echo "100644 blob $o0   d/e"
+               echo "100644 $o0 0      a"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o0 0      d/e"
+       ) >expected &&
+       git diff -u expected actual &&
+
+       rm -fr d && echo df-3 >d && git add d &&
+       o6=$(git hash-object d) &&
+
+       test_tick &&
+       git commit -m "df-3 makes d" &&
+       c6=$(git rev-parse --verify HEAD) &&
+       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       (
+               echo "100644 blob $o0   a"
+               echo "100644 blob $o0   b"
+               echo "100644 blob $o0   c"
+               echo "100644 blob $o6   d"
+               echo "100644 $o0 0      a"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o6 0      d"
+       ) >expected &&
+       git diff -u expected actual
+'
+
+test_expect_success 'merge-recursive simple' '
+
+       rm -fr [abcd] &&
+       git checkout -f "$c2" &&
+
+       git-merge-recursive "$c0" -- "$c2" "$c1"
+       status=$?
+       case "$status" in
+       1)
+               : happy
+               ;;
+       *)
+               echo >&2 "why status $status!!!"
+               false
+               ;;
+       esac
+'
+
+test_expect_success 'merge-recursive result' '
+
+       git ls-files -s >actual &&
+       (
+               echo "100644 $o0 1      a"
+               echo "100644 $o2 2      a"
+               echo "100644 $o1 3      a"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o1 0      d/e"
+       ) >expected &&
+       git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive remove conflict' '
+
+       rm -fr [abcd] &&
+       git checkout -f "$c1" &&
+
+       git-merge-recursive "$c0" -- "$c1" "$c5"
+       status=$?
+       case "$status" in
+       1)
+               : happy
+               ;;
+       *)
+               echo >&2 "why status $status!!!"
+               false
+               ;;
+       esac
+'
+
+test_expect_success 'merge-recursive remove conflict' '
+
+       git ls-files -s >actual &&
+       (
+               echo "100644 $o0 1      a"
+               echo "100644 $o1 2      a"
+               echo "100644 $o5 3      a"
+               echo "100644 $o0 0      c"
+               echo "100644 $o1 0      d/e"
+       ) >expected &&
+       git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f simple' '
+       rm -fr [abcd] &&
+       git reset --hard &&
+       git checkout -f "$c1" &&
+
+       git-merge-recursive "$c0" -- "$c1" "$c3"
+'
+
+test_expect_success 'merge-recursive result' '
+
+       git ls-files -s >actual &&
+       (
+               echo "100644 $o1 0      a"
+               echo "100644 $o3 0      b/c"
+               echo "100644 $o0 0      c"
+               echo "100644 $o1 0      d/e"
+       ) >expected &&
+       git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f conflict' '
+
+       rm -fr [abcd] &&
+       git reset --hard &&
+       git checkout -f "$c1" &&
+
+       git-merge-recursive "$c0" -- "$c1" "$c4"
+       status=$?
+       case "$status" in
+       1)
+               : happy
+               ;;
+       *)
+               echo >&2 "why status $status!!!"
+               false
+               ;;
+       esac
+'
+
+test_expect_success 'merge-recursive d/f conflict result' '
+
+       git ls-files -s >actual &&
+       (
+               echo "100644 $o0 1      a"
+               echo "100644 $o1 2      a"
+               echo "100644 $o4 0      a/c"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o1 0      d/e"
+       ) >expected &&
+       git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f conflict the other way' '
+
+       rm -fr [abcd] &&
+       git reset --hard &&
+       git checkout -f "$c4" &&
+
+       git-merge-recursive "$c0" -- "$c4" "$c1"
+       status=$?
+       case "$status" in
+       1)
+               : happy
+               ;;
+       *)
+               echo >&2 "why status $status!!!"
+               false
+               ;;
+       esac
+'
+
+test_expect_success 'merge-recursive d/f conflict result the other way' '
+
+       git ls-files -s >actual &&
+       (
+               echo "100644 $o0 1      a"
+               echo "100644 $o1 3      a"
+               echo "100644 $o4 0      a/c"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o1 0      d/e"
+       ) >expected &&
+       git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f conflict' '
+
+       rm -fr [abcd] &&
+       git reset --hard &&
+       git checkout -f "$c1" &&
+
+       git-merge-recursive "$c0" -- "$c1" "$c6"
+       status=$?
+       case "$status" in
+       1)
+               : happy
+               ;;
+       *)
+               echo >&2 "why status $status!!!"
+               false
+               ;;
+       esac
+'
+
+test_expect_success 'merge-recursive d/f conflict result' '
+
+       git ls-files -s >actual &&
+       (
+               echo "100644 $o1 0      a"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o6 3      d"
+               echo "100644 $o0 1      d/e"
+               echo "100644 $o1 2      d/e"
+       ) >expected &&
+       git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f conflict' '
+
+       rm -fr [abcd] &&
+       git reset --hard &&
+       git checkout -f "$c6" &&
+
+       git-merge-recursive "$c0" -- "$c6" "$c1"
+       status=$?
+       case "$status" in
+       1)
+               : happy
+               ;;
+       *)
+               echo >&2 "why status $status!!!"
+               false
+               ;;
+       esac
+'
+
+test_expect_success 'merge-recursive d/f conflict result' '
+
+       git ls-files -s >actual &&
+       (
+               echo "100644 $o1 0      a"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o6 2      d"
+               echo "100644 $o0 1      d/e"
+               echo "100644 $o1 3      d/e"
+       ) >expected &&
+       git diff -u expected actual
+
+'
+
+test_expect_success 'reset and 3-way merge' '
+
+       git reset --hard "$c2" &&
+       git read-tree -m "$c0" "$c2" "$c1"
+
+'
+
+test_expect_success 'reset and bind merge' '
+
+       git reset --hard master &&
+       git read-tree --prefix=M/ master &&
+       git ls-files -s >actual &&
+       (
+               echo "100644 $o1 0      M/a"
+               echo "100644 $o0 0      M/b"
+               echo "100644 $o0 0      M/c"
+               echo "100644 $o1 0      M/d/e"
+               echo "100644 $o1 0      a"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o1 0      d/e"
+       ) >expected &&
+       git diff -u expected actual &&
+
+       git read-tree --prefix=a1/ master &&
+       git ls-files -s >actual &&
+       (
+               echo "100644 $o1 0      M/a"
+               echo "100644 $o0 0      M/b"
+               echo "100644 $o0 0      M/c"
+               echo "100644 $o1 0      M/d/e"
+               echo "100644 $o1 0      a"
+               echo "100644 $o1 0      a1/a"
+               echo "100644 $o0 0      a1/b"
+               echo "100644 $o0 0      a1/c"
+               echo "100644 $o1 0      a1/d/e"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o1 0      d/e"
+       ) >expected &&
+       git diff -u expected actual
+
+       git read-tree --prefix=z/ master &&
+       git ls-files -s >actual &&
+       (
+               echo "100644 $o1 0      M/a"
+               echo "100644 $o0 0      M/b"
+               echo "100644 $o0 0      M/c"
+               echo "100644 $o1 0      M/d/e"
+               echo "100644 $o1 0      a"
+               echo "100644 $o1 0      a1/a"
+               echo "100644 $o0 0      a1/b"
+               echo "100644 $o0 0      a1/c"
+               echo "100644 $o1 0      a1/d/e"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o1 0      d/e"
+               echo "100644 $o1 0      z/a"
+               echo "100644 $o0 0      z/b"
+               echo "100644 $o0 0      z/c"
+               echo "100644 $o1 0      z/d/e"
+       ) >expected &&
+       git diff -u expected actual
+
+'
+
+test_done
+
index e31cf93a00ab377355734b3d88d536d36fe734e1..0a97b75288d44cf93e0a8f8d9ab1b76715f946d1 100755 (executable)
@@ -84,6 +84,30 @@ test_expect_success \
     'When the rm in "git-rm -f" fails, it should not remove the file from the index' \
     'git-ls-files --error-unmatch baz'
 
+test_expect_success 'Remove nonexistent file with --ignore-unmatch' '
+       git rm --ignore-unmatch nonexistent
+'
+
+test_expect_success '"rm" command printed' '
+       echo frotz > test-file &&
+       git add test-file &&
+       git commit -m "add file for rm test" &&
+       git rm test-file > rm-output &&
+       test `egrep "^rm " rm-output | wc -l` = 1 &&
+       rm -f test-file rm-output &&
+       git commit -m "remove file from rm test"
+'
+
+test_expect_success '"rm" command suppressed with --quiet' '
+       echo frotz > test-file &&
+       git add test-file &&
+       git commit -m "add file for rm --quiet test" &&
+       git rm --quiet test-file > rm-output &&
+       test `wc -l < rm-output` = 0 &&
+       rm -f test-file rm-output &&
+       git commit -m "remove file from rm --quiet test"
+'
+
 # Now, failure cases.
 test_expect_success 'Re-add foo and baz' '
        git add foo baz &&
@@ -154,4 +178,8 @@ test_expect_success 'Recursive with -r -f' '
        ! test -d frotz
 '
 
+test_expect_failure 'Remove nonexistent file returns nonzero exit status' '
+       git rm nonexistent
+'
+
 test_done
index 488e075c16611e5e6132744939574cdd878c3959..8f4c29a6b5a263d6f18d95757b76ac55e9443068 100755 (executable)
@@ -241,6 +241,7 @@ format-patch --attach --stdout initial..master
 format-patch --inline --stdout initial..side
 format-patch --inline --stdout initial..master^
 format-patch --inline --stdout initial..master
+format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
 
 diff --abbrev initial..side
 diff -r initial..side
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
new file mode 100644 (file)
index 0000000..a8093be
--- /dev/null
@@ -0,0 +1,164 @@
+$ git format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [TESTCASE] Second
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+
+This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [TESTCASE] Third
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [TESTCASE] Side
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
diff --git a/t/t4121-apply-diffs.sh b/t/t4121-apply-diffs.sh
new file mode 100755 (executable)
index 0000000..2b2f1ed
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+test_description='git-apply for contextually independent diffs'
+. ./test-lib.sh
+
+echo '1
+2
+3
+4
+5
+6
+7
+8' >file
+
+test_expect_success 'setup' \
+       'git add file &&
+       git commit -q -m 1 &&
+       git checkout -b test &&
+       mv file file.tmp &&
+       echo 0 >file &&
+       cat file.tmp >>file &&
+       rm file.tmp &&
+       git commit -a -q -m 2 &&
+       echo 9 >>file &&
+       git commit -a -q -m 3 &&
+       git checkout master'
+
+test_expect_success \
+       'check if contextually independent diffs for the same file apply' \
+       '( git diff test~2 test~1; git diff test~1 test~0 )| git apply'
+
+test_done
+
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
new file mode 100755 (executable)
index 0000000..c27e39c
--- /dev/null
@@ -0,0 +1,50 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Johannes E. Schindelin
+#
+
+test_description='git-shortlog
+'
+
+. ./test-lib.sh
+
+echo 1 > a1
+git add a1
+tree=$(git write-tree)
+commit=$( (echo "Test"; echo) | git commit-tree $tree )
+git update-ref HEAD $commit
+
+echo 2 > a1
+git commit -m "This is a very, very long first line for the commit message to see if it is wrapped correctly" a1
+
+# test if the wrapping is still valid when replacing all i's by treble clefs.
+echo 3 > a1
+git commit -m "$(echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" | sed "s/i/1234/g" | tr 1234 '\360\235\204\236')" a1
+
+# now fsck up the utf8
+git repo-config i18n.commitencoding non-utf-8
+echo 4 > a1
+git commit -m "$(echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" | sed "s/i/1234/g" | tr 1234 '\370\235\204\236')" a1
+
+echo 5 > a1
+git commit -m "a                                                               12      34      56      78" a1
+
+git shortlog -w HEAD > out
+
+cat > expect << EOF
+A U Thor (5):
+      Test
+      This is a very, very long first line for the commit message to see if
+         it is wrapped correctly
+      Th𝄞s 𝄞s a very, very long f𝄞rst l𝄞ne for the comm𝄞t message to see 𝄞f
+         𝄞t 𝄞s wrapped correctly
+      Thø\9d\84\9es ø\9d\84\9es a very, very long fø\9d\84\9erst lø\9d\84\9ene for the commø\9d\84\9et
+         message to see ø\9d\84\9ef ø\9d\84\9et ø\9d\84\9es wrapped correctly
+      a                                                                12      34
+         56    78
+
+EOF
+
+test_expect_success 'shortlog wrapping' 'diff -u expect out'
+
+test_done
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
new file mode 100755 (executable)
index 0000000..13e9379
--- /dev/null
@@ -0,0 +1,102 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Christian Couder
+#
+test_description='Tests git-bisect functionality'
+
+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
+
+    git-commit -m "$MSG" $_file
+}
+
+HASH1=
+HASH3=
+HASH4=
+
+test_expect_success \
+    'set up basic repo with 1 file (hello) and 4 commits' \
+    'add_line_into_file "1: Hello World" hello &&
+     add_line_into_file "2: A new day for git" hello &&
+     add_line_into_file "3: Another new day for git" hello &&
+     add_line_into_file "4: Ciao for now" hello &&
+     HASH1=$(git rev-list HEAD | tail -1) &&
+     HASH3=$(git rev-list HEAD | head -2 | tail -1) &&
+     HASH4=$(git rev-list HEAD | head -1)'
+
+test_expect_success 'bisect starts with only one bad' '
+       git bisect reset &&
+       git bisect start &&
+       git bisect bad $HASH4 &&
+       git bisect next
+'
+
+test_expect_success 'bisect does not start with only one good' '
+       git bisect reset &&
+       git bisect start &&
+       git bisect good $HASH1 || return 1
+
+       if git bisect next
+       then
+               echo Oops, should have failed.
+               false
+       else
+               :
+       fi
+'
+
+test_expect_success 'bisect start with one bad and good' '
+       git bisect reset &&
+       git bisect start &&
+       git bisect good $HASH1 &&
+       git bisect bad $HASH4 &&
+       git bisect next
+'
+
+# We want to automatically find the commit that
+# introduced "Another" into hello.
+test_expect_success \
+    '"git bisect run" simple case' \
+    'echo "#"\!"/bin/sh" > test_script.sh &&
+     echo "grep Another hello > /dev/null" >> test_script.sh &&
+     echo "test \$? -ne 0" >> test_script.sh &&
+     chmod +x test_script.sh &&
+     git bisect start &&
+     git bisect good $HASH1 &&
+     git bisect bad $HASH4 &&
+     git bisect run ./test_script.sh > my_bisect_log.txt &&
+     grep "$HASH3 is first bad commit" my_bisect_log.txt &&
+     git bisect reset'
+
+# We want to automatically find the commit that
+# introduced "Ciao" into hello.
+test_expect_success \
+    '"git bisect run" with more complex "git bisect start"' \
+    'echo "#"\!"/bin/sh" > test_script.sh &&
+     echo "grep Ciao hello > /dev/null" >> test_script.sh &&
+     echo "test \$? -ne 0" >> test_script.sh &&
+     chmod +x test_script.sh &&
+     git bisect start $HASH4 $HASH1 &&
+     git bisect run ./test_script.sh > my_bisect_log.txt &&
+     grep "$HASH4 is first bad commit" my_bisect_log.txt &&
+     git bisect reset'
+
+#
+#
+test_done
+
diff --git a/t/t6030-bisect-run.sh b/t/t6030-bisect-run.sh
deleted file mode 100755 (executable)
index de31235..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2007 Christian Couder
-#
-test_description='Tests git-bisect functionality'
-
-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
-
-    git-commit -m "$MSG" $_file
-}
-
-HASH1=
-HASH3=
-HASH4=
-
-test_expect_success \
-    'set up basic repo with 1 file (hello) and 4 commits' \
-    'add_line_into_file "1: Hello World" hello &&
-     add_line_into_file "2: A new day for git" hello &&
-     add_line_into_file "3: Another new day for git" hello &&
-     add_line_into_file "4: Ciao for now" hello &&
-     HASH1=$(git rev-list HEAD | tail -1) &&
-     HASH3=$(git rev-list HEAD | head -2 | tail -1) &&
-     HASH4=$(git rev-list HEAD | head -1)'
-
-test_expect_success 'bisect starts with only one bad' '
-       git bisect reset &&
-       git bisect start &&
-       git bisect bad $HASH4 &&
-       git bisect next
-'
-
-test_expect_success 'bisect starts with only one good' '
-       git bisect reset &&
-       git bisect start &&
-       git bisect good $HASH1 || return 1
-
-       if git bisect next
-       then
-               echo Oops, should have failed.
-               false
-       else
-               :
-       fi
-'
-
-test_expect_success 'bisect start with one bad and good' '
-       git bisect reset &&
-       git bisect start &&
-       git bisect good $HASH1 &&
-       git bisect bad $HASH4 &&
-       git bisect next
-'
-
-# We want to automatically find the commit that
-# introduced "Another" into hello.
-test_expect_success \
-    '"git bisect run" simple case' \
-    'echo "#"\!"/bin/sh" > test_script.sh &&
-     echo "grep Another hello > /dev/null" >> test_script.sh &&
-     echo "test \$? -ne 0" >> test_script.sh &&
-     chmod +x test_script.sh &&
-     git bisect start &&
-     git bisect good $HASH1 &&
-     git bisect bad $HASH4 &&
-     git bisect run ./test_script.sh > my_bisect_log.txt &&
-     grep "$HASH3 is first bad commit" my_bisect_log.txt &&
-     git bisect reset'
-
-# We want to automatically find the commit that
-# introduced "Ciao" into hello.
-test_expect_success \
-    '"git bisect run" with more complex "git bisect start"' \
-    'echo "#"\!"/bin/sh" > test_script.sh &&
-     echo "grep Ciao hello > /dev/null" >> test_script.sh &&
-     echo "test \$? -ne 0" >> test_script.sh &&
-     chmod +x test_script.sh &&
-     git bisect start $HASH4 $HASH1 &&
-     git bisect run ./test_script.sh > my_bisect_log.txt &&
-     grep "$HASH4 is first bad commit" my_bisect_log.txt &&
-     git bisect reset'
-
-#
-#
-test_done
-
old mode 100755 (executable)
new mode 100644 (file)
index 0ff03309e69aed6b506e333e087f23099ad80290..9d3795c6d055d3ca2b50ac0607607037cb2b8142 100644 (file)
@@ -34,13 +34,19 @@ fi
 allowunannotated=$(git-repo-config --bool hooks.allowunannotated)
 
 # check for no description
+projectdesc=$(sed -e '1p' "$GIT_DIR/description")
 if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb" ]; then
        echo "*** Project description file hasn't been set" >&2
        exit 1
 fi
 
 # --- Check types
-newrev_type=$(git-cat-file -t $newrev)
+# if $newrev is 0000...0000, it's a commit to delete a branch
+if [ -z "${newrev##0*}" ]; then
+       newrev_type=commit
+else
+       newrev_type=$(git-cat-file -t $newrev)
+fi
 
 case "$refname","$newrev_type" in
        refs/tags/*,commit)
index a0b676903ad042bfc1bb199a33f1658cdfd511c9..5139481358d0838a95db3fdb62e32221eb7343b1 100644 (file)
@@ -665,7 +665,6 @@ int threeway_merge(struct cache_entry **stages,
        int count;
        int head_match = 0;
        int remote_match = 0;
-       const char *path = NULL;
 
        int df_conflict_head = 0;
        int df_conflict_remote = 0;
@@ -675,13 +674,10 @@ int threeway_merge(struct cache_entry **stages,
        int i;
 
        for (i = 1; i < o->head_idx; i++) {
-               if (!stages[i])
+               if (!stages[i] || stages[i] == o->df_conflict_entry)
                        any_anc_missing = 1;
-               else {
-                       if (!path)
-                               path = stages[i]->name;
+               else
                        no_anc_exists = 0;
-               }
        }
 
        index = stages[0];
@@ -697,13 +693,6 @@ int threeway_merge(struct cache_entry **stages,
                remote = NULL;
        }
 
-       if (!path && index)
-               path = index->name;
-       if (!path && head)
-               path = head->name;
-       if (!path && remote)
-               path = remote->name;
-
        /* First, if there's a #16 situation, note that to prevent #13
         * and #14.
         */
@@ -755,6 +744,23 @@ int threeway_merge(struct cache_entry **stages,
        if (o->aggressive) {
                int head_deleted = !head && !df_conflict_head;
                int remote_deleted = !remote && !df_conflict_remote;
+               const char *path = NULL;
+
+               if (index)
+                       path = index->name;
+               else if (head)
+                       path = head->name;
+               else if (remote)
+                       path = remote->name;
+               else {
+                       for (i = 1; i < o->head_idx; i++) {
+                               if (stages[i] && stages[i] != o->df_conflict_entry) {
+                                       path = stages[i]->name;
+                                       break;
+                               }
+                       }
+               }
+
                /*
                 * Deleted in both.
                 * Deleted in one and unchanged in the other.
@@ -786,11 +792,11 @@ int threeway_merge(struct cache_entry **stages,
 
        o->nontrivial_merge = 1;
 
-       /* #2, #3, #4, #6, #7, #9, #11. */
+       /* #2, #3, #4, #6, #7, #9, #10, #11. */
        count = 0;
        if (!head_match || !remote_match) {
                for (i = 1; i < o->head_idx; i++) {
-                       if (stages[i]) {
+                       if (stages[i] && stages[i] != o->df_conflict_entry) {
                                keep_entry(stages[i], o);
                                count++;
                                break;