Merge branch 'sp/run-command'
authorJunio C Hamano <junkio@cox.net>
Mon, 19 Mar 2007 05:21:06 +0000 (22:21 -0700)
committerJunio C Hamano <junkio@cox.net>
Mon, 19 Mar 2007 05:21:06 +0000 (22:21 -0700)
* sp/run-command:
Use run_command within send-pack
Use run_command within receive-pack to invoke index-pack
Use run_command within merge-index
Use run_command for proxy connections
Use RUN_GIT_CMD to run push backends
Correct new compiler warnings in builtin-revert
Replace fork_with_pipe in bundle with run_command
Teach run-command to redirect stdout to /dev/null
Teach run-command about stdout redirection

69 files changed:
.gitignore
Documentation/RelNotes-1.5.0.4.txt [new file with mode: 0644]
Documentation/RelNotes-1.5.0.5.txt [new file with mode: 0644]
Documentation/cmd-list.perl
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-branch.txt
Documentation/git-checkout.txt
Documentation/git-config.txt
Documentation/git-cvsserver.txt
Documentation/git-mergetool.txt [new file with mode: 0644]
Documentation/git-send-email.txt
Documentation/git-svn.txt
Documentation/git.txt
Makefile
builtin-add.c
builtin-branch.c
builtin-diff-files.c
builtin-diff-index.c
builtin-diff-tree.c
builtin-diff.c
builtin-fetch--tool.c [new file with mode: 0644]
builtin-grep.c
builtin-mailinfo.c
builtin-pack-objects.c
builtin-shortlog.c
builtin.h
cache.h
config.c
diff-lib.c
diff.c
diff.h
git-am.sh
git-applymbox.sh
git-checkout.sh
git-fetch.sh
git-merge.sh
git-mergetool.sh [new file with mode: 0755]
git-parse-remote.sh
git-quiltimport.sh
git-send-email.perl
git-svn.perl
git.c
local-fetch.c
pack-check.c
pack-redundant.c
pack.h
perl/Makefile
revision.c
sha1_file.c
t/t3200-branch.sh
t/t4017-diff-retval.sh [new file with mode: 0755]
t/t4017-quiet.sh [new file with mode: 0755]
t/t5100-mailinfo.sh
t/t5100/info0007 [new file with mode: 0644]
t/t5100/info0008 [new file with mode: 0644]
t/t5100/msg0007 [new file with mode: 0644]
t/t5100/msg0008 [new file with mode: 0644]
t/t5100/patch0005
t/t5100/patch0007 [new file with mode: 0644]
t/t5100/patch0008 [new file with mode: 0644]
t/t5100/sample.mbox
t/t5300-pack-object.sh
t/t5520-pull.sh
templates/hooks--update
trace.c
tree-diff.c
tree-walk.c
tree-walk.h
index 0eaba0a278df33fbef7f247ce267181d7ccba5f0..e8d2731ee5d7b3ad58bd0bebfe1ae87bed140615 100644 (file)
@@ -38,6 +38,7 @@ git-diff-tree
 git-describe
 git-fast-import
 git-fetch
+git-fetch--tool
 git-fetch-pack
 git-findtags
 git-fmt-merge-msg
@@ -75,6 +76,7 @@ git-merge-ours
 git-merge-recursive
 git-merge-resolve
 git-merge-stupid
+git-mergetool
 git-mktag
 git-mktree
 git-name-rev
diff --git a/Documentation/RelNotes-1.5.0.4.txt b/Documentation/RelNotes-1.5.0.4.txt
new file mode 100644 (file)
index 0000000..b727a8d
--- /dev/null
@@ -0,0 +1,24 @@
+GIT v1.5.0.4 Release Notes
+==========================
+
+Fixes since v1.5.0.3
+--------------------
+
+* Bugfixes
+
+  - git.el does not add duplicate sign-off lines.
+
+  - git-commit shows the full stat of the resulting commit, not
+    just about the files in the current directory, when run from
+    a subdirectory.
+
+  - "git-checkout -m '@{8 hours ago}'" had a funny failure from
+    eval; fixed.
+
+  - git-gui updates.
+
+* Documentation updates
+
+* User manual updates
+
+
diff --git a/Documentation/RelNotes-1.5.0.5.txt b/Documentation/RelNotes-1.5.0.5.txt
new file mode 100644 (file)
index 0000000..aa86149
--- /dev/null
@@ -0,0 +1,28 @@
+GIT v1.5.0.5 Release Notes
+==========================
+
+Fixes since v1.5.0.3
+--------------------
+
+* Bugfixes
+
+  - git-merge (hence git-pull) did not refuse fast-forwarding
+    when the working tree had local changes that would have
+    conflicted with it.
+
+  - git.el does not add duplicate sign-off lines.
+
+  - git-commit shows the full stat of the resulting commit, not
+    just about the files in the current directory, when run from
+    a subdirectory.
+
+  - "git-checkout -m '@{8 hours ago}'" had a funny failure from
+    eval; fixed.
+
+  - git-gui updates.
+
+* Documentation updates
+
+* User manual updates
+
+
index f61c77aa7c360e12a3e11e9e5dd3ebecfad78d7e..b54382b2bfdfc7926af8c4b0552f82235619f1c6 100755 (executable)
@@ -124,6 +124,7 @@ sub format_one {
 git-merge                               mainporcelain
 git-merge-one-file                      purehelpers
 git-merge-tree                          ancillaryinterrogators
+git-mergetool                           ancillarymanipulators
 git-mktag                               plumbingmanipulators
 git-mktree                              plumbingmanipulators
 git-mv                                  mainporcelain
index 5408dd67d316ce3334f2843267a604f5a4ea044b..66886424bb6c30f1829482230ff3da4810461f22 100644 (file)
@@ -272,6 +272,10 @@ branch.<name>.merge::
        `git fetch`) to lookup the default branch for merging. Without
        this option, `git pull` defaults to merge the first refspec fetched.
        Specify multiple values to get an octopus merge.
+       If you wish to setup `git pull` so that it merges into <name> from
+       another branch in the local repository, you can point
+       branch.<name>.merge to the desired branch, and use the special setting
+       `.` (a period) for branch.<name>.remote.
 
 color.branch::
        A boolean to enable/disable color in the output of
@@ -453,6 +457,11 @@ merge.summary::
        Whether to include summaries of merged commits in newly created
        merge commit messages. False by default.
 
+merge.tool::
+       Controls which merge resolution program is used by
+       gitlink:git-mergetool[l].  Valid values are: "kdiff3", "tkdiff",
+       "meld", "xxdiff", "emerge", "vimdiff"
+
 merge.verbosity::
        Controls the amount of output shown by the recursive merge
        strategy.  Level 0 outputs nothing except a final error
index d8696b7b36ff81f47214da9d7c4ec53142b97653..77a3f78dd75514667b77ae4e1dbbb3731b05a33d 100644 (file)
 -w::
        Shorthand for "--ignore-all-space".
 
+--exit-code::
+       Make the program exit with codes similar to diff(1).
+       That is, it exits with 1 if there were differences and
+       0 means no differences.
+
 For more detailed explanation on these common options, see also
 link:diffcore.html[diffcore documentation].
index 3ea3b8063520fe2a6f97b767d5f2e2ac05d147d9..603f87f3b59358697da821ffe240be9caff43fa7 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git-branch' [--color | --no-color] [-r | -a]
           [-v [--abbrev=<length> | --no-abbrev]]
-'git-branch' [-l] [-f] <branchname> [<start-point>]
+'git-branch' [--track | --no-track] [-l] [-f] <branchname> [<start-point>]
 'git-branch' (-m | -M) [<oldbranch>] <newbranch>
 'git-branch' (-d | -D) [-r] <branchname>...
 
@@ -26,6 +26,13 @@ It will start out with a head equal to the one given as <start-point>.
 If no <start-point> is given, the branch will be created with a head
 equal to that of the currently checked out branch.
 
+When a local branch is started off a remote branch, git can setup the
+branch so that gitlink:git-pull[1] will appropriately merge from that
+remote branch.  If this behavior is desired, it is possible to make it
+the default using the global `branch.autosetupmerge` configuration
+flag.  Otherwise, it can be chosen per-branch using the `--track`
+and `--no-track` options.
+
 With a '-m' or '-M' option, <oldbranch> will be renamed to <newbranch>.
 If <oldbranch> had a corresponding reflog, it is renamed to match
 <newbranch>, and a reflog entry is created to remember the branch
index 1ae77be45055418b9c784d15db520841dd7bdd7a..f5b2d5017b5710a08c89a7cb150d8ef45527bf99 100644 (file)
@@ -8,7 +8,7 @@ git-checkout - Checkout and switch to a branch
 SYNOPSIS
 --------
 [verse]
-'git-checkout' [-q] [-f] [-b <new_branch> [-l]] [-m] [<branch>]
+'git-checkout' [-q] [-f] [-b [--track | --no-track] <new_branch> [-l]] [-m] [<branch>]
 'git-checkout' [<tree-ish>] <paths>...
 
 DESCRIPTION
@@ -18,7 +18,8 @@ When <paths> are not given, this command switches branches by
 updating the index and working tree to reflect the specified
 branch, <branch>, and updating HEAD to be <branch> or, if
 specified, <new_branch>.  Using -b will cause <new_branch> to
-be created.
+be created; in this case you can use the --track or --no-track
+options, which will be passed to `git branch`.
 
 When <paths> are given, this command does *not* switch
 branches.  It updates the named paths in the working tree from
@@ -45,6 +46,16 @@ OPTIONS
        by gitlink:git-check-ref-format[1].  Some of these checks
        may restrict the characters allowed in a branch name.
 
+--track::
+       When -b is given and a branch is created off a remote branch,
+       setup so that git-pull will automatically retrieve data from
+       the remote branch.
+
+--no-track::
+       When -b is given and a branch is created off a remote branch,
+       force that git-pull will automatically retrieve data from
+       the remote branch independent of the configuration settings.
+
 -l::
        Create the new branch's ref log.  This activates recording of
        all changes to made the branch ref, enabling use of date
index 68de5881bd36aabd1b4b13c2bce48a226c34788e..c759efb7fc6ebb69d9c6b59e23e31f33e0596675 100644 (file)
@@ -86,7 +86,7 @@ OPTIONS
        Remove the line matching the key from config file.
 
 --unset-all::
-       Remove all matching lines from config file.
+       Remove all lines matching the key from config file.
 
 -l, --list::
        List all variables set in config file.
index e328db3797888046802b82954d8946bc8d317075..1c6f6a7e27d74422085ce32cea1b74c822d1a4b2 100644 (file)
@@ -121,10 +121,10 @@ so that calling 'cvs' effectively calls git-cvsserver.
 Clients known to work
 ---------------------
 
-CVS 1.12.9 on Debian
-CVS 1.11.17 on MacOSX (from Fink package)
-Eclipse 3.0, 3.1.2 on MacOSX (see Eclipse CVS Client Notes)
-TortoiseCVS
+CVS 1.12.9 on Debian
+CVS 1.11.17 on MacOSX (from Fink package)
+Eclipse 3.0, 3.1.2 on MacOSX (see Eclipse CVS Client Notes)
+TortoiseCVS
 
 Operations supported
 --------------------
@@ -148,13 +148,16 @@ Copyright and Authors
 
 This program is copyright The Open University UK - 2006.
 
-Authors: Martyn Smith    <martyn@catalyst.net.nz>
-         Martin Langhoff <martin@catalyst.net.nz>
-         with ideas and patches from participants of the git-list <git@vger.kernel.org>.
+Authors:
+
+- Martyn Smith    <martyn@catalyst.net.nz>
+- Martin Langhoff <martin@catalyst.net.nz>
+
+with ideas and patches from participants of the git-list <git@vger.kernel.org>.
 
 Documentation
 --------------
-Documentation by Martyn Smith <martyn@catalyst.net.nz> and Martin Langhoff <martin@catalyst.net.nz> Matthias Urlichs <smurf@smurf.noris.de>.
+Documentation by Martyn Smith <martyn@catalyst.net.nz>, Martin Langhoff <martin@catalyst.net.nz>, and Matthias Urlichs <smurf@smurf.noris.de>.
 
 GIT
 ---
diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt
new file mode 100644 (file)
index 0000000..5baaaca
--- /dev/null
@@ -0,0 +1,46 @@
+git-mergetool(1)
+================
+
+NAME
+----
+git-mergetool - Run merge conflict resolution tools to resolve merge conflicts
+
+SYNOPSIS
+--------
+'git-mergetool' [--tool=<tool>] [<file>]...
+
+DESCRIPTION
+-----------
+
+Use 'git mergetool' to run one of several merge utilities to resolve
+merge conflicts.  It is typically run after gitlink:git-merge[1].
+
+If one or more <file> parameters are given, the merge tool program will
+be run to resolve differences on each file.  If no <file> names are
+specified, 'git mergetool' will run the merge tool program on every file
+with merge conflicts.
+
+OPTIONS
+-------
+-t or --tool=<tool>::
+       Use the merge resolution program specified by <tool>.
+       Valid merge tools are:
+       kdiff3, tkdiff, meld, xxdiff, emerge, and vimdiff.
+
+       If a merge resolution program is not specified, 'git mergetool'
+       will use the configuration variable merge.tool.  If the
+       configuration variable merge.tool is not set, 'git mergetool'
+       will pick a suitable default.
+
+Author
+------
+Written by Theodore Y Ts'o <tytso@mit.edu>
+
+Documentation
+--------------
+Documentation by Theodore Y Ts'o.
+
+GIT
+---
+Part of the gitlink:git[7] suite
+
index 9b3aabb6fe5590f41319298337e8f9ea6d9fff4e..682313e95dc9b96d3cc14f33554ad82bcede9a56 100644 (file)
@@ -60,7 +60,8 @@ The --cc option must be repeated for each user you want on the cc list.
        is not set, this will be prompted for.
 
 --no-signed-off-by-cc::
-       Do not add emails found in Signed-off-by: lines to the cc list.
+       Do not add emails found in Signed-off-by: or Cc: lines to the
+       cc list.
 
 --quiet::
        Make git-send-email less verbose.  One line per email should be
index 9b5a3d61966eaa8d24b5ee507f2e1be770a1c367..a0d34e0058d721e655fd23c36415aaaecfa0c666 100644 (file)
@@ -104,6 +104,14 @@ accepts.  However '--fetch-all' only fetches from the current
 
 Like 'git-rebase'; this requires that the working tree be clean
 and have no uncommitted changes.
++
+--
+-l;;
+--local;;
+       Do not fetch remotely; only run 'git-rebase' against the
+       last fetched commit from the upstream SVN.
+--
++
 
 'dcommit'::
        Commit each diff from a specified head directly to the SVN
index e875e8318d24fa046c7cddfc3e583b4c242d9302..31397dc5390d79343dc5d6fa5501cd493be0ea21 100644 (file)
@@ -35,7 +35,9 @@ ifdef::stalenotes[]
 You are reading the documentation for the latest version of git.
 Documentation for older releases are available here:
 
-* link:v1.5.0.3/git.html[documentation for release 1.5.0.3]
+* link:v1.5.0.5/git.html[documentation for release 1.5.0.5]
+
+* link:v1.5.0.5/RelNotes-1.5.0.5.txt[release notes for 1.5.0.5]
 
 * link:v1.5.0.3/RelNotes-1.5.0.3.txt[release notes for 1.5.0.3]
 
index 1a51f84f53e3b658f397721b7ccd97fcc8cf370b..dc024d45c143359e2e647990f37d927a59267cc0 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -179,7 +179,7 @@ SCRIPT_SH = \
        git-clean.sh git-clone.sh git-commit.sh \
        git-fetch.sh git-gc.sh \
        git-ls-remote.sh \
-       git-merge-one-file.sh git-parse-remote.sh \
+       git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
        git-pull.sh git-rebase.sh \
        git-repack.sh git-request-pull.sh git-reset.sh \
        git-sh-setup.sh \
@@ -293,6 +293,7 @@ BUILTIN_OBJS = \
        builtin-diff-files.o \
        builtin-diff-index.o \
        builtin-diff-tree.o \
+       builtin-fetch--tool.o \
        builtin-fmt-merge-msg.o \
        builtin-for-each-ref.o \
        builtin-fsck.o \
index 87e16aa2200f90e2f12673e856040ed29ee7d119..9fcf514dbc4cb76e15b47142e77c4019997ecd5d 100644 (file)
@@ -12,6 +12,8 @@
 static const char builtin_add_usage[] =
 "git-add [-n] [-v] [-f] [--interactive | -i] [--] <filepattern>...";
 
+static const char *excludes_file;
+
 static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
 {
        char *seen;
@@ -67,6 +69,8 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec)
        path = git_path("info/exclude");
        if (!access(path, R_OK))
                add_excludes_from_file(dir, path);
+       if (!access(excludes_file, R_OK))
+               add_excludes_from_file(dir, excludes_file);
 
        /*
         * Calculate common prefix for the pathspec, and
@@ -88,6 +92,18 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec)
                prune_directory(dir, pathspec, baselen);
 }
 
+static int git_add_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "core.excludesfile")) {
+               if (!value)
+                       die("core.excludesfile without value");
+               excludes_file = xstrdup(value);
+               return 0;
+       }
+
+       return git_default_config(var, value);
+}
+
 static struct lock_file lock_file;
 
 static const char ignore_warning[] =
@@ -115,7 +131,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                exit(1);
        }
 
-       git_config(git_default_config);
+       git_config(git_add_config);
 
        newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1);
 
index 06d8a8ce0432134f994aa30ac4133baf4ba7fef5..a4494ee337d70a400664e529e84f90d475a03b92 100644 (file)
@@ -12,7 +12,7 @@
 #include "builtin.h"
 
 static const char builtin_branch_usage[] =
-  "git-branch [-r] (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]]";
+  "git-branch [-r] (-d | -D) <branchname> | [--track | --no-track] [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]]";
 
 #define REF_UNKNOWN_TYPE    0x00
 #define REF_LOCAL_BRANCH    0x01
@@ -22,6 +22,8 @@ static const char builtin_branch_usage[] =
 static const char *head;
 static unsigned char head_sha1[20];
 
+static int branch_track_remotes;
+
 static int branch_use_color;
 static char branch_colors[][COLOR_MAXLEN] = {
        "\033[m",       /* reset */
@@ -64,6 +66,9 @@ int git_branch_config(const char *var, const char *value)
                color_parse(value, var, branch_colors[slot]);
                return 0;
        }
+       if (!strcmp(var, "branch.autosetupmerge"))
+               branch_track_remotes = git_config_bool(var, value);
+
        return git_default_config(var, value);
 }
 
@@ -309,14 +314,119 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev)
        free_ref_list(&ref_list);
 }
 
+static char *config_repo;
+static char *config_remote;
+static const char *start_ref;
+static int start_len;
+static int base_len;
+
+static int get_remote_branch_name(const char *value)
+{
+       const char *colon;
+       const char *end;
+
+       if (*value == '+')
+               value++;
+
+       colon = strchr(value, ':');
+       if (!colon)
+               return 0;
+
+       end = value + strlen(value);
+
+       /* Try an exact match first.  */
+       if (!strcmp(colon + 1, start_ref)) {
+               /* Truncate the value before the colon.  */
+               nfasprintf(&config_repo, "%.*s", colon - value, value);
+               return 1;
+       }
+
+       /* Try with a wildcard match now.  */
+       if (end - value > 2 && end[-2] == '/' && end[-1] == '*' &&
+           colon - value > 2 && colon[-2] == '/' && colon[-1] == '*' &&
+           (end - 2) - (colon + 1) == base_len &&
+           !strncmp(colon + 1, start_ref, base_len)) {
+               /* Replace the star with the remote branch name.  */
+               nfasprintf(&config_repo, "%.*s%s",
+                          (colon - 2) - value, value,
+                          start_ref + base_len);
+               return 1;
+       }
+
+       return 0;
+}
+
+static int get_remote_config(const char *key, const char *value)
+{
+       const char *var;
+       if (prefixcmp(key, "remote."))
+               return 0;
+
+       var = strrchr(key, '.');
+       if (var == key + 6)
+               return 0;
+
+       if (!strcmp(var, ".fetch") && get_remote_branch_name(value))
+               nfasprintf(&config_remote, "%.*s", var - (key + 7), key + 7);
+
+       return 0;
+}
+
+static void set_branch_merge(const char *name, const char *config_remote,
+                            const char *config_repo)
+{
+       char key[1024];
+       if (sizeof(key) <=
+           snprintf(key, sizeof(key), "branch.%s.remote", name))
+               die("what a long branch name you have!");
+       git_config_set(key, config_remote);
+
+       /*
+        * We do not have to check if we have enough space for
+        * the 'merge' key, since it's shorter than the
+        * previous 'remote' key, which we already checked.
+        */
+       snprintf(key, sizeof(key), "branch.%s.merge", name);
+       git_config_set(key, config_repo);
+}
+
+static void set_branch_defaults(const char *name, const char *real_ref)
+{
+       const char *slash = strrchr(real_ref, '/');
+
+       if (!slash)
+               return;
+
+       start_ref = real_ref;
+       start_len = strlen(real_ref);
+       base_len = slash - real_ref;
+       git_config(get_remote_config);
+       if (!config_repo && !config_remote &&
+           !prefixcmp(real_ref, "refs/heads/")) {
+               set_branch_merge(name, ".", real_ref);
+               printf("Branch %s set up to track local branch %s.\n",
+                      name, real_ref);
+       }
+
+       if (config_repo && config_remote) {
+               set_branch_merge(name, config_remote, config_repo);
+               printf("Branch %s set up to track remote branch %s.\n",
+                      name, real_ref);
+       }
+
+       if (config_repo)
+               free(config_repo);
+       if (config_remote)
+               free(config_remote);
+}
+
 static void create_branch(const char *name, const char *start_name,
-                         unsigned char *start_sha1,
-                         int force, int reflog)
+                         int force, int reflog, int track)
 {
        struct ref_lock *lock;
        struct commit *commit;
        unsigned char sha1[20];
-       char ref[PATH_MAX], msg[PATH_MAX + 20];
+       char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20];
        int forcing = 0;
 
        snprintf(ref, sizeof ref, "refs/heads/%s", name);
@@ -331,12 +441,23 @@ static void create_branch(const char *name, const char *start_name,
                forcing = 1;
        }
 
-       if (start_sha1)
-               /* detached HEAD */
-               hashcpy(sha1, start_sha1);
-       else if (get_sha1(start_name, sha1))
+       real_ref = NULL;
+       if (get_sha1(start_name, sha1))
                die("Not a valid object name: '%s'.", start_name);
 
+       switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
+       case 0:
+               /* Not branching from any existing branch */
+               real_ref = NULL;
+               break;
+       case 1:
+               /* Unique completion -- good */
+               break;
+       default:
+               die("Ambiguous object name: '%s'.", start_name);
+               break;
+       }
+
        if ((commit = lookup_commit_reference(sha1)) == NULL)
                die("Not a valid branch point: '%s'.", start_name);
        hashcpy(sha1, commit->object.sha1);
@@ -355,8 +476,17 @@ static void create_branch(const char *name, const char *start_name,
                snprintf(msg, sizeof msg, "branch: Created from %s",
                         start_name);
 
+       /* When branching off a remote branch, set up so that git-pull
+          automatically merges from there.  So far, this is only done for
+          remotes registered via .git/config.  */
+       if (real_ref && track)
+               set_branch_defaults(name, real_ref);
+
        if (write_ref_sha1(lock, sha1, msg) < 0)
                die("Failed to write ref: %s.", strerror(errno));
+
+       if (real_ref)
+               free(real_ref);
 }
 
 static void rename_branch(const char *oldname, const char *newname, int force)
@@ -398,11 +528,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        int delete = 0, force_delete = 0, force_create = 0;
        int rename = 0, force_rename = 0;
        int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
-       int reflog = 0;
+       int reflog = 0, track;
        int kinds = REF_LOCAL_BRANCH;
        int i;
 
        git_config(git_branch_config);
+       track = branch_track_remotes;
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
@@ -413,6 +544,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                        i++;
                        break;
                }
+               if (!strcmp(arg, "--track")) {
+                       track = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--no-track")) {
+                       track = 0;
+                       continue;
+               }
                if (!strcmp(arg, "-d")) {
                        delete = 1;
                        continue;
@@ -498,10 +637,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                rename_branch(head, argv[i], force_rename);
        else if (rename && (i == argc - 2))
                rename_branch(argv[i], argv[i + 1], force_rename);
-       else if (i == argc - 1)
-               create_branch(argv[i], head, head_sha1, force_create, reflog);
-       else if (i == argc - 2)
-               create_branch(argv[i], argv[i+1], NULL, force_create, reflog);
+       else if (i == argc - 1 || i == argc - 2)
+               create_branch(argv[i], (i == argc - 2) ? argv[i+1] : head,
+                             force_create, reflog, track);
        else
                usage(builtin_branch_usage);
 
index aec83384298042fc4509605299f82e82745ae675..6ba5077a2be6619f110280622cf46a7f4cfb8b01 100644 (file)
@@ -17,6 +17,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
        int nongit = 0;
+       int result;
 
        prefix = setup_git_directory_gently(&nongit);
        init_revisions(&rev, prefix);
@@ -29,5 +30,6 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
                argc = setup_revisions(argc, argv, &rev, NULL);
        if (!rev.diffopt.output_format)
                rev.diffopt.output_format = DIFF_FORMAT_RAW;
-       return run_diff_files_cmd(&rev, argc, argv);
+       result = run_diff_files_cmd(&rev, argc, argv);
+       return rev.diffopt.exit_with_status ? rev.diffopt.has_changes: result;
 }
index 083599d5c4c174cfab7c148428630534e4cd8174..d90eba95a6be17bd0486e0311c2657b1f69ab7d9 100644 (file)
@@ -14,6 +14,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
        struct rev_info rev;
        int cached = 0;
        int i;
+       int result;
 
        init_revisions(&rev, prefix);
        git_config(git_default_config); /* no "diff" UI options */
@@ -42,5 +43,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
                perror("read_cache");
                return -1;
        }
-       return run_diff_index(&rev, cached);
+       result = run_diff_index(&rev, cached);
+       return rev.diffopt.exit_with_status ? rev.diffopt.has_changes: result;
 }
index 24cb2d7f84064d7833fa04f33aeca6eafc8b931d..0b591c87169ff4b8c2173bedb26d6ed1a8a84b68 100644 (file)
@@ -118,7 +118,8 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
        }
 
        if (!read_stdin)
-               return 0;
+               return opt->diffopt.exit_with_status ?
+                   opt->diffopt.has_changes: 0;
 
        if (opt->diffopt.detect_rename)
                opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
@@ -133,5 +134,5 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
                else
                        diff_tree_stdin(line);
        }
-       return 0;
+       return opt->diffopt.exit_with_status ? opt->diffopt.has_changes: 0;
 }
index 4efbb8237bd49e8717a42833b2d9b2db064b45ac..21d13f0b30359295b8385754fccb4bb71f995dba 100644 (file)
@@ -190,6 +190,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        const char *path = NULL;
        struct blobinfo blob[2];
        int nongit = 0;
+       int result = 0;
 
        /*
         * We could get N tree-ish in the rev.pending_objects list.
@@ -292,17 +293,17 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        if (!ents) {
                switch (blobs) {
                case 0:
-                       return run_diff_files_cmd(&rev, argc, argv);
+                       result = run_diff_files_cmd(&rev, argc, argv);
                        break;
                case 1:
                        if (paths != 1)
                                usage(builtin_diff_usage);
-                       return builtin_diff_b_f(&rev, argc, argv, blob, path);
+                       result = builtin_diff_b_f(&rev, argc, argv, blob, path);
                        break;
                case 2:
                        if (paths)
                                usage(builtin_diff_usage);
-                       return builtin_diff_blobs(&rev, argc, argv, blob);
+                       result = builtin_diff_blobs(&rev, argc, argv, blob);
                        break;
                default:
                        usage(builtin_diff_usage);
@@ -311,19 +312,21 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        else if (blobs)
                usage(builtin_diff_usage);
        else if (ents == 1)
-               return builtin_diff_index(&rev, argc, argv);
+               result = builtin_diff_index(&rev, argc, argv);
        else if (ents == 2)
-               return builtin_diff_tree(&rev, argc, argv, ent);
+               result = builtin_diff_tree(&rev, argc, argv, ent);
        else if ((ents == 3) && (ent[0].item->flags & UNINTERESTING)) {
                /* diff A...B where there is one sane merge base between
                 * A and B.  We have ent[0] == merge-base, ent[1] == A,
                 * and ent[2] == B.  Show diff between the base and B.
                 */
                ent[1] = ent[2];
-               return builtin_diff_tree(&rev, argc, argv, ent);
+               result = builtin_diff_tree(&rev, argc, argv, ent);
        }
        else
-               return builtin_diff_combined(&rev, argc, argv,
+               result = builtin_diff_combined(&rev, argc, argv,
                                             ent, ents);
-       usage(builtin_diff_usage);
+       if (rev.diffopt.exit_with_status)
+               result = rev.diffopt.has_changes;
+       return result;
 }
diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c
new file mode 100644 (file)
index 0000000..e9d6764
--- /dev/null
@@ -0,0 +1,505 @@
+#include "cache.h"
+#include "refs.h"
+#include "commit.h"
+
+#define CHUNK_SIZE 1024
+
+static char *get_stdin(void)
+{
+       int offset = 0;
+       char *data = xmalloc(CHUNK_SIZE);
+
+       while (1) {
+               int cnt = xread(0, data + offset, CHUNK_SIZE);
+               if (cnt < 0)
+                       die("error reading standard input: %s",
+                           strerror(errno));
+               if (cnt == 0) {
+                       data[offset] = 0;
+                       break;
+               }
+               offset += cnt;
+               data = xrealloc(data, offset + CHUNK_SIZE);
+       }
+       return data;
+}
+
+static void show_new(enum object_type type, unsigned char *sha1_new)
+{
+       fprintf(stderr, "  %s: %s\n", typename(type),
+               find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
+}
+
+static int update_ref(const char *action,
+                     const char *refname,
+                     unsigned char *sha1,
+                     unsigned char *oldval)
+{
+       int len;
+       char msg[1024];
+       char *rla = getenv("GIT_REFLOG_ACTION");
+       static struct ref_lock *lock;
+
+       if (!rla)
+               rla = "(reflog update)";
+       len = snprintf(msg, sizeof(msg), "%s: %s", rla, action);
+       if (sizeof(msg) <= len)
+               die("insanely long action");
+       lock = lock_any_ref_for_update(refname, oldval);
+       if (!lock)
+               return 1;
+       if (write_ref_sha1(lock, sha1, msg) < 0)
+               return 1;
+       return 0;
+}
+
+static int update_local_ref(const char *name,
+                           const char *new_head,
+                           const char *note,
+                           int verbose, int force)
+{
+       unsigned char sha1_old[20], sha1_new[20];
+       char oldh[41], newh[41];
+       struct commit *current, *updated;
+       enum object_type type;
+
+       if (get_sha1_hex(new_head, sha1_new))
+               die("malformed object name %s", new_head);
+
+       type = sha1_object_info(sha1_new, NULL);
+       if (type < 0)
+               die("object %s not found", new_head);
+
+       if (!*name) {
+               /* Not storing */
+               if (verbose) {
+                       fprintf(stderr, "* fetched %s\n", note);
+                       show_new(type, sha1_new);
+               }
+               return 0;
+       }
+
+       if (get_sha1(name, sha1_old)) {
+               char *msg;
+       just_store:
+               /* new ref */
+               if (!strncmp(name, "refs/tags/", 10))
+                       msg = "storing tag";
+               else
+                       msg = "storing head";
+               fprintf(stderr, "* %s: storing %s\n",
+                       name, note);
+               show_new(type, sha1_new);
+               return update_ref(msg, name, sha1_new, NULL);
+       }
+
+       if (!hashcmp(sha1_old, sha1_new)) {
+               if (verbose) {
+                       fprintf(stderr, "* %s: same as %s\n", name, note);
+                       show_new(type, sha1_new);
+               }
+               return 0;
+       }
+
+       if (!strncmp(name, "refs/tags/", 10)) {
+               fprintf(stderr, "* %s: updating with %s\n", name, note);
+               show_new(type, sha1_new);
+               return update_ref("updating tag", name, sha1_new, NULL);
+       }
+
+       current = lookup_commit_reference(sha1_old);
+       updated = lookup_commit_reference(sha1_new);
+       if (!current || !updated)
+               goto just_store;
+
+       strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
+       strcpy(newh, find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
+
+       if (in_merge_bases(current, &updated, 1)) {
+               fprintf(stderr, "* %s: fast forward to %s\n",
+                       name, note);
+               fprintf(stderr, "  old..new: %s..%s\n", oldh, newh);
+               return update_ref("fast forward", name, sha1_new, sha1_old);
+       }
+       if (!force) {
+               fprintf(stderr,
+                       "* %s: not updating to non-fast forward %s\n",
+                       name, note);
+               fprintf(stderr,
+                       "  old...new: %s...%s\n", oldh, newh);
+               return 1;
+       }
+       fprintf(stderr,
+               "* %s: forcing update to non-fast forward %s\n",
+               name, note);
+       fprintf(stderr, "  old...new: %s...%s\n", oldh, newh);
+       return update_ref("forced-update", name, sha1_new, sha1_old);
+}
+
+static int append_fetch_head(FILE *fp,
+                            const char *head, const char *remote,
+                            const char *remote_name, const char *remote_nick,
+                            const char *local_name, int not_for_merge,
+                            int verbose, int force)
+{
+       struct commit *commit;
+       int remote_len, i, note_len;
+       unsigned char sha1[20];
+       char note[1024];
+       const char *what, *kind;
+
+       if (get_sha1(head, sha1))
+               return error("Not a valid object name: %s", head);
+       commit = lookup_commit_reference(sha1);
+       if (!commit)
+               not_for_merge = 1;
+
+       if (!strcmp(remote_name, "HEAD")) {
+               kind = "";
+               what = "";
+       }
+       else if (!strncmp(remote_name, "refs/heads/", 11)) {
+               kind = "branch";
+               what = remote_name + 11;
+       }
+       else if (!strncmp(remote_name, "refs/tags/", 10)) {
+               kind = "tag";
+               what = remote_name + 10;
+       }
+       else if (!strncmp(remote_name, "refs/remotes/", 13)) {
+               kind = "remote branch";
+               what = remote_name + 13;
+       }
+       else {
+               kind = "";
+               what = remote_name;
+       }
+
+       remote_len = strlen(remote);
+       for (i = remote_len - 1; remote[i] == '/' && 0 <= i; i--)
+               ;
+       remote_len = i + 1;
+       if (4 < i && !strncmp(".git", remote + i - 3, 4))
+               remote_len = i - 3;
+
+       note_len = 0;
+       if (*what) {
+               if (*kind)
+                       note_len += sprintf(note + note_len, "%s ", kind);
+               note_len += sprintf(note + note_len, "'%s' of ", what);
+       }
+       note_len += sprintf(note + note_len, "%.*s", remote_len, remote);
+       fprintf(fp, "%s\t%s\t%s\n",
+               sha1_to_hex(commit ? commit->object.sha1 : sha1),
+               not_for_merge ? "not-for-merge" : "",
+               note);
+       return update_local_ref(local_name, head, note, verbose, force);
+}
+
+static char *keep;
+static void remove_keep(void)
+{
+       if (keep && *keep)
+               unlink(keep);
+}
+
+static void remove_keep_on_signal(int signo)
+{
+       remove_keep();
+       signal(SIGINT, SIG_DFL);
+       raise(signo);
+}
+
+static char *find_local_name(const char *remote_name, const char *refs,
+                            int *force_p, int *not_for_merge_p)
+{
+       const char *ref = refs;
+       int len = strlen(remote_name);
+
+       while (ref) {
+               const char *next;
+               int single_force, not_for_merge;
+
+               while (*ref == '\n')
+                       ref++;
+               if (!*ref)
+                       break;
+               next = strchr(ref, '\n');
+
+               single_force = not_for_merge = 0;
+               if (*ref == '+') {
+                       single_force = 1;
+                       ref++;
+               }
+               if (*ref == '.') {
+                       not_for_merge = 1;
+                       ref++;
+                       if (*ref == '+') {
+                               single_force = 1;
+                               ref++;
+                       }
+               }
+               if (!strncmp(remote_name, ref, len) && ref[len] == ':') {
+                       const char *local_part = ref + len + 1;
+                       char *ret;
+                       int retlen;
+
+                       if (!next)
+                               retlen = strlen(local_part);
+                       else
+                               retlen = next - local_part;
+                       ret = xmalloc(retlen + 1);
+                       memcpy(ret, local_part, retlen);
+                       ret[retlen] = 0;
+                       *force_p = single_force;
+                       *not_for_merge_p = not_for_merge;
+                       return ret;
+               }
+               ref = next;
+       }
+       return NULL;
+}
+
+static int fetch_native_store(FILE *fp,
+                             const char *remote,
+                             const char *remote_nick,
+                             const char *refs,
+                             int verbose, int force)
+{
+       char buffer[1024];
+       int err = 0;
+
+       signal(SIGINT, remove_keep_on_signal);
+       atexit(remove_keep);
+
+       while (fgets(buffer, sizeof(buffer), stdin)) {
+               int len;
+               char *cp;
+               char *local_name;
+               int single_force, not_for_merge;
+
+               for (cp = buffer; *cp && !isspace(*cp); cp++)
+                       ;
+               if (*cp)
+                       *cp++ = 0;
+               len = strlen(cp);
+               if (len && cp[len-1] == '\n')
+                       cp[--len] = 0;
+               if (!strcmp(buffer, "failed"))
+                       die("Fetch failure: %s", remote);
+               if (!strcmp(buffer, "pack"))
+                       continue;
+               if (!strcmp(buffer, "keep")) {
+                       char *od = get_object_directory();
+                       int len = strlen(od) + strlen(cp) + 50;
+                       keep = xmalloc(len);
+                       sprintf(keep, "%s/pack/pack-%s.keep", od, cp);
+                       continue;
+               }
+
+               local_name = find_local_name(cp, refs,
+                                            &single_force, &not_for_merge);
+               if (!local_name)
+                       continue;
+               err |= append_fetch_head(fp,
+                                        buffer, remote, cp, remote_nick,
+                                        local_name, not_for_merge,
+                                        verbose, force || single_force);
+       }
+       return err;
+}
+
+static int parse_reflist(const char *reflist)
+{
+       const char *ref;
+
+       printf("refs='");
+       for (ref = reflist; ref; ) {
+               const char *next;
+               while (*ref && isspace(*ref))
+                       ref++;
+               if (!*ref)
+                       break;
+               for (next = ref; *next && !isspace(*next); next++)
+                       ;
+               printf("\n%.*s", (int)(next - ref), ref);
+               ref = next;
+       }
+       printf("'\n");
+
+       printf("rref='");
+       for (ref = reflist; ref; ) {
+               const char *next, *colon;
+               while (*ref && isspace(*ref))
+                       ref++;
+               if (!*ref)
+                       break;
+               for (next = ref; *next && !isspace(*next); next++)
+                       ;
+               if (*ref == '.')
+                       ref++;
+               if (*ref == '+')
+                       ref++;
+               colon = strchr(ref, ':');
+               putchar('\n');
+               printf("%.*s", (int)((colon ? colon : next) - ref), ref);
+               ref = next;
+       }
+       printf("'\n");
+       return 0;
+}
+
+static int expand_refs_wildcard(const char *ls_remote_result, int numrefs,
+                               const char **refs)
+{
+       int i, matchlen, replacelen;
+       int found_one = 0;
+       const char *remote = *refs++;
+       numrefs--;
+
+       if (numrefs == 0) {
+               fprintf(stderr, "Nothing specified for fetching with remote.%s.fetch\n",
+                       remote);
+               printf("empty\n");
+       }
+
+       for (i = 0; i < numrefs; i++) {
+               const char *ref = refs[i];
+               const char *lref = ref;
+               const char *colon;
+               const char *tail;
+               const char *ls;
+               const char *next;
+
+               if (*lref == '+')
+                       lref++;
+               colon = strchr(lref, ':');
+               tail = lref + strlen(lref);
+               if (!(colon &&
+                     2 < colon - lref &&
+                     colon[-1] == '*' &&
+                     colon[-2] == '/' &&
+                     2 < tail - (colon + 1) &&
+                     tail[-1] == '*' &&
+                     tail[-2] == '/')) {
+                       /* not a glob */
+                       if (!found_one++)
+                               printf("explicit\n");
+                       printf("%s\n", ref);
+                       continue;
+               }
+
+               /* glob */
+               if (!found_one++)
+                       printf("glob\n");
+
+               /* lref to colon-2 is remote hierarchy name;
+                * colon+1 to tail-2 is local.
+                */
+               matchlen = (colon-1) - lref;
+               replacelen = (tail-1) - (colon+1);
+               for (ls = ls_remote_result; ls; ls = next) {
+                       const char *eol;
+                       unsigned char sha1[20];
+                       int namelen;
+
+                       while (*ls && isspace(*ls))
+                               ls++;
+                       next = strchr(ls, '\n');
+                       eol = !next ? (ls + strlen(ls)) : next;
+                       if (!memcmp("^{}", eol-3, 3))
+                               continue;
+                       if (eol - ls < 40)
+                               continue;
+                       if (get_sha1_hex(ls, sha1))
+                               continue;
+                       ls += 40;
+                       while (ls < eol && isspace(*ls))
+                               ls++;
+                       /* ls to next (or eol) is the name.
+                        * is it identical to lref to colon-2?
+                        */
+                       if ((eol - ls) <= matchlen ||
+                           strncmp(ls, lref, matchlen))
+                               continue;
+
+                       /* Yes, it is a match */
+                       namelen = eol - ls;
+                       if (lref != ref)
+                               putchar('+');
+                       printf("%.*s:%.*s%.*s\n",
+                              namelen, ls,
+                              replacelen, colon + 1,
+                              namelen - matchlen, ls + matchlen);
+               }
+       }
+       return 0;
+}
+
+int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
+{
+       int verbose = 0;
+       int force = 0;
+
+       while (1 < argc) {
+               const char *arg = argv[1];
+               if (!strcmp("-v", arg))
+                       verbose = 1;
+               else if (!strcmp("-f", arg))
+                       force = 1;
+               else
+                       break;
+               argc--;
+               argv++;
+       }
+
+       if (argc <= 1)
+               return error("Missing subcommand");
+
+       if (!strcmp("append-fetch-head", argv[1])) {
+               int result;
+               FILE *fp;
+
+               if (argc != 8)
+                       return error("append-fetch-head takes 6 args");
+               fp = fopen(git_path("FETCH_HEAD"), "a");
+               result = append_fetch_head(fp, argv[2], argv[3],
+                                          argv[4], argv[5],
+                                          argv[6], !!argv[7][0],
+                                          verbose, force);
+               fclose(fp);
+               return result;
+       }
+       if (!strcmp("native-store", argv[1])) {
+               int result;
+               FILE *fp;
+
+               if (argc != 5)
+                       return error("fetch-native-store takes 3 args");
+               fp = fopen(git_path("FETCH_HEAD"), "a");
+               result = fetch_native_store(fp, argv[2], argv[3], argv[4],
+                                           verbose, force);
+               fclose(fp);
+               return result;
+       }
+       if (!strcmp("parse-reflist", argv[1])) {
+               const char *reflist;
+               if (argc != 3)
+                       return error("parse-reflist takes 1 arg");
+               reflist = argv[2];
+               if (!strcmp(reflist, "-"))
+                       reflist = get_stdin();
+               return parse_reflist(reflist);
+       }
+       if (!strcmp("expand-refs-wildcard", argv[1])) {
+               const char *reflist;
+               if (argc < 4)
+                       return error("expand-refs-wildcard takes at least 2 args");
+               reflist = argv[2];
+               if (!strcmp(reflist, "-"))
+                       reflist = get_stdin();
+               return expand_refs_wildcard(reflist, argc - 3, argv + 3);
+       }
+
+       return error("Unknown subcommand: %s", argv[1]);
+}
index 694da5ba09d302c1e44f863070776bc3ac7f32e2..4510d353247355a28979fbf63b15523c2e705591 100644 (file)
@@ -431,6 +431,19 @@ static const char emsg_missing_context_len[] =
 static const char emsg_missing_argument[] =
 "option requires an argument -%s";
 
+static int strtoul_ui(char const *s, unsigned int *result)
+{
+       unsigned long ul;
+       char *p;
+
+       errno = 0;
+       ul = strtoul(s, &p, 10);
+       if (errno || *p || p == s || (unsigned int) ul != ul)
+               return -1;
+       *result = ul;
+       return 0;
+}
+
 int cmd_grep(int argc, const char **argv, const char *prefix)
 {
        int hit = 0;
@@ -553,7 +566,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                                scan = arg + 1;
                                break;
                        }
-                       if (sscanf(scan, "%u", &num) != 1)
+                       if (strtoul_ui(scan, &num))
                                die(emsg_invalid_context_len, scan);
                        switch (arg[1]) {
                        case 'A':
index f54e8752fb6dd0f7f251fe6bfe7bf3b2f8cd91f3..d94578cb4ac0649913db1542f876d5010ece7f0f 100644 (file)
@@ -11,19 +11,22 @@ static FILE *cmitmsg, *patchfile, *fin, *fout;
 static int keep_subject;
 static const char *metainfo_charset;
 static char line[1000];
-static char date[1000];
 static char name[1000];
 static char email[1000];
-static char subject[1000];
 
 static enum  {
        TE_DONTCARE, TE_QP, TE_BASE64,
 } transfer_encoding;
-static char charset[256];
+static enum  {
+       TYPE_TEXT, TYPE_OTHER,
+} message_type;
 
-static char multipart_boundary[1000];
-static int multipart_boundary_len;
+static char charset[256];
 static int patch_lines;
+static char **p_hdr_data, **s_hdr_data;
+
+#define MAX_HDR_PARSED 10
+#define MAX_BOUNDARIES 5
 
 static char *sanity_check(char *name, char *email)
 {
@@ -137,15 +140,13 @@ static int handle_from(char *in_line)
        return 1;
 }
 
-static int handle_date(char *line)
+static int handle_header(char *line, char *data, int ofs)
 {
-       strcpy(date, line);
-       return 0;
-}
+       if (!line || !data)
+               return 1;
+
+       strcpy(data, line+ofs);
 
-static int handle_subject(char *line)
-{
-       strcpy(subject, line);
        return 0;
 }
 
@@ -177,17 +178,32 @@ static int slurp_attr(const char *line, const char *name, char *attr)
        return 1;
 }
 
-static int handle_subcontent_type(char *line)
+struct content_type {
+       char *boundary;
+       int boundary_len;
+};
+
+static struct content_type content[MAX_BOUNDARIES];
+
+static struct content_type *content_top = content;
+
+static int handle_content_type(char *line)
 {
-       /* We do not want to mess with boundary.  Note that we do not
-        * handle nested multipart.
-        */
-       if (strcasestr(line, "boundary=")) {
-               fprintf(stderr, "Not handling nested multipart message.\n");
-               exit(1);
+       char boundary[256];
+
+       if (strcasestr(line, "text/") == NULL)
+                message_type = TYPE_OTHER;
+       if (slurp_attr(line, "boundary=", boundary + 2)) {
+               memcpy(boundary, "--", 2);
+               if (content_top++ >= &content[MAX_BOUNDARIES]) {
+                       fprintf(stderr, "Too many boundaries to handle\n");
+                       exit(1);
+               }
+               content_top->boundary_len = strlen(boundary);
+               content_top->boundary = xmalloc(content_top->boundary_len+1);
+               strcpy(content_top->boundary, boundary);
        }
-       slurp_attr(line, "charset=", charset);
-       if (*charset) {
+       if (slurp_attr(line, "charset=", charset)) {
                int i, c;
                for (i = 0; (c = charset[i]) != 0; i++)
                        charset[i] = tolower(c);
@@ -195,17 +211,6 @@ static int handle_subcontent_type(char *line)
        return 0;
 }
 
-static int handle_content_type(char *line)
-{
-       *multipart_boundary = 0;
-       if (slurp_attr(line, "boundary=", multipart_boundary + 2)) {
-               memcpy(multipart_boundary, "--", 2);
-               multipart_boundary_len = strlen(multipart_boundary);
-       }
-       slurp_attr(line, "charset=", charset);
-       return 0;
-}
-
 static int handle_content_transfer_encoding(char *line)
 {
        if (strcasestr(line, "base64"))
@@ -219,7 +224,7 @@ static int handle_content_transfer_encoding(char *line)
 
 static int is_multipart_boundary(const char *line)
 {
-       return (!memcmp(line, multipart_boundary, multipart_boundary_len));
+       return (!memcmp(line, content_top->boundary, content_top->boundary_len));
 }
 
 static int eatspace(char *line)
@@ -230,62 +235,6 @@ static int eatspace(char *line)
        return len;
 }
 
-#define SEEN_FROM 01
-#define SEEN_DATE 02
-#define SEEN_SUBJECT 04
-#define SEEN_BOGUS_UNIX_FROM 010
-#define SEEN_PREFIX  020
-
-/* First lines of body can have From:, Date:, and Subject: or empty */
-static void handle_inbody_header(int *seen, char *line)
-{
-       if (*seen & SEEN_PREFIX)
-               return;
-       if (isspace(*line)) {
-               char *cp;
-               for (cp = line + 1; *cp; cp++) {
-                       if (!isspace(*cp))
-                               break;
-               }
-               if (!*cp)
-                       return;
-       }
-       if (!memcmp(">From", line, 5) && isspace(line[5])) {
-               if (!(*seen & SEEN_BOGUS_UNIX_FROM)) {
-                       *seen |= SEEN_BOGUS_UNIX_FROM;
-                       return;
-               }
-       }
-       if (!memcmp("From:", line, 5) && isspace(line[5])) {
-               if (!(*seen & SEEN_FROM) && handle_from(line+6)) {
-                       *seen |= SEEN_FROM;
-                       return;
-               }
-       }
-       if (!memcmp("Date:", line, 5) && isspace(line[5])) {
-               if (!(*seen & SEEN_DATE)) {
-                       handle_date(line+6);
-                       *seen |= SEEN_DATE;
-                       return;
-               }
-       }
-       if (!memcmp("Subject:", line, 8) && isspace(line[8])) {
-               if (!(*seen & SEEN_SUBJECT)) {
-                       handle_subject(line+9);
-                       *seen |= SEEN_SUBJECT;
-                       return;
-               }
-       }
-       if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
-               if (!(*seen & SEEN_SUBJECT)) {
-                       handle_subject(line);
-                       *seen |= SEEN_SUBJECT;
-                       return;
-               }
-       }
-       *seen |= SEEN_PREFIX;
-}
-
 static char *cleanup_subject(char *subject)
 {
        if (keep_subject)
@@ -296,7 +245,7 @@ static char *cleanup_subject(char *subject)
                switch (*subject) {
                case 'r': case 'R':
                        if (!memcmp("e:", subject+1, 2)) {
-                               subject +=3;
+                               subject += 3;
                                continue;
                        }
                        break;
@@ -341,57 +290,62 @@ static void cleanup_space(char *buf)
 }
 
 static void decode_header(char *it);
-typedef int (*header_fn_t)(char *);
-struct header_def {
-       const char *name;
-       header_fn_t func;
-       int namelen;
+static char *header[MAX_HDR_PARSED] = {
+       "From","Subject","Date",
 };
 
-static void check_header(char *line, struct header_def *header)
+static int check_header(char *line, char **hdr_data)
 {
        int i;
 
-       if (header[0].namelen <= 0) {
-               for (i = 0; header[i].name; i++)
-                       header[i].namelen = strlen(header[i].name);
-       }
-       for (i = 0; header[i].name; i++) {
-               int len = header[i].namelen;
-               if (!strncasecmp(line, header[i].name, len) &&
+       /* search for the interesting parts */
+       for (i = 0; header[i]; i++) {
+               int len = strlen(header[i]);
+               if (!hdr_data[i] &&
+                   !strncasecmp(line, header[i], len) &&
                    line[len] == ':' && isspace(line[len + 1])) {
                        /* Unwrap inline B and Q encoding, and optionally
                         * normalize the meta information to utf8.
                         */
                        decode_header(line + len + 2);
-                       header[i].func(line + len + 2);
-                       break;
+                       hdr_data[i] = xmalloc(1000 * sizeof(char));
+                       if (! handle_header(line, hdr_data[i], len + 2)) {
+                               return 1;
+                       }
                }
        }
-}
 
-static void check_subheader_line(char *line)
-{
-       static struct header_def header[] = {
-               { "Content-Type", handle_subcontent_type },
-               { "Content-Transfer-Encoding",
-                 handle_content_transfer_encoding },
-               { NULL },
-       };
-       check_header(line, header);
-}
-static void check_header_line(char *line)
-{
-       static struct header_def header[] = {
-               { "From", handle_from },
-               { "Date", handle_date },
-               { "Subject", handle_subject },
-               { "Content-Type", handle_content_type },
-               { "Content-Transfer-Encoding",
-                 handle_content_transfer_encoding },
-               { NULL },
-       };
-       check_header(line, header);
+       /* Content stuff */
+       if (!strncasecmp(line, "Content-Type", 12) &&
+               line[12] == ':' && isspace(line[12 + 1])) {
+               decode_header(line + 12 + 2);
+               if (! handle_content_type(line)) {
+                       return 1;
+               }
+       }
+       if (!strncasecmp(line, "Content-Transfer-Encoding", 25) &&
+               line[25] == ':' && isspace(line[25 + 1])) {
+               decode_header(line + 25 + 2);
+               if (! handle_content_transfer_encoding(line)) {
+                       return 1;
+               }
+       }
+
+       /* for inbody stuff */
+       if (!memcmp(">From", line, 5) && isspace(line[5]))
+               return 1;
+       if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
+               for (i = 0; header[i]; i++) {
+                       if (!memcmp("Subject: ", header[i], 9)) {
+                               if (! handle_header(line, hdr_data[i], 0)) {
+                                       return 1;
+                               }
+                       }
+               }
+       }
+
+       /* no match */
+       return 0;
 }
 
 static int is_rfc2822_header(char *line)
@@ -647,147 +601,253 @@ static void decode_transfer_encoding(char *line)
        }
 }
 
-static void handle_info(void)
+static int handle_filter(char *line);
+
+static int find_boundary(void)
 {
-       char *sub;
+       while(fgets(line, sizeof(line), fin) != NULL) {
+               if (is_multipart_boundary(line))
+                       return 1;
+       }
+       return 0;
+}
+
+static int handle_boundary(void)
+{
+again:
+       if (!memcmp(line+content_top->boundary_len, "--", 2)) {
+               /* we hit an end boundary */
+               /* pop the current boundary off the stack */
+               free(content_top->boundary);
+
+               /* technically won't happen as is_multipart_boundary()
+                  will fail first.  But just in case..
+                */
+               if (content_top-- < content) {
+                       fprintf(stderr, "Detected mismatched boundaries, "
+                                       "can't recover\n");
+                       exit(1);
+               }
+               handle_filter("\n");
+
+               /* skip to the next boundary */
+               if (!find_boundary())
+                       return 0;
+               goto again;
+       }
+
+       /* set some defaults */
+       transfer_encoding = TE_DONTCARE;
+       charset[0] = 0;
+       message_type = TYPE_TEXT;
 
-       sub = cleanup_subject(subject);
-       cleanup_space(name);
-       cleanup_space(date);
-       cleanup_space(email);
-       cleanup_space(sub);
+       /* slurp in this section's info */
+       while (read_one_header_line(line, sizeof(line), fin))
+               check_header(line, p_hdr_data);
 
-       fprintf(fout, "Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n",
-              name, email, sub, date);
+       /* eat the blank line after section info */
+       return (fgets(line, sizeof(line), fin) != NULL);
 }
 
-/* We are inside message body and have read line[] already.
- * Spit out the commit log.
- */
-static int handle_commit_msg(int *seen)
+static inline int patchbreak(const char *line)
 {
-       if (!cmitmsg)
-               return 0;
-       do {
-               if (!memcmp("diff -", line, 6) ||
-                   !memcmp("---", line, 3) ||
-                   !memcmp("Index: ", line, 7))
-                       break;
-               if ((multipart_boundary[0] && is_multipart_boundary(line))) {
-                       /* We come here when the first part had only
-                        * the commit message without any patch.  We
-                        * pretend we have not seen this line yet, and
-                        * go back to the loop.
-                        */
+       /* Beginning of a "diff -" header? */
+       if (!memcmp("diff -", line, 6))
+               return 1;
+
+       /* CVS "Index: " line? */
+       if (!memcmp("Index: ", line, 7))
+               return 1;
+
+       /*
+        * "--- <filename>" starts patches without headers
+        * "---<sp>*" is a manual separator
+        */
+       if (!memcmp("---", line, 3)) {
+               line += 3;
+               /* space followed by a filename? */
+               if (line[0] == ' ' && !isspace(line[1]))
                        return 1;
+               /* Just whitespace? */
+               for (;;) {
+                       unsigned char c = *line++;
+                       if (c == '\n')
+                               return 1;
+                       if (!isspace(c))
+                               break;
                }
+               return 0;
+       }
+       return 0;
+}
 
-               /* Unwrap transfer encoding and optionally
-                * normalize the log message to UTF-8.
-                */
-               decode_transfer_encoding(line);
-               if (metainfo_charset)
-                       convert_to_utf8(line, charset);
 
-               handle_inbody_header(seen, line);
-               if (!(*seen & SEEN_PREFIX))
-                       continue;
+static int handle_commit_msg(char *line)
+{
+       static int still_looking = 1;
 
-               fputs(line, cmitmsg);
-       } while (fgets(line, sizeof(line), fin) != NULL);
-       fclose(cmitmsg);
-       cmitmsg = NULL;
+       if (!cmitmsg)
+               return 0;
+
+       if (still_looking) {
+               char *cp = line;
+               if (isspace(*line)) {
+                       for (cp = line + 1; *cp; cp++) {
+                               if (!isspace(*cp))
+                                       break;
+                       }
+                       if (!*cp)
+                               return 0;
+               }
+               if ((still_looking = check_header(cp, s_hdr_data)) != 0)
+                       return 0;
+       }
+
+       if (patchbreak(line)) {
+               fclose(cmitmsg);
+               cmitmsg = NULL;
+               return 1;
+       }
+
+       fputs(line, cmitmsg);
        return 0;
 }
 
-/* We have done the commit message and have the first
- * line of the patch in line[].
- */
-static void handle_patch(void)
+static int handle_patch(char *line)
 {
-       do {
-               if (multipart_boundary[0] && is_multipart_boundary(line))
-                       break;
-               /* Only unwrap transfer encoding but otherwise do not
-                * do anything.  We do *NOT* want UTF-8 conversion
-                * here; we are dealing with the user payload.
-                */
-               decode_transfer_encoding(line);
-               fputs(line, patchfile);
-               patch_lines++;
-       } while (fgets(line, sizeof(line), fin) != NULL);
+       fputs(line, patchfile);
+       patch_lines++;
+       return 0;
 }
 
-/* multipart boundary and transfer encoding are set up for us, and we
- * are at the end of the sub header.  do equivalent of handle_body up
- * to the next boundary without closing patchfile --- we will expect
- * that the first part to contain commit message and a patch, and
- * handle other parts as pure patches.
- */
-static int handle_multipart_one_part(int *seen)
+static int handle_filter(char *line)
 {
-       int n = 0;
+       static int filter = 0;
 
-       while (fgets(line, sizeof(line), fin) != NULL) {
-       again:
-               n++;
-               if (is_multipart_boundary(line))
+       /* filter tells us which part we left off on
+        * a non-zero return indicates we hit a filter point
+        */
+       switch (filter) {
+       case 0:
+               if (!handle_commit_msg(line))
                        break;
-               if (handle_commit_msg(seen))
-                       goto again;
-               handle_patch();
-               break;
+               filter++;
+       case 1:
+               if (!handle_patch(line))
+                       break;
+               filter++;
+       default:
+               return 1;
        }
-       if (n == 0)
-               return -1;
+
        return 0;
 }
 
-static void handle_multipart_body(void)
+static void handle_body(void)
 {
-       int seen = 0;
-       int part_num = 0;
+       int rc = 0;
+       static char newline[2000];
+       static char *np = newline;
 
        /* Skip up to the first boundary */
-       while (fgets(line, sizeof(line), fin) != NULL)
-               if (is_multipart_boundary(line)) {
-                       part_num = 1;
+       if (content_top->boundary) {
+               if (!find_boundary())
+                       return;
+       }
+
+       do {
+               /* process any boundary lines */
+               if (content_top->boundary && is_multipart_boundary(line)) {
+                       /* flush any leftover */
+                       if ((transfer_encoding == TE_BASE64)  &&
+                           (np != newline)) {
+                               handle_filter(newline);
+                       }
+                       if (!handle_boundary())
+                               return;
+               }
+
+               /* Unwrap transfer encoding and optionally
+                * normalize the log message to UTF-8.
+                */
+               decode_transfer_encoding(line);
+               if (metainfo_charset)
+                       convert_to_utf8(line, charset);
+
+               switch (transfer_encoding) {
+               case TE_BASE64:
+               {
+                       char *op = line;
+
+                       /* binary data most likely doesn't have newlines */
+                       if (message_type != TYPE_TEXT) {
+                               rc = handle_filter(line);
+                               break;
+                       }
+
+                       /* this is a decoded line that may contain
+                        * multiple new lines.  Pass only one chunk
+                        * at a time to handle_filter()
+                        */
+
+                       do {
+                               while (*op != '\n' && *op != 0)
+                                       *np++ = *op++;
+                               *np = *op;
+                               if (*np != 0) {
+                                       /* should be sitting on a new line */
+                                       *(++np) = 0;
+                                       op++;
+                                       rc = handle_filter(newline);
+                                       np = newline;
+                               }
+                       } while (*op != 0);
+                       /* the partial chunk is saved in newline and
+                        * will be appended by the next iteration of fgets
+                        */
                        break;
                }
-       if (!part_num)
-               return;
-       /* We are on boundary line.  Start slurping the subhead. */
-       while (1) {
-               int hdr = read_one_header_line(line, sizeof(line), fin);
-               if (!hdr) {
-                       if (handle_multipart_one_part(&seen) < 0)
-                               return;
-                       /* Reset per part headers */
-                       transfer_encoding = TE_DONTCARE;
-                       charset[0] = 0;
+               default:
+                       rc = handle_filter(line);
                }
-               else
-                       check_subheader_line(line);
-       }
-       fclose(patchfile);
-       if (!patch_lines) {
-               fprintf(stderr, "No patch found\n");
-               exit(1);
-       }
+               if (rc)
+                       /* nothing left to filter */
+                       break;
+       } while (fgets(line, sizeof(line), fin));
+
+       return;
 }
 
-/* Non multipart message */
-static void handle_body(void)
+static void handle_info(void)
 {
-       int seen = 0;
+       char *sub;
+       char *hdr;
+       int i;
 
-       handle_commit_msg(&seen);
-       handle_patch();
-       fclose(patchfile);
-       if (!patch_lines) {
-               fprintf(stderr, "No patch found\n");
-               exit(1);
+       for (i = 0; header[i]; i++) {
+
+               /* only print inbody headers if we output a patch file */
+               if (patch_lines && s_hdr_data[i])
+                       hdr = s_hdr_data[i];
+               else if (p_hdr_data[i])
+                       hdr = p_hdr_data[i];
+               else
+                       continue;
+
+               if (!memcmp(header[i], "Subject", 7)) {
+                       sub = cleanup_subject(hdr);
+                       cleanup_space(sub);
+                       fprintf(fout, "Subject: %s\n", sub);
+               } else if (!memcmp(header[i], "From", 4)) {
+                       handle_from(hdr);
+                       fprintf(fout, "Author: %s\n", name);
+                       fprintf(fout, "Email: %s\n", email);
+               } else {
+                       cleanup_space(hdr);
+                       fprintf(fout, "%s: %s\n", header[i], hdr);
+               }
        }
+       fprintf(fout, "\n");
 }
 
 int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
@@ -809,18 +869,16 @@ int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
                fclose(cmitmsg);
                return -1;
        }
-       while (1) {
-               int hdr = read_one_header_line(line, sizeof(line), fin);
-               if (!hdr) {
-                       if (multipart_boundary[0])
-                               handle_multipart_body();
-                       else
-                               handle_body();
-                       handle_info();
-                       break;
-               }
-               check_header_line(line);
-       }
+
+       p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *));
+       s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *));
+
+       /* process the email header */
+       while (read_one_header_line(line, sizeof(line), fin))
+               check_header(line, p_hdr_data);
+
+       handle_body();
+       handle_info();
 
        return 0;
 }
index f8ebad0b2f2006d619dc06814acb6024ee674eec..73d448b890d61b79793290b6b9b9aceea0a89cdc 100644 (file)
@@ -166,11 +166,12 @@ static void prepare_pack_revindex(struct pack_revindex *rix)
        struct packed_git *p = rix->p;
        int num_ent = num_packed_objects(p);
        int i;
-       void *index = p->index_base + 256;
+       const char *index = p->index_data;
 
+       index += 4 * 256;
        rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1));
        for (i = 0; i < num_ent; i++) {
-               unsigned int hl = *((unsigned int *)((char *) index + 24*i));
+               uint32_t hl = *((uint32_t *)(index + 24 * i));
                rix->revindex[i].offset = ntohl(hl);
                rix->revindex[i].nr = i;
        }
@@ -217,11 +218,11 @@ static off_t find_packed_object_size(struct packed_git *p, off_t ofs)
        return entry[1].offset - ofs;
 }
 
-static unsigned char *find_packed_object_name(struct packed_git *p,
-                                             off_t ofs)
+static const unsigned char *find_packed_object_name(struct packed_git *p,
+                                                   off_t ofs)
 {
        struct revindex_entry *entry = find_packed_object(p, ofs);
-       return (unsigned char *)(p->index_base + 256) + 24 * entry->nr + 4;
+       return ((unsigned char *)p->index_data) + 4 * 256 + 24 * entry->nr + 4;
 }
 
 static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
@@ -996,7 +997,8 @@ static void check_object(struct object_entry *entry)
                 * delta.
                 */
                if (!no_reuse_delta) {
-                       unsigned char c, *base_name;
+                       unsigned char c;
+                       const unsigned char *base_name;
                        off_t ofs;
                        unsigned long used_0;
                        /* there is at least 20 bytes left in the pack */
index 2d7726e8b975d9977e14638cc127ed0b30d3058b..29343aefc843c4dd22095f559262bc6b5e381440 100644 (file)
@@ -304,8 +304,11 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
        if (!access(".mailmap", R_OK))
                read_mailmap(".mailmap");
 
-       if (rev.pending.nr == 0)
+       if (rev.pending.nr == 0) {
+               if (isatty(0))
+                       fprintf(stderr, "(reading log to summarize from standard input)\n");
                read_from_stdin(&list);
+       }
        else
                get_from_rev(&rev, &list);
 
index c58d3e3445d0801ac3181e429365535b9ec11095..1cb64b7ecd60f71ca0fd0d4d47e93c5f3d05d9ce 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -32,6 +32,7 @@ extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
 extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
 extern int cmd_diff(int argc, const char **argv, const char *prefix);
 extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_fetch__tool(int argc, const char **argv, const char *prefix);
 extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
 extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
diff --git a/cache.h b/cache.h
index 4f1066744c186695374008c1d3edad6d6dcac939..5396d3366dd5d802a4e3ce43d296957c94ddb028 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -371,9 +371,11 @@ struct pack_window {
 extern struct packed_git {
        struct packed_git *next;
        struct pack_window *windows;
-       uint32_t *index_base;
+       const void *index_data;
        off_t index_size;
        off_t pack_size;
+       time_t mtime;
+       int index_version;
        int pack_fd;
        int pack_local;
        unsigned char sha1[20];
@@ -411,7 +413,7 @@ extern int server_supports(const char *feature);
 
 extern struct packed_git *parse_pack_index(unsigned char *sha1);
 extern struct packed_git *parse_pack_index_file(const unsigned char *sha1,
-                                               char *idx_path);
+                                               const char *idx_path);
 
 extern void prepare_packed_git(void);
 extern void reprepare_packed_git(void);
@@ -423,7 +425,7 @@ extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
 extern void pack_report(void);
 extern unsigned char* use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
 extern void unuse_pack(struct pack_window **);
-extern struct packed_git *add_packed_git(char *, int, int);
+extern struct packed_git *add_packed_git(const char *, int, int);
 extern uint32_t num_packed_objects(const struct packed_git *p);
 extern int nth_packed_object_sha1(const struct packed_git *, uint32_t, unsigned char*);
 extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
@@ -480,6 +482,7 @@ extern struct tag *alloc_tag_node(void);
 extern void alloc_report(void);
 
 /* trace.c */
+extern int nfasprintf(char **str, const char *fmt, ...);
 extern int nfvasprintf(char **str, const char *fmt, va_list va);
 extern void trace_printf(const char *format, ...);
 extern void trace_argv_printf(const char **argv, int count, const char *format, ...);
index a3c7b772bce1d302e60bbf02212bb4ad4da0ee7b..d537311ae1d4321c3f5f90930f7451262caf2ae5 100644 (file)
--- a/config.c
+++ b/config.c
@@ -351,12 +351,12 @@ int git_default_config(const char *var, const char *value)
        }
 
        if (!strcmp(var, "i18n.commitencoding")) {
-               git_commit_encoding = strdup(value);
+               git_commit_encoding = xstrdup(value);
                return 0;
        }
 
        if (!strcmp(var, "i18n.logoutputencoding")) {
-               git_log_output_encoding = strdup(value);
+               git_log_output_encoding = xstrdup(value);
                return 0;
        }
 
index 6abb981534bf032d50e28eb3a14033c1b6546762..5c5b05bfe32bc90484b5bd6a9c171e0f9b04fbd6 100644 (file)
@@ -170,8 +170,10 @@ static int handle_diff_files_args(struct rev_info *revs,
                else if (!strcmp(argv[1], "--theirs"))
                        revs->max_count = 3;
                else if (!strcmp(argv[1], "-n") ||
-                               !strcmp(argv[1], "--no-index"))
+                               !strcmp(argv[1], "--no-index")) {
                        revs->max_count = -2;
+                       revs->diffopt.exit_with_status = 1;
+               }
                else if (!strcmp(argv[1], "-q"))
                        *silent = 1;
                else
@@ -237,6 +239,7 @@ int setup_diff_no_index(struct rev_info *revs,
                        break;
                } else if (i < argc - 3 && !strcmp(argv[i], "--no-index")) {
                        i = argc - 3;
+                       revs->diffopt.exit_with_status = 1;
                        break;
                }
        if (argc != i + 2 || (!is_outside_repo(argv[i + 1], nongit, prefix) &&
@@ -321,6 +324,9 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
                struct cache_entry *ce = active_cache[i];
                int changed;
 
+               if (revs->diffopt.quiet && revs->diffopt.has_changes)
+                       break;
+
                if (!ce_path_match(ce, revs->prune_data))
                        continue;
 
@@ -562,6 +568,9 @@ static int diff_cache(struct rev_info *revs,
                struct cache_entry *ce = *ac;
                int same = (entries > 1) && ce_same_name(ce, ac[1]);
 
+               if (revs->diffopt.quiet && revs->diffopt.has_changes)
+                       break;
+
                if (!ce_path_match(ce, pathspec))
                        goto skip_entry;
 
diff --git a/diff.c b/diff.c
index 954ca83e0b0c95f55c0287d2deeb4cab76153fe0..d8f9242ea8fe2ee92884623939a85b6587a3ad9d 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1958,6 +1958,23 @@ int diff_setup_done(struct diff_options *options)
        if (options->abbrev <= 0 || 40 < options->abbrev)
                options->abbrev = 40; /* full */
 
+       /*
+        * It does not make sense to show the first hit we happened
+        * to have found.  It does not make sense not to return with
+        * exit code in such a case either.
+        */
+       if (options->quiet) {
+               options->output_format = DIFF_FORMAT_NO_OUTPUT;
+               options->exit_with_status = 1;
+       }
+
+       /*
+        * If we postprocess in diffcore, we cannot simply return
+        * upon the first hit.  We need to run diff as usual.
+        */
+       if (options->pickaxe || options->filter)
+               options->quiet = 0;
+
        return 0;
 }
 
@@ -2134,6 +2151,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->color_diff = options->color_diff_words = 1;
        else if (!strcmp(arg, "--no-renames"))
                options->detect_rename = 0;
+       else if (!strcmp(arg, "--exit-code"))
+               options->exit_with_status = 1;
+       else if (!strcmp(arg, "--quiet"))
+               options->quiet = 1;
        else
                return 0;
        return 1;
@@ -2898,6 +2919,8 @@ static void diffcore_apply_filter(const char *filter)
 
 void diffcore_std(struct diff_options *options)
 {
+       if (options->quiet)
+               return;
        if (options->break_opt != -1)
                diffcore_break(options->break_opt);
        if (options->detect_rename)
@@ -2910,18 +2933,11 @@ void diffcore_std(struct diff_options *options)
                diffcore_order(options->orderfile);
        diff_resolve_rename_copy();
        diffcore_apply_filter(options->filter);
-}
 
-
-void diffcore_std_no_resolve(struct diff_options *options)
-{
-       if (options->pickaxe)
-               diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
-       if (options->orderfile)
-               diffcore_order(options->orderfile);
-       diffcore_apply_filter(options->filter);
+       options->has_changes = !!diff_queued_diff.nr;
 }
 
+
 void diff_addremove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
@@ -2957,6 +2973,7 @@ void diff_addremove(struct diff_options *options,
                fill_filespec(two, sha1, mode);
 
        diff_queue(&diff_queued_diff, one, two);
+       options->has_changes = 1;
 }
 
 void diff_change(struct diff_options *options,
@@ -2982,6 +2999,7 @@ void diff_change(struct diff_options *options,
        fill_filespec(two, new_sha1, new_mode);
 
        diff_queue(&diff_queued_diff, one, two);
+       options->has_changes = 1;
 }
 
 void diff_unmerge(struct diff_options *options,
diff --git a/diff.h b/diff.h
index 4b435e8b1933222da02f9e4cf2c28d2fef44d292..a0d2ce13994c1a8751bf7b207671e95c5bc5db97 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -56,7 +56,10 @@ struct diff_options {
                 silent_on_remove:1,
                 find_copies_harder:1,
                 color_diff:1,
-                color_diff_words:1;
+                color_diff_words:1,
+                has_changes:1,
+                quiet:1,
+                exit_with_status:1;
        int context;
        int break_opt;
        int detect_rename;
@@ -170,8 +173,6 @@ extern int diff_setup_done(struct diff_options *);
 
 extern void diffcore_std(struct diff_options *);
 
-extern void diffcore_std_no_resolve(struct diff_options *);
-
 #define COMMON_DIFF_OPTIONS_HELP \
 "\ncommon diff options:\n" \
 "  -z            output diff-raw with lines terminated with NUL.\n" \
index 2c73d116b28d0d89d1fbdfc1d454506bff9e3f46..88af8dd256ef871e8a35ec47df925db9b13b95f7 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -290,6 +290,10 @@ do
                git-mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
                        <"$dotest/$msgnum" >"$dotest/info" ||
                        stop_here $this
+               test -s $dotest/patch || {
+                       echo "Patch is empty.  Was is split wrong?"
+                       stop_here $this
+               }
                git-stripspace < "$dotest/msg" > "$dotest/msg-clean"
                ;;
        esac
index 1f68599ae51a671f8c7a3a01c6aa9c6b205dd217..3efd6a746407bdc38ba9d251332427c508e5ee40 100755 (executable)
@@ -77,6 +77,10 @@ do
     *)
            git-mailinfo $keep_subject $utf8 \
                .dotest/msg .dotest/patch <$i >.dotest/info || exit 1
+           test -s .dotest/patch || {
+               echo "Patch is empty.  Was is split wrong?"
+               exit 1
+           }
            git-stripspace < .dotest/msg > .dotest/msg-clean
            ;;
     esac
index 14835a4aa982af88d758ea014afacf7b2fb1bb1e..39ffa8b8a3322b623a6d4938d566453fe206ca69 100755 (executable)
@@ -12,6 +12,7 @@ new=
 new_name=
 force=
 branch=
+track=
 newbranch=
 newbranch_log=
 merge=
@@ -33,7 +34,10 @@ while [ "$#" != "0" ]; do
                        die "git checkout: we do not like '$newbranch' as a branch name."
                ;;
        "-l")
-               newbranch_log=1
+               newbranch_log=-l
+               ;;
+       "--track"|"--no-track")
+               track="$arg"
                ;;
        "-f")
                force=1
@@ -85,6 +89,11 @@ while [ "$#" != "0" ]; do
     esac
 done
 
+case "$newbranch,$track" in
+,--*)
+       die "git checkout: --track and --no-track require -b"
+esac
+
 case "$force$merge" in
 11)
        die "git checkout: -f and -m are incompatible"
@@ -196,7 +205,7 @@ else
        work=`git write-tree` &&
        git read-tree --reset -u $new || exit
 
-       eval GITHEAD_$new=${new_name:-${branch:-$new}} &&
+       eval GITHEAD_$new='${new_name:-${branch:-$new}}' &&
        eval GITHEAD_$work=local &&
        export GITHEAD_$new GITHEAD_$work &&
        git merge-recursive $old -- $new $work
@@ -235,11 +244,7 @@ fi
 #
 if [ "$?" -eq 0 ]; then
        if [ "$newbranch" ]; then
-               if [ "$newbranch_log" ]; then
-                       mkdir -p $(dirname "$GIT_DIR/logs/refs/heads/$newbranch")
-                       touch "$GIT_DIR/logs/refs/heads/$newbranch"
-               fi
-               git-update-ref -m "checkout: Created from $new_name" "refs/heads/$newbranch" $new || exit
+               git-branch $track $newbranch_log "$newbranch" "$new_name" || exit
                branch="$newbranch"
        fi
        if test -n "$branch"
index 5ae0d28cc09a0b3935262a542c9811b66eb5b85e..e21804284398016a789b415dec9332700d33f8e6 100755 (executable)
@@ -109,133 +109,11 @@ ls_remote_result=$(git ls-remote $exec "$remote") ||
        die "Cannot get the repository state from $remote"
 
 append_fetch_head () {
-    head_="$1"
-    remote_="$2"
-    remote_name_="$3"
-    remote_nick_="$4"
-    local_name_="$5"
-    case "$6" in
-    t) not_for_merge_='not-for-merge' ;;
-    '') not_for_merge_= ;;
-    esac
-
-    # remote-nick is the URL given on the command line (or a shorthand)
-    # remote-name is the $GIT_DIR relative refs/ path we computed
-    # for this refspec.
-
-    # the $note_ variable will be fed to git-fmt-merge-msg for further
-    # processing.
-    case "$remote_name_" in
-    HEAD)
-       note_= ;;
-    refs/heads/*)
-       note_="$(expr "$remote_name_" : 'refs/heads/\(.*\)')"
-       note_="branch '$note_' of " ;;
-    refs/tags/*)
-       note_="$(expr "$remote_name_" : 'refs/tags/\(.*\)')"
-       note_="tag '$note_' of " ;;
-    refs/remotes/*)
-       note_="$(expr "$remote_name_" : 'refs/remotes/\(.*\)')"
-       note_="remote branch '$note_' of " ;;
-    *)
-       note_="$remote_name of " ;;
-    esac
-    remote_1_=$(expr "z$remote_" : 'z\(.*\)\.git/*$') &&
-       remote_="$remote_1_"
-    note_="$note_$remote_"
-
-    # 2.6.11-tree tag would not be happy to be fed to resolve.
-    if git-cat-file commit "$head_" >/dev/null 2>&1
-    then
-       headc_=$(git-rev-parse --verify "$head_^0") || exit
-       echo "$headc_   $not_for_merge_ $note_" >>"$GIT_DIR/FETCH_HEAD"
-    else
-       echo "$head_    not-for-merge   $note_" >>"$GIT_DIR/FETCH_HEAD"
-    fi
-
-    update_local_ref "$local_name_" "$head_" "$note_"
-}
-
-update_local_ref () {
-    # If we are storing the head locally make sure that it is
-    # a fast forward (aka "reverse push").
-
-    label_=$(git-cat-file -t $2)
-    newshort_=$(git-rev-parse --short $2)
-    if test -z "$1" ; then
-       [ "$verbose" ] && echo >&2 "* fetched $3"
-       [ "$verbose" ] && echo >&2 "  $label_: $newshort_"
-       return 0
-    fi
-    oldshort_=$(git show-ref --hash --abbrev "$1" 2>/dev/null)
-
-    case "$1" in
-    refs/tags/*)
-       # Tags need not be pointing at commits so there
-       # is no way to guarantee "fast-forward" anyway.
-       if test -n "$oldshort_"
-       then
-               if now_=$(git show-ref --hash "$1") && test "$now_" = "$2"
-               then
-                       [ "$verbose" ] && echo >&2 "* $1: same as $3"
-                       [ "$verbose" ] && echo >&2 "  $label_: $newshort_" ||:
-               else
-                       echo >&2 "* $1: updating with $3"
-                       echo >&2 "  $label_: $newshort_"
-                       git-update-ref -m "$GIT_REFLOG_ACTION: updating tag" "$1" "$2"
-               fi
-       else
-               echo >&2 "* $1: storing $3"
-               echo >&2 "  $label_: $newshort_"
-               git-update-ref -m "$GIT_REFLOG_ACTION: storing tag" "$1" "$2"
-       fi
-       ;;
-
-    refs/heads/* | refs/remotes/*)
-       # $1 is the ref being updated.
-       # $2 is the new value for the ref.
-       local=$(git-rev-parse --verify "$1^0" 2>/dev/null)
-       if test "$local"
-       then
-           # Require fast-forward.
-           mb=$(git-merge-base "$local" "$2") &&
-           case "$2,$mb" in
-           $local,*)
-               if test -n "$verbose"
-               then
-                       echo >&2 "* $1: same as $3"
-                       echo >&2 "  $label_: $newshort_"
-               fi
-               ;;
-           *,$local)
-               echo >&2 "* $1: fast forward to $3"
-               echo >&2 "  old..new: $oldshort_..$newshort_"
-               git-update-ref -m "$GIT_REFLOG_ACTION: fast-forward" "$1" "$2" "$local"
-               ;;
-           *)
-               false
-               ;;
-           esac || {
-               case ",$force,$single_force," in
-               *,t,*)
-                       echo >&2 "* $1: forcing update to non-fast forward $3"
-                       echo >&2 "  old...new: $oldshort_...$newshort_"
-                       git-update-ref -m "$GIT_REFLOG_ACTION: forced-update" "$1" "$2" "$local"
-                       ;;
-               *)
-                       echo >&2 "* $1: not updating to non-fast forward $3"
-                       echo >&2 "  old...new: $oldshort_...$newshort_"
-                       exit 1
-                       ;;
-               esac
-           }
-       else
-           echo >&2 "* $1: storing $3"
-           echo >&2 "  $label_: $newshort_"
-           git-update-ref -m "$GIT_REFLOG_ACTION: storing head" "$1" "$2"
-       fi
-       ;;
-    esac
+       flags=
+       test -n "$verbose" && flags="$flags -v"
+       test -n "$force" && flags="$flags -f"
+       GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \
+               git-fetch--tool $flags append-fetch-head "$@"
 }
 
 # updating the current HEAD with git-fetch in a bare
@@ -279,7 +157,40 @@ then
        fi
 fi
 
-fetch_main () {
+fetch_all_at_once () {
+
+  eval=$(echo "$1" | git-fetch--tool parse-reflist "-")
+  eval "$eval"
+
+    ( : subshell because we muck with IFS
+      IFS="    $LF"
+      (
+       if test "$remote" = . ; then
+           git-show-ref $rref || echo failed "$remote"
+       elif test -f "$remote" ; then
+           test -n "$shallow_depth" &&
+               die "shallow clone with bundle is not supported"
+           git-bundle unbundle "$remote" $rref ||
+           echo failed "$remote"
+       else
+         git-fetch-pack --thin $exec $keep $shallow_depth $no_progress \
+               "$remote" $rref ||
+         echo failed "$remote"
+       fi
+      ) |
+      (
+       flags=
+       test -n "$verbose" && flags="$flags -v"
+       test -n "$force" && flags="$flags -f"
+       GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \
+               git-fetch--tool $flags native-store \
+                       "$remote" "$remote_nick" "$refs"
+      )
+    ) || exit
+
+}
+
+fetch_per_ref () {
   reflist="$1"
   refs=
   rref=
@@ -371,9 +282,6 @@ fetch_main () {
              rsync_slurped_objects=t
          }
          ;;
-      *)
-         # We will do git native transport with just one call later.
-         continue ;;
       esac
 
       append_fetch_head "$head" "$remote" \
@@ -381,80 +289,17 @@ fetch_main () {
 
   done
 
-  case "$remote" in
-  http://* | https://* | ftp://* | rsync://* )
-      ;; # we are already done.
-  *)
-    ( : subshell because we muck with IFS
-      IFS="    $LF"
-      (
-       if test -f "$remote" ; then
-           test -n "$shallow_depth" &&
-               die "shallow clone with bundle is not supported"
-           git-bundle unbundle "$remote" $rref ||
-           echo failed "$remote"
-       else
-         git-fetch-pack --thin $exec $keep $shallow_depth $no_progress \
-               "$remote" $rref ||
-         echo failed "$remote"
-       fi
-      ) |
-      (
-       trap '
-               if test -n "$keepfile" && test -f "$keepfile"
-               then
-                       rm -f "$keepfile"
-               fi
-       ' 0
-
-        keepfile=
-       while read sha1 remote_name
-       do
-         case "$sha1" in
-         failed)
-                 echo >&2 "Fetch failure: $remote"
-                 exit 1 ;;
-         # special line coming from index-pack with the pack name
-         pack)
-                 continue ;;
-         keep)
-                 keepfile="$GIT_OBJECT_DIRECTORY/pack/pack-$remote_name.keep"
-                 continue ;;
-         esac
-         found=
-         single_force=
-         for ref in $refs
-         do
-             case "$ref" in
-             +$remote_name:*)
-                 single_force=t
-                 not_for_merge=
-                 found="$ref"
-                 break ;;
-             .+$remote_name:*)
-                 single_force=t
-                 not_for_merge=t
-                 found="$ref"
-                 break ;;
-             .$remote_name:*)
-                 not_for_merge=t
-                 found="$ref"
-                 break ;;
-             $remote_name:*)
-                 not_for_merge=
-                 found="$ref"
-                 break ;;
-             esac
-         done
-         local_name=$(expr "z$found" : 'z[^:]*:\(.*\)')
-         append_fetch_head "$sha1" "$remote" \
-                 "$remote_name" "$remote_nick" "$local_name" \
-                 "$not_for_merge" || exit
-        done
-      )
-    ) || exit ;;
-  esac
+}
 
+fetch_main () {
+       case "$remote" in
+       http://* | https://* | ftp://* | rsync://* )
+               fetch_per_ref "$@"
+               ;;
+       *)
+               fetch_all_at_once "$@"
+               ;;
+       esac
 }
 
 fetch_main "$reflist" || exit
index 6ce62c860a3732e1239e216fb7492a126216be67..8759c5a7e0f8748108d6eff005cde1e0893f3592 100755 (executable)
@@ -292,13 +292,13 @@ f,*)
        # Again the most common case of merging one remote.
        echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $1)"
        git-update-index --refresh 2>/dev/null
-       new_head=$(git-rev-parse --verify "$1^0") &&
-       git-read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" &&
        msg="Fast forward"
        if test -n "$have_message"
        then
                msg="$msg (no commit created; -m option ignored)"
        fi
+       new_head=$(git-rev-parse --verify "$1^0") &&
+       git-read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" &&
        finish "$new_head" "$msg" || exit
        dropsave
        exit 0
diff --git a/git-mergetool.sh b/git-mergetool.sh
new file mode 100755 (executable)
index 0000000..7942fd0
--- /dev/null
@@ -0,0 +1,359 @@
+#!/bin/sh
+#
+# This program resolves merge conflicts in git
+#
+# Copyright (c) 2006 Theodore Y. Ts'o
+#
+# This file is licensed under the GPL v2, or a later version
+# at the discretion of Junio C Hammano.
+#
+
+USAGE='[--tool=tool] [file to merge] ...'
+SUBDIRECTORY_OK=Yes
+. git-sh-setup
+require_work_tree
+
+# Returns true if the mode reflects a symlink
+function is_symlink () {
+    test "$1" = 120000
+}
+
+function local_present () {
+    test -n "$local_mode"
+}
+
+function remote_present () {
+    test -n "$remote_mode"
+}
+
+function base_present () {
+    test -n "$base_mode"
+}
+
+cleanup_temp_files () {
+    if test "$1" = --save-backup ; then
+       mv -- "$BACKUP" "$path.orig"
+       rm -f -- "$LOCAL" "$REMOTE" "$BASE"
+    else
+       rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
+    fi
+}
+
+function describe_file () {
+    mode="$1"
+    branch="$2"
+    file="$3"
+
+    echo -n "    "
+    if test -z "$mode"; then
+       echo -n "'$path' was deleted"
+    elif is_symlink "$mode" ; then
+       echo -n "'$path' is a symlink containing '"
+       cat "$file"
+       echo -n "'"
+    else
+       if base_present; then
+           echo -n "'$path' was created"
+       else
+           echo -n "'$path' was modified"
+       fi
+    fi
+    echo " in the $branch branch"
+}
+
+
+resolve_symlink_merge () {
+    while /bin/true; do
+       echo -n "Use (r)emote or (l)ocal, or (a)bort? "
+       read ans
+       case "$ans" in
+           [lL]*)
+               git-checkout-index -f --stage=2 -- "$path"
+               git-add -- "$path"
+               cleanup_temp_files --save-backup
+               return
+               ;;
+          [rR]*)
+               git-checkout-index -f --stage=3 -- "$path"
+               git-add -- "$path"
+               cleanup_temp_files --save-backup
+               return
+               ;;
+           [qQ]*)
+               exit 1
+               ;;
+           esac
+       done
+}
+
+resolve_deleted_merge () {
+    while /bin/true; do
+       echo -n "Use (m)odified or (d)eleted file, or (a)bort? "
+       read ans
+       case "$ans" in
+           [mM]*)
+               git-add -- "$path"
+               cleanup_temp_files --save-backup
+               return
+               ;;
+          [dD]*)
+               git-rm -- "$path"
+               cleanup_temp_files
+               return
+               ;;
+           [qQ]*)
+               exit 1
+               ;;
+           esac
+       done
+}
+
+merge_file () {
+    path="$1"
+
+    if test ! -f "$path" ; then
+       echo "$path: file not found"
+       exit 1
+    fi
+
+    f=`git-ls-files -u -- "$path"`
+    if test -z "$f" ; then
+       echo "$path: file does not need merging"
+       exit 1
+    fi
+
+    BACKUP="$path.BACKUP.$$"
+    LOCAL="$path.LOCAL.$$"
+    REMOTE="$path.REMOTE.$$"
+    BASE="$path.BASE.$$"
+
+    mv -- "$path" "$BACKUP"
+    cp -- "$BACKUP" "$path"
+
+    base_mode=`git ls-files -u -- "$path" | awk '{if ($3==1) print $1;}'`
+    local_mode=`git ls-files -u -- "$path" | awk '{if ($3==2) print $1;}'`
+    remote_mode=`git ls-files -u -- "$path" | awk '{if ($3==3) print $1;}'`
+
+    base_present   && git cat-file blob ":1:$path" > "$BASE" 2>/dev/null
+    local_present  && git cat-file blob ":2:$path" > "$LOCAL" 2>/dev/null
+    remote_present && git cat-file blob ":3:$path" > "$REMOTE" 2>/dev/null
+
+    if test -z "$local_mode" -o -z "$remote_mode"; then
+       echo "Deleted merge conflict for $path:"
+       describe_file "$local_mode" "local" "$LOCAL"
+       describe_file "$remote_mode" "remote" "$REMOTE"
+       resolve_deleted_merge
+       return
+    fi
+
+    if is_symlink "$local_mode" || is_symlink "$remote_mode"; then
+       echo "Symlink merge conflict for $path:"
+       describe_file "$local_mode" "local" "$LOCAL"
+       describe_file "$remote_mode" "remote" "$REMOTE"
+       resolve_symlink_merge
+       return
+    fi
+
+    echo "Normal merge conflict for $path:"
+    describe_file "$local_mode" "local" "$LOCAL"
+    describe_file "$remote_mode" "remote" "$REMOTE"
+    echo -n "Hit return to start merge resolution tool ($merge_tool): "
+    read ans
+
+    case "$merge_tool" in
+       kdiff3)
+           if base_present ; then
+               (kdiff3 --auto --L1 "$path (Base)" -L2 "$path (Local)" --L3 "$path (Remote)" \
+                   -o "$path" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
+           else
+               (kdiff3 --auto -L1 "$path (Local)" --L2 "$path (Remote)" \
+                   -o "$path" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
+           fi
+           status=$?
+           if test "$status" -eq 0; then
+               rm "$BACKUP"
+           fi
+           ;;
+       tkdiff)
+           if base_present ; then
+               tkdiff -a "$BASE" -o "$path" -- "$LOCAL" "$REMOTE"
+           else
+               tkdiff -o "$path" -- "$LOCAL" "$REMOTE"
+           fi
+           status=$?
+           if test "$status" -eq 0; then
+               mv -- "$BACKUP" "$path.orig"
+           fi
+           ;;
+       meld|vimdiff)
+           touch "$BACKUP"
+           $merge_tool -- "$LOCAL" "$path" "$REMOTE"
+           if test "$path" -nt "$BACKUP" ; then
+               status=0;
+           else
+               while true; do
+                   echo "$path seems unchanged."
+                   echo -n "Was the merge successful? [y/n] "
+                   read answer < /dev/tty
+                   case "$answer" in
+                       y*|Y*) status=0; break ;;
+                       n*|N*) status=1; break ;;
+                   esac
+               done
+           fi
+           if test "$status" -eq 0; then
+               mv -- "$BACKUP" "$path.orig"
+           fi
+           ;;
+       xxdiff)
+           touch "$BACKUP"
+           if base_present ; then
+               xxdiff -X --show-merged-pane \
+                   -R 'Accel.SaveAsMerged: "Ctrl-S"' \
+                   -R 'Accel.Search: "Ctrl+F"' \
+                   -R 'Accel.SearchForward: "Ctrl-G"' \
+                   --merged-file "$path" -- "$LOCAL" "$BASE" "$REMOTE"
+           else
+               xxdiff -X --show-merged-pane \
+                   -R 'Accel.SaveAsMerged: "Ctrl-S"' \
+                   -R 'Accel.Search: "Ctrl+F"' \
+                   -R 'Accel.SearchForward: "Ctrl-G"' \
+                   --merged-file "$path" -- "$LOCAL" "$REMOTE"
+           fi
+           if test "$path" -nt "$BACKUP" ; then
+               status=0;
+           else
+               while true; do
+                   echo "$path seems unchanged."
+                   echo -n "Was the merge successful? [y/n] "
+                   read answer < /dev/tty
+                   case "$answer" in
+                       y*|Y*) status=0; break ;;
+                       n*|N*) status=1; break ;;
+                   esac
+               done
+           fi
+           if test "$status" -eq 0; then
+               mv -- "$BACKUP" "$path.orig"
+           fi
+           ;;
+       emerge)
+           if base_present ; then
+               emacs -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$path"
+           else
+               emacs -f emerge-files-command "$LOCAL" "$REMOTE" "$path"
+           fi
+           status=$?
+           if test "$status" -eq 0; then
+               mv -- "$BACKUP" "$path.orig"
+           fi
+           ;;
+    esac
+    if test "$status" -ne 0; then
+       echo "merge of $path failed" 1>&2
+       mv -- "$BACKUP" "$path"
+       exit 1
+    fi
+    git add -- "$path"
+    cleanup_temp_files
+}
+
+while case $# in 0) break ;; esac
+do
+    case "$1" in
+       -t|--tool*)
+           case "$#,$1" in
+               *,*=*)
+                   merge_tool=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+                   ;;
+               1,*)
+                   usage ;;
+               *)
+                   merge_tool="$2"
+                   shift ;;
+           esac
+           ;;
+       --)
+           break
+           ;;
+       -*)
+           usage
+           ;;
+       *)
+           break
+           ;;
+    esac
+    shift
+done
+
+if test -z "$merge_tool"; then
+    merge_tool=`git-config merge.tool`
+    case "$merge_tool" in
+       kdiff3 | tkdiff | xxdiff | meld | emerge | vimdiff)
+           ;; # happy
+       *)
+           echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
+           echo >&2 "Resetting to default..."
+           unset merge_tool
+           ;;
+    esac
+fi
+
+if test -z "$merge_tool" ; then
+    if type kdiff3 >/dev/null 2>&1 && test -n "$DISPLAY"; then
+       merge_tool="kdiff3";
+    elif type tkdiff >/dev/null 2>&1 && test -n "$DISPLAY"; then
+       merge_tool=tkdiff
+    elif type xxdiff >/dev/null 2>&1 && test -n "$DISPLAY"; then
+       merge_tool=xxdiff
+    elif type meld >/dev/null 2>&1 && test -n "$DISPLAY"; then
+       merge_tool=meld
+    elif type emacs >/dev/null 2>&1; then
+       merge_tool=emerge
+    elif type vimdiff >/dev/null 2>&1; then
+       merge_tool=vimdiff
+    else
+       echo "No available merge resolution programs available."
+       exit 1
+    fi
+fi
+
+case "$merge_tool" in
+    kdiff3|tkdiff|meld|xxdiff|vimdiff)
+       if ! type "$merge_tool" > /dev/null 2>&1; then
+           echo "The merge tool $merge_tool is not available"
+           exit 1
+       fi
+       ;;
+    emerge)
+       if ! type "emacs" > /dev/null 2>&1; then
+           echo "Emacs is not available"
+           exit 1
+       fi
+       ;;
+    *)
+       echo "Unknown merge tool: $merge_tool"
+       exit 1
+       ;;
+esac
+
+if test $# -eq 0 ; then
+       files=`git ls-files -u | sed -e 's/^[^  ]*      //' | sort -u`
+       if test -z "$files" ; then
+               echo "No files need merging"
+               exit 0
+       fi
+       echo Merging the files: $files
+       git ls-files -u | sed -e 's/^[^ ]*      //' | sort -u | while read i
+       do
+               echo ""
+               merge_file "$i" < /dev/tty > /dev/tty
+       done
+else
+       while test $# -gt 0; do
+               echo ""
+               merge_file "$1"
+               shift
+       done
+fi
+exit 0
index 5208ee6ce0bb07dd22ad5ca63ee469cb03011164..437b0c3b1bc522c4b5a4a3a656b97cb2009c43a0 100755 (executable)
@@ -9,6 +9,9 @@ get_data_source () {
        */*)
                echo ''
                ;;
+       .)
+               echo self
+               ;;
        *)
                if test "$(git-config --get "remote.$1.url")"
                then
@@ -31,6 +34,9 @@ get_remote_url () {
        '')
                echo "$1"
                ;;
+       self)
+               echo "$1"
+               ;;
        config)
                git-config --get "remote.$1.url"
                ;;
@@ -57,7 +63,7 @@ get_default_remote () {
 get_remote_default_refs_for_push () {
        data_source=$(get_data_source "$1")
        case "$data_source" in
-       '' | branches)
+       '' | branches | self)
                ;; # no default push mapping, just send matching refs.
        config)
                git-config --get-all "remote.$1.push" ;;
@@ -81,51 +87,8 @@ get_remote_default_refs_for_push () {
 # is to help prevent randomly "globbed" ref from being chosen as
 # a merge candidate
 expand_refs_wildcard () {
-       remote="$1"
-       shift
-       first_one=yes
-       if test "$#" = 0
-       then
-               echo empty
-               echo >&2 "Nothing specified for fetching with remote.$remote.fetch"
-       fi
-       for ref
-       do
-               lref=${ref#'+'}
-               # a non glob pattern is given back as-is.
-               expr "z$lref" : 'zrefs/.*/\*:refs/.*/\*$' >/dev/null || {
-                       if test -n "$first_one"
-                       then
-                               echo "explicit"
-                               first_one=
-                       fi
-                       echo "$ref"
-                       continue
-               }
-
-               # glob
-               if test -n "$first_one"
-               then
-                       echo "glob"
-                       first_one=
-               fi
-               from=`expr "z$lref" : 'z\(refs/.*/\)\*:refs/.*/\*$'`
-               to=`expr "z$lref" : 'zrefs/.*/\*:\(refs/.*/\)\*$'`
-               local_force=
-               test "z$lref" = "z$ref" || local_force='+'
-               echo "$ls_remote_result" |
-               sed -e '/\^{}$/d' |
-               (
-                       IFS='   '
-                       while read sha1 name
-                       do
-                               # ignore the ones that do not start with $from
-                               mapped=${name#"$from"}
-                               test "z$name" = "z$mapped" && continue
-                               echo "${local_force}${name}:${to}${mapped}"
-                       done
-               )
-       done
+       echo "$ls_remote_result" |
+       git fetch--tool expand-refs-wildcard "-" "$@"
 }
 
 # Subroutine to canonicalize remote:local notation.
@@ -206,6 +169,10 @@ get_remote_default_refs_for_fetch () {
        case "$data_source" in
        '')
                echo "HEAD:" ;;
+       self)
+               canon_refs_list_for_fetch -d "$1" \
+                       $(git-for-each-ref --format='%(refname):')
+               ;;
        config)
                canon_refs_list_for_fetch -d "$1" \
                        $(git-config --get-all "remote.$1.fetch") ;;
@@ -220,7 +187,7 @@ get_remote_default_refs_for_fetch () {
                                        }' "$GIT_DIR/remotes/$1")
                ;;
        *)
-               die "internal error: get-remote-default-ref-for-push $1" ;;
+               die "internal error: get-remote-default-ref-for-fetch $1" ;;
        esac
 }
 
index 671a5ff865b6ab891da4251ee3dd90892b13cbad..08ac9bb729744660631fd5bdc9b7e11e0fd7d78a 100755 (executable)
@@ -73,6 +73,10 @@ mkdir $tmp_dir || exit 2
 for patch_name in $(cat "$QUILT_PATCHES/series" | grep -v '^#'); do
        echo $patch_name
        (cat $QUILT_PATCHES/$patch_name | git-mailinfo "$tmp_msg" "$tmp_patch" > "$tmp_info") || exit 3
+       test -s $dotest/patch || {
+               echo "Patch is empty.  Was is split wrong?"
+               stop_here $this
+       }
 
        # Parse the author information
        export GIT_AUTHOR_NAME=$(sed -ne 's/Author: //p' "$tmp_info")
index 6989c0260fcafe29397c89cfcc61a8ef9a49d58e..ae50990d081c3709c0a498422be382b9a632f5ec 100755 (executable)
@@ -65,8 +65,8 @@ sub usage {
                   Defaults to on.
 
    --no-signed-off-cc Suppress the automatic addition of email addresses
-                 that appear in a Signed-off-by: line, to the cc: list.
-                Note: Using this option is not recommended.
+                 that appear in Signed-off-by: or Cc: lines to the cc:
+                 list.  Note: Using this option is not recommended.
 
    --smtp-server  If set, specifies the outgoing SMTP server to use.
                   Defaults to localhost.
@@ -572,8 +572,8 @@ sub send_message
                        }
                } else {
                        $message .=  $_;
-                       if (/^Signed-off-by: (.*)$/i && !$no_signed_off_cc) {
-                               my $c = $1;
+                       if (/^(Signed-off-by|Cc): (.*)$/i && !$no_signed_off_cc) {
+                               my $c = $2;
                                chomp $c;
                                push @cc, $c;
                                printf("(sob) Adding cc: %s from line '%s'\n",
index 326e89fe037561c1ad72d91c7783bf2a0f603826..e8457893db963ad3bb6be270aba0a81481119b1c 100755 (executable)
@@ -56,7 +56,7 @@ BEGIN
        $_message, $_file,
        $_template, $_shared,
        $_version, $_fetch_all,
-       $_merge, $_strategy, $_dry_run,
+       $_merge, $_strategy, $_dry_run, $_local,
        $_prefix, $_no_checkout, $_verbose);
 $Git::SVN::_follow_parent = 1;
 my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username,
@@ -145,6 +145,7 @@ BEGIN
                        { 'merge|m|M' => \$_merge,
                          'verbose|v' => \$_verbose,
                          'strategy|s=s' => \$_strategy,
+                         'local|l' => \$_local,
                          'fetch-all|all' => \$_fetch_all,
                          %fc_opts } ],
        'commit-diff' => [ \&cmd_commit_diff,
@@ -439,7 +440,9 @@ sub cmd_rebase {
                command_noisy('status');
                exit 1;
        }
-       $_fetch_all ? $gs->fetch_all : $gs->fetch;
+       unless ($_local) {
+               $_fetch_all ? $gs->fetch_all : $gs->fetch;
+       }
        command_noisy(rebase_cmd(), $gs->refname);
 }
 
diff --git a/git.c b/git.c
index fe2b74ab79619c425d7944c02a7f37c5e218bdef..dde4d07e8fbb830791417d422e4b9204373935e4 100644 (file)
--- a/git.c
+++ b/git.c
@@ -243,6 +243,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "diff-files", cmd_diff_files },
                { "diff-index", cmd_diff_index, RUN_SETUP },
                { "diff-tree", cmd_diff_tree, RUN_SETUP },
+               { "fetch--tool", cmd_fetch__tool, RUN_SETUP },
                { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
                { "for-each-ref", cmd_for_each_ref, RUN_SETUP },
                { "format-patch", cmd_format_patch, RUN_SETUP },
index 7cfe8b3587c10bf0791c066a72482771e7daae77..4b650efa8b19424a61566b7ec87e7808874a6721 100644 (file)
@@ -64,9 +64,9 @@ static int copy_file(const char *source, char *dest, const char *hex,
                }
                /* If we got ENOENT there is no point continuing. */
                if (errno == ENOENT) {
-                       if (warn_if_not_exists)
-                               fprintf(stderr, "does not exist %s\n", source);
-                       return -1;
+                       if (!warn_if_not_exists)
+                               return -1;
+                       return error("does not exist %s", source);
                }
        }
        if (use_symlink) {
@@ -74,9 +74,8 @@ static int copy_file(const char *source, char *dest, const char *hex,
                if (stat(source, &st)) {
                        if (!warn_if_not_exists && errno == ENOENT)
                                return -1;
-                       fprintf(stderr, "cannot stat %s: %s\n", source,
-                               strerror(errno));
-                       return -1;
+                       return error("cannot stat %s: %s", source,
+                                    strerror(errno));
                }
                if (!symlink(source, dest)) {
                        pull_say("symlink %s\n", hex);
@@ -90,25 +89,21 @@ static int copy_file(const char *source, char *dest, const char *hex,
                if (ifd < 0) {
                        if (!warn_if_not_exists && errno == ENOENT)
                                return -1;
-                       fprintf(stderr, "cannot open %s\n", source);
-                       return -1;
+                       return error("cannot open %s", source);
                }
                ofd = open(dest, O_WRONLY | O_CREAT | O_EXCL, 0666);
                if (ofd < 0) {
-                       fprintf(stderr, "cannot open %s\n", dest);
                        close(ifd);
-                       return -1;
+                       return error("cannot open %s", dest);
                }
                status = copy_fd(ifd, ofd);
                close(ofd);
                if (status)
-                       fprintf(stderr, "cannot write %s\n", dest);
-               else
-                       pull_say("copy %s\n", hex);
-               return status;
+                       return error("cannot write %s", dest);
+               pull_say("copy %s\n", hex);
+               return 0;
        }
-       fprintf(stderr, "failed to copy %s with given copy methods.\n", hex);
-       return -1;
+       return error("failed to copy %s with given copy methods.", hex);
 }
 
 static int fetch_pack(const unsigned char *sha1)
@@ -181,13 +176,11 @@ int fetch_ref(char *ref, unsigned char *sha1)
        ifd = open(filename, O_RDONLY);
        if (ifd < 0) {
                close(ifd);
-               fprintf(stderr, "cannot open %s\n", filename);
-               return -1;
+               return error("cannot open %s", filename);
        }
        if (read_in_full(ifd, hex, 40) != 40 || get_sha1_hex(hex, sha1)) {
                close(ifd);
-               fprintf(stderr, "cannot read from %s\n", filename);
-               return -1;
+               return error("cannot read from %s", filename);
        }
        close(ifd);
        pull_say("ref %s\n", sha1_to_hex(sha1));
index 299c514128b8b6330561a1a2b8ac30324a7ac479..d9883225eabf10ad9a3a169e7049c9ee25e5d9cd 100644 (file)
@@ -5,7 +5,7 @@ static int verify_packfile(struct packed_git *p,
                struct pack_window **w_curs)
 {
        off_t index_size = p->index_size;
-       void *index_base = p->index_base;
+       const unsigned char *index_base = p->index_data;
        SHA_CTX ctx;
        unsigned char sha1[20];
        off_t offset = 0, pack_sig = p->pack_size - 20;
@@ -31,7 +31,7 @@ static int verify_packfile(struct packed_git *p,
        if (hashcmp(sha1, use_pack(p, w_curs, pack_sig, NULL)))
                return error("Packfile %s SHA1 mismatch with itself",
                             p->pack_name);
-       if (hashcmp(sha1, (unsigned char *)index_base + index_size - 40))
+       if (hashcmp(sha1, index_base + index_size - 40))
                return error("Packfile %s SHA1 mismatch with idx",
                             p->pack_name);
        unuse_pack(w_curs);
@@ -127,7 +127,7 @@ static void show_pack_info(struct packed_git *p)
 int verify_pack(struct packed_git *p, int verbose)
 {
        off_t index_size = p->index_size;
-       void *index_base = p->index_base;
+       const unsigned char *index_base = p->index_data;
        SHA_CTX ctx;
        unsigned char sha1[20];
        int ret;
@@ -137,7 +137,7 @@ int verify_pack(struct packed_git *p, int verbose)
        SHA1_Init(&ctx);
        SHA1_Update(&ctx, index_base, (unsigned int)(index_size - 20));
        SHA1_Final(sha1, &ctx);
-       if (hashcmp(sha1, (unsigned char *)index_base + index_size - 20))
+       if (hashcmp(sha1, index_base + index_size - 20))
                ret = error("Packfile index for %s SHA1 mismatch",
                            p->pack_name);
 
index c8f7d9af7b502e12a3d36fc3b3e796e43b4f2bdb..40e579b2d9788bb0867b345c3596b1ad1539272a 100644 (file)
@@ -17,7 +17,7 @@ static int load_all_packs, verbose, alt_odb;
 
 struct llist_item {
        struct llist_item *next;
-       unsigned char *sha1;
+       const unsigned char *sha1;
 };
 static struct llist {
        struct llist_item *front;
@@ -104,9 +104,9 @@ static struct llist * llist_copy(struct llist *list)
        return ret;
 }
 
-static inline struct llist_item * llist_insert(struct llist *list,
-                                              struct llist_item *after,
-                                              unsigned char *sha1)
+static inline struct llist_item *llist_insert(struct llist *list,
+                                             struct llist_item *after,
+                                              const unsigned char *sha1)
 {
        struct llist_item *new = llist_item_get();
        new->sha1 = sha1;
@@ -128,12 +128,14 @@ static inline struct llist_item * llist_insert(struct llist *list,
        return new;
 }
 
-static inline struct llist_item *llist_insert_back(struct llist *list, unsigned char *sha1)
+static inline struct llist_item *llist_insert_back(struct llist *list,
+                                                  const unsigned char *sha1)
 {
        return llist_insert(list, list->back, sha1);
 }
 
-static inline struct llist_item *llist_insert_sorted_unique(struct llist *list, unsigned char *sha1, struct llist_item *hint)
+static inline struct llist_item *llist_insert_sorted_unique(struct llist *list,
+                       const unsigned char *sha1, struct llist_item *hint)
 {
        struct llist_item *prev = NULL, *l;
 
@@ -246,12 +248,12 @@ static struct pack_list * pack_list_difference(const struct pack_list *A,
 static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
 {
        int p1_off, p2_off;
-       unsigned char *p1_base, *p2_base;
+       const unsigned char *p1_base, *p2_base;
        struct llist_item *p1_hint = NULL, *p2_hint = NULL;
 
        p1_off = p2_off = 256 * 4 + 4;
-       p1_base = (unsigned char *) p1->pack->index_base;
-       p2_base = (unsigned char *) p2->pack->index_base;
+       p1_base = p1->pack->index_data;
+       p2_base = p2->pack->index_data;
 
        while (p1_off <= p1->pack->index_size - 3 * 20 &&
               p2_off <= p2->pack->index_size - 3 * 20)
@@ -351,11 +353,11 @@ static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
 {
        size_t ret = 0;
        int p1_off, p2_off;
-       unsigned char *p1_base, *p2_base;
+       const unsigned char *p1_base, *p2_base;
 
        p1_off = p2_off = 256 * 4 + 4;
-       p1_base = (unsigned char *)p1->index_base;
-       p2_base = (unsigned char *)p2->index_base;
+       p1_base = p1->index_data;
+       p2_base = p2->index_data;
 
        while (p1_off <= p1->index_size - 3 * 20 &&
               p2_off <= p2->index_size - 3 * 20)
@@ -534,7 +536,7 @@ static struct pack_list * add_pack(struct packed_git *p)
 {
        struct pack_list l;
        size_t off;
-       unsigned char *base;
+       const unsigned char *base;
 
        if (!p->pack_local && !(alt_odb || verbose))
                return NULL;
@@ -543,7 +545,7 @@ static struct pack_list * add_pack(struct packed_git *p)
        llist_init(&l.all_objects);
 
        off = 256 * 4 + 4;
-       base = (unsigned char *)p->index_base;
+       base = p->index_data;
        while (off <= p->index_size - 3 * 20) {
                llist_insert_back(l.all_objects, base + off);
                off += 24;
diff --git a/pack.h b/pack.h
index deb427edbe43710dd2b76a3af0dbd87750344103..d4d412ccbb403f1374d41a00791eec3c16ba64ef 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -16,24 +16,15 @@ struct pack_header {
 };
 
 /*
- * Packed object index header
- *
- * struct pack_idx_header {
- *     uint32_t idx_signature;
- *     uint32_t idx_version;
- * };
- *
- * Note: this header isn't active yet.  In future versions of git
- * we may change the index file format.  At that time we would start
- * the first four bytes of the new index format with this signature,
- * as all older git binaries would find this value illegal and abort
- * reading the file.
+ * The first four bytes of index formats later than version 1 should
+ * start with this signature, as all older git binaries would find this
+ * value illegal and abort reading the file.
  *
  * This is the case because the number of objects in a packfile
  * cannot exceed 1,431,660,000 as every object would need at least
- * 3 bytes of data and the overall packfile cannot exceed 4 GiB due
- * to the 32 bit offsets used by the index.  Clearly the signature
- * exceeds this maximum.
+ * 3 bytes of data and the overall packfile cannot exceed 4 GiB with
+ * version 1 of the index file due to the offsets limited to 32 bits.
+ * Clearly the signature exceeds this maximum.
  *
  * Very old git binaries will also compare the first 4 bytes to the
  * next 4 bytes in the index and abort with a "non-monotonic index"
@@ -43,6 +34,15 @@ struct pack_header {
  */
 #define PACK_IDX_SIGNATURE 0xff744f63  /* "\377tOc" */
 
+/*
+ * Packed object index header
+ */
+struct pack_idx_header {
+       uint32_t idx_signature;
+       uint32_t idx_version;
+};
+
+
 extern int verify_pack(struct packed_git *, int);
 
 #define PH_ERROR_EOF           (-1)
index 5ec0389883555c22cece5f9e3edf04c85c6282f1..17d004e5a0a19dc59b928a96a71d1470deb06e53 100644 (file)
@@ -33,7 +33,7 @@ $(makfile): ../GIT-CFLAGS Makefile
        echo '  echo $(instdir_SQ)' >> $@
 else
 $(makfile): Makefile.PL ../GIT-CFLAGS
-       $(QUIET_GEN)'$(PERL_PATH_SQ)' $< PREFIX='$(prefix_SQ)'
+       '$(PERL_PATH_SQ)' $< PREFIX='$(prefix_SQ)'
 endif
 
 # this is just added comfort for calling make directly in perl dir
index 3c2eb125e6e9332fe32be9bcec6fb2005228c211..bcdb6a12122871e743dd3b2362ca7bd5b1b787df 100644 (file)
@@ -213,6 +213,13 @@ static int everybody_uninteresting(struct commit_list *orig)
        return 1;
 }
 
+/*
+ * The goal is to get REV_TREE_NEW as the result only if the
+ * diff consists of all '+' (and no other changes), and
+ * REV_TREE_DIFFERENT otherwise (of course if the trees are
+ * the same we want REV_TREE_SAME).  That means that once we
+ * get to REV_TREE_DIFFERENT, we do not have to look any further.
+ */
 static int tree_difference = REV_TREE_SAME;
 
 static void file_add_remove(struct diff_options *options,
@@ -236,6 +243,8 @@ static void file_add_remove(struct diff_options *options,
                diff = REV_TREE_NEW;
        }
        tree_difference = diff;
+       if (tree_difference == REV_TREE_DIFFERENT)
+               options->has_changes = 1;
 }
 
 static void file_change(struct diff_options *options,
@@ -245,6 +254,7 @@ static void file_change(struct diff_options *options,
                 const char *base, const char *path)
 {
        tree_difference = REV_TREE_DIFFERENT;
+       options->has_changes = 1;
 }
 
 int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
@@ -254,6 +264,7 @@ int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
        if (!t2)
                return REV_TREE_DIFFERENT;
        tree_difference = REV_TREE_SAME;
+       revs->pruning.has_changes = 0;
        if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "",
                           &revs->pruning) < 0)
                return REV_TREE_DIFFERENT;
@@ -277,11 +288,12 @@ int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1)
        empty.buf = "";
        empty.size = 0;
 
-       tree_difference = 0;
+       tree_difference = REV_TREE_SAME;
+       revs->pruning.has_changes = 0;
        retval = diff_tree(&empty, &real, "", &revs->pruning);
        free(tree);
 
-       return retval >= 0 && !tree_difference;
+       return retval >= 0 && (tree_difference == REV_TREE_SAME);
 }
 
 static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
@@ -545,6 +557,7 @@ void init_revisions(struct rev_info *revs, const char *prefix)
        revs->ignore_merges = 1;
        revs->simplify_history = 1;
        revs->pruning.recursive = 1;
+       revs->pruning.quiet = 1;
        revs->pruning.add_remove = file_add_remove;
        revs->pruning.change = file_change;
        revs->lifo = 1;
index 7faa8bcd507f54a55cb9e2ac685654921475f995..ee64865b607c70ff42f851b91c19316ef90ce7b9 100644 (file)
@@ -432,16 +432,15 @@ void pack_report()
                pack_mapped, peak_pack_mapped);
 }
 
-static int check_packed_git_idx(const char *path,
-       unsigned long *idx_size_,
-       void **idx_map_)
+static int check_packed_git_idx(const char *path,  struct packed_git *p)
 {
        void *idx_map;
-       uint32_t *index;
+       struct pack_idx_header *hdr;
        size_t idx_size;
-       uint32_t nr, i;
+       uint32_t nr, i, *index;
        int fd = open(path, O_RDONLY);
        struct stat st;
+
        if (fd < 0)
                return -1;
        if (fstat(fd, &st)) {
@@ -456,15 +455,12 @@ static int check_packed_git_idx(const char *path,
        idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
        close(fd);
 
-       index = idx_map;
-       *idx_map_ = idx_map;
-       *idx_size_ = idx_size;
-
        /* a future index format would start with this, as older git
         * binaries would fail the non-monotonic index check below.
         * give a nicer warning to the user if we can.
         */
-       if (index[0] == htonl(PACK_IDX_SIGNATURE)) {
+       hdr = idx_map;
+       if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
                munmap(idx_map, idx_size);
                return error("index file %s is a newer version"
                        " and is not supported by this binary"
@@ -473,6 +469,7 @@ static int check_packed_git_idx(const char *path,
        }
 
        nr = 0;
+       index = idx_map;
        for (i = 0; i < 256; i++) {
                uint32_t n = ntohl(index[i]);
                if (n < nr) {
@@ -494,6 +491,9 @@ static int check_packed_git_idx(const char *path,
                return error("wrong index file size in %s", path);
        }
 
+       p->index_version = 1;
+       p->index_data = idx_map;
+       p->index_size = idx_size;
        return 0;
 }
 
@@ -614,7 +614,7 @@ static int open_packed_git_1(struct packed_git *p)
                return error("end of packfile %s is unavailable", p->pack_name);
        if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1))
                return error("packfile %s signature is unavailable", p->pack_name);
-       idx_sha1 = ((unsigned char *)p->index_base) + p->index_size - 40;
+       idx_sha1 = ((unsigned char *)p->index_data) + p->index_size - 40;
        if (hashcmp(sha1, idx_sha1))
                return error("packfile %s does not match index", p->pack_name);
        return 0;
@@ -710,37 +710,37 @@ unsigned char* use_pack(struct packed_git *p,
        return win->base + offset;
 }
 
-struct packed_git *add_packed_git(char *path, int path_len, int local)
+struct packed_git *add_packed_git(const char *path, int path_len, int local)
 {
        struct stat st;
-       struct packed_git *p;
-       unsigned long idx_size;
-       void *idx_map;
-       unsigned char sha1[20];
+       struct packed_git *p = xmalloc(sizeof(*p) + path_len + 2);
 
-       if (check_packed_git_idx(path, &idx_size, &idx_map))
+       /*
+        * Make sure a corresponding .pack file exists and that
+        * the index looks sane.
+        */
+       path_len -= strlen(".idx");
+       if (path_len < 1)
                return NULL;
-
-       /* do we have a corresponding .pack file? */
-       strcpy(path + path_len - 4, ".pack");
-       if (stat(path, &st) || !S_ISREG(st.st_mode)) {
-               munmap(idx_map, idx_size);
+       memcpy(p->pack_name, path, path_len);
+       strcpy(p->pack_name + path_len, ".pack");
+       if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode) ||
+           check_packed_git_idx(path, p)) {
+               free(p);
                return NULL;
        }
+
        /* ok, it looks sane as far as we can check without
         * actually mapping the pack file.
         */
-       p = xmalloc(sizeof(*p) + path_len + 2);
-       strcpy(p->pack_name, path);
-       p->index_size = idx_size;
        p->pack_size = st.st_size;
-       p->index_base = idx_map;
        p->next = NULL;
        p->windows = NULL;
        p->pack_fd = -1;
        p->pack_local = local;
-       if ((path_len > 44) && !get_sha1_hex(path + path_len - 44, sha1))
-               hashcpy(p->sha1, sha1);
+       p->mtime = st.st_mtime;
+       if (path_len < 40 || get_sha1_hex(path + path_len - 40, p->sha1))
+               hashclr(p->sha1);
        return p;
 }
 
@@ -750,23 +750,19 @@ struct packed_git *parse_pack_index(unsigned char *sha1)
        return parse_pack_index_file(sha1, path);
 }
 
-struct packed_git *parse_pack_index_file(const unsigned char *sha1, char *idx_path)
+struct packed_git *parse_pack_index_file(const unsigned char *sha1,
+                                        const char *idx_path)
 {
-       struct packed_git *p;
-       unsigned long idx_size;
-       void *idx_map;
-       char *path;
+       const char *path = sha1_pack_name(sha1);
+       struct packed_git *p = xmalloc(sizeof(*p) + strlen(path) + 2);
 
-       if (check_packed_git_idx(idx_path, &idx_size, &idx_map))
+       if (check_packed_git_idx(idx_path, p)) {
+               free(p);
                return NULL;
+       }
 
-       path = sha1_pack_name(sha1);
-
-       p = xmalloc(sizeof(*p) + strlen(path) + 2);
        strcpy(p->pack_name, path);
-       p->index_size = idx_size;
        p->pack_size = 0;
-       p->index_base = idx_map;
        p->next = NULL;
        p->windows = NULL;
        p->pack_fd = -1;
@@ -823,6 +819,60 @@ static void prepare_packed_git_one(char *objdir, int local)
        closedir(dir);
 }
 
+static int sort_pack(const void *a_, const void *b_)
+{
+       struct packed_git *a = *((struct packed_git **)a_);
+       struct packed_git *b = *((struct packed_git **)b_);
+       int st;
+
+       /*
+        * Local packs tend to contain objects specific to our
+        * variant of the project than remote ones.  In addition,
+        * remote ones could be on a network mounted filesystem.
+        * Favor local ones for these reasons.
+        */
+       st = a->pack_local - b->pack_local;
+       if (st)
+               return -st;
+
+       /*
+        * Younger packs tend to contain more recent objects,
+        * and more recent objects tend to get accessed more
+        * often.
+        */
+       if (a->mtime < b->mtime)
+               return 1;
+       else if (a->mtime == b->mtime)
+               return 0;
+       return -1;
+}
+
+static void rearrange_packed_git(void)
+{
+       struct packed_git **ary, *p;
+       int i, n;
+
+       for (n = 0, p = packed_git; p; p = p->next)
+               n++;
+       if (n < 2)
+               return;
+
+       /* prepare an array of packed_git for easier sorting */
+       ary = xcalloc(n, sizeof(struct packed_git *));
+       for (n = 0, p = packed_git; p; p = p->next)
+               ary[n++] = p;
+
+       qsort(ary, n, sizeof(struct packed_git *), sort_pack);
+
+       /* link them back again */
+       for (i = 0; i < n - 1; i++)
+               ary[i]->next = ary[i + 1];
+       ary[n - 1]->next = NULL;
+       packed_git = ary[0];
+
+       free(ary);
+}
+
 static int prepare_packed_git_run_once = 0;
 void prepare_packed_git(void)
 {
@@ -837,6 +887,7 @@ void prepare_packed_git(void)
                prepare_packed_git_one(alt->base, 0);
                alt->name[-1] = '/';
        }
+       rearrange_packed_git();
        prepare_packed_git_run_once = 1;
 }
 
@@ -1301,6 +1352,65 @@ static void *unpack_compressed_entry(struct packed_git *p,
        return buffer;
 }
 
+#define MAX_DELTA_CACHE (256)
+
+static struct delta_base_cache_entry {
+       struct packed_git *p;
+       off_t base_offset;
+       unsigned long size;
+       void *data;
+       enum object_type type;
+} delta_base_cache[MAX_DELTA_CACHE];
+
+static unsigned long pack_entry_hash(struct packed_git *p, off_t base_offset)
+{
+       unsigned long hash;
+
+       hash = (unsigned long)p + (unsigned long)base_offset;
+       hash += (hash >> 8) + (hash >> 16);
+       return hash & 0xff;
+}
+
+static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
+       unsigned long *base_size, enum object_type *type, int keep_cache)
+{
+       void *ret;
+       unsigned long hash = pack_entry_hash(p, base_offset);
+       struct delta_base_cache_entry *ent = delta_base_cache + hash;
+
+       ret = ent->data;
+       if (ret && ent->p == p && ent->base_offset == base_offset)
+               goto found_cache_entry;
+       return unpack_entry(p, base_offset, type, base_size);
+
+found_cache_entry:
+       if (!keep_cache)
+               ent->data = NULL;
+       else {
+               ret = xmalloc(ent->size + 1);
+               memcpy(ret, ent->data, ent->size);
+               ((char *)ret)[ent->size] = 0;
+       }
+       *type = ent->type;
+       *base_size = ent->size;
+       return ret;
+}
+
+static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
+       void *base, unsigned long base_size, enum object_type type)
+{
+       unsigned long hash = pack_entry_hash(p, base_offset);
+       struct delta_base_cache_entry *ent = delta_base_cache + hash;
+
+       if (ent->data)
+               free(ent->data);
+       ent->p = p;
+       ent->base_offset = base_offset;
+       ent->type = type;
+       ent->data = base;
+       ent->size = base_size;
+}
+
 static void *unpack_delta_entry(struct packed_git *p,
                                struct pack_window **w_curs,
                                off_t curpos,
@@ -1314,7 +1424,7 @@ static void *unpack_delta_entry(struct packed_git *p,
        off_t base_offset;
 
        base_offset = get_delta_base(p, w_curs, &curpos, *type, obj_offset);
-       base = unpack_entry(p, base_offset, type, &base_size);
+       base = cache_or_unpack_entry(p, base_offset, &base_size, type, 0);
        if (!base)
                die("failed to read delta base object"
                    " at %"PRIuMAX" from %s",
@@ -1327,7 +1437,7 @@ static void *unpack_delta_entry(struct packed_git *p,
        if (!result)
                die("failed to apply delta");
        free(delta_data);
-       free(base);
+       add_delta_base_cache(p, base_offset, base, base_size, *type);
        return result;
 }
 
@@ -1367,24 +1477,27 @@ uint32_t num_packed_objects(const struct packed_git *p)
 int nth_packed_object_sha1(const struct packed_git *p, uint32_t n,
                           unsigned char* sha1)
 {
-       void *index = p->index_base + 256;
+       const unsigned char *index = p->index_data;
+       index += 4 * 256;
        if (num_packed_objects(p) <= n)
                return -1;
-       hashcpy(sha1, (unsigned char *) index + (24 * n) + 4);
+       hashcpy(sha1, index + 24 * n + 4);
        return 0;
 }
 
 off_t find_pack_entry_one(const unsigned char *sha1,
                                  struct packed_git *p)
 {
-       uint32_t *level1_ofs = p->index_base;
+       const uint32_t *level1_ofs = p->index_data;
        int hi = ntohl(level1_ofs[*sha1]);
        int lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
-       void *index = p->index_base + 256;
+       const unsigned char *index = p->index_data;
+
+       index += 4 * 256;
 
        do {
                int mi = (lo + hi) / 2;
-               int cmp = hashcmp((unsigned char *)index + (24 * mi) + 4, sha1);
+               int cmp = hashcmp(index + 24 * mi + 4, sha1);
                if (!cmp)
                        return ntohl(*((uint32_t *)((char *)index + (24 * mi))));
                if (cmp > 0)
@@ -1508,7 +1621,7 @@ static void *read_packed_sha1(const unsigned char *sha1,
        if (!find_pack_entry(sha1, &e, NULL))
                return NULL;
        else
-               return unpack_entry(e.p, e.offset, type, size);
+               return cache_or_unpack_entry(e.p, e.offset, size, type, 1);
 }
 
 /*
index 5565c27033ab89959f2a39dfa9abaea99e40432b..9558bdb6318cc2956c43153084e1f1cc8386973f 100755 (executable)
@@ -47,17 +47,6 @@ test_expect_success \
         test ! -f .git/refs/heads/d/e/f &&
         test ! -f .git/logs/refs/heads/d/e/f'
 
-cat >expect <<EOF
-0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000     checkout: Created from master
-EOF
-test_expect_success \
-    'git checkout -b g/h/i -l should create a branch and a log' \
-       'GIT_COMMITTER_DATE="2005-05-26 23:30" \
-     git-checkout -b g/h/i -l master &&
-        test -f .git/refs/heads/g/h/i &&
-        test -f .git/logs/refs/heads/g/h/i &&
-        diff expect .git/logs/refs/heads/g/h/i'
-
 test_expect_success \
     'git branch j/k should work after branch j has been deleted' \
        'git-branch j &&
@@ -117,4 +106,64 @@ test_expect_failure \
      ln -s real-u .git/logs/refs/heads/u &&
      git-branch -m u v'
 
+test_expect_success 'test tracking setup via --track' \
+    'git-config remote.local.url . &&
+     git-config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+     (git-show-ref -q refs/remotes/local/master || git-fetch local) &&
+     git-branch --track my1 local/master &&
+     test $(git-config branch.my1.remote) = local &&
+     test $(git-config branch.my1.merge) = refs/heads/master'
+
+test_expect_success 'test tracking setup (non-wildcard, matching)' \
+    'git-config remote.local.url . &&
+     git-config remote.local.fetch refs/heads/master:refs/remotes/local/master &&
+     (git-show-ref -q refs/remotes/local/master || git-fetch local) &&
+     git-branch --track my4 local/master &&
+     test $(git-config branch.my4.remote) = local &&
+     test $(git-config branch.my4.merge) = refs/heads/master'
+
+test_expect_success 'test tracking setup (non-wildcard, not matching)' \
+    'git-config remote.local.url . &&
+     git-config remote.local.fetch refs/heads/s:refs/remotes/local/s &&
+     (git-show-ref -q refs/remotes/local/master || git-fetch local) &&
+     git-branch --track my5 local/master &&
+     ! test $(git-config branch.my5.remote) = local &&
+     ! test $(git-config branch.my5.merge) = refs/heads/master'
+
+test_expect_success 'test tracking setup via config' \
+    'git-config branch.autosetupmerge true &&
+     git-config remote.local.url . &&
+     git-config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+     (git-show-ref -q refs/remotes/local/master || git-fetch local) &&
+     git-branch my3 local/master &&
+     test $(git-config branch.my3.remote) = local &&
+     test $(git-config branch.my3.merge) = refs/heads/master'
+
+test_expect_success 'test overriding tracking setup via --no-track' \
+    'git-config branch.autosetupmerge true &&
+     git-config remote.local.url . &&
+     git-config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+     (git-show-ref -q refs/remotes/local/master || git-fetch local) &&
+     git-branch --no-track my2 local/master &&
+     git-config branch.autosetupmerge false &&
+     ! test $(git-config branch.my2.remote) = local &&
+     ! test $(git-config branch.my2.merge) = refs/heads/master'
+
+test_expect_success 'test local tracking setup' \
+    'git branch --track my6 s &&
+     test $(git-config branch.my6.remote) = . &&
+     test $(git-config branch.my6.merge) = refs/heads/s'
+
+# Keep this test last, as it changes the current branch
+cat >expect <<EOF
+0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000     branch: Created from master
+EOF
+test_expect_success \
+    'git checkout -b g/h/i -l should create a branch and a log' \
+       'GIT_COMMITTER_DATE="2005-05-26 23:30" \
+     git-checkout -b g/h/i -l master &&
+        test -f .git/refs/heads/g/h/i &&
+        test -f .git/logs/refs/heads/g/h/i &&
+        diff expect .git/logs/refs/heads/g/h/i'
+
 test_done
diff --git a/t/t4017-diff-retval.sh b/t/t4017-diff-retval.sh
new file mode 100755 (executable)
index 0000000..6873190
--- /dev/null
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+test_description='Return value of diffs'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       echo 1 >a &&
+       git add . &&
+       git commit -m first &&
+       echo 2 >b &&
+       git add . &&
+       git commit -a -m second
+'
+
+test_expect_success 'git diff-tree HEAD^ HEAD' '
+       git diff-tree --exit-code HEAD^ HEAD
+       test $? = 1
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
+       git diff-tree --exit-code HEAD^ HEAD -- a
+       test $? = 0
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
+       git diff-tree --exit-code HEAD^ HEAD -- b
+       test $? = 1
+'
+test_expect_success 'echo HEAD | git diff-tree --stdin' '
+       echo $(git rev-parse HEAD) | git diff-tree --exit-code --stdin
+       test $? = 1
+'
+test_expect_success 'git diff-tree HEAD HEAD' '
+       git diff-tree --exit-code HEAD HEAD
+       test $? = 0
+'
+test_expect_success 'git diff-files' '
+       git diff-files --exit-code
+       test $? = 0
+'
+test_expect_success 'git diff-index --cached HEAD' '
+       git diff-index --exit-code --cached HEAD
+       test $? = 0
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+       git diff-index --exit-code --cached HEAD^
+       test $? = 1
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+       echo text >>b &&
+       echo 3 >c &&
+       git add . && {
+               git diff-index --exit-code --cached HEAD^
+               test $? = 1
+       }
+'
+test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
+       git commit -m "text in b" && {
+               git diff-tree -p --exit-code -Stext HEAD^ HEAD -- b
+               test $? = 1
+       }
+'
+test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
+       git diff-tree -p --exit-code -Snot-found HEAD^ HEAD -- b
+       test $? = 0
+'
+test_expect_success 'git diff-files' '
+       echo 3 >>c && {
+               git diff-files --exit-code
+               test $? = 1
+       }
+'
+test_expect_success 'git diff-index --cached HEAD' '
+       git update-index c && {
+               git diff-index --exit-code --cached HEAD
+               test $? = 1
+       }
+'
+
+test_done
diff --git a/t/t4017-quiet.sh b/t/t4017-quiet.sh
new file mode 100755 (executable)
index 0000000..e747e84
--- /dev/null
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+test_description='Return value of diffs'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       echo 1 >a &&
+       git add . &&
+       git commit -m first &&
+       echo 2 >b &&
+       git add . &&
+       git commit -a -m second
+'
+
+test_expect_success 'git diff-tree HEAD^ HEAD' '
+       git diff-tree --quiet HEAD^ HEAD >cnt
+       test $? = 1 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
+       git diff-tree --quiet HEAD^ HEAD -- a >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
+       git diff-tree --quiet HEAD^ HEAD -- b >cnt
+       test $? = 1 && test $(wc -l <cnt) = 0
+'
+# this diff outputs one line: sha1 of the given head
+test_expect_success 'echo HEAD | git diff-tree --stdin' '
+       echo $(git rev-parse HEAD) | git diff-tree --quiet --stdin >cnt
+       test $? = 1 && test $(wc -l <cnt) = 1
+'
+test_expect_success 'git diff-tree HEAD HEAD' '
+       git diff-tree --quiet HEAD HEAD >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-files' '
+       git diff-files --quiet >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD' '
+       git diff-index --quiet --cached HEAD >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+       git diff-index --quiet --cached HEAD^ >cnt
+       test $? = 1 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+       echo text >>b &&
+       echo 3 >c &&
+       git add . && {
+               git diff-index --quiet --cached HEAD^ >cnt
+               test $? = 1 && test $(wc -l <cnt) = 0
+       }
+'
+test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
+       git commit -m "text in b" && {
+               git diff-tree --quiet -Stext HEAD^ HEAD -- b >cnt
+               test $? = 1 && test $(wc -l <cnt) = 0
+       }
+'
+test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
+       git diff-tree --quiet -Snot-found HEAD^ HEAD -- b >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-files' '
+       echo 3 >>c && {
+               git diff-files --quiet >cnt
+               test $? = 1 && test $(wc -l <cnt) = 0
+       }
+'
+test_expect_success 'git diff-index --cached HEAD' '
+       git update-index c && {
+               git diff-index --quiet --cached HEAD >cnt
+               test $? = 1 && test $(wc -l <cnt) = 0
+       }
+'
+
+test_done
index 4d2b781a1877b2718b961a057b0dddea8f87c51b..ca96918da2008e9036624fac67c6337961e3c2b0 100755 (executable)
@@ -11,7 +11,7 @@ test_expect_success 'split sample box' \
        'git-mailsplit -o. ../t5100/sample.mbox >last &&
        last=`cat last` &&
        echo total is $last &&
-       test `cat last` = 6'
+       test `cat last` = 8'
 
 for mail in `echo 00*`
 do
diff --git a/t/t5100/info0007 b/t/t5100/info0007
new file mode 100644 (file)
index 0000000..49bb0fe
--- /dev/null
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: another patch
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/info0008 b/t/t5100/info0008
new file mode 100644 (file)
index 0000000..e8a2951
--- /dev/null
@@ -0,0 +1,5 @@
+Author: Junio C Hamano
+Email: junio@kernel.org
+Subject: another patch
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/msg0007 b/t/t5100/msg0007
new file mode 100644 (file)
index 0000000..71b23c0
--- /dev/null
@@ -0,0 +1,2 @@
+Here is an empty patch from A U Thor.
+
diff --git a/t/t5100/msg0008 b/t/t5100/msg0008
new file mode 100644 (file)
index 0000000..a80ecb9
--- /dev/null
@@ -0,0 +1,4 @@
+>Here is an empty patch from A U Thor.
+
+Hey you forgot the patch!
+
index 7d24b24af83c861a1dc615c19cbaa392fdd69dec..e7d6f666083c65e152571153f63691596ed9d193 100644 (file)
@@ -61,7 +61,7 @@ diff --git a/git-cvsimport-script b/git-cvsimport-script
                push(@old,$fn);
 
 -- 
-David Kågedal
+David Kågedal
 -
 To unsubscribe from this list: send the line "unsubscribe git" in
 the body of a message to majordomo@vger.kernel.org
diff --git a/t/t5100/patch0007 b/t/t5100/patch0007
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/t/t5100/patch0008 b/t/t5100/patch0008
new file mode 100644 (file)
index 0000000..e69de29
index 86bfc27147f7df8458f32fee5a9aa199a0c76b3a..b80c981c165e9c82f56f826a0542f3bef3f13eb3 100644 (file)
@@ -386,3 +386,21 @@ index 9123cdc..918dcf8 100644
 -- 
 1.4.0.g6f2b
 
+From nobody Mon Sep 17 00:00:00 2001
+From: A U Thor <a.u.thor@example.com>
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+Subject: [PATCH] another patch
+
+Here is an empty patch from A U Thor.
+
+From nobody Mon Sep 17 00:00:00 2001
+From: Junio C Hamano <junio@kernel.org>
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+Subject: re: [PATCH] another patch
+
+From: A U Thor <a.u.thor@example.com>
+Subject: [PATCH] another patch
+>Here is an empty patch from A U Thor.
+
+Hey you forgot the patch!
+
index f51154745586b246ecb30caee3f2150441911e1d..eacb1e92c20bf0efef069ff5ec0af792f95b1252 100755 (executable)
@@ -64,7 +64,7 @@ test_expect_success \
 cd "$TRASH"
 
 test_expect_success \
-    'pack with delta' \
+    'pack with REF_DELTA' \
     'pwd &&
      packname_2=$(git-pack-objects test-2 <obj-list)'
 
@@ -72,7 +72,7 @@ rm -fr .git2
 mkdir .git2
 
 test_expect_success \
-    'unpack with delta' \
+    'unpack with REF_DELTA' \
     'GIT_OBJECT_DIRECTORY=.git2/objects &&
      export GIT_OBJECT_DIRECTORY &&
      git-init &&
@@ -82,7 +82,7 @@ test_expect_success \
 unset GIT_OBJECT_DIRECTORY
 cd "$TRASH/.git2"
 test_expect_success \
-    'check unpack with delta' \
+    'check unpack with REF_DELTA' \
     '(cd ../.git && find objects -type f -print) |
      while read path
      do
@@ -93,6 +93,42 @@ test_expect_success \
      done'
 cd "$TRASH"
 
+test_expect_success \
+    'pack with OFS_DELTA' \
+    'pwd &&
+     packname_3=$(git-pack-objects --delta-base-offset test-3 <obj-list)'
+
+rm -fr .git2
+mkdir .git2
+
+test_expect_success \
+    'unpack with OFS_DELTA' \
+    'GIT_OBJECT_DIRECTORY=.git2/objects &&
+     export GIT_OBJECT_DIRECTORY &&
+     git-init &&
+     git-unpack-objects -n <test-3-${packname_3}.pack &&
+     git-unpack-objects <test-3-${packname_3}.pack'
+
+unset GIT_OBJECT_DIRECTORY
+cd "$TRASH/.git2"
+test_expect_success \
+    'check unpack with OFS_DELTA' \
+    '(cd ../.git && find objects -type f -print) |
+     while read path
+     do
+         cmp $path ../.git/$path || {
+            echo $path differs.
+            return 1
+        }
+     done'
+cd "$TRASH"
+
+test_expect_success \
+    'compare delta flavors' \
+    'size_2=`stat -c "%s" test-2-${packname_2}.pack` &&
+     size_3=`stat -c "%s" test-3-${packname_3}.pack` &&
+     test $size_2 -gt $size_3'
+
 rm -fr .git2
 mkdir .git2
 
@@ -111,12 +147,11 @@ test_expect_success \
     } >current &&
     diff expect current'
 
-
 test_expect_success \
-    'use packed deltified objects' \
+    'use packed deltified (REF_DELTA) objects' \
     'GIT_OBJECT_DIRECTORY=.git2/objects &&
      export GIT_OBJECT_DIRECTORY &&
-     rm -f .git2/objects/pack/test-?.idx &&
+     rm .git2/objects/pack/test-* &&
      cp test-2-${packname_2}.pack test-2-${packname_2}.idx .git2/objects/pack && {
         git-diff-tree --root -p $commit &&
         while read object
@@ -127,11 +162,28 @@ test_expect_success \
     } >current &&
     diff expect current'
 
+test_expect_success \
+    'use packed deltified (OFS_DELTA) objects' \
+    'GIT_OBJECT_DIRECTORY=.git2/objects &&
+     export GIT_OBJECT_DIRECTORY &&
+     rm .git2/objects/pack/test-* &&
+     cp test-3-${packname_3}.pack test-3-${packname_3}.idx .git2/objects/pack && {
+        git-diff-tree --root -p $commit &&
+        while read object
+        do
+           t=`git-cat-file -t $object` &&
+           git-cat-file $t $object || return 1
+        done <obj-list
+    } >current &&
+    diff expect current'
+
 unset GIT_OBJECT_DIRECTORY
 
 test_expect_success \
     'verify pack' \
-    'git-verify-pack test-1-${packname_1}.idx test-2-${packname_2}.idx'
+    'git-verify-pack   test-1-${packname_1}.idx \
+                       test-2-${packname_2}.idx \
+                       test-3-${packname_3}.idx'
 
 test_expect_success \
     'corrupt a pack and see if verify catches' \
@@ -194,6 +246,13 @@ test_expect_success \
      git-index-pack test-3.pack &&
      cmp test-3.idx test-2-${packname_2}.idx &&
 
+     cp test-3-${packname_3}.pack test-3.pack &&
+     git-index-pack -o tmp.idx test-3-${packname_3}.pack &&
+     cmp tmp.idx test-3-${packname_3}.idx &&
+
+     git-index-pack test-3.pack &&
+     cmp test-3.idx test-3-${packname_3}.idx &&
+
      :'
 
 test_done
index 7eb37838bb788dfb68361bca95860fd532276deb..243212d3dac03c19db18e9c8fcfc3b9049137cdd 100755 (executable)
@@ -29,5 +29,29 @@ test_expect_success 'checking the results' '
        diff file cloned/file
 '
 
+test_expect_success 'test . as a remote' '
+
+       git branch copy master &&
+       git config branch.copy.remote . &&
+       git config branch.copy.merge refs/heads/master &&
+       echo updated >file &&
+       git commit -a -m updated &&
+       git checkout copy &&
+       test `cat file` = file &&
+       git pull &&
+       test `cat file` = updated
+'
+
+test_expect_success 'the default remote . should not break explicit pull' '
+       git checkout -b second master^ &&
+       echo modified >file &&
+       git commit -a -m modified &&
+       git checkout copy &&
+       git reset --hard HEAD^ &&
+       test `cat file` = file &&
+       git pull . second &&
+       test `cat file` = modified
+'
+
 test_done
 
index 5b82b68e93980924befa9ecc99e099e77da27b5f..8f6c4fea2429cef0da4748f8871ceaed94b432cc 100644 (file)
@@ -210,7 +210,7 @@ case "$refname_type" in
                fi
 
                # If this tag succeeds another, then show which tag it replaces
-               prevtag=$(git describe $newrev^ 2>/dev/null | sed 's/-g.*//')
+               prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null)
                if [ -n "$prevtag" ]; then
                        echo "  replaces  $prevtag"
                fi
diff --git a/trace.c b/trace.c
index 27fef868c4e2276638873cbda7d353cf4b8f67ed..7961a27a2ed4f32c766dabdf12c4115c3d3b36ba 100644 (file)
--- a/trace.c
+++ b/trace.c
 #include "quote.h"
 
 /* Stolen from "imap-send.c". */
-static int git_vasprintf(char **strp, const char *fmt, va_list ap)
+int nfvasprintf(char **strp, const char *fmt, va_list ap)
 {
        int len;
        char tmp[1024];
 
        if ((len = vsnprintf(tmp, sizeof(tmp), fmt, ap)) < 0 ||
            !(*strp = xmalloc(len + 1)))
-               return -1;
+               die("Fatal: Out of memory\n");
        if (len >= (int)sizeof(tmp))
                vsprintf(*strp, fmt, ap);
        else
@@ -41,13 +41,15 @@ static int git_vasprintf(char **strp, const char *fmt, va_list ap)
        return len;
 }
 
-/* Stolen from "imap-send.c". */
-int nfvasprintf(char **str, const char *fmt, va_list va)
+int nfasprintf(char **str, const char *fmt, ...)
 {
-       int ret = git_vasprintf(str, fmt, va);
-       if (ret < 0)
-               die("Fatal: Out of memory\n");
-       return ret;
+       int rc;
+       va_list args;
+
+       va_start(args, fmt);
+       rc = nfvasprintf(str, fmt, args);
+       va_end(args);
+       return rc;
 }
 
 /* Get a trace file descriptor from GIT_TRACE env variable. */
index c8275823d0eb976e95b256f2bce5497f45d5da77..3940962e79a5094e61270cd951d5d37c307d11a9 100644 (file)
@@ -5,9 +5,8 @@
 #include "diff.h"
 #include "tree.h"
 
-static char *malloc_base(const char *base, const char *path, int pathlen)
+static char *malloc_base(const char *base, int baselen, const char *path, int pathlen)
 {
-       int baselen = strlen(base);
        char *newbase = xmalloc(baselen + pathlen + 2);
        memcpy(newbase, base, baselen);
        memcpy(newbase + baselen, path, pathlen);
@@ -16,9 +15,9 @@ static char *malloc_base(const char *base, const char *path, int pathlen)
 }
 
 static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,
-                      const char *base);
+                      const char *base, int baselen);
 
-static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
+static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const char *base, int baselen, struct diff_options *opt)
 {
        unsigned mode1, mode2;
        const char *path1, *path2;
@@ -28,15 +27,15 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
        sha1 = tree_entry_extract(t1, &path1, &mode1);
        sha2 = tree_entry_extract(t2, &path2, &mode2);
 
-       pathlen1 = strlen(path1);
-       pathlen2 = strlen(path2);
+       pathlen1 = tree_entry_len(path1, sha1);
+       pathlen2 = tree_entry_len(path2, sha2);
        cmp = base_name_compare(path1, pathlen1, mode1, path2, pathlen2, mode2);
        if (cmp < 0) {
-               show_entry(opt, "-", t1, base);
+               show_entry(opt, "-", t1, base, baselen);
                return -1;
        }
        if (cmp > 0) {
-               show_entry(opt, "+", t2, base);
+               show_entry(opt, "+", t2, base, baselen);
                return 1;
        }
        if (!opt->find_copies_harder && !hashcmp(sha1, sha2) && mode1 == mode2)
@@ -47,14 +46,14 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
         * file, we need to consider it a remove and an add.
         */
        if (S_ISDIR(mode1) != S_ISDIR(mode2)) {
-               show_entry(opt, "-", t1, base);
-               show_entry(opt, "+", t2, base);
+               show_entry(opt, "-", t1, base, baselen);
+               show_entry(opt, "+", t2, base, baselen);
                return 0;
        }
 
        if (opt->recursive && S_ISDIR(mode1)) {
                int retval;
-               char *newbase = malloc_base(base, path1, pathlen1);
+               char *newbase = malloc_base(base, baselen, path1, pathlen1);
                if (opt->tree_in_recursive)
                        opt->change(opt, mode1, mode2,
                                    sha1, sha2, base, path1);
@@ -67,20 +66,20 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
        return 0;
 }
 
-static int interesting(struct tree_desc *desc, const char *base, struct diff_options *opt)
+static int interesting(struct tree_desc *desc, const char *base, int baselen, struct diff_options *opt)
 {
        const char *path;
+       const unsigned char *sha1;
        unsigned mode;
        int i;
-       int baselen, pathlen;
+       int pathlen;
 
        if (!opt->nr_paths)
                return 1;
 
-       (void)tree_entry_extract(desc, &path, &mode);
+       sha1 = tree_entry_extract(desc, &path, &mode);
 
-       pathlen = strlen(path);
-       baselen = strlen(base);
+       pathlen = tree_entry_len(path, sha1);
 
        for (i=0; i < opt->nr_paths; i++) {
                const char *match = opt->paths[i];
@@ -121,18 +120,18 @@ static int interesting(struct tree_desc *desc, const char *base, struct diff_opt
 }
 
 /* A whole sub-tree went away or appeared */
-static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base)
+static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base, int baselen)
 {
        while (desc->size) {
-               if (interesting(desc, base, opt))
-                       show_entry(opt, prefix, desc, base);
+               if (interesting(desc, base, baselen, opt))
+                       show_entry(opt, prefix, desc, base, baselen);
                update_tree_entry(desc);
        }
 }
 
 /* A file entry went away or appeared */
 static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,
-                      const char *base)
+                      const char *base, int baselen)
 {
        unsigned mode;
        const char *path;
@@ -140,7 +139,8 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
 
        if (opt->recursive && S_ISDIR(mode)) {
                enum object_type type;
-               char *newbase = malloc_base(base, path, strlen(path));
+               int pathlen = tree_entry_len(path, sha1);
+               char *newbase = malloc_base(base, baselen, path, pathlen);
                struct tree_desc inner;
                void *tree;
 
@@ -149,7 +149,7 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
                        die("corrupt tree sha %s", sha1_to_hex(sha1));
 
                inner.buf = tree;
-               show_tree(opt, prefix, &inner, newbase);
+               show_tree(opt, prefix, &inner, newbase, baselen + 1 + pathlen);
 
                free(tree);
                free(newbase);
@@ -160,26 +160,30 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
 
 int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
 {
+       int baselen = strlen(base);
+
        while (t1->size | t2->size) {
-               if (opt->nr_paths && t1->size && !interesting(t1, base, opt)) {
+               if (opt->quiet && opt->has_changes)
+                       break;
+               if (opt->nr_paths && t1->size && !interesting(t1, base, baselen, opt)) {
                        update_tree_entry(t1);
                        continue;
                }
-               if (opt->nr_paths && t2->size && !interesting(t2, base, opt)) {
+               if (opt->nr_paths && t2->size && !interesting(t2, base, baselen, opt)) {
                        update_tree_entry(t2);
                        continue;
                }
                if (!t1->size) {
-                       show_entry(opt, "+", t2, base);
+                       show_entry(opt, "+", t2, base, baselen);
                        update_tree_entry(t2);
                        continue;
                }
                if (!t2->size) {
-                       show_entry(opt, "-", t1, base);
+                       show_entry(opt, "-", t1, base, baselen);
                        update_tree_entry(t1);
                        continue;
                }
-               switch (compare_tree_entry(t1, t2, base, opt)) {
+               switch (compare_tree_entry(t1, t2, base, baselen, opt)) {
                case -1:
                        update_tree_entry(t1);
                        continue;
index 70f899957e8ce511f0f5a470e8b6927d58d1b63e..a4a4e2a9892fb10df32b113ca8b05848d11aff78 100644 (file)
@@ -32,7 +32,7 @@ static void entry_clear(struct name_entry *a)
 static void entry_extract(struct tree_desc *t, struct name_entry *a)
 {
        a->sha1 = tree_entry_extract(t, &a->path, &a->mode);
-       a->pathlen = strlen(a->path);
+       a->pathlen = tree_entry_len(a->path, a->sha1);
 }
 
 void update_tree_entry(struct tree_desc *desc)
@@ -169,7 +169,7 @@ static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char
 
                sha1 = tree_entry_extract(t, &entry, mode);
                update_tree_entry(t);
-               entrylen = strlen(entry);
+               entrylen = tree_entry_len(entry, sha1);
                if (entrylen > namelen)
                        continue;
                cmp = memcmp(name, entry, entrylen);
index e57befa4dac0854906d5b792ce546dab0e6dcdc3..a0d7afd89bee1746912c61164e839daaa05a9b0c 100644 (file)
@@ -13,6 +13,11 @@ struct name_entry {
        int pathlen;
 };
 
+static inline int tree_entry_len(const char *name, const unsigned char *sha1)
+{
+       return (char *)sha1 - (char *)name - 1;
+}
+
 void update_tree_entry(struct tree_desc *);
 const unsigned char *tree_entry_extract(struct tree_desc *, const char **, unsigned int *);