Merge branch 'ap/clone-into-empty'
authorJunio C Hamano <gitster@pobox.com>
Sun, 18 Jan 2009 07:05:54 +0000 (23:05 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sun, 18 Jan 2009 07:05:54 +0000 (23:05 -0800)
* ap/clone-into-empty:
Allow cloning to an existing empty directory
add is_dot_or_dotdot inline function

68 files changed:
Documentation/RelNotes-1.6.1.1.txt
Documentation/RelNotes-1.6.2.txt
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-cherry.txt
Documentation/git-describe.txt
Documentation/git-diff-files.txt
Documentation/git-filter-branch.txt
Documentation/git-ls-files.txt
Documentation/git-ls-tree.txt
Documentation/git-mergetool.txt
Documentation/git-push.txt
Documentation/gitcore-tutorial.txt
Documentation/githooks.txt
Documentation/howto/rebase-from-internal-branch.txt
Documentation/pretty-options.txt
Makefile
builtin-add.c
builtin-commit.c
builtin-fsck.c
builtin-gc.c
builtin-grep.c
builtin-log.c
builtin-ls-tree.c
builtin-mv.c
builtin-shortlog.c
bundle.c
cache.h
configure.ac
contrib/completion/git-completion.bash
contrib/difftool/git-difftool [new file with mode: 0755]
contrib/difftool/git-difftool-helper [new file with mode: 0755]
contrib/difftool/git-difftool.txt [new file with mode: 0644]
contrib/examples/README [new file with mode: 0644]
contrib/vim/README
diff-lib.c
diff-no-index.c
fast-import.c
git-bisect.sh
git-cvsserver.perl
git-filter-branch.sh
git-rebase--interactive.sh
git-sh-setup.sh
git.c
grep.c
grep.h
http-push.c
perl/Git.pm
pretty.c
sha1_file.c
t/lib-httpd/apache.conf
t/t1001-read-tree-m-2way.sh
t/t2300-cd-to-toplevel.sh
t/t3404-rebase-interactive.sh
t/t3411-rebase-preserve-around-merges.sh [changed mode: 0644->0755]
t/t3501-revert-cherry-pick.sh
t/t4013-diff-various.sh
t/t4013/diff.diff_--no-index_--name-status_--_dir2_dir [new file with mode: 0644]
t/t5540-http-push.sh
t/t5704-bundle.sh [new file with mode: 0755]
t/t7001-mv.sh
t/t7002-grep.sh
t/t7003-filter-branch.sh
t/t7400-submodule-basic.sh
t/t7500-commit.sh
t/t7501-commit.sh
t/t9130-git-svn-authors-file.sh [new file with mode: 0755]
unpack-trees.c
index 10b38e6ec1cd588c3e8ac870ba0e843db42a8cee..5cd1ca9cc64717a6b8623fc69dc11362c3c64689 100644 (file)
@@ -4,20 +4,46 @@ GIT v1.6.1.1 Release Notes
 Fixes since v1.6.1
 ------------------
 
+* "git apply" took file modes from the patch text and updated the mode
+  bits of the target tree even when the patch was not about mode changes.
+
+* "git checkout $tree" did not trigger an error.
+
+* "git commit" tried to remove COMMIT_EDITMSG from the work tree by mistake.
+
 * "git describe --all" complained when a commit is described with a tag,
   which was nonsense.
 
+* "git fsck branch" did not work as advertised; instead it behaved the same
+  way as "git fsck".
+
 * "git log --pretty=format:%s" did not handle a multi-line subject the
   same way as built-in log listers (i.e. shortlog, --pretty=oneline, etc.)
 
 * "git daemon", and "git merge-file" are more careful when freopen fails
   and barf, instead of going on and writing to unopened filehandle.
 
-Other documentation fixes.
+* "git http-push" did not like some RFC 4918 compliant DAV server
+  responses.
+
+* "git merge -s recursive" mistakenly overwritten an untracked file in the
+  work tree upon delete/modify conflict.
+
+* "git merge -s recursive" didn't leave the index unmerged for entries with
+  rename/delete conflictd.
+
+* "git merge -s recursive" clobbered untracked files in the work tree.
+
+* "git mv -k" with more than one errorneous paths misbehaved.
+
+* "git rebase -i" issued an unnecessary error message upon a user error of
+  marking the first commit to be "squash"ed.
+
+Other documentation updates.
 
 ---
 exec >/var/tmp/1
-O=v1.6.1-15-ga9e67c8
+O=v1.6.1-60-g78f111e
 echo O=$(git describe maint)
 git shortlog --no-merges $O..maint
 
index 1a80626781ab8226c58c0de291edd6ed3d76141e..296804301f38a21baa260c1e5fda41f03c51a88b 100644 (file)
@@ -10,8 +10,33 @@ Updates since v1.6.1
 
 (performance)
 
+* pack-objects autodetects the number of CPUs available and uses threaded
+  version.
+
 (usability, bells and whistles)
 
+* "git-add -p" learned 'g'oto action to jump directly to a hunk.
+
+* git-cherry defaults to HEAD when the <upstream> argument is not given.
+
+* git-cvsserver can be told not to add extra "via git-CVS emulator" to the
+  commit log message it serves via gitcvs.commitmsgannotation configuration.
+
+* git-diff learned a new option --inter-hunk-context to coalesce close
+  hunks together and show context between them.
+
+* git-filter-branch learned --prune-empty option that discards commits
+  that do not change the contents.
+
+* git-ls-tree learned --full-tree option that shows the path in full
+  regardless of where in the work tree hierarchy the command was started.
+
+* git-mergetool learned -y(--no-prompt) option to disable prompting.
+
+* "git-reset --merge" is a new mode that works similar to the way
+  "git checkout" switches branches, taking the local changes while
+  switching to another commit.
+
 (internal)
 
 
@@ -21,8 +46,20 @@ Fixes since v1.6.1
 All of the fixes in v1.6.1.X maintenance series are included in this
 release, unless otherwise noted.
 
+* "git-add sub/file" when sub is a submodule incorrectly added the path to
+  the superproject.
+
+* git-bundle did not exclude annotated tags even when a range given from the
+  command line wanted to.
+
+* git-grep did not work correctly for index entries with assume-unchanged bit.
+
+* branch switching and merges had a silly bug that did not validate
+  the correct directory when making sure an existing subdirectory is
+  clean.
+
 --
 exec >/var/tmp/1
-O=v1.6.1
+O=v1.6.1-134-ge98c6a1
 echo O=$(git describe master)
 git shortlog --no-merges $O..master ^maint
index 7408bb2d34441dede61718e5d77dd08e26de3731..6b3ac5aa90757cde33f6a5d442c570aee06d2f0b 100644 (file)
@@ -702,7 +702,9 @@ gc.packrefs::
 
 gc.pruneexpire::
        When 'git-gc' is run, it will call 'prune --expire 2.weeks.ago'.
-       Override the grace period with this config variable.
+       Override the grace period with this config variable.  The value
+       "now" may be used to disable this  grace period and always prune
+       unreachable objects immediately.
 
 gc.reflogexpire::
        'git-reflog expire' removes reflog entries older than
@@ -723,6 +725,10 @@ gc.rerereunresolved::
        kept for this many days when 'git-rerere gc' is run.
        The default is 15 days.  See linkgit:git-rerere[1].
 
+gitcvs.commitmsgannotation::
+       Append this string to each commit message. Set to empty string
+       to disable this feature. Defaults to "via git-CVS emulator".
+
 gitcvs.enabled::
        Whether the CVS server interface is enabled for this repository.
        See linkgit:git-cvsserver[1].
index 671f533ca30c1fb0ddae8a15f9c586c431c8fef5..43793d75005a7af875a055d377a8ab6a13510682 100644 (file)
@@ -116,7 +116,7 @@ endif::git-format-patch[]
 --abbrev[=<n>]::
        Instead of showing the full 40-byte hexadecimal object
        name in diff-raw format output and diff-tree header
-       lines, show only handful hexdigits prefix.  This is
+       lines, show only a partial prefix.  This is
        independent of --full-index option above, which controls
        the diff-patch output format.  Non default number of
        digits can be specified with --abbrev=<n>.
index 74d14c4e7fc88e702e4781b22eb8ed5ce0480c6f..7deefdae8f995d843971f6beff24af8325b99f1f 100644 (file)
@@ -7,7 +7,7 @@ git-cherry - Find commits not merged upstream
 
 SYNOPSIS
 --------
-'git cherry' [-v] <upstream> [<head>] [<limit>]
+'git cherry' [-v] [<upstream> [<head> [<limit>]]]
 
 DESCRIPTION
 -----------
@@ -51,6 +51,7 @@ OPTIONS
 
 <upstream>::
        Upstream branch to compare against.
+       Defaults to the first tracked remote branch, if available.
 
 <head>::
        Working branch; defaults to HEAD.
index 3d79f05995d28214f68640dd3f693033dff03547..a99b4ef9434f11d46cecbd80cca9548866c1aa48 100644 (file)
@@ -87,7 +87,7 @@ With something like git.git current tree, I get:
        v1.0.4-14-g2414721
 
 i.e. the current head of my "parent" branch is based on v1.0.4,
-but since it has a handful commits on top of that,
+but since it has a few commits on top of that,
 describe has added the number of additional commits ("14") and
 an abbreviated object name for the commit itself ("2414721")
 at the end.
index 5c8c1d95a89b15e936816f486a8114cbc6788fb9..c5261415643d359648900e17f522ba7b96fed44a 100644 (file)
@@ -21,7 +21,10 @@ OPTIONS
 -------
 include::diff-options.txt[]
 
--1 -2 -3 or --base --ours --theirs, and -0::
+-1 --base::
+-2 --ours::
+-3 --theirs::
+-0::
        Diff against the "base" version, "our branch" or "their
        branch" respectively.  With these options, diffs for
        merged entries are not shown.
index fed6de6a7fa0e720994f7094be2f464818af289a..451950bab67702a236c55e77d6f3a0723a812488 100644 (file)
@@ -122,6 +122,10 @@ You can use the 'map' convenience function in this filter, and other
 convenience functions, too.  For example, calling 'skip_commit "$@"'
 will leave out the current commit (but not its changes! If you want
 that, use 'git-rebase' instead).
++
+You can also use the 'git_commit_non_empty_tree "$@"' instead of
+'git commit-tree "$@"' if you don't wish to keep commits with a single parent
+and that makes no change to the tree.
 
 --tag-name-filter <command>::
        This is the filter for rewriting tag names. When passed,
@@ -151,6 +155,16 @@ to other tags will be rewritten to point to the underlying commit.
        The result will contain that directory (and only that) as its
        project root.
 
+--prune-empty::
+       Some kind of filters will generate empty commits, that left the tree
+       untouched.  This switch allow git-filter-branch to ignore such
+       commits.  Though, this switch only applies for commits that have one
+       and only one parent, it will hence keep merges points. Also, this
+       option is not compatible with the use of '--commit-filter'. Though you
+       just need to use the function 'git_commit_non_empty_tree "$@"' instead
+       of the 'git commit-tree "$@"' idiom in your commit filter to make that
+       happen.
+
 --original <namespace>::
        Use this option to set the namespace where the original commits
        will be stored. The default value is 'refs/original'.
index 9f85d60b5fb6d6ae1b4d8c2e65a6131cbe21450b..057a021eb50899ed5fe1ee5a7b6c59e7fc27becf 100644 (file)
@@ -126,7 +126,7 @@ OPTIONS
 
 --abbrev[=<n>]::
        Instead of showing the full 40-byte hexadecimal object
-       lines, show only handful hexdigits prefix.
+       lines, show only a partial prefix.
        Non default number of digits can be specified with --abbrev=<n>.
 
 \--::
index 4c7262f1cd82ca8d9ea6be638d23b18d9bba3738..f68e5c5c1abd15350e679eebf976f0ac8516123b 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git ls-tree' [-d] [-r] [-t] [-l] [-z]
-           [--name-only] [--name-status] [--full-name] [--abbrev=[<n>]]
+           [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev=[<n>]]
            <tree-ish> [paths...]
 
 DESCRIPTION
@@ -30,6 +30,8 @@ in the current working directory.  Note that:
    'sub/dir' in 'HEAD').  You don't want to give a tree that is not at the
    root level (e.g. 'git ls-tree -r HEAD:sub dir') in this case, as that
    would result in asking for 'sub/sub/dir' in the 'HEAD' commit.
+   However, the current working directory can be ignored by passing
+   --full-tree option.
 
 OPTIONS
 -------
@@ -59,13 +61,17 @@ OPTIONS
 
 --abbrev[=<n>]::
        Instead of showing the full 40-byte hexadecimal object
-       lines, show only handful hexdigits prefix.
+       lines, show only a partial prefix.
        Non default number of digits can be specified with --abbrev=<n>.
 
 --full-name::
        Instead of showing the path names relative to the current working
        directory, show the full path names.
 
+--full-tree::
+       Do not limit the listing to the current working directory.
+       Implies --full-name.
+
 paths::
        When paths are given, show them (note that this isn't really raw
        pathnames, but rather a list of patterns to match).  Otherwise
index 4c0ffec507185dfe1e88683e1a23066089571d2c..5d3c6328726ba1af829026dd97840fc43335cfe0 100644 (file)
@@ -22,7 +22,8 @@ with merge conflicts.
 
 OPTIONS
 -------
--t or --tool=<tool>::
+-t <tool>::
+--tool=<tool>::
        Use the merge resolution program specified by <tool>.
        Valid merge tools are:
        kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
@@ -60,7 +61,8 @@ variable `mergetool.<tool>.trustExitCode` can be set to `true`.
 Otherwise, 'git-mergetool' will prompt the user to indicate the
 success of the resolution after the custom tool has exited.
 
--y or --no-prompt::
+-y::
+--no-prompt::
        Don't prompt before each invocation of the merge resolution
        program.
 
index 6150b1b959e17655a2875d39ec3b70449684a0eb..3321966c6b802e575fcd3f67969ccc4e8572925e 100644 (file)
@@ -86,14 +86,12 @@ nor in any Push line of the corresponding remotes file---see below).
        line.
 
 --receive-pack=<git-receive-pack>::
+--exec=<git-receive-pack>::
        Path to the 'git-receive-pack' program on the remote
        end.  Sometimes useful when pushing to a remote
        repository over ssh, and you do not have the program in
        a directory on the default $PATH.
 
---exec=<git-receive-pack>::
-       Same as \--receive-pack=<git-receive-pack>.
-
 -f::
 --force::
        Usually, the command refuses to update a remote ref that is
index e4dd5518c81ac98ef3da9cbb1cea4a9e1fe6e62c..7ba5e589d7e824c526482c9707a5c26ac730cc9e 100644 (file)
@@ -1243,10 +1243,10 @@ $ git ls-files --stage
 ------------
 
 In our example of only two files, we did not have unchanged
-files so only 'example' resulted in collapsing, but in real-life
-large projects, only small number of files change in one commit,
-and this 'collapsing' tends to trivially merge most of the paths
-fairly quickly, leaving only a handful the real changes in non-zero
+files so only 'example' resulted in collapsing.  But in real-life
+large projects, when only a small number of files change in one commit,
+this 'collapsing' tends to trivially merge most of the paths
+fairly quickly, leaving only a handful of real changes in non-zero
 stages.
 
 To look at only non-zero stages, use `\--unmerged` flag:
index cfdae1efa2d446e76218df7a2586a26789a151f6..1fd512bca2e12f5eb0abac0edb0dfb2a86949cec 100644 (file)
@@ -15,7 +15,7 @@ DESCRIPTION
 
 Hooks are little scripts you can place in `$GIT_DIR/hooks`
 directory to trigger action at certain points.  When
-'git-init' is run, a handful example hooks are copied in the
+'git-init' is run, a handful of example hooks are copied into the
 `hooks` directory of the new repository, but by default they are
 all disabled.  To enable a hook, rename it by removing its `.sample`
 suffix.
@@ -90,7 +90,7 @@ This hook is invoked by 'git-commit' right after preparing the
 default log message, and before the editor is started.
 
 It takes one to three parameters.  The first is the name of the file
-that the commit log message.  The second is the source of the commit
+that contains the commit log message.  The second is the source of the commit
 message, and can be: `message` (if a `-m` or `-F` option was
 given); `template` (if a `-t` option was given or the
 configuration option `commit.template` is set); `merge` (if the
index d214d4bf9d0e539c6bf58ba24dcd12aabbaea1d8..74a1c0c4ba3a03ba02cbbabcf0ee6cff22e4b099 100644 (file)
@@ -27,7 +27,7 @@ the kind of task StGIT is designed to do.
 I just have done a simpler one, this time using only the core
 GIT tools.
 
-I had a handful commits that were ahead of master in pu, and I
+I had a handful of commits that were ahead of master in pu, and I
 wanted to add some documentation bypassing my usual habit of
 placing new things in pu first.  At the beginning, the commit
 ancestry graph looked like this:
index 6d66c74cc11e6622892061f8328d04dfe38f87bf..5f21efe40745a98eafff7df67a916b1f1193fae4 100644 (file)
@@ -10,7 +10,7 @@ configuration (see linkgit:git-config[1]).
 
 --abbrev-commit::
        Instead of showing the full 40-byte hexadecimal commit object
-       name, show only handful hexdigits prefix.  Non default number of
+       name, show only a partial prefix.  Non default number of
        digits can be specified with "--abbrev=<n>" (which also modifies
        diff output, if it is displayed).
 +
index dee97c1b010503acb170be08b721e69e45f5d569..2b873fa99f8ddc70c7560fd64a516a051afc3a7a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1356,7 +1356,14 @@ endif
 
 ### Testing rules
 
-TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-parse-options$X test-path-utils$X
+TEST_PROGRAMS += test-chmtime$X
+TEST_PROGRAMS += test-date$X
+TEST_PROGRAMS += test-delta$X
+TEST_PROGRAMS += test-genrandom$X
+TEST_PROGRAMS += test-match-trees$X
+TEST_PROGRAMS += test-parse-options$X
+TEST_PROGRAMS += test-path-utils$X
+TEST_PROGRAMS += test-sha1$X
 
 all:: $(TEST_PROGRAMS)
 
index 719de8b0f2d2d831f326d948aa18700e5c474950..ac98c8354d84a7f556c22c53fbe9007832ac4346 100644 (file)
@@ -68,6 +68,33 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
         free(seen);
 }
 
+static void treat_gitlinks(const char **pathspec)
+{
+       int i;
+
+       if (!pathspec || !*pathspec)
+               return;
+
+       for (i = 0; i < active_nr; i++) {
+               struct cache_entry *ce = active_cache[i];
+               if (S_ISGITLINK(ce->ce_mode)) {
+                       int len = ce_namelen(ce), j;
+                       for (j = 0; pathspec[j]; j++) {
+                               int len2 = strlen(pathspec[j]);
+                               if (len2 <= len || pathspec[j][len] != '/' ||
+                                   memcmp(ce->name, pathspec[j], len))
+                                       continue;
+                               if (len2 == len + 1)
+                                       /* strip trailing slash */
+                                       pathspec[j] = xstrndup(ce->name, len);
+                               else
+                                       die ("Path '%s' is in submodule '%.*s'",
+                                               pathspec[j], len, ce->name);
+                       }
+               }
+       }
+}
+
 static void fill_directory(struct dir_struct *dir, const char **pathspec,
                int ignored_too)
 {
@@ -261,6 +288,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
        if (read_cache() < 0)
                die("index file corrupt");
+       treat_gitlinks(pathspec);
 
        if (add_new_files)
                /* This picks up the paths that are not tracked */
index e88b78f8114e48c542a52bdc42b9c573a7e94cad..2f0b00a174ff970939d2758334cb8d1dca41cb42 100644 (file)
@@ -624,7 +624,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
        if (!commitable && !in_merge && !allow_empty &&
            !(amend && is_a_merge(head_sha1))) {
                run_status(stdout, index_file, prefix, 0);
-               unlink(commit_editmsg);
                return 0;
        }
 
@@ -866,6 +865,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        if (wt_status_use_color == -1)
                wt_status_use_color = git_use_color_default;
 
+       if (diff_use_color_default == -1)
+               diff_use_color_default = git_use_color_default;
+
        argc = parse_and_validate_options(argc, argv, builtin_status_usage, prefix);
 
        index_file = prepare_index(argc, argv, prefix);
@@ -945,6 +947,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
        git_config(git_commit_config, NULL);
 
+       if (wt_status_use_color == -1)
+               wt_status_use_color = git_use_color_default;
+
        argc = parse_and_validate_options(argc, argv, builtin_commit_usage, prefix);
 
        index_file = prepare_index(argc, argv, prefix);
index 79b87edaf9326dee7544e04ab8d2ef3c1e82b449..aecc8280a0aedb37437ccce446f3382ccba08829 100644 (file)
@@ -622,7 +622,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
        }
 
        heads = 0;
-       for (i = 1; i < argc; i++) {
+       for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
                if (!get_sha1(arg, head_sha1)) {
                        struct object *obj = lookup_object(head_sha1);
index 781df601c5f95e874297e0e8bec3fddeb929cc2d..f8eae4adb41d9c338c07aa161e7305bb16742a1e 100644 (file)
@@ -188,7 +188,9 @@ static int need_to_gc(void)
         * there is no need.
         */
        if (too_many_packs())
-               append_option(argv_repack, "-A", MAX_ADD);
+               append_option(argv_repack,
+                             !strcmp(prune_expire, "now") ? "-a" : "-A",
+                             MAX_ADD);
        else if (!too_many_loose_objects())
                return 0;
 
@@ -243,7 +245,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                        "run \"git gc\" manually. See "
                        "\"git help gc\" for more information.\n");
        } else
-               append_option(argv_repack, "-A", MAX_ADD);
+               append_option(argv_repack,
+                             !strcmp(prune_expire, "now") ? "-a" : "-A",
+                             MAX_ADD);
 
        if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
                return error(FAILED_RUN, argv_pack_refs[0]);
index 624f86e287cf6304d122850f8258444c5f916702..bebf15cd6f7d82b773f985ce238688b4759e3c37 100644 (file)
@@ -20,6 +20,8 @@
 #endif
 #endif
 
+static int builtin_grep;
+
 /*
  * git grep pathspecs are somewhat different from diff-tree pathspecs;
  * pathname wildcards are allowed.
@@ -389,7 +391,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
         * we grep through the checked-out files. It tends to
         * be a lot more optimized
         */
-       if (!cached) {
+       if (!cached && !builtin_grep) {
                hit = external_grep(opt, paths, cached);
                if (hit >= 0)
                        return hit;
@@ -402,7 +404,12 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
                        continue;
                if (!pathspec_matches(paths, ce->name))
                        continue;
-               if (cached) {
+               /*
+                * If CE_VALID is on, we assume worktree file and its cache entry
+                * are identical, even if worktree file has been modified, so use
+                * cache version instead
+                */
+               if (cached || (ce->ce_flags & CE_VALID)) {
                        if (ce_stage(ce))
                                continue;
                        hit |= grep_sha1(opt, ce->sha1, ce->name, 0);
@@ -545,6 +552,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        cached = 1;
                        continue;
                }
+               if (!strcmp("--no-ext-grep", arg)) {
+                       builtin_grep = 1;
+                       continue;
+               }
                if (!strcmp("-a", arg) ||
                    !strcmp("--text", arg)) {
                        opt.binary = GREP_BINARY_TEXT;
index 4a02ee987ac421f3a89a53873f00d50760cd0044..c7aa48e74821b9b8aea2f99041cf323c9057c8da 100644 (file)
@@ -16,6 +16,7 @@
 #include "patch-ids.h"
 #include "run-command.h"
 #include "shortlog.h"
+#include "remote.h"
 
 /* Set a default date-time format for git log ("log.date" config variable) */
 static const char *default_date_mode = NULL;
@@ -935,6 +936,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                 * get_revision() to do the usual traversal.
                 */
        }
+
+       /*
+        * We cannot move this anywhere earlier because we do want to
+        * know if --root was given explicitly from the comand line.
+        */
+       rev.show_root_diff = 1;
+
        if (cover_letter) {
                /* remember the range */
                int i;
@@ -1061,13 +1069,14 @@ static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
 }
 
 static const char cherry_usage[] =
-"git cherry [-v] <upstream> [<head>] [<limit>]";
+"git cherry [-v] [<upstream> [<head> [<limit>]]]";
 int cmd_cherry(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
        struct patch_ids ids;
        struct commit *commit;
        struct commit_list *list = NULL;
+       struct branch *current_branch;
        const char *upstream;
        const char *head = "HEAD";
        const char *limit = NULL;
@@ -1090,7 +1099,17 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
                upstream = argv[1];
                break;
        default:
-               usage(cherry_usage);
+               current_branch = branch_get(NULL);
+               if (!current_branch || !current_branch->merge
+                                       || !current_branch->merge[0]
+                                       || !current_branch->merge[0]->dst) {
+                       fprintf(stderr, "Could not find a tracked"
+                                       " remote branch, please"
+                                       " specify <upstream> manually.\n");
+                       usage(cherry_usage);
+               }
+
+               upstream = current_branch->merge[0]->dst;
        }
 
        init_revisions(&revs, prefix);
index cb61717685b09a2e409440206e27fce68831e04d..5b63e6eada5cd6de764acef694da624a70ce6dab 100644 (file)
@@ -23,7 +23,7 @@ static int chomp_prefix;
 static const char *ls_tree_prefix;
 
 static const char ls_tree_usage[] =
-       "git ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]";
+       "git ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]] <tree-ish> [path...]";
 
 static int show_recursive(const char *base, int baselen, const char *pathname)
 {
@@ -156,6 +156,11 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
                                chomp_prefix = 0;
                                break;
                        }
+                       if (!strcmp(argv[1]+2, "full-tree")) {
+                               ls_tree_prefix = prefix = NULL;
+                               chomp_prefix = 0;
+                               break;
+                       }
                        if (!prefixcmp(argv[1]+2, "abbrev=")) {
                                abbrev = strtoul(argv[1]+9, NULL, 10);
                                if (abbrev && abbrev < MINIMUM_ABBREV)
index 4f65b5ae9baf66953e79886fd93fe31786b24d36..bce9959293e30925c4b16c40ac33a3f2e0474e30 100644 (file)
@@ -192,6 +192,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                                        memmove(destination + i,
                                                destination + i + 1,
                                                (argc - i) * sizeof(char *));
+                                       i--;
                                }
                        } else
                                die ("%s, source=%s, destination=%s",
index d03f14fdad3d17dde06734d78ddb4aade6ed4f2b..5f9f3f09b11b2e7f54a26bdbce7cb0e6974740d5 100644 (file)
@@ -29,6 +29,9 @@ static int compare_by_number(const void *a1, const void *a2)
                return -1;
 }
 
+const char *format_subject(struct strbuf *sb, const char *msg,
+                          const char *line_separator);
+
 static void insert_one_record(struct shortlog *log,
                              const char *author,
                              const char *oneline)
@@ -36,11 +39,11 @@ static void insert_one_record(struct shortlog *log,
        const char *dot3 = log->common_repo_prefix;
        char *buffer, *p;
        struct string_list_item *item;
-       struct string_list *onelines;
        char namebuf[1024];
        size_t len;
        const char *eol;
        const char *boemail, *eoemail;
+       struct strbuf subject = STRBUF_INIT;
 
        boemail = strchr(author, '<');
        if (!boemail)
@@ -68,12 +71,9 @@ static void insert_one_record(struct shortlog *log,
                snprintf(namebuf + len, room, " %.*s", maillen, boemail);
        }
 
-       buffer = xstrdup(namebuf);
-       item = string_list_insert(buffer, &log->list);
+       item = string_list_insert(namebuf, &log->list);
        if (item->util == NULL)
                item->util = xcalloc(1, sizeof(struct string_list));
-       else
-               free(buffer);
 
        /* Skip any leading whitespace, including any blank lines. */
        while (*oneline && isspace(*oneline))
@@ -89,9 +89,8 @@ static void insert_one_record(struct shortlog *log,
        while (*oneline && isspace(*oneline) && *oneline != '\n')
                oneline++;
        len = eol - oneline;
-       while (len && isspace(oneline[len-1]))
-               len--;
-       buffer = xmemdupz(oneline, len);
+       format_subject(&subject, oneline, " ");
+       buffer = strbuf_detach(&subject, NULL);
 
        if (dot3) {
                int dot3len = strlen(dot3);
@@ -104,16 +103,7 @@ static void insert_one_record(struct shortlog *log,
                }
        }
 
-       onelines = item->util;
-       if (onelines->nr >= onelines->alloc) {
-               onelines->alloc = alloc_nr(onelines->nr);
-               onelines->items = xrealloc(onelines->items,
-                               onelines->alloc
-                               * sizeof(struct string_list_item));
-       }
-
-       onelines->items[onelines->nr].util = NULL;
-       onelines->items[onelines->nr++].string = buffer;
+       string_list_append(buffer, item->util);
 }
 
 static void read_from_stdin(struct shortlog *log)
@@ -323,7 +313,7 @@ void shortlog_output(struct shortlog *log)
                }
 
                onelines->strdup_strings = 1;
-               string_list_clear(onelines, 1);
+               string_list_clear(onelines, 0);
                free(onelines);
                log->list.items[i].util = NULL;
        }
index daecd8e1cad4a301e2faa3888c561746d029f09d..4977962eb56cbdfee54d2d88c27008064dc13d0b 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -167,6 +167,32 @@ int list_bundle_refs(struct bundle_header *header, int argc, const char **argv)
        return list_refs(&header->references, argc, argv);
 }
 
+static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
+{
+       unsigned long size;
+       enum object_type type;
+       char *buf, *line, *lineend;
+       unsigned long date;
+
+       if (revs->max_age == -1 && revs->min_age == -1)
+               return 1;
+
+       buf = read_sha1_file(tag->sha1, &type, &size);
+       if (!buf)
+               return 1;
+       line = memmem(buf, size, "\ntagger ", 8);
+       if (!line++)
+               return 1;
+       lineend = memchr(line, buf + size - line, '\n');
+       line = memchr(line, lineend ? lineend - line : buf + size - line, '>');
+       if (!line++)
+               return 1;
+       date = strtoul(line, NULL, 10);
+       free(buf);
+       return (revs->max_age == -1 || revs->max_age < date) &&
+               (revs->min_age == -1 || revs->min_age > date);
+}
+
 int create_bundle(struct bundle_header *header, const char *path,
                int argc, const char **argv)
 {
@@ -255,6 +281,12 @@ int create_bundle(struct bundle_header *header, const char *path,
                        flag = 0;
                display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
 
+               if (e->item->type == OBJ_TAG &&
+                               !is_tag_in_date_range(e->item, &revs)) {
+                       e->item->flags |= UNINTERESTING;
+                       continue;
+               }
+
                /*
                 * Make sure the refs we wrote out is correct; --max-count and
                 * other limiting options could have prevented all the tips
diff --git a/cache.h b/cache.h
index 231c06d7726b575f6e522d5b0c0fe43557e8c651..8e1af2669bd2e9af03a73b7058bec014d4d3a3aa 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -631,9 +631,6 @@ extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsig
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 
-/* just like read_sha1_file(), but non fatal in presence of bad objects */
-extern void *read_object(const unsigned char *sha1, enum object_type *type, unsigned long *size);
-
 /* global flag to enable extra checks when accessing packed objects */
 extern int do_check_packed_object_crc;
 
index 8821b5080af2eb3ec8cabd3ae40ee20e10c80f2e..0a5fc8c6f6f91099c3c5df10f08240e547126e0a 100644 (file)
@@ -127,7 +127,7 @@ else
       SAVE_LDFLAGS="${LDFLAGS}"
       LDFLAGS="${SAVE_LDFLAGS} -Wl,-rpath,/"
       AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [ld_wl_rpath=yes], [ld_wl_rpath=no])
-      LDFLAGS="${SAVE_LD_FLAGS}"
+      LDFLAGS="${SAVE_LDFLAGS}"
    ])
    if test "$ld_wl_rpath" = "yes"; then
       AC_SUBST(CC_LD_DYNPATH, [-Wl,-rpath,])
@@ -136,7 +136,7 @@ else
          SAVE_LDFLAGS="${LDFLAGS}"
          LDFLAGS="${SAVE_LDFLAGS} -rpath /"
          AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [ld_rpath=yes], [ld_rpath=no])
-         LDFLAGS="${SAVE_LD_FLAGS}"
+         LDFLAGS="${SAVE_LDFLAGS}"
       ])
       if test "$ld_rpath" = "yes"; then
          AC_SUBST(CC_LD_DYNPATH, [-rpath])
index 8ec782dc54c9eb49873e38b75d925babe9a7746c..ec701e8069e995e50aa1acb3c78832bc525272f0 100755 (executable)
@@ -1,3 +1,4 @@
+#!bash
 #
 # bash completion support for core Git.
 #
@@ -50,9 +51,11 @@ case "$COMP_WORDBREAKS" in
 *)   COMP_WORDBREAKS="$COMP_WORDBREAKS:"
 esac
 
+# __gitdir accepts 0 or 1 arguments (i.e., location)
+# returns location of .git repo
 __gitdir ()
 {
-       if [ -z "$1" ]; then
+       if [ -z "${1-}" ]; then
                if [ -n "$__git_dir" ]; then
                        echo "$__git_dir"
                elif [ -d .git ]; then
@@ -67,6 +70,8 @@ __gitdir ()
        fi
 }
 
+# __git_ps1 accepts 0 or 1 arguments (i.e., format string)
+# returns text to add to bash PS1 prompt (includes branch name)
 __git_ps1 ()
 {
        local g="$(git rev-parse --git-dir 2>/dev/null)"
@@ -111,7 +116,7 @@ __git_ps1 ()
                        fi
                fi
 
-               if [ -n "$1" ]; then
+               if [ -n "${1-}" ]; then
                        printf "$1" "${b##refs/heads/}$r"
                else
                        printf " (%s)" "${b##refs/heads/}$r"
@@ -119,6 +124,7 @@ __git_ps1 ()
        fi
 }
 
+# __gitcomp_1 requires 2 arguments
 __gitcomp_1 ()
 {
        local c IFS=' '$'\t'$'\n'
@@ -131,6 +137,8 @@ __gitcomp_1 ()
        done
 }
 
+# __gitcomp accepts 1, 2, 3, or 4 arguments
+# generates completion reply with compgen
 __gitcomp ()
 {
        local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -143,22 +151,23 @@ __gitcomp ()
                ;;
        *)
                local IFS=$'\n'
-               COMPREPLY=($(compgen -P "$2" \
-                       -W "$(__gitcomp_1 "$1" "$4")" \
+               COMPREPLY=($(compgen -P "${2-}" \
+                       -W "$(__gitcomp_1 "${1-}" "${4-}")" \
                        -- "$cur"))
                ;;
        esac
 }
 
+# __git_heads accepts 0 or 1 arguments (to pass to __gitdir)
 __git_heads ()
 {
-       local cmd i is_hash=y dir="$(__gitdir "$1")"
+       local cmd i is_hash=y dir="$(__gitdir "${1-}")"
        if [ -d "$dir" ]; then
                git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
                        refs/heads
                return
        fi
-       for i in $(git ls-remote "$1" 2>/dev/null); do
+       for i in $(git ls-remote "${1-}" 2>/dev/null); do
                case "$is_hash,$i" in
                y,*) is_hash=n ;;
                n,*^{}) is_hash=y ;;
@@ -168,15 +177,16 @@ __git_heads ()
        done
 }
 
+# __git_tags accepts 0 or 1 arguments (to pass to __gitdir)
 __git_tags ()
 {
-       local cmd i is_hash=y dir="$(__gitdir "$1")"
+       local cmd i is_hash=y dir="$(__gitdir "${1-}")"
        if [ -d "$dir" ]; then
                git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
                        refs/tags
                return
        fi
-       for i in $(git ls-remote "$1" 2>/dev/null); do
+       for i in $(git ls-remote "${1-}" 2>/dev/null); do
                case "$is_hash,$i" in
                y,*) is_hash=n ;;
                n,*^{}) is_hash=y ;;
@@ -186,9 +196,10 @@ __git_tags ()
        done
 }
 
+# __git_refs accepts 0 or 1 arguments (to pass to __gitdir)
 __git_refs ()
 {
-       local i is_hash=y dir="$(__gitdir "$1")"
+       local i is_hash=y dir="$(__gitdir "${1-}")"
        local cur="${COMP_WORDS[COMP_CWORD]}" format refs
        if [ -d "$dir" ]; then
                case "$cur" in
@@ -218,6 +229,7 @@ __git_refs ()
        done
 }
 
+# __git_refs2 requires 1 argument (to pass to __git_refs)
 __git_refs2 ()
 {
        local i
@@ -226,6 +238,7 @@ __git_refs2 ()
        done
 }
 
+# __git_refs_remotes requires 1 argument (to pass to ls-remote)
 __git_refs_remotes ()
 {
        local cmd i is_hash=y
@@ -470,6 +483,7 @@ __git_aliases ()
        done
 }
 
+# __git_aliased_command requires 1 argument
 __git_aliased_command ()
 {
        local word cmdline=$(git --git-dir="$(__gitdir)" \
@@ -482,6 +496,7 @@ __git_aliased_command ()
        done
 }
 
+# __git_find_subcommand requires 1 argument
 __git_find_subcommand ()
 {
        local word subcommand c=1
@@ -563,7 +578,7 @@ _git_add ()
        --*)
                __gitcomp "
                        --interactive --refresh --patch --update --dry-run
-                       --ignore-errors
+                       --ignore-errors --intent-to-add
                        "
                return
        esac
@@ -628,7 +643,6 @@ _git_branch ()
        done
 
        case "${COMP_WORDS[COMP_CWORD]}" in
-       --*=*)  COMPREPLY=() ;;
        --*)
                __gitcomp "
                        --color --no-color --verbose --abbrev= --no-abbrev
@@ -824,6 +838,8 @@ _git_format_patch ()
                        --not --all
                        --cover-letter
                        --no-prefix --src-prefix= --dst-prefix=
+                       --inline --suffix= --ignore-if-in-upstream
+                       --subject-prefix=
                        "
                return
                ;;
@@ -931,6 +947,8 @@ _git_ls_tree ()
        __git_complete_file
 }
 
+__git_log_pretty_formats="oneline short medium full fuller email raw format:"
+
 _git_log ()
 {
        __git_has_doubledash && return
@@ -938,8 +956,7 @@ _git_log ()
        local cur="${COMP_WORDS[COMP_CWORD]}"
        case "$cur" in
        --pretty=*)
-               __gitcomp "
-                       oneline short medium full fuller email raw
+               __gitcomp "$__git_log_pretty_formats
                        " "" "${cur##--pretty=}"
                return
                ;;
@@ -1467,8 +1484,7 @@ _git_show ()
        local cur="${COMP_WORDS[COMP_CWORD]}"
        case "$cur" in
        --pretty=*)
-               __gitcomp "
-                       oneline short medium full fuller email raw
+               __gitcomp "$__git_log_pretty_formats
                        " "" "${cur##--pretty=}"
                return
                ;;
@@ -1674,7 +1690,6 @@ _git ()
 
        if [ -z "$command" ]; then
                case "${COMP_WORDS[COMP_CWORD]}" in
-               --*=*) COMPREPLY=() ;;
                --*)   __gitcomp "
                        --paginate
                        --no-pager
@@ -1738,6 +1753,7 @@ _git ()
        show)        _git_show ;;
        show-branch) _git_show_branch ;;
        stash)       _git_stash ;;
+       stage)       _git_add ;;
        submodule)   _git_submodule ;;
        svn)         _git_svn ;;
        tag)         _git_tag ;;
@@ -1765,13 +1781,16 @@ _gitk ()
        __git_complete_revlist
 }
 
-complete -o default -o nospace -F _git git
-complete -o default -o nospace -F _gitk gitk
+complete -o bashdefault -o default -o nospace -F _git git 2>/dev/null \
+       || complete -o default -o nospace -F _git git
+complete -o bashdefault -o default -o nospace -F _gitk gitk 2>/dev/null \
+       || complete -o default -o nospace -F _gitk gitk
 
 # The following are necessary only for Cygwin, and only are needed
 # when the user has tab-completed the executable name and consequently
 # included the '.exe' suffix.
 #
 if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
-complete -o default -o nospace -F _git git.exe
+complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \
+       || complete -o default -o nospace -F _git git.exe
 fi
diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool
new file mode 100755 (executable)
index 0000000..1fc087c
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/env perl
+# Copyright (c) 2009 David Aguilar
+#
+# This is a wrapper around the GIT_EXTERNAL_DIFF-compatible
+# git-difftool-helper script.  This script exports
+# GIT_EXTERNAL_DIFF and GIT_PAGER for use by git, and
+# GIT_NO_PROMPT and GIT_MERGE_TOOL for use by git-difftool-helper.
+# Any arguments that are unknown to this script are forwarded to 'git diff'.
+
+use strict;
+use warnings;
+use Cwd qw(abs_path);
+use File::Basename qw(dirname);
+
+my $DIR = abs_path(dirname($0));
+
+
+sub usage
+{
+       print << 'USAGE';
+
+usage: git difftool [--no-prompt] [--tool=tool] ["git diff" options]
+USAGE
+       exit 1;
+}
+
+sub setup_environment
+{
+       $ENV{PATH} = "$DIR:$ENV{PATH}";
+       $ENV{GIT_PAGER} = '';
+       $ENV{GIT_EXTERNAL_DIFF} = 'git-difftool-helper';
+}
+
+sub exe
+{
+       my $exe = shift;
+       return defined $ENV{COMSPEC} ? "$exe.exe" : $exe;
+}
+
+sub generate_command
+{
+       my @command = (exe('git'), 'diff');
+       my $skip_next = 0;
+       my $idx = -1;
+       for my $arg (@ARGV) {
+               $idx++;
+               if ($skip_next) {
+                       $skip_next = 0;
+                       next;
+               }
+               if ($arg eq '-t' or $arg eq '--tool') {
+                       usage() if $#ARGV <= $idx;
+                       $ENV{GIT_MERGE_TOOL} = $ARGV[$idx + 1];
+                       $skip_next = 1;
+                       next;
+               }
+               if ($arg =~ /^--tool=/) {
+                       $ENV{GIT_MERGE_TOOL} = substr($arg, 7);
+                       next;
+               }
+               if ($arg eq '--no-prompt') {
+                       $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
+                       next;
+               }
+               if ($arg eq '-h' or $arg eq '--help') {
+                       usage();
+               }
+               push @command, $arg;
+       }
+       return @command
+}
+
+setup_environment();
+exec(generate_command());
diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper
new file mode 100755 (executable)
index 0000000..0b266e3
--- /dev/null
@@ -0,0 +1,240 @@
+#!/bin/sh
+# git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
+# It supports kdiff3, tkdiff, xxdiff, meld, opendiff, emerge, ecmerge,
+# vimdiff, gvimdiff, and custom user-configurable tools.
+# This script is typically launched by using the 'git difftool'
+# convenience command.
+#
+# Copyright (c) 2009 David Aguilar
+
+# Set GIT_DIFFTOOL_NO_PROMPT to bypass the per-file prompt.
+should_prompt () {
+       ! test -n "$GIT_DIFFTOOL_NO_PROMPT"
+}
+
+# Should we keep the backup .orig file?
+keep_backup_mode="$(git config --bool merge.keepBackup || echo true)"
+keep_backup () {
+       test "$keep_backup_mode" = "true"
+}
+
+# This function manages the backup .orig file.
+# A backup $MERGED.orig file is created if changes are detected.
+cleanup_temp_files () {
+       if test -n "$MERGED"; then
+               if keep_backup && test "$MERGED" -nt "$BACKUP"; then
+                       test -f "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
+               else
+                       rm -f -- "$BACKUP"
+               fi
+       fi
+}
+
+# This is called when users Ctrl-C out of git-difftool-helper
+sigint_handler () {
+       echo
+       cleanup_temp_files
+       exit 1
+}
+
+# This function prepares temporary files and launches the appropriate
+# merge tool.
+launch_merge_tool () {
+       # Merged is the filename as it appears in the work tree
+       # Local is the contents of a/filename
+       # Remote is the contents of b/filename
+       # Custom merge tool commands might use $BASE so we provide it
+       MERGED="$1"
+       LOCAL="$2"
+       REMOTE="$3"
+       BASE="$1"
+       ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
+       BACKUP="$MERGED.BACKUP.$ext"
+
+       # Create and ensure that we clean up $BACKUP
+       test -f "$MERGED" && cp -- "$MERGED" "$BACKUP"
+       trap sigint_handler SIGINT
+
+       # $LOCAL and $REMOTE are temporary files so prompt
+       # the user with the real $MERGED name before launching $merge_tool.
+       if should_prompt; then
+               printf "\nViewing: '$MERGED'\n"
+               printf "Hit return to launch '%s': " "$merge_tool"
+               read ans
+       fi
+
+       # Run the appropriate merge tool command
+       case "$merge_tool" in
+       kdiff3)
+               basename=$(basename "$MERGED")
+               "$merge_tool_path" --auto \
+                       --L1 "$basename (A)" \
+                       --L2 "$basename (B)" \
+                       -o "$MERGED" "$LOCAL" "$REMOTE" \
+                       > /dev/null 2>&1
+               ;;
+
+       tkdiff)
+               "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
+               ;;
+
+       meld|vimdiff)
+               "$merge_tool_path" "$LOCAL" "$REMOTE"
+               ;;
+
+       gvimdiff)
+               "$merge_tool_path" -f "$LOCAL" "$REMOTE"
+               ;;
+
+       xxdiff)
+               "$merge_tool_path" \
+                       -X \
+                       -R 'Accel.SaveAsMerged: "Ctrl-S"' \
+                       -R 'Accel.Search: "Ctrl+F"' \
+                       -R 'Accel.SearchForward: "Ctrl-G"' \
+                       --merged-file "$MERGED" \
+                       "$LOCAL" "$REMOTE"
+               ;;
+
+       opendiff)
+               "$merge_tool_path" "$LOCAL" "$REMOTE" \
+                       -merge "$MERGED" | cat
+               ;;
+
+       ecmerge)
+               "$merge_tool_path" "$LOCAL" "$REMOTE" \
+                       --default --mode=merge2 --to="$MERGED"
+               ;;
+
+       emerge)
+               "$merge_tool_path" -f emerge-files-command \
+                       "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
+               ;;
+
+       *)
+               if test -n "$merge_tool_cmd"; then
+                       ( eval $merge_tool_cmd )
+               fi
+               ;;
+       esac
+
+       cleanup_temp_files
+}
+
+# Verifies that mergetool.<tool>.cmd exists
+valid_custom_tool() {
+       merge_tool_cmd="$(git config mergetool.$1.cmd)"
+       test -n "$merge_tool_cmd"
+}
+
+# Verifies that the chosen merge tool is properly setup.
+# Built-in merge tools are always valid.
+valid_tool() {
+       case "$1" in
+       kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
+               ;; # happy
+       *)
+               if ! valid_custom_tool "$1"
+               then
+                       return 1
+               fi
+               ;;
+       esac
+}
+
+# Sets up the merge_tool_path variable.
+# This handles the mergetool.<tool>.path configuration.
+init_merge_tool_path() {
+       merge_tool_path=$(git config mergetool."$1".path)
+       if test -z "$merge_tool_path"; then
+               case "$1" in
+               emerge)
+                       merge_tool_path=emacs
+                       ;;
+               *)
+                       merge_tool_path="$1"
+                       ;;
+               esac
+       fi
+}
+
+# Allow the GIT_MERGE_TOOL variable to provide a default value
+test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
+
+# If not merge tool was specified then use the merge.tool
+# configuration variable.  If that's invalid then reset merge_tool.
+if test -z "$merge_tool"; then
+       merge_tool=$(git config merge.tool)
+       if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
+               echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
+               echo >&2 "Resetting to default..."
+               unset merge_tool
+       fi
+fi
+
+# Try to guess an appropriate merge tool if no tool has been set.
+if test -z "$merge_tool"; then
+
+       # We have a $DISPLAY so try some common UNIX merge tools
+       if test -n "$DISPLAY"; then
+               merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff"
+               # If gnome then prefer meld
+               if test -n "$GNOME_DESKTOP_SESSION_ID"; then
+                       merge_tool_candidates="meld $merge_tool_candidates"
+               fi
+               # If KDE then prefer kdiff3
+               if test "$KDE_FULL_SESSION" = "true"; then
+                       merge_tool_candidates="kdiff3 $merge_tool_candidates"
+               fi
+       fi
+
+       # $EDITOR is emacs so add emerge as a candidate
+       if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
+               merge_tool_candidates="$merge_tool_candidates emerge"
+       fi
+
+       # $EDITOR is vim so add vimdiff as a candidate
+       if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
+               merge_tool_candidates="$merge_tool_candidates vimdiff"
+       fi
+
+       merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
+       echo "merge tool candidates: $merge_tool_candidates"
+
+       # Loop over each candidate and stop when a valid merge tool is found.
+       for i in $merge_tool_candidates
+       do
+               init_merge_tool_path $i
+               if type "$merge_tool_path" > /dev/null 2>&1; then
+                       merge_tool=$i
+                       break
+               fi
+       done
+
+       if test -z "$merge_tool" ; then
+               echo "No known merge resolution program available."
+               exit 1
+       fi
+
+else
+       # A merge tool has been set, so verify that it's valid.
+       if ! valid_tool "$merge_tool"; then
+               echo >&2 "Unknown merge tool $merge_tool"
+               exit 1
+       fi
+
+       init_merge_tool_path "$merge_tool"
+
+       if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
+               echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
+               exit 1
+       fi
+fi
+
+
+# Launch the merge tool on each path provided by 'git diff'
+while test $# -gt 6
+do
+       launch_merge_tool "$1" "$2" "$5"
+       shift 7
+done
diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt
new file mode 100644 (file)
index 0000000..3940c70
--- /dev/null
@@ -0,0 +1,104 @@
+git-difftool(1)
+===============
+
+NAME
+----
+git-difftool - compare changes using common merge tools
+
+SYNOPSIS
+--------
+'git difftool' [--tool=<tool>] [--no-prompt] ['git diff' options]
+
+DESCRIPTION
+-----------
+'git difftool' is a git command that allows you to compare and edit files
+between revisions using common merge tools.  At its most basic level,
+'git difftool' does what 'git mergetool' does but its use is for non-merge
+situations such as when preparing commits or comparing changes against
+the index.
+
+'git difftool' is a frontend to 'git diff' and accepts the same
+arguments and options.
+
+See linkgit:git-diff[7] for the full list of supported options.
+
+OPTIONS
+-------
+-t <tool>::
+--tool=<tool>::
+       Use the merge resolution program specified by <tool>.
+       Valid merge tools are:
+       kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
++
+If a merge resolution program is not specified, 'git difftool'
+will use the configuration variable `merge.tool`.  If the
+configuration variable `merge.tool` is not set, 'git difftool'
+will pick a suitable default.
++
+You can explicitly provide a full path to the tool by setting the
+configuration variable `mergetool.<tool>.path`. For example, you
+can configure the absolute path to kdiff3 by setting
+`mergetool.kdiff3.path`. Otherwise, 'git difftool' assumes the
+tool is available in PATH.
++
+Instead of running one of the known merge tool programs,
+'git difftool' can be customized to run an alternative program
+by specifying the command line to invoke in a configuration
+variable `mergetool.<tool>.cmd`.
++
+When 'git difftool' is invoked with this tool (either through the
+`-t` or `--tool` option or the `merge.tool` configuration variable)
+the configured command line will be invoked with the following
+variables available: `$LOCAL` is set to the name of the temporary
+file containing the contents of the diff pre-image and `$REMOTE`
+is set to the name of the temporary file containing the contents
+of the diff post-image.  `$BASE` is provided for compatibility
+with custom merge tool commands and has the same value as `$LOCAL`.
+
+--no-prompt::
+       Do not prompt before launching a merge tool.
+
+CONFIG VARIABLES
+----------------
+merge.tool::
+       The default merge tool to use.
++
+See the `--tool=<tool>` option above for more details.
+
+merge.keepBackup::
+       The original, unedited file content can be saved to a file with
+       a `.orig` extension.  Defaults to `true` (i.e. keep the backup files).
+
+mergetool.<tool>.path::
+       Override the path for the given tool.  This is useful in case
+       your tool is not in the PATH.
+
+mergetool.<tool>.cmd::
+       Specify the command to invoke the specified merge tool.
++
+See the `--tool=<tool>` option above for more details.
+
+
+SEE ALSO
+--------
+linkgit:git-diff[7]::
+        Show changes between commits, commit and working tree, etc
+
+linkgit:git-mergetool[1]::
+       Run merge conflict resolution tools to resolve merge conflicts
+
+linkgit:git-config[7]::
+        Get and set repository or global options
+
+
+AUTHOR
+------
+Written by David Aguilar <davvid@gmail.com>.
+
+Documentation
+--------------
+Documentation by David Aguilar and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/contrib/examples/README b/contrib/examples/README
new file mode 100644 (file)
index 0000000..6946f3d
--- /dev/null
@@ -0,0 +1,3 @@
+These are original scripted implementations, kept primarily for their
+reference value to any aspiring plumbing users who want to learn how
+pieces can be fit together.
index c487346eba0f4ecad01903ccc68963040d9944ab..fca1e17251f1f414a1d97bf6e6240bdf20daa648 100644 (file)
@@ -5,11 +5,13 @@ automatically.
 If you have an older version of vim, you can get the latest syntax
 files from the vim project:
 
-  http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/git.vim
-  http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitcommit.vim
-  http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitconfig.vim
-  http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitrebase.vim
-  http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitsendemail.vim
+  http://ftp.vim.org/pub/vim/runtime/syntax/git.vim
+  http://ftp.vim.org/pub/vim/runtime/syntax/gitcommit.vim
+  http://ftp.vim.org/pub/vim/runtime/syntax/gitconfig.vim
+  http://ftp.vim.org/pub/vim/runtime/syntax/gitrebase.vim
+  http://ftp.vim.org/pub/vim/runtime/syntax/gitsendemail.vim
+
+These files are also available via FTP at the same location.
 
 To install:
 
index ae96c64ca209f4df9008198e8a04b160bed618c7..a41e1ec07ccd707969aa51768dac3e2b6356ddc6 100644 (file)
@@ -61,14 +61,12 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
        int silent_on_removed = option & DIFF_SILENT_ON_REMOVED;
        unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED)
                              ? CE_MATCH_RACY_IS_DIRTY : 0);
-       char symcache[PATH_MAX];
 
        diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/");
 
        if (diff_unmerged_stage < 0)
                diff_unmerged_stage = 2;
        entries = active_nr;
-       symcache[0] = '\0';
        for (i = 0; i < entries; i++) {
                struct stat st;
                unsigned int oldmode, newmode;
@@ -198,11 +196,6 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
  * diff-index
  */
 
-struct oneway_unpack_data {
-       struct rev_info *revs;
-       char symcache[PATH_MAX];
-};
-
 /* A file entry went away or appeared */
 static void diff_index_show_file(struct rev_info *revs,
                                 const char *prefix,
@@ -216,8 +209,7 @@ static void diff_index_show_file(struct rev_info *revs,
 static int get_stat_data(struct cache_entry *ce,
                         const unsigned char **sha1p,
                         unsigned int *modep,
-                        int cached, int match_missing,
-                        struct oneway_unpack_data *cbdata)
+                        int cached, int match_missing)
 {
        const unsigned char *sha1 = ce->sha1;
        unsigned int mode = ce->ce_mode;
@@ -248,25 +240,24 @@ static int get_stat_data(struct cache_entry *ce,
        return 0;
 }
 
-static void show_new_file(struct oneway_unpack_data *cbdata,
+static void show_new_file(struct rev_info *revs,
                          struct cache_entry *new,
                          int cached, int match_missing)
 {
        const unsigned char *sha1;
        unsigned int mode;
-       struct rev_info *revs = cbdata->revs;
 
        /*
         * New file in the index: it might actually be different in
         * the working copy.
         */
-       if (get_stat_data(new, &sha1, &mode, cached, match_missing, cbdata) < 0)
+       if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0)
                return;
 
        diff_index_show_file(revs, "+", new, sha1, mode);
 }
 
-static int show_modified(struct oneway_unpack_data *cbdata,
+static int show_modified(struct rev_info *revs,
                         struct cache_entry *old,
                         struct cache_entry *new,
                         int report_missing,
@@ -274,9 +265,8 @@ static int show_modified(struct oneway_unpack_data *cbdata,
 {
        unsigned int mode, oldmode;
        const unsigned char *sha1;
-       struct rev_info *revs = cbdata->revs;
 
-       if (get_stat_data(new, &sha1, &mode, cached, match_missing, cbdata) < 0) {
+       if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) {
                if (report_missing)
                        diff_index_show_file(revs, "-", old,
                                             old->sha1, old->ce_mode);
@@ -344,8 +334,7 @@ static void do_oneway_diff(struct unpack_trees_options *o,
        struct cache_entry *idx,
        struct cache_entry *tree)
 {
-       struct oneway_unpack_data *cbdata = o->unpack_data;
-       struct rev_info *revs = cbdata->revs;
+       struct rev_info *revs = o->unpack_data;
        int match_missing, cached;
 
        /*
@@ -368,7 +357,7 @@ static void do_oneway_diff(struct unpack_trees_options *o,
         * Something added to the tree?
         */
        if (!tree) {
-               show_new_file(cbdata, idx, cached, match_missing);
+               show_new_file(revs, idx, cached, match_missing);
                return;
        }
 
@@ -381,7 +370,7 @@ static void do_oneway_diff(struct unpack_trees_options *o,
        }
 
        /* Show difference between old and new */
-       show_modified(cbdata, tree, idx, 1, cached, match_missing);
+       show_modified(revs, tree, idx, 1, cached, match_missing);
 }
 
 static inline void skip_same_name(struct cache_entry *ce, struct unpack_trees_options *o)
@@ -418,8 +407,7 @@ static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
 {
        struct cache_entry *idx = src[0];
        struct cache_entry *tree = src[1];
-       struct oneway_unpack_data *cbdata = o->unpack_data;
-       struct rev_info *revs = cbdata->revs;
+       struct rev_info *revs = o->unpack_data;
 
        if (idx && ce_stage(idx))
                skip_same_name(idx, o);
@@ -446,7 +434,6 @@ int run_diff_index(struct rev_info *revs, int cached)
        const char *tree_name;
        struct unpack_trees_options opts;
        struct tree_desc t;
-       struct oneway_unpack_data unpack_cb;
 
        mark_merge_entries();
 
@@ -456,14 +443,12 @@ int run_diff_index(struct rev_info *revs, int cached)
        if (!tree)
                return error("bad tree object %s", tree_name);
 
-       unpack_cb.revs = revs;
-       unpack_cb.symcache[0] = '\0';
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = 1;
        opts.index_only = cached;
        opts.merge = 1;
        opts.fn = oneway_diff;
-       opts.unpack_data = &unpack_cb;
+       opts.unpack_data = revs;
        opts.src_index = &the_index;
        opts.dst_index = NULL;
 
@@ -486,7 +471,6 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
        struct cache_entry *last = NULL;
        struct unpack_trees_options opts;
        struct tree_desc t;
-       struct oneway_unpack_data unpack_cb;
 
        /*
         * This is used by git-blame to run diff-cache internally;
@@ -515,14 +499,12 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
        if (!tree)
                die("bad tree object %s", sha1_to_hex(tree_sha1));
 
-       unpack_cb.revs = &revs;
-       unpack_cb.symcache[0] = '\0';
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = 1;
        opts.index_only = 1;
        opts.merge = 1;
        opts.fn = oneway_diff;
-       opts.unpack_data = &unpack_cb;
+       opts.unpack_data = &revs;
        opts.src_index = &the_index;
        opts.dst_index = &the_index;
 
index b60d3455dae14a7a2cf2daeec8eb47fc7dcd9a09..60ed17470a6a2bf9bea202a04004b06d207a77d7 100644 (file)
@@ -173,8 +173,10 @@ void diff_no_index(struct rev_info *revs,
 
        /* Were we asked to do --no-index explicitly? */
        for (i = 1; i < argc; i++) {
-               if (!strcmp(argv[i], "--"))
-                       return;
+               if (!strcmp(argv[i], "--")) {
+                       i++;
+                       break;
+               }
                if (!strcmp(argv[i], "--no-index"))
                        no_index = 1;
                if (argv[i][0] != '-')
@@ -198,13 +200,6 @@ void diff_no_index(struct rev_info *revs,
                die("git diff %s takes two paths",
                    no_index ? "--no-index" : "[--no-index]");
 
-       /*
-        * If the user asked for our exit code then don't start a
-        * pager or we would end up reporting its exit code instead.
-        */
-       if (!DIFF_OPT_TST(&revs->diffopt, EXIT_WITH_STATUS))
-               setup_pager();
-
        diff_setup(&revs->diffopt);
        if (!revs->diffopt.output_format)
                revs->diffopt.output_format = DIFF_FORMAT_PATCH;
@@ -212,8 +207,12 @@ void diff_no_index(struct rev_info *revs,
                int j;
                if (!strcmp(argv[i], "--no-index"))
                        i++;
-               else if (!strcmp(argv[1], "-q"))
+               else if (!strcmp(argv[i], "-q")) {
                        options |= DIFF_SILENT_ON_REMOVED;
+                       i++;
+               }
+               else if (!strcmp(argv[i], "--"))
+                       i++;
                else {
                        j = diff_opt_parse(&revs->diffopt, argv + i, argc - i);
                        if (!j)
@@ -222,6 +221,13 @@ void diff_no_index(struct rev_info *revs,
                }
        }
 
+       /*
+        * If the user asked for our exit code then don't start a
+        * pager or we would end up reporting its exit code instead.
+        */
+       if (!DIFF_OPT_TST(&revs->diffopt, EXIT_WITH_STATUS))
+               setup_pager();
+
        if (prefix) {
                int len = strlen(prefix);
 
index a6bce661963812691503116e8d61d9ef90f96526..f0e08aca70c16e9309dde87954593a76ad37b9ef 100644 (file)
@@ -1872,12 +1872,13 @@ static void file_change_m(struct branch *b)
        if (!p)
                die("Corrupt mode: %s", command_buf.buf);
        switch (mode) {
+       case 0644:
+       case 0755:
+               mode |= S_IFREG;
        case S_IFREG | 0644:
        case S_IFREG | 0755:
        case S_IFLNK:
        case S_IFGITLINK:
-       case 0644:
-       case 0755:
                /* ok */
                break;
        default:
@@ -1944,7 +1945,7 @@ static void file_change_m(struct branch *b)
                            typename(type), command_buf.buf);
        }
 
-       tree_content_set(&b->branch_tree, p, sha1, S_IFREG | mode, NULL);
+       tree_content_set(&b->branch_tree, p, sha1, mode, NULL);
 }
 
 static void file_change_d(struct branch *b)
index 17a35f6adc79480d0533a4ff98b2817c836a7e78..85db4ba40022e3a9e5790879d6d21fa59475b316 100755 (executable)
@@ -508,7 +508,7 @@ bisect_visualize() {
 
        if test $# = 0
        then
-               case "${DISPLAY+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
+               case "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
                '')     set git log ;;
                set*)   set gitk ;;
                esac
index b0a805c688f59af29e1f25b514d73f3991285dee..fef7faf33947a87ded0e8a2d9fd6f1af6047d951 100755 (executable)
@@ -1358,7 +1358,13 @@ sub req_ci
     # write our commit message out if we have one ...
     my ( $msg_fh, $msg_filename ) = tempfile( DIR => $TEMP_DIR );
     print $msg_fh $state->{opt}{m};# if ( exists ( $state->{opt}{m} ) );
-    print $msg_fh "\n\nvia git-CVS emulator\n";
+    if ( defined ( $cfg->{gitcvs}{commitmsgannotation} ) ) {
+        if ($cfg->{gitcvs}{commitmsgannotation} !~ /^\s*$/ ) {
+            print $msg_fh "\n\n".$cfg->{gitcvs}{commitmsgannotation}."\n"
+        }
+    } else {
+        print $msg_fh "\n\nvia git-CVS emulator\n";
+    }
     close $msg_fh;
 
     my $commithash = `git-commit-tree $treehash -p $parenthash < $msg_filename`;
@@ -2527,12 +2533,18 @@ sub open_blob_or_die
     return $fh;
 }
 
-# Generate a CVS author name from Git author information, by taking
-# the first eight characters of the user part of the email address.
+# Generate a CVS author name from Git author information, by taking the local
+# part of the email address and replacing characters not in the Portable
+# Filename Character Set (see IEEE Std 1003.1-2001, 3.276) by underscores. CVS
+# Login names are Unix login names, which should be restricted to this
+# character set.
 sub cvs_author
 {
     my $author_line = shift;
-    (my $author) = $author_line =~ /<([^>@]{1,8})/;
+    (my $author) = $author_line =~ /<([^@>]*)/;
+
+    $author =~ s/[^-a-zA-Z0-9_.]/_/g;
+    $author =~ s/^-/_/;
 
     $author;
 }
index c106f45af73446d26630030f77107efa520aa296..eb62f719b0ad1d015bb116e8447971c5fe7fdba6 100755 (executable)
@@ -40,6 +40,16 @@ skip_commit()
        done;
 }
 
+# if you run 'git_commit_non_empty_tree "$@"' in a commit filter,
+# it will skip commits that leave the tree untouched, commit the other.
+git_commit_non_empty_tree()
+{
+       if test $# = 3 && test "$1" = $(git rev-parse "$3^{tree}"); then
+               map "$3"
+       else
+               git commit-tree "$@"
+       fi
+}
 # override die(): this version puts in an extra line break, so that
 # the progress is still visible
 
@@ -109,11 +119,12 @@ filter_tree=
 filter_index=
 filter_parent=
 filter_msg=cat
-filter_commit='git commit-tree "$@"'
+filter_commit=
 filter_tag_name=
 filter_subdir=
 orig_namespace=refs/original/
 force=
+prune_empty=
 while :
 do
        case "$1" in
@@ -126,6 +137,11 @@ do
                force=t
                continue
                ;;
+       --prune-empty)
+               shift
+               prune_empty=t
+               continue
+               ;;
        -*)
                ;;
        *)
@@ -176,6 +192,17 @@ do
        esac
 done
 
+case "$prune_empty,$filter_commit" in
+,)
+       filter_commit='git commit-tree "$@"';;
+t,)
+       filter_commit="$functions;"' git_commit_non_empty_tree "$@"';;
+,*)
+       ;;
+*)
+       die "Cannot set --prune-empty and --filter-commit at the same time"
+esac
+
 case "$force" in
 t)
        rm -rf "$tempdir"
index c8b0861c085035c85002f6e2fe969369ea32016f..8ed2244819d0950ddf1ffaa151b4d46bdc8b6db4 100755 (executable)
@@ -349,7 +349,7 @@ do_next () {
        squash|s)
                comment_for_reflog squash
 
-               has_action "$DONE" ||
+               test -f "$DONE" && has_action "$DONE" ||
                        die "Cannot 'squash' without a previous commit"
 
                mark_action_done
index f07d96b9b5e3997b21736893be39ce91950f4878..2142308bcc6d2e2c4962859d18e12070cd4c1b1d 100755 (executable)
@@ -96,7 +96,7 @@ cd_to_toplevel () {
                ..|../*|*/..|*/../*)
                        # Interpret $cdup relative to the physical, not logical, cwd.
                        # Probably /bin/pwd is more portable than passing -P to cd or pwd.
-                       phys="$(/bin/pwd)/$cdup"
+                       phys="$(unset PWD; /bin/pwd)/$cdup"
                        ;;
                *)
                        # There's no "..", so no need to make things absolute.
diff --git a/git.c b/git.c
index a53e24feae183b77986947f9b120a02bc1c18113..ecc8fad09aebd16410f2736458d60e04f9dfb296 100644 (file)
--- a/git.c
+++ b/git.c
@@ -416,12 +416,34 @@ static void execv_dashed_external(const char **argv)
        strbuf_release(&cmd);
 }
 
+static int run_argv(int *argcp, const char ***argv)
+{
+       int done_alias = 0;
+
+       while (1) {
+               /* See if it's an internal command */
+               handle_internal_command(*argcp, *argv);
+
+               /* .. then try the external ones */
+               execv_dashed_external(*argv);
+
+               /* It could be an alias -- this works around the insanity
+                * of overriding "git log" with "git show" by having
+                * alias.log = show
+                */
+               if (done_alias || !handle_alias(argcp, argv))
+                       break;
+               done_alias = 1;
+       }
+
+       return done_alias;
+}
+
 
 int main(int argc, const char **argv)
 {
        const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help";
        char *slash = (char *)cmd + strlen(cmd);
-       int done_alias = 0;
 
        /*
         * Take the basename of argv[0] as the command
@@ -479,31 +501,22 @@ int main(int argc, const char **argv)
        setup_path();
 
        while (1) {
-               /* See if it's an internal command */
-               handle_internal_command(argc, argv);
-
-               /* .. then try the external ones */
-               execv_dashed_external(argv);
-
-               /* It could be an alias -- this works around the insanity
-                * of overriding "git log" with "git show" by having
-                * alias.log = show
-                */
-               if (done_alias || !handle_alias(&argc, &argv))
+               static int done_help = 0;
+               static int was_alias = 0;
+               was_alias = run_argv(&argc, &argv);
+               if (errno != ENOENT)
                        break;
-               done_alias = 1;
-       }
-
-       if (errno == ENOENT) {
-               if (done_alias) {
+               if (was_alias) {
                        fprintf(stderr, "Expansion of alias '%s' failed; "
                                "'%s' is not a git-command\n",
                                cmd, argv[0]);
                        exit(1);
                }
-               argv[0] = help_unknown_cmd(cmd);
-               handle_internal_command(argc, argv);
-               execv_dashed_external(argv);
+               if (!done_help) {
+                       cmd = argv[0] = help_unknown_cmd(cmd);
+                       done_help = 1;
+               } else
+                       break;
        }
 
        fprintf(stderr, "Failed to run command '%s': %s\n",
diff --git a/grep.c b/grep.c
index 49e93199658e6b9214839232b5b464b7d9e76a7f..6485760ff30e0575ee13b43fc8c7fb8e2e291346 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -28,9 +28,31 @@ void append_grep_pattern(struct grep_opt *opt, const char *pat,
        p->next = NULL;
 }
 
+static int isregexspecial(int c)
+{
+       return isspecial(c) || c == '$' || c == '(' || c == ')' || c == '+' ||
+                              c == '.' || c == '^' || c == '{' || c == '|';
+}
+
+static int is_fixed(const char *s)
+{
+       while (!isregexspecial(*s))
+               s++;
+       return !*s;
+}
+
 static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
 {
-       int err = regcomp(&p->regexp, p->pattern, opt->regflags);
+       int err;
+
+       if (opt->fixed || is_fixed(p->pattern))
+               p->fixed = 1;
+       if (opt->regflags & REG_ICASE)
+               p->fixed = 0;
+       if (p->fixed)
+               return;
+
+       err = regcomp(&p->regexp, p->pattern, opt->regflags);
        if (err) {
                char errbuf[1024];
                char where[1024];
@@ -159,8 +181,7 @@ void compile_grep_patterns(struct grep_opt *opt)
                case GREP_PATTERN: /* atom */
                case GREP_PATTERN_HEAD:
                case GREP_PATTERN_BODY:
-                       if (!opt->fixed)
-                               compile_regexp(p, opt);
+                       compile_regexp(p, opt);
                        break;
                default:
                        opt->extended = 1;
@@ -294,7 +315,6 @@ static struct {
 static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx)
 {
        int hit = 0;
-       int at_true_bol = 1;
        int saved_ch = 0;
        regmatch_t pmatch[10];
 
@@ -315,7 +335,7 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
        }
 
  again:
-       if (!opt->fixed) {
+       if (!p->fixed) {
                regex_t *exp = &p->regexp;
                hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
                               pmatch, 0);
@@ -337,7 +357,7 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
                 * either end of the line, or at word boundary
                 * (i.e. the next char must not be a word char).
                 */
-               if ( ((pmatch[0].rm_so == 0 && at_true_bol) ||
+               if ( ((pmatch[0].rm_so == 0) ||
                      !word_char(bol[pmatch[0].rm_so-1])) &&
                     ((pmatch[0].rm_eo == (eol-bol)) ||
                      !word_char(bol[pmatch[0].rm_eo])) )
@@ -349,10 +369,14 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
                        /* There could be more than one match on the
                         * line, and the first match might not be
                         * strict word match.  But later ones could be!
+                        * Forward to the next possible start, i.e. the
+                        * next position following a non-word char.
                         */
                        bol = pmatch[0].rm_so + bol + 1;
-                       at_true_bol = 0;
-                       goto again;
+                       while (word_char(bol[-1]) && bol < eol)
+                               bol++;
+                       if (bol < eol)
+                               goto again;
                }
        }
        if (p->token == GREP_PATTERN_HEAD && saved_ch)
diff --git a/grep.h b/grep.h
index 45a222d904b5898758c3c84f6ef1e0119c81f6b2..5102ce335d29811dd448e173f2e90e8d03b5f011 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -30,6 +30,7 @@ struct grep_pat {
        const char *pattern;
        enum grep_header_field field;
        regex_t regexp;
+       unsigned fixed:1;
 };
 
 enum grep_expr_node {
index a4b7d08663504a57008f66a39fffe293f62c1d08..6ad853e2d01b4ee3b3ee282b30b8b7f374ae3d47 100644 (file)
@@ -1201,7 +1201,8 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
        /* Make sure leading directories exist for the remote ref */
        ep = strchr(url + strlen(remote->url) + 1, '/');
        while (ep) {
-               *ep = 0;
+               char saved_character = ep[1];
+               ep[1] = '\0';
                slot = get_active_slot();
                slot->results = &results;
                curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
@@ -1223,7 +1224,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
                        free(url);
                        return NULL;
                }
-               *ep = '/';
+               ep[1] = saved_character;
                ep = strchr(ep + 1, '/');
        }
 
@@ -1434,10 +1435,8 @@ static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
                        }
                        if (path) {
                                path += remote->path_len;
+                               ls->dentry_name = xstrdup(path);
                        }
-                       ls->dentry_name = xmalloc(strlen(path) -
-                                                 remote->path_len + 1);
-                       strcpy(ls->dentry_name, path + remote->path_len);
                } else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {
                        ls->dentry_flags |= IS_DIR;
                }
@@ -1448,6 +1447,12 @@ static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
        }
 }
 
+/*
+ * NEEDSWORK: remote_ls() ignores info/refs on the remote side.  But it
+ * should _only_ heed the information from that file, instead of trying to
+ * determine the refs from the remote file system (badly: it does not even
+ * know about packed-refs).
+ */
 static void remote_ls(const char *path, int flags,
                      void (*userFunc)(struct remote_ls_ctx *ls),
                      void *userData)
index 8392a68333cd57b899962ef6a7a9ca80dd5d583d..7d7f2b1d367b505676032878615b2843eb64ed7b 100644 (file)
@@ -166,11 +166,12 @@ sub repository {
                }
        }
 
-       if (not defined $opts{Repository} and not defined $opts{WorkingCopy}) {
-               $opts{Directory} ||= '.';
+       if (not defined $opts{Repository} and not defined $opts{WorkingCopy}
+               and not defined $opts{Directory}) {
+               $opts{Directory} = '.';
        }
 
-       if ($opts{Directory}) {
+       if (defined $opts{Directory}) {
                -d $opts{Directory} or throw Error::Simple("Directory not found: $!");
 
                my $search = Git->repository(WorkingCopy => $opts{Directory});
@@ -1010,8 +1011,8 @@ sub _temp_cache {
        my $temp_fd = \$TEMP_FILEMAP{$name};
        if (defined $$temp_fd and $$temp_fd->opened) {
                if ($TEMP_FILES{$$temp_fd}{locked}) {
-                       throw Error::Simple("Temp file with moniker '",
-                               $name, "' already in use");
+                       throw Error::Simple("Temp file with moniker '" .
+                               $name . "' already in use");
                }
        } else {
                if (defined $$temp_fd) {
index 343dca556cc73031bb073b6ebf8bf511db3a60c4..421d9c5bca2224777808ccc06fe4912ba8793229 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -486,8 +486,8 @@ static void parse_commit_header(struct format_commit_context *context)
        context->commit_header_parsed = 1;
 }
 
-static const char *format_subject(struct strbuf *sb, const char *msg,
-                                 const char *line_separator)
+const char *format_subject(struct strbuf *sb, const char *msg,
+                          const char *line_separator)
 {
        int first = 1;
 
index 52d1ead15b4db62138d9cedd5fe04068bcd10461..f08493f039fa28c90e3edc708bbcf0a527e4f659 100644 (file)
@@ -1700,6 +1700,9 @@ static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
        delta_base_cache_lru.prev = &ent->lru;
 }
 
+static void *read_object(const unsigned char *sha1, enum object_type *type,
+                        unsigned long *size);
+
 static void *unpack_delta_entry(struct packed_git *p,
                                struct pack_window **w_curs,
                                off_t curpos,
@@ -2130,8 +2133,8 @@ int pretend_sha1_file(void *buf, unsigned long len, enum object_type type,
        return 0;
 }
 
-void *read_object(const unsigned char *sha1, enum object_type *type,
-                 unsigned long *size)
+static void *read_object(const unsigned char *sha1, enum object_type *type,
+                        unsigned long *size)
 {
        unsigned long mapsize;
        void *map, *buf;
index 4717c2d33b70af6527f8951ec8a414e8caf87095..fdb19a50f11c8c71c9f7addcceeab8847558cc49 100644 (file)
@@ -1,6 +1,8 @@
 ServerName dummy
 PidFile httpd.pid
 DocumentRoot www
+LogFormat "%h %l %u %t \"%r\" %>s %b" common
+CustomLog access.log common
 ErrorLog error.log
 
 <IfDefine SSL>
index 4b44e131b27df0cc6a73590b045c2eb87b104f59..271bc4e17f0c12cda550ffa4f54f1ad7555b3bed 100755 (executable)
@@ -341,4 +341,55 @@ test_expect_success \
      check_cache_at DF/DF dirty &&
      :'
 
+test_expect_success \
+    'a/b (untracked) vs a case setup.' \
+    'rm -f .git/index &&
+     : >a &&
+     git update-index --add a &&
+     treeM=`git write-tree` &&
+     echo treeM $treeM &&
+     git ls-tree $treeM &&
+     git ls-files --stage >treeM.out &&
+
+     rm -f a &&
+     git update-index --remove a &&
+     mkdir a &&
+     : >a/b &&
+     treeH=`git write-tree` &&
+     echo treeH $treeH &&
+     git ls-tree $treeH'
+
+test_expect_success \
+    'a/b (untracked) vs a, plus c/d case test.' \
+    '! git read-tree -u -m "$treeH" "$treeM" &&
+     git ls-files --stage &&
+     test -f a/b'
+
+test_expect_success \
+    'a/b vs a, plus c/d case setup.' \
+    'rm -f .git/index &&
+     rm -fr a &&
+     : >a &&
+     mkdir c &&
+     : >c/d &&
+     git update-index --add a c/d &&
+     treeM=`git write-tree` &&
+     echo treeM $treeM &&
+     git ls-tree $treeM &&
+     git ls-files --stage >treeM.out &&
+
+     rm -f a &&
+     mkdir a
+     : >a/b &&
+     git update-index --add --remove a a/b &&
+     treeH=`git write-tree` &&
+     echo treeH $treeH &&
+     git ls-tree $treeH'
+
+test_expect_success \
+    'a/b vs a, plus c/d case test.' \
+    'git read-tree -u -m "$treeH" "$treeM" &&
+     git ls-files --stage | tee >treeMcheck.out &&
+     test_cmp treeM.out treeMcheck.out'
+
 test_done
index beddb4e9f26cb3ade276825a204d950d3173d583..e42cbfe6c61951c6887a363cb668d26a7adcf20c 100755 (executable)
@@ -10,12 +10,12 @@ test_cd_to_toplevel () {
                        cd '"'$1'"' &&
                        . git-sh-setup &&
                        cd_to_toplevel &&
-                       [ "$(/bin/pwd)" = "$TOPLEVEL" ]
+                       [ "$(unset PWD; /bin/pwd)" = "$TOPLEVEL" ]
                )
        '
 }
 
-TOPLEVEL="$(/bin/pwd)/repo"
+TOPLEVEL="$(unset PWD; /bin/pwd)/repo"
 mkdir -p repo/sub/dir
 mv .git repo/
 SUBDIRECTORY_OK=1
index 7d10a27f1dcb8058f0e3c6180c3d45705fe3b21d..2cc8e7abe1b9244b2d6520cdbb0e0769e2a6d986 100755 (executable)
@@ -373,6 +373,38 @@ test_expect_success '--continue tries to commit, even for "edit"' '
        test $parent = $(git rev-parse HEAD^)
 '
 
+test_expect_success 'aborted --continue does not squash commits after "edit"' '
+       old=$(git rev-parse HEAD) &&
+       test_tick &&
+       FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+       echo "edited again" > file7 &&
+       git add file7 &&
+       (
+               FAKE_COMMIT_MESSAGE=" " &&
+               export FAKE_COMMIT_MESSAGE &&
+               test_must_fail git rebase --continue
+       ) &&
+       test $old = $(git rev-parse HEAD) &&
+       git rebase --abort
+'
+
+test_expect_success 'auto-amend only edited commits after "edit"' '
+       test_tick &&
+       FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+       echo "edited again" > file7 &&
+       git add file7 &&
+       FAKE_COMMIT_MESSAGE="edited file7 again" git commit &&
+       echo "and again" > file7 &&
+       git add file7 &&
+       test_tick &&
+       (
+               FAKE_COMMIT_MESSAGE="and again" &&
+               export FAKE_COMMIT_MESSAGE &&
+               test_must_fail git rebase --continue
+       ) &&
+       git rebase --abort
+'
+
 test_expect_success 'rebase a detached HEAD' '
        grandparent=$(git rev-parse HEAD~2) &&
        git checkout $(git rev-parse HEAD) &&
old mode 100644 (file)
new mode 100755 (executable)
index 6da212825a447866364979c2fb10778b6bbc02d5..bb4cf00d78637b3bdf0c4e3da99291e0ab3c718f 100755 (executable)
@@ -45,6 +45,7 @@ test_expect_success 'cherry-pick after renaming branch' '
 
        git checkout rename2 &&
        git cherry-pick added &&
+       test $(git rev-parse HEAD^) = $(git rev-parse rename2) &&
        test -f opos &&
        grep "Add extra line at the end" opos
 
@@ -54,6 +55,7 @@ test_expect_success 'revert after renaming branch' '
 
        git checkout rename1 &&
        git revert added &&
+       test $(git rev-parse HEAD^) = $(git rev-parse rename1) &&
        test -f spoo &&
        ! grep "Add extra line at the end" spoo
 
index aeb5405cfeee83f13f3d543db2eba07f1fb5c241..aba53202f84b6c6c16a68f45ccfa42368e47d421 100755 (executable)
@@ -261,6 +261,7 @@ diff --patch-with-stat -r initial..side
 diff --patch-with-raw -r initial..side
 diff --name-status dir2 dir
 diff --no-index --name-status dir2 dir
+diff --no-index --name-status -- dir2 dir
 diff master master^ side
 EOF
 
diff --git a/t/t4013/diff.diff_--no-index_--name-status_--_dir2_dir b/t/t4013/diff.diff_--no-index_--name-status_--_dir2_dir
new file mode 100644 (file)
index 0000000..6756f8d
--- /dev/null
@@ -0,0 +1,3 @@
+$ git diff --no-index --name-status -- dir2 dir
+A      dir/sub
+$
index da9588645cd9d0054440e5ed3ba14f630c44f506..c236b5e83beed8997e8f6c3a50f0bb74f73f3f33 100755 (executable)
@@ -51,17 +51,29 @@ test_expect_success 'clone remote repository' '
        git clone $HTTPD_URL/test_repo.git test_repo_clone
 '
 
-test_expect_failure 'push to remote repository' '
+test_expect_failure 'push to remote repository with packed refs' '
        cd "$ROOT_PATH"/test_repo_clone &&
        : >path2 &&
        git add path2 &&
        test_tick &&
        git commit -m path2 &&
+       HEAD=$(git rev-parse --verify HEAD) &&
        git push &&
-       [ -f "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/refs/heads/master" ]
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+        test $HEAD = $(git rev-parse --verify HEAD))
 '
 
-test_expect_failure 'create and delete remote branch' '
+test_expect_success ' push to remote repository with unpacked refs' '
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+        rm packed-refs &&
+        git update-ref refs/heads/master \
+               0c973ae9bd51902a28466f3850b543fa66a6aaf4) &&
+       git push &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+        test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'create and delete remote branch' '
        cd "$ROOT_PATH"/test_repo_clone &&
        git checkout -b dev &&
        : >path3 &&
@@ -76,6 +88,12 @@ test_expect_failure 'create and delete remote branch' '
        test_must_fail git show-ref --verify refs/remotes/origin/dev
 '
 
+test_expect_success 'MKCOL sends directory names with trailing slashes' '
+
+       ! grep "\"MKCOL.*[^/] HTTP/[^ ]*\"" < "$HTTPD_ROOT_PATH"/access.log
+
+'
+
 stop_httpd
 
 test_done
diff --git a/t/t5704-bundle.sh b/t/t5704-bundle.sh
new file mode 100755 (executable)
index 0000000..a8f4419
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+test_description='some bundle related tests'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+       : > file &&
+       git add file &&
+       test_tick &&
+       git commit -m initial &&
+       test_tick &&
+       git tag -m tag tag &&
+       : > file2 &&
+       git add file2 &&
+       : > file3 &&
+       test_tick &&
+       git commit -m second &&
+       git add file3 &&
+       test_tick &&
+       git commit -m third
+
+'
+
+test_expect_success 'tags can be excluded by rev-list options' '
+
+       git bundle create bundle --all --since=7.Apr.2005.15:16:00.-0700 &&
+       git ls-remote bundle > output &&
+       ! grep tag output
+
+'
+
+test_done
index 575ef5beb2bdd3a0814fb45010ae7889b936f543..ef2e78f9df951cfacf5914dfff2a218d2e6f89f7 100755 (executable)
@@ -38,6 +38,31 @@ test_expect_success \
     'git diff-tree -r -M --name-status  HEAD^ HEAD | \
     grep "^R100..*path1/COPYING..*path0/COPYING"'
 
+test_expect_success \
+    'checking -k on non-existing file' \
+    'git mv -k idontexist path0'
+
+test_expect_success \
+    'checking -k on untracked file' \
+    'touch untracked1 &&
+     git mv -k untracked1 path0 &&
+     test -f untracked1 &&
+     test ! -f path0/untracked1'
+
+test_expect_success \
+    'checking -k on multiple untracked files' \
+    'touch untracked2 &&
+     git mv -k untracked1 untracked2 path0 &&
+     test -f untracked1 &&
+     test -f untracked2 &&
+     test ! -f path0/untracked1
+     test ! -f path0/untracked2'
+
+# clean up the mess in case bad things happen
+rm -f idontexist untracked1 untracked2 \
+     path0/idontexist path0/untracked1 path0/untracked2 \
+     .git/index.lock
+
 test_expect_success \
     'adding another file' \
     'cp "$TEST_DIRECTORY"/../README path0/README &&
index 18fe6f2d576d35706b1a712292b58155680cc9dc..c4938544d4ca7c5e7e886a59144c9b10b7971748 100755 (executable)
@@ -161,7 +161,14 @@ test_expect_success 'log grep (6)' '
        git log --author=-0700  --pretty=tformat:%s >actual &&
        >expect &&
        test_cmp expect actual
+'
 
+test_expect_success 'grep with CE_VALID file' '
+       git update-index --assume-unchanged t/t &&
+       rm t/t &&
+       test "$(git grep --no-ext-grep t)" = "t/t:test" &&
+       git update-index --no-assume-unchanged t/t &&
+       git checkout t/t
 '
 
 test_done
index b0a9d7d536314ec842b141c09ba0d6f8b06b6288..8537bf91606282161ab92b6f2f7367c9a3c016fc 100755 (executable)
@@ -262,4 +262,12 @@ test_expect_success 'Tag name filtering allows slashes in tag names' '
        test_cmp expect actual
 '
 
+test_expect_success 'Prune empty commits' '
+       git rev-list HEAD > expect &&
+       make_commit to_remove &&
+       git filter-branch -f --index-filter "git update-index --remove to_remove" --prune-empty HEAD &&
+       git rev-list HEAD > actual &&
+       test_cmp expect actual
+'
+
 test_done
index be73f7b60ac0c8857cbe34c9e0bf8dd43a3dec39..2ec7ac6a510c5b83bc1ee6ce428379beb7a8b5ef 100755 (executable)
@@ -209,4 +209,29 @@ test_expect_success 'update --init' '
 
 '
 
+test_expect_success 'do not add files from a submodule' '
+
+       git reset --hard &&
+       test_must_fail git add init/a
+
+'
+
+test_expect_success 'gracefully add submodule with a trailing slash' '
+
+       git reset --hard &&
+       git commit -m "commit subproject" init &&
+       (cd init &&
+        echo b > a) &&
+       git add init/ &&
+       git diff --exit-code --cached init &&
+       commit=$(cd init &&
+        git commit -m update a >/dev/null &&
+        git rev-parse HEAD) &&
+       git add init/ &&
+       test_must_fail git diff --exit-code --cached init &&
+       test $commit = $(git ls-files --stage |
+               sed -n "s/^160000 \([^ ]*\).*/\1/p")
+
+'
+
 test_done
index 6e18a96319bef5c4ddfbc86ce79b02b364f04387..5998baf27b5ad0fb32bf0fd42023d029a65d0e6b 100755 (executable)
@@ -149,10 +149,7 @@ EOF
 
 test_expect_success '--signoff' '
        echo "yet another content *narf*" >> foo &&
-       echo "zort" | (
-               test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
-               git commit -s -F - foo
-       ) &&
+       echo "zort" | git commit -s -F - foo &&
        git cat-file commit HEAD | sed "1,/^$/d" > output &&
        test_cmp expect output
 '
index 63bfc6d8b3f6917cad1b51a73a84ba61762b220d..b4e2b4db8421c24cb714fcad7cd6ec00ea88c016 100755 (executable)
@@ -127,6 +127,26 @@ test_expect_success \
        "showing committed revisions" \
        "git rev-list HEAD >current"
 
+cat >editor <<\EOF
+#!/bin/sh
+sed -e "s/good/bad/g" < "$1" > "$1-"
+mv "$1-" "$1"
+EOF
+chmod 755 editor
+
+cat >msg <<EOF
+A good commit message.
+EOF
+
+test_expect_success \
+       'editor not invoked if -F is given' '
+        echo "moo" >file &&
+        VISUAL=./editor git commit -a -F msg &&
+        git show -s --pretty=format:"%s" | grep -q good &&
+        echo "quack" >file &&
+        echo "Another good message." | VISUAL=./editor git commit -a -F - &&
+        git show -s --pretty=format:"%s" | grep -q good
+        '
 # We could just check the head sha1, but checking each commit makes it
 # easier to isolate bugs.
 
diff --git a/t/t9130-git-svn-authors-file.sh b/t/t9130-git-svn-authors-file.sh
new file mode 100755 (executable)
index 0000000..b8fb277
--- /dev/null
@@ -0,0 +1,94 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Eric Wong
+#
+
+test_description='git svn authors file tests'
+
+. ./lib-git-svn.sh
+
+cat > svn-authors <<EOF
+aa = AAAAAAA AAAAAAA <aa@example.com>
+bb = BBBBBBB BBBBBBB <bb@example.com>
+EOF
+
+test_expect_success 'setup svnrepo' '
+       for i in aa bb cc dd
+       do
+               svn mkdir -m $i --username $i "$svnrepo"/$i
+       done
+       '
+
+test_expect_success 'start import with incomplete authors file' '
+       ! git svn clone --authors-file=svn-authors "$svnrepo" x
+       '
+
+test_expect_success 'imported 2 revisions successfully' '
+       (
+               cd x
+               test "`git rev-list refs/remotes/git-svn | wc -l`" -eq 2 &&
+               git rev-list -1 --pretty=raw refs/remotes/git-svn | \
+                 grep "^author BBBBBBB BBBBBBB <bb@example\.com> " &&
+               git rev-list -1 --pretty=raw refs/remotes/git-svn~1 | \
+                 grep "^author AAAAAAA AAAAAAA <aa@example\.com> "
+       )
+       '
+
+cat >> svn-authors <<EOF
+cc = CCCCCCC CCCCCCC <cc@example.com>
+dd = DDDDDDD DDDDDDD <dd@example.com>
+EOF
+
+test_expect_success 'continues to import once authors have been added' '
+       (
+               cd x
+               git svn fetch --authors-file=../svn-authors &&
+               test "`git rev-list refs/remotes/git-svn | wc -l`" -eq 4 &&
+               git rev-list -1 --pretty=raw refs/remotes/git-svn | \
+                 grep "^author DDDDDDD DDDDDDD <dd@example\.com> " &&
+               git rev-list -1 --pretty=raw refs/remotes/git-svn~1 | \
+                 grep "^author CCCCCCC CCCCCCC <cc@example\.com> "
+       )
+       '
+
+test_expect_success 'authors-file against globs' '
+       svn mkdir -m globs --username aa \
+         "$svnrepo"/aa/trunk "$svnrepo"/aa/branches "$svnrepo"/aa/tags &&
+       git svn clone --authors-file=svn-authors -s "$svnrepo"/aa aa-work &&
+       for i in bb ee cc
+       do
+               branch="aa/branches/$i"
+               svn mkdir -m "$branch" --username $i "$svnrepo/$branch"
+       done
+       '
+
+test_expect_success 'fetch fails on ee' '
+       ( cd aa-work && ! git svn fetch --authors-file=../svn-authors )
+       '
+
+tmp_config_get () {
+       GIT_CONFIG=.git/svn/.metadata git config --get "$1"
+}
+
+test_expect_success 'failure happened without negative side effects' '
+       (
+               cd aa-work &&
+               test 6 -eq "`tmp_config_get svn-remote.svn.branches-maxRev`" &&
+               test 6 -eq "`tmp_config_get svn-remote.svn.tags-maxRev`"
+       )
+       '
+
+cat >> svn-authors <<EOF
+ee = EEEEEEE EEEEEEE <ee@example.com>
+EOF
+
+test_expect_success 'fetch continues after authors-file is fixed' '
+       (
+               cd aa-work &&
+               git svn fetch --authors-file=../svn-authors &&
+               test 8 -eq "`tmp_config_get svn-remote.svn.branches-maxRev`" &&
+               test 8 -eq "`tmp_config_get svn-remote.svn.tags-maxRev`"
+       )
+       '
+
+test_done
index 54f301da67be879c80426bc21776427fdd38c02e..15c9ef592b393410354496c577a6e8c2dfb39940 100644 (file)
@@ -494,7 +494,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
         * anything in the existing directory there.
         */
        int namelen;
-       int pos, i;
+       int i;
        struct dir_struct d;
        char *pathbuf;
        int cnt = 0;
@@ -515,24 +515,20 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
         * in that directory.
         */
        namelen = strlen(ce->name);
-       pos = index_name_pos(o->src_index, ce->name, namelen);
-       if (0 <= pos)
-               return cnt; /* we have it as nondirectory */
-       pos = -pos - 1;
-       for (i = pos; i < o->src_index->cache_nr; i++) {
-               struct cache_entry *ce = o->src_index->cache[i];
-               int len = ce_namelen(ce);
+       for (i = o->pos; i < o->src_index->cache_nr; i++) {
+               struct cache_entry *ce2 = o->src_index->cache[i];
+               int len = ce_namelen(ce2);
                if (len < namelen ||
-                   strncmp(ce->name, ce->name, namelen) ||
-                   ce->name[namelen] != '/')
+                   strncmp(ce->name, ce2->name, namelen) ||
+                   ce2->name[namelen] != '/')
                        break;
                /*
-                * ce->name is an entry in the subdirectory.
+                * ce2->name is an entry in the subdirectory.
                 */
-               if (!ce_stage(ce)) {
-                       if (verify_uptodate(ce, o))
+               if (!ce_stage(ce2)) {
+                       if (verify_uptodate(ce2, o))
                                return -1;
-                       add_entry(o, ce, CE_REMOVE, 0);
+                       add_entry(o, ce2, CE_REMOVE, 0);
                }
                cnt++;
        }
@@ -588,7 +584,7 @@ static int verify_absent(struct cache_entry *ce, const char *action,
                return 0;
 
        if (!lstat(ce->name, &st)) {
-               int cnt;
+               int ret;
                int dtype = ce_to_dtype(ce);
                struct cache_entry *result;
 
@@ -616,13 +612,15 @@ static int verify_absent(struct cache_entry *ce, const char *action,
                         * files that are in "foo/" we would lose
                         * it.
                         */
-                       cnt = verify_clean_subdirectory(ce, action, o);
+                       ret = verify_clean_subdirectory(ce, action, o);
+                       if (ret < 0)
+                               return ret;
 
                        /*
                         * If this removed entries from the index,
                         * what that means is:
                         *
-                        * (1) the caller unpack_trees_rec() saw path/foo
+                        * (1) the caller unpack_callback() saw path/foo
                         * in the index, and it has not removed it because
                         * it thinks it is handling 'path' as blob with
                         * D/F conflict;
@@ -635,7 +633,7 @@ static int verify_absent(struct cache_entry *ce, const char *action,
                         * We need to increment it by the number of
                         * deleted entries here.
                         */
-                       o->pos += cnt;
+                       o->pos += ret;
                        return 0;
                }