Merge branch 'mh/ref-directory-file' into maint
authorJunio C Hamano <gitster@pobox.com>
Fri, 5 Jun 2015 19:00:16 +0000 (12:00 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 5 Jun 2015 19:00:16 +0000 (12:00 -0700)
The ref API did not handle cases where 'refs/heads/xyzzy/frotz' is
removed at the same time as 'refs/heads/xyzzy' is added (or vice
versa) very well.

* mh/ref-directory-file:
reflog_expire(): integrate lock_ref_sha1_basic() errors into ours
ref_transaction_commit(): delete extra "the" from error message
ref_transaction_commit(): provide better error messages
rename_ref(): integrate lock_ref_sha1_basic() errors into ours
lock_ref_sha1_basic(): improve diagnostics for ref D/F conflicts
lock_ref_sha1_basic(): report errors via a "struct strbuf *err"
verify_refname_available(): report errors via a "struct strbuf *err"
verify_refname_available(): rename function
refs: check for D/F conflicts among refs created in a transaction
ref_transaction_commit(): use a string_list for detecting duplicates
is_refname_available(): use dirname in first loop
struct nonmatching_ref_data: store a refname instead of a ref_entry
report_refname_conflict(): inline function
entry_matches(): inline function
is_refname_available(): convert local variable "dirname" to strbuf
is_refname_available(): avoid shadowing "dir" variable
is_refname_available(): revamp the comments
t1404: new tests of ref D/F conflicts within transactions

62 files changed:
Documentation/RelNotes/2.3.8.txt [new file with mode: 0644]
Documentation/RelNotes/2.4.1.txt [new file with mode: 0644]
Documentation/RelNotes/2.4.2.txt [new file with mode: 0644]
Documentation/blame-options.txt
Documentation/git-credential-store.txt
Documentation/git-hash-object.txt
Documentation/git-log.txt
Documentation/git.txt
Documentation/gitk.txt
Documentation/line-range-format.txt
GIT-VERSION-GEN
RelNotes
attr.c
builtin/add.c
builtin/blame.c
builtin/clone.c
builtin/commit.c
builtin/config.c
builtin/fmt-merge-msg.c
builtin/hash-object.c
builtin/init-db.c
builtin/pack-objects.c
cache.h
config.c
connect.c
contrib/completion/git-completion.bash
credential-store.c
daemon.c
date.c
diff-no-index.c
dir.c
exec_cmd.c
git-filter-branch.sh
git-rebase--interactive.sh
git-rebase.sh
git-stash.sh
line-log.c
log-tree.c
object.c
pack-bitmap.c
path.c
reachable.c
revision.c
sha1_file.c
t/t0008-ignores.sh
t/t0302-credential-store.sh
t/t1007-hash-object.sh
t/t1020-subdirectory.sh
t/t1509/prepare-chroot.sh
t/t3404-rebase-interactive.sh
t/t3702-add-edit.sh
t/t3903-stash.sh
t/t4013/diff.log_--decorate=full_--all
t/t4053-diff-no-index.sh
t/t4211-line-log.sh
t/t5601-clone.sh
t/t7003-filter-branch.sh
t/t7061-wtstatus-ignore.sh
t/t8003-blame-corner-cases.sh
t/test-lib-functions.sh
utf8.c
utf8.h
diff --git a/Documentation/RelNotes/2.3.8.txt b/Documentation/RelNotes/2.3.8.txt
new file mode 100644 (file)
index 0000000..0b67268
--- /dev/null
@@ -0,0 +1,22 @@
+Git v2.3.8 Release Notes
+========================
+
+Fixes since v2.3.7
+------------------
+
+ * The usual "git diff" when seeing a file turning into a directory
+   showed a patchset to remove the file and create all files in the
+   directory, but "git diff --no-index" simply refused to work.  Also,
+   when asked to compare a file and a directory, imitate POSIX "diff"
+   and compare the file with the file with the same name in the
+   directory, instead of refusing to run.
+
+ * The default $HOME/.gitconfig file created upon "git config --global"
+   that edits it had incorrectly spelled user.name and user.email
+   entries in it.
+
+ * "git commit --date=now" or anything that relies on approxidate lost
+   the daylight-saving-time offset.
+
+Also contains typofixes, documentation updates and trivial code
+clean-ups.
diff --git a/Documentation/RelNotes/2.4.1.txt b/Documentation/RelNotes/2.4.1.txt
new file mode 100644 (file)
index 0000000..a65a6c5
--- /dev/null
@@ -0,0 +1,40 @@
+Git v2.4.1 Release Notes
+========================
+
+Fixes since v2.4
+----------------
+
+ * The usual "git diff" when seeing a file turning into a directory
+   showed a patchset to remove the file and create all files in the
+   directory, but "git diff --no-index" simply refused to work.  Also,
+   when asked to compare a file and a directory, imitate POSIX "diff"
+   and compare the file with the file with the same name in the
+   directory, instead of refusing to run.
+
+ * The default $HOME/.gitconfig file created upon "git config --global"
+   that edits it had incorrectly spelled user.name and user.email
+   entries in it.
+
+ * "git commit --date=now" or anything that relies on approxidate lost
+   the daylight-saving-time offset.
+
+ * "git cat-file bl $blob" failed to barf even though there is no
+   object type that is "bl".
+
+ * Teach the codepaths that read .gitignore and .gitattributes files
+   that these files encoded in UTF-8 may have UTF-8 BOM marker at the
+   beginning; this makes it in line with what we do for configuration
+   files already.
+
+ * Access to objects in repositories that borrow from another one on a
+   slow NFS server unnecessarily got more expensive due to recent code
+   becoming more cautious in a naive way not to lose objects to pruning.
+
+ * We avoid setting core.worktree when the repository location is the
+   ".git" directory directly at the top level of the working tree, but
+   the code misdetected the case in which the working tree is at the
+   root level of the filesystem (which arguably is a silly thing to
+   do, but still valid).
+
+Also contains typofixes, documentation updates and trivial code
+clean-ups.
diff --git a/Documentation/RelNotes/2.4.2.txt b/Documentation/RelNotes/2.4.2.txt
new file mode 100644 (file)
index 0000000..250cdc4
--- /dev/null
@@ -0,0 +1,45 @@
+Git v2.4.2 Release Notes
+========================
+
+Fixes since v2.4.1
+------------------
+
+ * "git rev-list --objects $old --not --all" to see if everything that
+   is reachable from $old is already connected to the existing refs
+   was very inefficient.
+
+ * "hash-object --literally" introduced in v2.2 was not prepared to
+   take a really long object type name.
+
+ * "git rebase --quiet" was not quite quiet when there is nothing to
+   do.
+
+ * The completion for "log --decorate=" parameter value was incorrect.
+
+ * "filter-branch" corrupted commit log message that ends with an
+   incomplete line on platforms with some "sed" implementations that
+   munge such a line.  Work it around by avoiding to use "sed".
+
+ * "git daemon" fails to build from the source under NO_IPV6
+   configuration (regression in 2.4).
+
+ * "git stash pop/apply" forgot to make sure that not just the working
+   tree is clean but also the index is clean. The latter is important
+   as a stash application can conflict and the index will be used for
+   conflict resolution.
+
+ * We have prepended $GIT_EXEC_PATH and the path "git" is installed in
+   (typically "/usr/bin") to $PATH when invoking subprograms and hooks
+   for almost eternity, but the original use case the latter tried to
+   support was semi-bogus (i.e. install git to /opt/foo/git and run it
+   without having /opt/foo on $PATH), and more importantly it has
+   become less and less relevant as Git grew more mainstream (i.e. the
+   users would _want_ to have it on their $PATH).  Stop prepending the
+   path in which "git" is installed to users' $PATH, as that would
+   interfere the command search order people depend on (e.g. they may
+   not like versions of programs that are unrelated to Git in /usr/bin
+   and want to override them by having different ones in /usr/local/bin
+   and have the latter directory earlier in their $PATH).
+
+Also contains typofixes, documentation updates and trivial code
+clean-ups.
index b299b5902360bdd9f784e5b3acdae3b558a6565a..a09969ba086609af5d35bbb3c5f30dfea5db00b6 100644 (file)
@@ -10,7 +10,7 @@
        Include additional statistics at the end of blame output.
 
 -L <start>,<end>::
--L :<regex>::
+-L :<funcname>::
        Annotate only the given line range. May be specified multiple times.
        Overlapping ranges are allowed.
 +
index bc97071e7668ac48d07065190e88d1ce74822041..e3c8f276b14ed188284536636d259974f46d755e 100644 (file)
@@ -31,10 +31,41 @@ OPTIONS
 
 --file=<path>::
 
-       Use `<path>` to store credentials. The file will have its
+       Use `<path>` to lookup and store credentials. The file will have its
        filesystem permissions set to prevent other users on the system
        from reading it, but will not be encrypted or otherwise
-       protected. Defaults to `~/.git-credentials`.
+       protected. If not specified, credentials will be searched for from
+       `~/.git-credentials` and `$XDG_CONFIG_HOME/git/credentials`, and
+       credentials will be written to `~/.git-credentials` if it exists, or
+       `$XDG_CONFIG_HOME/git/credentials` if it exists and the former does
+       not. See also <<FILES>>.
+
+[[FILES]]
+FILES
+-----
+
+If not set explicitly with '--file', there are two files where
+git-credential-store will search for credentials in order of precedence:
+
+~/.git-credentials::
+       User-specific credentials file.
+
+$XDG_CONFIG_HOME/git/credentials::
+       Second user-specific credentials file. If '$XDG_CONFIG_HOME' is not set
+       or empty, `$HOME/.config/git/credentials` will be used. Any credentials
+       stored in this file will not be used if `~/.git-credentials` has a
+       matching credential as well. It is a good idea not to create this file
+       if you sometimes use older versions of Git that do not support it.
+
+For credential lookups, the files are read in the order given above, with the
+first matching credential found taking precedence over credentials found in
+files further down the list.
+
+Credential storage will by default write to the first existing file in the
+list. If none of these files exist, `~/.git-credentials` will be created and
+written to.
+
+When erasing credentials, matching credentials will be erased from all files.
 
 EXAMPLES
 --------
index 02c1f126855b24dec121fb49bf920ad14af0b0b8..0c75f3b610678d9a6811ac73d208cabd706b2276 100644 (file)
@@ -9,7 +9,7 @@ git-hash-object - Compute object ID and optionally creates a blob from a file
 SYNOPSIS
 --------
 [verse]
-'git hash-object' [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>...
+'git hash-object' [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin [--literally]] [--] <file>...
 'git hash-object' [-t <type>] [-w] --stdin-paths [--no-filters] < <list-of-paths>
 
 DESCRIPTION
@@ -51,7 +51,13 @@ OPTIONS
        Hash the contents as is, ignoring any input filter that would
        have been chosen by the attributes mechanism, including the end-of-line
        conversion. If the file is read from standard input then this
-       is always implied, unless the --path option is given.
+       is always implied, unless the `--path` option is given.
+
+--literally::
+       Allow `--stdin` to hash any garbage into a loose object which might not
+       otherwise pass standard object parsing or git-fsck checks. Useful for
+       stress-testing Git itself or reproducing characteristics of corrupt or
+       bogus objects encountered in the wild.
 
 GIT
 ---
index 18bc716a0c89e42d91973b510da1d61d33aad977..5692945a0b38c0b9e8e79e73a9a755c292224c98 100644 (file)
@@ -62,9 +62,9 @@ produced by `--stat`, etc.
        output by allowing them to allocate space in advance.
 
 -L <start>,<end>:<file>::
--L :<regex>:<file>::
+-L :<funcname>:<file>::
        Trace the evolution of the line range given by "<start>,<end>"
-       (or the funcname regex <regex>) within the <file>.  You may
+       (or the function name regex <funcname>) within the <file>.  You may
        not give any pathspec limiters.  This is currently limited to
        a walk starting from a single revision, i.e., you may only
        give zero or one positive revision arguments.
index c71e818cf445d55e793229dd9cb46777e5f53a98..f582742bc81a761218f30cead342491cf5580a5d 100644 (file)
@@ -43,14 +43,17 @@ unreleased) version of Git, that is available from the 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v2.4.0/git.html[documentation for release 2.4]
+* link:v2.4.2/git.html[documentation for release 2.4.2]
 
 * release notes for
+  link:RelNotes/2.4.2.txt[2.4.2],
+  link:RelNotes/2.4.1.txt[2.4.1],
   link:RelNotes/2.4.0.txt[2.4].
 
-* link:v2.3.7/git.html[documentation for release 2.3.7]
+* link:v2.3.8/git.html[documentation for release 2.3.8]
 
 * release notes for
+  link:RelNotes/2.3.8.txt[2.3.8],
   link:RelNotes/2.3.7.txt[2.3.7],
   link:RelNotes/2.3.6.txt[2.3.6],
   link:RelNotes/2.3.5.txt[2.3.5],
index 7ae50aa26aff26cb9dd619abc7c29372c4156bcc..6ade00217606cd111811dcd5777df5c66bb9bf39 100644 (file)
@@ -99,10 +99,10 @@ linkgit:git-rev-list[1] for a complete list.
        detailed explanation.)
 
 -L<start>,<end>:<file>::
--L:<regex>:<file>::
+-L:<funcname>:<file>::
 
        Trace the evolution of the line range given by "<start>,<end>"
-       (or the funcname regex <regex>) within the <file>.  You may
+       (or the function name regex <funcname>) within the <file>.  You may
        not give any pathspec limiters.  This is currently limited to
        a walk starting from a single revision, i.e., you may only
        give zero or one positive revision arguments.
index d7f26039cac500ff922dd3fb9470912b03718335..829676ff98335913ec037396a659f42f74e5a90d 100644 (file)
@@ -22,8 +22,9 @@ This is only valid for <end> and will specify a number
 of lines before or after the line given by <start>.
 
 +
-If ``:<regex>'' is given in place of <start> and <end>, it denotes the range
-from the first funcname line that matches <regex>, up to the next
-funcname line. ``:<regex>'' searches from the end of the previous `-L` range,
-if any, otherwise from the start of file.
-``^:<regex>'' searches from the start of file.
+If ``:<funcname>'' is given in place of <start> and <end>, it is a
+regular expression that denotes the range from the first funcname line
+that matches <funcname>, up to the next funcname line. ``:<funcname>''
+searches from the end of the previous `-L` range, if any, otherwise
+from the start of file. ``^:<funcname>'' searches from the start of
+file.
index 48aee9c41729f880a3b341bf55429bd0f64291a3..c4a6631fc0458faaa52ed57dcaef7eea5def20df 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.4.0
+DEF_VER=v2.4.2
 
 LF='
 '
index 1addbec92533510e6e368c607deb36e504bbc438..f43dfd9767a9ca8117d12bdecdae682d733eebc3 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.4.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.4.2.txt
\ No newline at end of file
diff --git a/attr.c b/attr.c
index 1f9eebd2ddb7fe8a561500c41f33a52d1fb4fdd1..8f2ac6c88c8c2f7cff514981a7c01c136f734892 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -12,6 +12,7 @@
 #include "exec_cmd.h"
 #include "attr.h"
 #include "dir.h"
+#include "utf8.h"
 
 const char git_attr__true[] = "(builtin)true";
 const char git_attr__false[] = "\0(builtin)false";
@@ -379,8 +380,12 @@ static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
                return NULL;
        }
        res = xcalloc(1, sizeof(*res));
-       while (fgets(buf, sizeof(buf), fp))
-               handle_attr_line(res, buf, path, ++lineno, macro_ok);
+       while (fgets(buf, sizeof(buf), fp)) {
+               char *bufp = buf;
+               if (!lineno)
+                       skip_utf8_bom(&bufp, strlen(bufp));
+               handle_attr_line(res, bufp, path, ++lineno, macro_ok);
+       }
        fclose(fp);
        return res;
 }
@@ -488,7 +493,6 @@ static int git_attr_system(void)
 static void bootstrap_attr_stack(void)
 {
        struct attr_stack *elem;
-       char *xdg_attributes_file;
 
        if (attr_stack)
                return;
@@ -507,10 +511,8 @@ static void bootstrap_attr_stack(void)
                }
        }
 
-       if (!git_attributes_file) {
-               home_config_paths(NULL, &xdg_attributes_file, "attributes");
-               git_attributes_file = xdg_attributes_file;
-       }
+       if (!git_attributes_file)
+               git_attributes_file = xdg_config_home("attributes");
        if (git_attributes_file) {
                elem = read_attr_from_file(git_attributes_file, 1);
                if (elem) {
index 3390933d68b2dd6c7296a7d2103d009fa27f2ca8..4bd98b799e7e1a46beb09249ab2299f4d2b335b8 100644 (file)
@@ -208,7 +208,8 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
        if (run_diff_files(&rev, 0))
                die(_("Could not write patch"));
 
-       launch_editor(file, NULL, NULL);
+       if (launch_editor(file, NULL, NULL))
+               die(_("editing patch failed"));
 
        if (stat(file, &st))
                die_errno(_("Could not stat '%s'"), file);
index 06484c2e0e23237bff711bbe3cda6e1382004ef3..8d70623cb8714a87650f2425daf4ee7aaf6307a8 100644 (file)
@@ -2348,6 +2348,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
                if (strbuf_read(&buf, 0, 0) < 0)
                        die_errno("failed to read from stdin");
        }
+       convert_to_git(path, buf.buf, buf.len, &buf, 0);
        origin->file.ptr = buf.buf;
        origin->file.size = buf.len;
        pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1);
index 53a2e5af35ebfc37b378442a238f1894e5bec962..13030ee1d15d59b049134cefe5b46e48e1253109 100644 (file)
@@ -906,6 +906,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
        remote = remote_get(option_origin);
        transport = transport_get(remote, remote->url[0]);
+       transport_set_verbosity(transport, option_verbosity, option_progress);
+
        path = get_repo_path(remote->url[0], &is_bundle);
        is_local = option_local != 0 && path && !is_bundle;
        if (is_local) {
@@ -932,8 +934,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (option_single_branch)
                transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
 
-       transport_set_verbosity(transport, option_verbosity, option_progress);
-
        if (option_upload_pack)
                transport_set_option(transport, TRANS_OPT_UPLOADPACK,
                                     option_upload_pack);
index da79ac4bc7a7247017e2f952b35642a1976c8101..c2ebea4ed31d13cfe48603042edfb0ca63f5de6e 100644 (file)
@@ -1398,12 +1398,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 
 static const char *implicit_ident_advice(void)
 {
-       char *user_config = NULL;
-       char *xdg_config = NULL;
-       int config_exists;
+       char *user_config = expand_user_path("~/.gitconfig");
+       char *xdg_config = xdg_config_home("config");
+       int config_exists = file_exists(user_config) || file_exists(xdg_config);
 
-       home_config_paths(&user_config, &xdg_config, "config");
-       config_exists = file_exists(user_config) || file_exists(xdg_config);
        free(user_config);
        free(xdg_config);
 
index d32c5327e53e14a7af72e4ec91efc1b42a414659..a58f99c2d78f8e06a2e9df7af155af7aa8eaf4c8 100644 (file)
@@ -455,9 +455,9 @@ static char *default_user_config(void)
        struct strbuf buf = STRBUF_INIT;
        strbuf_addf(&buf,
                    _("# This is Git's per-user configuration file.\n"
-                     "[core]\n"
+                     "[user]\n"
                      "# Please adapt and uncomment the following lines:\n"
-                     "#        user = %s\n"
+                     "#        name = %s\n"
                      "#        email = %s\n"),
                    ident_default_name(),
                    ident_default_email());
@@ -488,10 +488,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
        }
 
        if (use_global_config) {
-               char *user_config = NULL;
-               char *xdg_config = NULL;
-
-               home_config_paths(&user_config, &xdg_config, "config");
+               char *user_config = expand_user_path("~/.gitconfig");
+               char *xdg_config = xdg_config_home("config");
 
                if (!user_config)
                        /*
index 1d962dc569eaafc8429c4cec815922f382c576b9..05f4c263112ae94978e4961988e9a4805fde2427 100644 (file)
@@ -223,16 +223,14 @@ static void add_branch_desc(struct strbuf *out, const char *name)
 
 #define util_as_integral(elem) ((intptr_t)((elem)->util))
 
-static void record_person(int which, struct string_list *people,
-                         struct commit *commit)
+static void record_person_from_buf(int which, struct string_list *people,
+                                  const char *buffer)
 {
-       const char *buffer;
        char *name_buf, *name, *name_end;
        struct string_list_item *elem;
        const char *field;
 
        field = (which == 'a') ? "\nauthor " : "\ncommitter ";
-       buffer = get_commit_buffer(commit, NULL);
        name = strstr(buffer, field);
        if (!name)
                return;
@@ -245,7 +243,6 @@ static void record_person(int which, struct string_list *people,
        if (name_end < name)
                return;
        name_buf = xmemdupz(name, name_end - name + 1);
-       unuse_commit_buffer(commit, buffer);
 
        elem = string_list_lookup(people, name_buf);
        if (!elem) {
@@ -256,6 +253,15 @@ static void record_person(int which, struct string_list *people,
        free(name_buf);
 }
 
+
+static void record_person(int which, struct string_list *people,
+                         struct commit *commit)
+{
+       const char *buffer = get_commit_buffer(commit, NULL);
+       record_person_from_buf(which, people, buffer);
+       unuse_commit_buffer(commit, buffer);
+}
+
 static int cmp_string_list_util_as_integral(const void *a_, const void *b_)
 {
        const struct string_list_item *a = a_, *b = b_;
index 207b90c7b13e103b29305b68aa99d1b9b09342e7..07fef3cc6b832c257bd3256dc6fff3d4c51760d9 100644 (file)
@@ -22,10 +22,8 @@ static int hash_literally(unsigned char *sha1, int fd, const char *type, unsigne
 
        if (strbuf_read(&buf, fd, 4096) < 0)
                ret = -1;
-       else if (flags & HASH_WRITE_OBJECT)
-               ret = write_sha1_file(buf.buf, buf.len, type, sha1);
        else
-               ret = hash_sha1_file(buf.buf, buf.len, type, sha1);
+               ret = hash_sha1_file_literally(buf.buf, buf.len, type, sha1, flags);
        strbuf_release(&buf);
        return ret;
 }
index 6723d39c3baf3571552a57732e4da16278269edf..ab9f86b8890ed99547144096ff56c3cbef780505 100644 (file)
@@ -182,6 +182,20 @@ static int git_init_db_config(const char *k, const char *v, void *cb)
        return 0;
 }
 
+/*
+ * If the git_dir is not directly inside the working tree, then git will not
+ * find it by default, and we need to set the worktree explicitly.
+ */
+static int needs_work_tree_config(const char *git_dir, const char *work_tree)
+{
+       if (!strcmp(work_tree, "/") && !strcmp(git_dir, "/.git"))
+               return 0;
+       if (skip_prefix(git_dir, work_tree, &git_dir) &&
+           !strcmp(git_dir, "/.git"))
+               return 0;
+       return 1;
+}
+
 static int create_default_files(const char *template_path)
 {
        const char *git_dir = get_git_dir();
@@ -274,10 +288,8 @@ static int create_default_files(const char *template_path)
                /* allow template config file to override the default */
                if (log_all_ref_updates == -1)
                    git_config_set("core.logallrefupdates", "true");
-               if (!starts_with(git_dir, work_tree) ||
-                   strcmp(git_dir + strlen(work_tree), "/.git")) {
+               if (needs_work_tree_config(git_dir, work_tree))
                        git_config_set("core.worktree", work_tree);
-               }
        }
 
        if (!reinit) {
index c3a75166bd10918d21d3df19832880c4373dc8d7..c067107a6a6b8d6f797854d1e3a7cc9ce5da0cdc 100644 (file)
@@ -961,10 +961,8 @@ static int want_object_in_pack(const unsigned char *sha1,
                off_t offset = find_pack_entry_one(sha1, p);
                if (offset) {
                        if (!*found_pack) {
-                               if (!is_pack_valid(p)) {
-                                       warning("packfile %s cannot be accessed", p->pack_name);
+                               if (!is_pack_valid(p))
                                        continue;
-                               }
                                *found_offset = offset;
                                *found_pack = p;
                        }
diff --git a/cache.h b/cache.h
index 3d3244ba647db66c4d3ed7071bcca3de77c97cf8..badf3da3405dab75ecb03b6fa2e885182ba56475 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -816,7 +816,6 @@ enum scld_error safe_create_leading_directories(char *path);
 enum scld_error safe_create_leading_directories_const(const char *path);
 
 int mkdir_in_gitdir(const char *path);
-extern void home_config_paths(char **global, char **xdg, char *file);
 extern char *expand_user_path(const char *path);
 const char *enter_repo(const char *path, int strict);
 static inline int is_absolute_path(const char *path)
@@ -836,6 +835,13 @@ char *strip_path_suffix(const char *path, const char *suffix);
 int daemon_avoid_alias(const char *path);
 extern int is_ntfs_dotgit(const char *name);
 
+/**
+ * Return a newly allocated string with the evaluation of
+ * "$XDG_CONFIG_HOME/git/$filename" if $XDG_CONFIG_HOME is non-empty, otherwise
+ * "$HOME/.config/git/$filename". Return NULL upon error.
+ */
+extern char *xdg_config_home(const char *filename);
+
 /* object replacement */
 #define LOOKUP_REPLACE_OBJECT 1
 extern void *read_sha1_file_extended(const unsigned char *sha1, enum object_type *type, unsigned long *size, unsigned flag);
@@ -874,6 +880,7 @@ static inline const unsigned char *lookup_replace_object_extended(const unsigned
 extern int sha1_object_info(const unsigned char *, unsigned long *);
 extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
 extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
+extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags);
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 extern int git_open_noatime(const char *name);
@@ -1174,6 +1181,7 @@ extern struct packed_git {
        int pack_fd;
        unsigned pack_local:1,
                 pack_keep:1,
+                freshened:1,
                 do_not_close:1;
        unsigned char sha1[20];
        /* something like ".git/objects/pack/xxxxx.pack" */
@@ -1289,14 +1297,16 @@ int for_each_loose_file_in_objdir_buf(struct strbuf *path,
 
 /*
  * Iterate over loose and packed objects in both the local
- * repository and any alternates repositories.
+ * repository and any alternates repositories (unless the
+ * LOCAL_ONLY flag is set).
  */
+#define FOR_EACH_OBJECT_LOCAL_ONLY 0x1
 typedef int each_packed_object_fn(const unsigned char *sha1,
                                  struct packed_git *pack,
                                  uint32_t pos,
                                  void *data);
-extern int for_each_loose_object(each_loose_object_fn, void *);
-extern int for_each_packed_object(each_packed_object_fn, void *);
+extern int for_each_loose_object(each_loose_object_fn, void *, unsigned flags);
+extern int for_each_packed_object(each_packed_object_fn, void *, unsigned flags);
 
 struct object_info {
        /* Request */
index 66c0a51bce529e4c027f11017697a62dd737b3bd..286907b97e5a378e2be9677ee5b6a4fe7f6169af 100644 (file)
--- a/config.c
+++ b/config.c
@@ -12,6 +12,7 @@
 #include "quote.h"
 #include "hashmap.h"
 #include "string-list.h"
+#include "utf8.h"
 
 struct config_source {
        struct config_source *prev;
@@ -417,8 +418,7 @@ static int git_parse_source(config_fn_t fn, void *data)
        struct strbuf *var = &cf->var;
 
        /* U+FEFF Byte Order Mark in UTF8 */
-       static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
-       const unsigned char *bomptr = utf8_bom;
+       const char *bomptr = utf8_bom;
 
        for (;;) {
                int c = get_next_char();
@@ -426,7 +426,7 @@ static int git_parse_source(config_fn_t fn, void *data)
                        /* We are at the file beginning; skip UTF8-encoded BOM
                         * if present. Sane editors won't put this in on their
                         * own, but e.g. Windows Notepad will do it happily. */
-                       if ((unsigned char) c == *bomptr) {
+                       if (c == (*bomptr & 0377)) {
                                bomptr++;
                                continue;
                        } else {
@@ -1185,10 +1185,8 @@ int git_config_system(void)
 int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 {
        int ret = 0, found = 0;
-       char *xdg_config = NULL;
-       char *user_config = NULL;
-
-       home_config_paths(&user_config, &xdg_config, "config");
+       char *xdg_config = xdg_config_home("config");
+       char *user_config = expand_user_path("~/.gitconfig");
 
        if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) {
                ret += git_config_from_file(fn, git_etc_gitconfig(),
index 391d21192f8d9593ce9b194488019eae3ec7d8d3..c0144d859ae4275860df464f73a688c649d092fe 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -724,7 +724,7 @@ struct child_process *git_connect(int fd[2], const char *url,
                conn->in = conn->out = -1;
                if (protocol == PROTO_SSH) {
                        const char *ssh;
-                       int putty;
+                       int putty, tortoiseplink = 0;
                        char *ssh_host = hostandport;
                        const char *port = NULL;
                        get_host_and_port(&ssh_host, &port);
@@ -743,28 +743,40 @@ struct child_process *git_connect(int fd[2], const char *url,
                                free(path);
                                free(conn);
                                return NULL;
+                       }
+
+                       ssh = getenv("GIT_SSH_COMMAND");
+                       if (ssh) {
+                               conn->use_shell = 1;
+                               putty = 0;
                        } else {
-                               ssh = getenv("GIT_SSH_COMMAND");
-                               if (ssh) {
-                                       conn->use_shell = 1;
-                                       putty = 0;
-                               } else {
-                                       ssh = getenv("GIT_SSH");
-                                       if (!ssh)
-                                               ssh = "ssh";
-                                       putty = !!strcasestr(ssh, "plink");
-                               }
-
-                               argv_array_push(&conn->args, ssh);
-                               if (putty && !strcasestr(ssh, "tortoiseplink"))
-                                       argv_array_push(&conn->args, "-batch");
-                               if (port) {
-                                       /* P is for PuTTY, p is for OpenSSH */
-                                       argv_array_push(&conn->args, putty ? "-P" : "-p");
-                                       argv_array_push(&conn->args, port);
-                               }
-                               argv_array_push(&conn->args, ssh_host);
+                               const char *base;
+                               char *ssh_dup;
+
+                               ssh = getenv("GIT_SSH");
+                               if (!ssh)
+                                       ssh = "ssh";
+
+                               ssh_dup = xstrdup(ssh);
+                               base = basename(ssh_dup);
+
+                               tortoiseplink = !strcasecmp(base, "tortoiseplink") ||
+                                       !strcasecmp(base, "tortoiseplink.exe");
+                               putty = !strcasecmp(base, "plink") ||
+                                       !strcasecmp(base, "plink.exe") || tortoiseplink;
+
+                               free(ssh_dup);
+                       }
+
+                       argv_array_push(&conn->args, ssh);
+                       if (tortoiseplink)
+                               argv_array_push(&conn->args, "-batch");
+                       if (port) {
+                               /* P is for PuTTY, p is for OpenSSH */
+                               argv_array_push(&conn->args, putty ? "-P" : "-p");
+                               argv_array_push(&conn->args, port);
                        }
+                       argv_array_push(&conn->args, ssh_host);
                } else {
                        /* remove repo-local variables from the environment */
                        conn->env = local_repo_env;
index 5944c824abe9aa4dee8b63b0e91a047125722b22..eae9dce590e0000eb93e441106ef6538c07e86cc 100644 (file)
@@ -1448,7 +1448,7 @@ _git_log ()
                return
                ;;
        --decorate=*)
-               __gitcomp "long short" "" "${cur##--decorate=}"
+               __gitcomp "full short no" "" "${cur##--decorate=}"
                return
                ;;
        --*)
index 925d3f40247d7f41d709b4e6b3eaabaf646572b6..f6925096ffa7e036b4e63f65ce372c596c9e1b70 100644 (file)
@@ -6,7 +6,7 @@
 
 static struct lock_file credential_lock;
 
-static void parse_credential_file(const char *fn,
+static int parse_credential_file(const char *fn,
                                  struct credential *c,
                                  void (*match_cb)(struct credential *),
                                  void (*other_cb)(struct strbuf *))
@@ -14,18 +14,20 @@ static void parse_credential_file(const char *fn,
        FILE *fh;
        struct strbuf line = STRBUF_INIT;
        struct credential entry = CREDENTIAL_INIT;
+       int found_credential = 0;
 
        fh = fopen(fn, "r");
        if (!fh) {
-               if (errno != ENOENT)
+               if (errno != ENOENT && errno != EACCES)
                        die_errno("unable to open %s", fn);
-               return;
+               return found_credential;
        }
 
        while (strbuf_getline(&line, fh, '\n') != EOF) {
                credential_from_url(&entry, line.buf);
                if (entry.username && entry.password &&
                    credential_match(c, &entry)) {
+                       found_credential = 1;
                        if (match_cb) {
                                match_cb(&entry);
                                break;
@@ -38,6 +40,7 @@ static void parse_credential_file(const char *fn,
        credential_clear(&entry);
        strbuf_release(&line);
        fclose(fh);
+       return found_credential;
 }
 
 static void print_entry(struct credential *c)
@@ -64,21 +67,10 @@ static void rewrite_credential_file(const char *fn, struct credential *c,
                die_errno("unable to commit credential store");
 }
 
-static void store_credential(const char *fn, struct credential *c)
+static void store_credential_file(const char *fn, struct credential *c)
 {
        struct strbuf buf = STRBUF_INIT;
 
-       /*
-        * Sanity check that what we are storing is actually sensible.
-        * In particular, we can't make a URL without a protocol field.
-        * Without either a host or pathname (depending on the scheme),
-        * we have no primary key. And without a username and password,
-        * we are not actually storing a credential.
-        */
-       if (!c->protocol || !(c->host || c->path) ||
-           !c->username || !c->password)
-               return;
-
        strbuf_addf(&buf, "%s://", c->protocol);
        strbuf_addstr_urlencode(&buf, c->username, 1);
        strbuf_addch(&buf, ':');
@@ -95,8 +87,37 @@ static void store_credential(const char *fn, struct credential *c)
        strbuf_release(&buf);
 }
 
-static void remove_credential(const char *fn, struct credential *c)
+static void store_credential(const struct string_list *fns, struct credential *c)
+{
+       struct string_list_item *fn;
+
+       /*
+        * Sanity check that what we are storing is actually sensible.
+        * In particular, we can't make a URL without a protocol field.
+        * Without either a host or pathname (depending on the scheme),
+        * we have no primary key. And without a username and password,
+        * we are not actually storing a credential.
+        */
+       if (!c->protocol || !(c->host || c->path) || !c->username || !c->password)
+               return;
+
+       for_each_string_list_item(fn, fns)
+               if (!access(fn->string, F_OK)) {
+                       store_credential_file(fn->string, c);
+                       return;
+               }
+       /*
+        * Write credential to the filename specified by fns->items[0], thus
+        * creating it
+        */
+       if (fns->nr)
+               store_credential_file(fns->items[0].string, c);
+}
+
+static void remove_credential(const struct string_list *fns, struct credential *c)
 {
+       struct string_list_item *fn;
+
        /*
         * Sanity check that we actually have something to match
         * against. The input we get is a restrictive pattern,
@@ -105,14 +126,20 @@ static void remove_credential(const char *fn, struct credential *c)
         * to empty input. So explicitly disallow it, and require that the
         * pattern have some actual content to match.
         */
-       if (c->protocol || c->host || c->path || c->username)
-               rewrite_credential_file(fn, c, NULL);
+       if (!c->protocol && !c->host && !c->path && !c->username)
+               return;
+       for_each_string_list_item(fn, fns)
+               if (!access(fn->string, F_OK))
+                       rewrite_credential_file(fn->string, c, NULL);
 }
 
-static int lookup_credential(const char *fn, struct credential *c)
+static void lookup_credential(const struct string_list *fns, struct credential *c)
 {
-       parse_credential_file(fn, c, print_entry, NULL);
-       return c->username && c->password;
+       struct string_list_item *fn;
+
+       for_each_string_list_item(fn, fns)
+               if (parse_credential_file(fn->string, c, print_entry, NULL))
+                       return; /* Found credential */
 }
 
 int main(int argc, char **argv)
@@ -123,6 +150,7 @@ int main(int argc, char **argv)
        };
        const char *op;
        struct credential c = CREDENTIAL_INIT;
+       struct string_list fns = STRING_LIST_INIT_DUP;
        char *file = NULL;
        struct option options[] = {
                OPT_STRING(0, "file", &file, "path",
@@ -137,22 +165,30 @@ int main(int argc, char **argv)
                usage_with_options(usage, options);
        op = argv[0];
 
-       if (!file)
-               file = expand_user_path("~/.git-credentials");
-       if (!file)
+       if (file) {
+               string_list_append(&fns, file);
+       } else {
+               if ((file = expand_user_path("~/.git-credentials")))
+                       string_list_append_nodup(&fns, file);
+               file = xdg_config_home("credentials");
+               if (file)
+                       string_list_append_nodup(&fns, file);
+       }
+       if (!fns.nr)
                die("unable to set up default path; use --file");
 
        if (credential_read(&c, stdin) < 0)
                die("unable to read credential");
 
        if (!strcmp(op, "get"))
-               lookup_credential(file, &c);
+               lookup_credential(&fns, &c);
        else if (!strcmp(op, "erase"))
-               remove_credential(file, &c);
+               remove_credential(&fns, &c);
        else if (!strcmp(op, "store"))
-               store_credential(file, &c);
+               store_credential(&fns, &c);
        else
                ; /* Ignore unknown operation. */
 
+       string_list_clear(&fns, 0);
        return 0;
 }
index 9ee21877cd952a7aa47c793d877174d858488794..4be10914e63bda68272c25e52cb199346cf9ec3c 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -633,7 +633,7 @@ static void lookup_hostname(struct hostinfo *hi)
                char **ap;
                static char addrbuf[HOST_NAME_MAX + 1];
 
-               hent = gethostbyname(hostname.buf);
+               hent = gethostbyname(hi->hostname.buf);
                if (hent) {
                        ap = hent->h_addr_list;
                        memset(&sa, 0, sizeof sa);
diff --git a/date.c b/date.c
index 3eba2dfe8841cee28850e2f1f4177e1e32f1c8a8..733d1b29b19fc7dda04d599eb6a311e809212775 100644 (file)
--- a/date.c
+++ b/date.c
@@ -704,10 +704,17 @@ int parse_date_basic(const char *date, unsigned long *timestamp, int *offset)
                date += match;
        }
 
-       /* mktime uses local timezone */
+       /* do not use mktime(), which uses local timezone, here */
        *timestamp = tm_to_time_t(&tm);
+       if (*timestamp == -1)
+               return -1;
+
        if (*offset == -1) {
-               time_t temp_time = mktime(&tm);
+               time_t temp_time;
+
+               /* gmtime_r() in match_digit() may have clobbered it */
+               tm.tm_isdst = -1;
+               temp_time = mktime(&tm);
                if ((time_t)*timestamp > temp_time) {
                        *offset = ((time_t)*timestamp - temp_time) / 60;
                } else {
@@ -715,9 +722,6 @@ int parse_date_basic(const char *date, unsigned long *timestamp, int *offset)
                }
        }
 
-       if (*timestamp == -1)
-               return -1;
-
        if (!tm_gmt)
                *timestamp -= *offset * 60;
        return 0; /* success */
index 265709ba8c51a4390fad11c7d1aa23551a4fd266..0320605a84178ffb2ab9384b8ee9fbac7df16a73 100644 (file)
@@ -97,8 +97,27 @@ static int queue_diff(struct diff_options *o,
        if (get_mode(name1, &mode1) || get_mode(name2, &mode2))
                return -1;
 
-       if (mode1 && mode2 && S_ISDIR(mode1) != S_ISDIR(mode2))
-               return error("file/directory conflict: %s, %s", name1, name2);
+       if (mode1 && mode2 && S_ISDIR(mode1) != S_ISDIR(mode2)) {
+               struct diff_filespec *d1, *d2;
+
+               if (S_ISDIR(mode1)) {
+                       /* 2 is file that is created */
+                       d1 = noindex_filespec(NULL, 0);
+                       d2 = noindex_filespec(name2, mode2);
+                       name2 = NULL;
+                       mode2 = 0;
+               } else {
+                       /* 1 is file that is deleted */
+                       d1 = noindex_filespec(name1, mode1);
+                       d2 = noindex_filespec(NULL, 0);
+                       name1 = NULL;
+                       mode1 = 0;
+               }
+               /* emit that file */
+               diff_queue(&diff_queued_diff, d1, d2);
+
+               /* and then let the entire directory be created or deleted */
+       }
 
        if (S_ISDIR(mode1) || S_ISDIR(mode2)) {
                struct strbuf buffer1 = STRBUF_INIT;
@@ -182,12 +201,50 @@ static int queue_diff(struct diff_options *o,
        }
 }
 
+/* append basename of F to D */
+static void append_basename(struct strbuf *path, const char *dir, const char *file)
+{
+       const char *tail = strrchr(file, '/');
+
+       strbuf_addstr(path, dir);
+       while (path->len && path->buf[path->len - 1] == '/')
+               path->len--;
+       strbuf_addch(path, '/');
+       strbuf_addstr(path, tail ? tail + 1 : file);
+}
+
+/*
+ * DWIM "diff D F" into "diff D/F F" and "diff F D" into "diff F D/F"
+ * Note that we append the basename of F to D/, so "diff a/b/file D"
+ * becomes "diff a/b/file D/file", not "diff a/b/file D/a/b/file".
+ */
+static void fixup_paths(const char **path, struct strbuf *replacement)
+{
+       unsigned int isdir0, isdir1;
+
+       if (path[0] == file_from_standard_input ||
+           path[1] == file_from_standard_input)
+               return;
+       isdir0 = is_directory(path[0]);
+       isdir1 = is_directory(path[1]);
+       if (isdir0 == isdir1)
+               return;
+       if (isdir0) {
+               append_basename(replacement, path[0], path[1]);
+               path[0] = replacement->buf;
+       } else {
+               append_basename(replacement, path[1], path[0]);
+               path[1] = replacement->buf;
+       }
+}
+
 void diff_no_index(struct rev_info *revs,
                   int argc, const char **argv,
                   const char *prefix)
 {
        int i, prefixlen;
        const char *paths[2];
+       struct strbuf replacement = STRBUF_INIT;
 
        diff_setup(&revs->diffopt);
        for (i = 1; i < argc - 2; ) {
@@ -217,6 +274,9 @@ void diff_no_index(struct rev_info *revs,
                        p = xstrdup(prefix_filename(prefix, prefixlen, p));
                paths[i] = p;
        }
+
+       fixup_paths(paths, &replacement);
+
        revs->diffopt.skip_stat_unmatch = 1;
        if (!revs->diffopt.output_format)
                revs->diffopt.output_format = DIFF_FORMAT_PATCH;
@@ -235,6 +295,8 @@ void diff_no_index(struct rev_info *revs,
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
 
+       strbuf_release(&replacement);
+
        /*
         * The return code for --no-index imitates diff(1):
         * 0 = no changes, 1 = changes, else error
diff --git a/dir.c b/dir.c
index 0943a81964ddb7b5b1d83c9c8eafe2b3b2b9da09..4183acc082671f135fe64cbcaa66ed3b17bc6364 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -12,6 +12,7 @@
 #include "refs.h"
 #include "wildmatch.h"
 #include "pathspec.h"
+#include "utf8.h"
 
 struct path_simplify {
        int len;
@@ -617,7 +618,12 @@ int add_excludes_from_file_to_list(const char *fname,
        }
 
        el->filebuf = buf;
+
+       if (skip_utf8_bom(&buf, size))
+               size -= buf - el->filebuf;
+
        entry = buf;
+
        for (i = 0; i < size; i++) {
                if (buf[i] == '\n') {
                        if (entry != buf + i && entry[0] != '#') {
@@ -1665,18 +1671,19 @@ int remove_dir_recursively(struct strbuf *path, int flag)
 void setup_standard_excludes(struct dir_struct *dir)
 {
        const char *path;
-       char *xdg_path;
 
        dir->exclude_per_dir = ".gitignore";
+
+       /* core.excludefile defaulting to $XDG_HOME/git/ignore */
+       if (!excludes_file)
+               excludes_file = xdg_config_home("ignore");
+       if (excludes_file && !access_or_warn(excludes_file, R_OK, 0))
+               add_excludes_from_file(dir, excludes_file);
+
+       /* per repository user preference */
        path = git_path("info/exclude");
-       if (!excludes_file) {
-               home_config_paths(NULL, &xdg_path, "ignore");
-               excludes_file = xdg_path;
-       }
        if (!access_or_warn(path, R_OK, 0))
                add_excludes_from_file(dir, path);
-       if (excludes_file && !access_or_warn(excludes_file, R_OK, 0))
-               add_excludes_from_file(dir, excludes_file);
 }
 
 int remove_path(const char *name)
index 8ab37b5f74f360abf8ff4d689b4ff4f3d29cf785..e85f0fd8d897f4823a7c765d6b6ef929a15ca679 100644 (file)
@@ -96,7 +96,6 @@ void setup_path(void)
        struct strbuf new_path = STRBUF_INIT;
 
        add_path(&new_path, git_exec_path());
-       add_path(&new_path, argv0_path);
 
        if (old_path)
                strbuf_addstr(&new_path, old_path);
index e6e99f5bb5102d394a25f156dbcba956f246054b..5b3f63d8bbc65e80d1f4278e1ec6e27df604dc9a 100755 (executable)
@@ -346,7 +346,15 @@ while read commit parents; do
                                die "parent filter failed: $filter_parent"
        fi
 
-       sed -e '1,/^$/d' <../commit | \
+       {
+               while read -r header_line && test -n "$header_line"
+               do
+                       # skip header lines...
+                       :;
+               done
+               # and output the actual commit message
+               cat
+       } <../commit |
                eval "$filter_msg" > ../message ||
                        die "msg filter failed: $filter_msg"
        workdir=$workdir @SHELL_PATH@ -c "$filter_commit" "git commit-tree" \
index f7deeb096e24f4de69bdfe08f0aa35ebf170577a..00080572529ae4c7e0c86c0aa2ba7cb83146dd2a 100644 (file)
@@ -132,6 +132,16 @@ mark_action_done () {
        fi
 }
 
+# Put the last action marked done at the beginning of the todo list
+# again. If there has not been an action marked done yet, leave the list of
+# items on the todo list unchanged.
+reschedule_last_action () {
+       tail -n 1 "$done" | cat - "$todo" >"$todo".new
+       sed -e \$d <"$done" >"$done".new
+       mv -f "$todo".new "$todo"
+       mv -f "$done".new "$done"
+}
+
 append_todo_help () {
        git stripspace --comment-lines >>"$todo" <<\EOF
 
@@ -252,6 +262,12 @@ pick_one () {
        output eval git cherry-pick \
                        ${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
                        "$strategy_args" $empty_args $ff "$@"
+
+       # If cherry-pick dies it leaves the to-be-picked commit unrecorded. Reschedule
+       # previous task so this commit is not lost.
+       ret=$?
+       case "$ret" in [01]) ;; *) reschedule_last_action ;; esac
+       return $ret
 }
 
 pick_one_preserving_merges () {
index 55da9db818665f39ed205d3c77352987e3ca9963..90854e38cb9ceb6985a5d7bd9faae722ed92361f 100755 (executable)
@@ -582,7 +582,7 @@ then
                # Lazily switch to the target branch if needed...
                test -z "$switch_to" ||
                GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to" \
-                       git checkout "$switch_to" --
+                       git checkout -q "$switch_to" --
                say "$(eval_gettext "Current branch \$branch_name is up to date.")"
                finish_rebase
                exit 0
index d4cf818be9488f1b94fdf4766a8f73db6bfd1029..cc28368b01fc218f32ae98c21cce4d7f84c68eb0 100755 (executable)
@@ -442,6 +442,8 @@ apply_stash () {
        assert_stash_like "$@"
 
        git update-index -q --refresh || die "$(gettext "unable to refresh index")"
+       git diff-index --cached --quiet --ignore-submodules HEAD -- ||
+               die "$(gettext "Cannot apply stash: Your index contains uncommitted changes.")"
 
        # current index state
        c_tree=$(git write-tree) ||
index a490efea07519edb006e515c8a0fdf60241e546d..a5ed9e3642271afd69a0434876515b97aaceb657 100644 (file)
@@ -575,7 +575,7 @@ parse_lines(struct commit *commit, const char *prefix, struct string_list *args)
 
                name_part = skip_range_arg(item->string);
                if (!name_part || *name_part != ':' || !name_part[1])
-                       die("-L argument '%s' not of the form start,end:file",
+                       die("-L argument not 'start,end:file' or ':funcname:file': %s",
                            item->string);
                range_part = xstrndup(item->string, name_part - item->string);
                name_part++;
@@ -1099,6 +1099,7 @@ static int process_all_files(struct line_log_data **range_out,
                        rg->pair = diff_filepair_dup(queue->queue[i]);
                        memcpy(&rg->diff, pairdiff, sizeof(struct diff_ranges));
                }
+               free(pairdiff);
        }
 
        return changed;
index 2c1ed0fa90170438e00a2eadbf74e15d89531613..c931615d9267fd774d886e5414e051a25895ffdf 100644 (file)
@@ -13,6 +13,8 @@
 #include "line-log.h"
 
 static struct decoration name_decoration = { "object names" };
+static int decoration_loaded;
+static int decoration_flags;
 
 static char decoration_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_RESET,
@@ -92,6 +94,8 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in
        struct object *obj;
        enum decoration_type type = DECORATION_NONE;
 
+       assert(cb_data == NULL);
+
        if (starts_with(refname, "refs/replace/")) {
                unsigned char original_sha1[20];
                if (!check_replace_refs)
@@ -121,8 +125,6 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in
        else if (!strcmp(refname, "HEAD"))
                type = DECORATION_REF_HEAD;
 
-       if (!cb_data || *(int *)cb_data == DECORATE_SHORT_REFS)
-               refname = prettify_refname(refname);
        add_name_decoration(type, refname, obj);
        while (obj->type == OBJ_TAG) {
                obj = ((struct tag *)obj)->tagged;
@@ -146,11 +148,11 @@ static int add_graft_decoration(const struct commit_graft *graft, void *cb_data)
 
 void load_ref_decorations(int flags)
 {
-       static int loaded;
-       if (!loaded) {
-               loaded = 1;
-               for_each_ref(add_ref_decoration, &flags);
-               head_ref(add_ref_decoration, &flags);
+       if (!decoration_loaded) {
+               decoration_loaded = 1;
+               decoration_flags = flags;
+               for_each_ref(add_ref_decoration, NULL);
+               head_ref(add_ref_decoration, NULL);
                for_each_commit_graft(add_graft_decoration, NULL);
        }
 }
@@ -196,7 +198,8 @@ static const struct name_decoration *current_pointed_by_HEAD(const struct name_d
        branch_name = resolve_ref_unsafe("HEAD", 0, unused, &rru_flags);
        if (!(rru_flags & REF_ISSYMREF))
                return NULL;
-       if (!skip_prefix(branch_name, "refs/heads/", &branch_name))
+
+       if (!starts_with(branch_name, "refs/"))
                return NULL;
 
        /* OK, do we have that ref in the list? */
@@ -209,6 +212,14 @@ static const struct name_decoration *current_pointed_by_HEAD(const struct name_d
        return NULL;
 }
 
+static void show_name(struct strbuf *sb, const struct name_decoration *decoration)
+{
+       if (decoration_flags == DECORATE_SHORT_REFS)
+               strbuf_addstr(sb, prettify_refname(decoration->name));
+       else
+               strbuf_addstr(sb, decoration->name);
+}
+
 /*
  * The caller makes sure there is no funny color before calling.
  * format_decorations_extended makes sure the same after return.
@@ -246,7 +257,7 @@ void format_decorations_extended(struct strbuf *sb,
                        if (decoration->type == DECORATION_REF_TAG)
                                strbuf_addstr(sb, "tag: ");
 
-                       strbuf_addstr(sb, decoration->name);
+                       show_name(sb, decoration);
 
                        if (current_and_HEAD &&
                            decoration->type == DECORATION_REF_HEAD) {
@@ -255,7 +266,7 @@ void format_decorations_extended(struct strbuf *sb,
                                strbuf_addstr(sb, " -> ");
                                strbuf_addstr(sb, color_reset);
                                strbuf_addstr(sb, decorate_get_color(use_color, current_and_HEAD->type));
-                               strbuf_addstr(sb, current_and_HEAD->name);
+                               show_name(sb, current_and_HEAD);
                        }
                        strbuf_addstr(sb, color_reset);
 
index 23d6c9671904cfe273fc16de8a4d2db56b91c3b4..980ac5fcdfa7654667c9902aa56407f1ce7053ba 100644 (file)
--- a/object.c
+++ b/object.c
@@ -41,7 +41,8 @@ int type_from_string_gently(const char *str, ssize_t len, int gentle)
                len = strlen(str);
 
        for (i = 1; i < ARRAY_SIZE(object_type_strings); i++)
-               if (!strncmp(str, object_type_strings[i], len))
+               if (!strncmp(str, object_type_strings[i], len) &&
+                   object_type_strings[i][len] == '\0')
                        return i;
 
        if (gentle)
index 365f9d92ed8b57c1e766a126b6e6571009eedde5..62a98cc119e3bf5a7b71e6b47143e06653ee9d6e 100644 (file)
@@ -986,6 +986,8 @@ void test_bitmap_walk(struct rev_info *revs)
                fprintf(stderr, "OK!\n");
        else
                fprintf(stderr, "Mismatch!\n");
+
+       free(result);
 }
 
 static int rebuild_bitmap(uint32_t *reposition,
diff --git a/path.c b/path.c
index 595da81ca67096bae9592d9455bdade442b92628..6b537ccfff452b7fbbe41446c001c8fda0679bea 100644 (file)
--- a/path.c
+++ b/path.c
@@ -130,34 +130,6 @@ char *git_path(const char *fmt, ...)
        return ret;
 }
 
-void home_config_paths(char **global, char **xdg, char *file)
-{
-       char *xdg_home = getenv("XDG_CONFIG_HOME");
-       char *home = getenv("HOME");
-       char *to_free = NULL;
-
-       if (!home) {
-               if (global)
-                       *global = NULL;
-       } else {
-               if (!xdg_home) {
-                       to_free = mkpathdup("%s/.config", home);
-                       xdg_home = to_free;
-               }
-               if (global)
-                       *global = mkpathdup("%s/.gitconfig", home);
-       }
-
-       if (xdg) {
-               if (!xdg_home)
-                       *xdg = NULL;
-               else
-                       *xdg = mkpathdup("%s/git/%s", xdg_home, file);
-       }
-
-       free(to_free);
-}
-
 char *git_path_submodule(const char *path, const char *fmt, ...)
 {
        char *pathname = get_pathname();
@@ -851,3 +823,18 @@ int is_ntfs_dotgit(const char *name)
                        len = -1;
                }
 }
+
+char *xdg_config_home(const char *filename)
+{
+       const char *home, *config_home;
+
+       assert(filename);
+       config_home = getenv("XDG_CONFIG_HOME");
+       if (config_home && *config_home)
+               return mkpathdup("%s/git/%s", config_home, filename);
+
+       home = getenv("HOME");
+       if (home)
+               return mkpathdup("%s/.config/git/%s", home, filename);
+       return NULL;
+}
index a647267ae9cb94d960810f470993ba22cbe3763d..69fa6851da8a37540da66faf96c7bc878fdc53c9 100644 (file)
@@ -142,10 +142,12 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
        data.revs = revs;
        data.timestamp = timestamp;
 
-       r = for_each_loose_object(add_recent_loose, &data);
+       r = for_each_loose_object(add_recent_loose, &data,
+                                 FOR_EACH_OBJECT_LOCAL_ONLY);
        if (r)
                return r;
-       return for_each_packed_object(add_recent_packed, &data);
+       return for_each_packed_object(add_recent_packed, &data,
+                                     FOR_EACH_OBJECT_LOCAL_ONLY);
 }
 
 void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
index 6399a0412cc9005875e282a139200a4f918743f5..7ddbaa083e9e59d3db270b96b47a20e6c99bdaa3 100644 (file)
@@ -345,14 +345,24 @@ static struct commit *handle_commit(struct rev_info *revs,
        die("%s is unknown object", name);
 }
 
-static int everybody_uninteresting(struct commit_list *orig)
+static int everybody_uninteresting(struct commit_list *orig,
+                                  struct commit **interesting_cache)
 {
        struct commit_list *list = orig;
+
+       if (*interesting_cache) {
+               struct commit *commit = *interesting_cache;
+               if (!(commit->object.flags & UNINTERESTING))
+                       return 0;
+       }
+
        while (list) {
                struct commit *commit = list->item;
                list = list->next;
                if (commit->object.flags & UNINTERESTING)
                        continue;
+               if (interesting_cache)
+                       *interesting_cache = commit;
                return 0;
        }
        return 1;
@@ -940,7 +950,8 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
 /* How many extra uninteresting commits we want to see.. */
 #define SLOP 5
 
-static int still_interesting(struct commit_list *src, unsigned long date, int slop)
+static int still_interesting(struct commit_list *src, unsigned long date, int slop,
+                            struct commit **interesting_cache)
 {
        /*
         * No source list at all? We're definitely done..
@@ -959,7 +970,7 @@ static int still_interesting(struct commit_list *src, unsigned long date, int sl
         * Does the source list still have interesting commits in
         * it? Definitely not done..
         */
-       if (!everybody_uninteresting(src))
+       if (!everybody_uninteresting(src, interesting_cache))
                return SLOP;
 
        /* Ok, we're closing in.. */
@@ -1078,6 +1089,7 @@ static int limit_list(struct rev_info *revs)
        struct commit_list *newlist = NULL;
        struct commit_list **p = &newlist;
        struct commit_list *bottom = NULL;
+       struct commit *interesting_cache = NULL;
 
        if (revs->ancestry_path) {
                bottom = collect_bottom_commits(list);
@@ -1094,6 +1106,9 @@ static int limit_list(struct rev_info *revs)
                list = list->next;
                free(entry);
 
+               if (commit == interesting_cache)
+                       interesting_cache = NULL;
+
                if (revs->max_age != -1 && (commit->date < revs->max_age))
                        obj->flags |= UNINTERESTING;
                if (add_parents_to_list(revs, commit, &list, NULL) < 0)
@@ -1102,7 +1117,7 @@ static int limit_list(struct rev_info *revs)
                        mark_parents_uninteresting(commit);
                        if (revs->show_all)
                                p = &commit_list_insert(commit, p)->next;
-                       slop = still_interesting(list, date, slop);
+                       slop = still_interesting(list, date, slop, &interesting_cache);
                        if (slop)
                                continue;
                        /* If showing all, add the whole pending list to the end */
index 88f06bac926008dd40b6a53e0696274438278cfc..001537c9359bf83fdc4d6c59426a25424be2e803 100644 (file)
@@ -2473,10 +2473,8 @@ static int fill_pack_entry(const unsigned char *sha1,
         * answer, as it may have been deleted since the index was
         * loaded!
         */
-       if (!is_pack_valid(p)) {
-               warning("packfile %s cannot be accessed", p->pack_name);
+       if (!is_pack_valid(p))
                return 0;
-       }
        e->offset = offset;
        e->p = p;
        hashcpy(e->sha1, sha1);
@@ -2999,12 +2997,18 @@ static int freshen_loose_object(const unsigned char *sha1)
 static int freshen_packed_object(const unsigned char *sha1)
 {
        struct pack_entry e;
-       return find_pack_entry(sha1, &e) && freshen_file(e.p->pack_name);
+       if (!find_pack_entry(sha1, &e))
+               return 0;
+       if (e.p->freshened)
+               return 1;
+       if (!freshen_file(e.p->pack_name))
+               return 0;
+       e.p->freshened = 1;
+       return 1;
 }
 
-int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
+int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1)
 {
-       unsigned char sha1[20];
        char hdr[32];
        int hdrlen;
 
@@ -3012,13 +3016,32 @@ int write_sha1_file(const void *buf, unsigned long len, const char *type, unsign
         * it out into .git/objects/??/?{38} file.
         */
        write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
-       if (returnsha1)
-               hashcpy(returnsha1, sha1);
-       if (freshen_loose_object(sha1) || freshen_packed_object(sha1))
+       if (freshen_packed_object(sha1) || freshen_loose_object(sha1))
                return 0;
        return write_loose_object(sha1, hdr, hdrlen, buf, len, 0);
 }
 
+int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type,
+                            unsigned char *sha1, unsigned flags)
+{
+       char *header;
+       int hdrlen, status = 0;
+
+       /* type string, SP, %lu of the length plus NUL must fit this */
+       header = xmalloc(strlen(type) + 32);
+       write_sha1_file_prepare(buf, len, type, sha1, header, &hdrlen);
+
+       if (!(flags & HASH_WRITE_OBJECT))
+               goto cleanup;
+       if (freshen_packed_object(sha1) || freshen_loose_object(sha1))
+               goto cleanup;
+       status = write_loose_object(sha1, header, hdrlen, buf, len, 0);
+
+cleanup:
+       free(header);
+       return status;
+}
+
 int force_object_loose(const unsigned char *sha1, time_t mtime)
 {
        void *buf;
@@ -3418,7 +3441,7 @@ static int loose_from_alt_odb(struct alternate_object_database *alt,
        return r;
 }
 
-int for_each_loose_object(each_loose_object_fn cb, void *data)
+int for_each_loose_object(each_loose_object_fn cb, void *data, unsigned flags)
 {
        struct loose_alt_odb_data alt;
        int r;
@@ -3428,6 +3451,9 @@ int for_each_loose_object(each_loose_object_fn cb, void *data)
        if (r)
                return r;
 
+       if (flags & FOR_EACH_OBJECT_LOCAL_ONLY)
+               return 0;
+
        alt.cb = cb;
        alt.data = data;
        return foreach_alt_odb(loose_from_alt_odb, &alt);
@@ -3452,13 +3478,15 @@ static int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn c
        return r;
 }
 
-int for_each_packed_object(each_packed_object_fn cb, void *data)
+int for_each_packed_object(each_packed_object_fn cb, void *data, unsigned flags)
 {
        struct packed_git *p;
        int r = 0;
 
        prepare_packed_git();
        for (p = packed_git; p; p = p->next) {
+               if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
+                       continue;
                r = for_each_object_in_pack(p, cb, data);
                if (r)
                        break;
index 8dc6939b9049baa32aca1200e36378e6e2296e9d..4ef5ed484c12a4509b9bc66f2cc41118f7254431 100755 (executable)
@@ -831,4 +831,14 @@ test_expect_success !MINGW,!CYGWIN 'correct handling of backslashes' '
        test_cmp err.expect err
 '
 
+test_expect_success 'info/exclude trumps core.excludesfile' '
+       echo >>global-excludes usually-ignored &&
+       echo >>.git/info/exclude "!usually-ignored" &&
+       >usually-ignored &&
+       echo "?? usually-ignored" >expect &&
+
+       git status --porcelain usually-ignored >actual &&
+       test_cmp expect actual
+'
+
 test_done
index f61b40c69ba04f6ee364356751c6f21a59552aff..0979df93a1062020e92fa55a780678e20db4f9db 100755 (executable)
@@ -6,4 +6,118 @@ test_description='credential-store tests'
 
 helper_test store
 
+test_expect_success 'when xdg file does not exist, xdg file not created' '
+       test_path_is_missing "$HOME/.config/git/credentials" &&
+       test -s "$HOME/.git-credentials"
+'
+
+test_expect_success 'setup xdg file' '
+       rm -f "$HOME/.git-credentials" &&
+       mkdir -p "$HOME/.config/git" &&
+       >"$HOME/.config/git/credentials"
+'
+
+helper_test store
+
+test_expect_success 'when xdg file exists, home file not created' '
+       test -s "$HOME/.config/git/credentials" &&
+       test_path_is_missing "$HOME/.git-credentials"
+'
+
+test_expect_success 'setup custom xdg file' '
+       rm -f "$HOME/.git-credentials" &&
+       rm -f "$HOME/.config/git/credentials" &&
+       mkdir -p "$HOME/xdg/git" &&
+       >"$HOME/xdg/git/credentials"
+'
+
+XDG_CONFIG_HOME="$HOME/xdg"
+export XDG_CONFIG_HOME
+helper_test store
+unset XDG_CONFIG_HOME
+
+test_expect_success 'if custom xdg file exists, home and xdg files not created' '
+       test_when_finished "rm -f $HOME/xdg/git/credentials" &&
+       test -s "$HOME/xdg/git/credentials" &&
+       test_path_is_missing "$HOME/.git-credentials" &&
+       test_path_is_missing "$HOME/.config/git/credentials"
+'
+
+test_expect_success 'get: use home file if both home and xdg files have matches' '
+       echo "https://home-user:home-pass@example.com" >"$HOME/.git-credentials" &&
+       mkdir -p "$HOME/.config/git" &&
+       echo "https://xdg-user:xdg-pass@example.com" >"$HOME/.config/git/credentials" &&
+       check fill store <<-\EOF
+       protocol=https
+       host=example.com
+       --
+       protocol=https
+       host=example.com
+       username=home-user
+       password=home-pass
+       --
+       EOF
+'
+
+test_expect_success 'get: use xdg file if home file has no matches' '
+       >"$HOME/.git-credentials" &&
+       mkdir -p "$HOME/.config/git" &&
+       echo "https://xdg-user:xdg-pass@example.com" >"$HOME/.config/git/credentials" &&
+       check fill store <<-\EOF
+       protocol=https
+       host=example.com
+       --
+       protocol=https
+       host=example.com
+       username=xdg-user
+       password=xdg-pass
+       --
+       EOF
+'
+
+test_expect_success POSIXPERM 'get: use xdg file if home file is unreadable' '
+       echo "https://home-user:home-pass@example.com" >"$HOME/.git-credentials" &&
+       chmod -r "$HOME/.git-credentials" &&
+       mkdir -p "$HOME/.config/git" &&
+       echo "https://xdg-user:xdg-pass@example.com" >"$HOME/.config/git/credentials" &&
+       check fill store <<-\EOF
+       protocol=https
+       host=example.com
+       --
+       protocol=https
+       host=example.com
+       username=xdg-user
+       password=xdg-pass
+       --
+       EOF
+'
+
+test_expect_success 'store: if both xdg and home files exist, only store in home file' '
+       >"$HOME/.git-credentials" &&
+       mkdir -p "$HOME/.config/git" &&
+       >"$HOME/.config/git/credentials" &&
+       check approve store <<-\EOF &&
+       protocol=https
+       host=example.com
+       username=store-user
+       password=store-pass
+       EOF
+       echo "https://store-user:store-pass@example.com" >expected &&
+       test_cmp expected "$HOME/.git-credentials" &&
+       test_must_be_empty "$HOME/.config/git/credentials"
+'
+
+
+test_expect_success 'erase: erase matching credentials from both xdg and home files' '
+       echo "https://home-user:home-pass@example.com" >"$HOME/.git-credentials" &&
+       mkdir -p "$HOME/.config/git" &&
+       echo "https://xdg-user:xdg-pass@example.com" >"$HOME/.config/git/credentials" &&
+       check reject store <<-\EOF &&
+       protocol=https
+       host=example.com
+       EOF
+       test_must_be_empty "$HOME/.git-credentials" &&
+       test_must_be_empty "$HOME/.config/git/credentials"
+'
+
 test_done
index f83df8eb8b143086df00e163a30ad96e43404001..7d2baa15bbd0bbbe8c12631ad2d24b77e90b8775 100755 (executable)
@@ -201,4 +201,23 @@ test_expect_success 'corrupt tag' '
        test_must_fail git hash-object -t tag --stdin </dev/null
 '
 
+test_expect_success 'hash-object complains about bogus type name' '
+       test_must_fail git hash-object -t bogus --stdin </dev/null
+'
+
+test_expect_success 'hash-object complains about truncated type name' '
+       test_must_fail git hash-object -t bl --stdin </dev/null
+'
+
+test_expect_success '--literally' '
+       t=1234567890 &&
+       echo example | git hash-object -t $t --literally --stdin
+'
+
+test_expect_success '--literally with extra-long type' '
+       t=12345678901234567890123456789012345678901234567890 &&
+       t="$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t" &&
+       echo example | git hash-object -t $t --literally --stdin
+'
+
 test_done
index 2edb4f2de5cc32d0f8847790d06a8de0c98d99c3..8e22b03cdd132bd54f9db44d362c15d129415651 100755 (executable)
@@ -162,16 +162,20 @@ test_expect_success 'no file/rev ambiguity check inside .git' '
        )
 '
 
-test_expect_success 'no file/rev ambiguity check inside a bare repo' '
+test_expect_success 'no file/rev ambiguity check inside a bare repo (explicit GIT_DIR)' '
+       test_when_finished "rm -fr foo.git" &&
        git clone -s --bare .git foo.git &&
        (
                cd foo.git &&
+               # older Git needed help by exporting GIT_DIR=.
+               # to realize that it is inside a bare repository.
+               # We keep this test around for regression testing.
                GIT_DIR=. git show -s HEAD
        )
 '
 
-# This still does not work as it should...
-: test_expect_success 'no file/rev ambiguity check inside a bare repo' '
+test_expect_success 'no file/rev ambiguity check inside a bare repo' '
+       test_when_finished "rm -fr foo.git" &&
        git clone -s --bare .git foo.git &&
        (
                cd foo.git &&
@@ -180,7 +184,6 @@ test_expect_success 'no file/rev ambiguity check inside a bare repo' '
 '
 
 test_expect_success SYMLINKS 'detection should not be fooled by a symlink' '
-       rm -fr foo.git &&
        git clone -s .git another &&
        ln -s another yetanother &&
        (
index 62691172e34f0d7b5bdfff2920bab4261f9d3b5c..6d47e2c725f7c7842a604b7a72fd4c4ca64e71bb 100755 (executable)
@@ -14,25 +14,45 @@ xmkdir() {
 
 R="$1"
 
+[ "$(id -u)" -eq 0 ] && die "This script should not be run as root, what if it does rm -rf /?"
 [ -n "$R" ] || die "usage: prepare-chroot.sh <root>"
 [ -x git ] || die "This script needs to be executed at git source code's top directory"
-[ -x /bin/busybox ] || die "You need busybox"
+if [ -x /bin/busybox ]; then
+       BB=/bin/busybox
+elif [ -x /usr/bin/busybox ]; then
+       BB=/usr/bin/busybox
+else
+       die "You need busybox"
+fi
 
 xmkdir "$R" "$R/bin" "$R/etc" "$R/lib" "$R/dev"
-[ -c "$R/dev/null" ] || die "/dev/null is missing. Do mknod $R/dev/null c 1 3 && chmod 666 $R/dev/null"
+touch "$R/dev/null"
 echo "root:x:0:0:root:/:/bin/sh" > "$R/etc/passwd"
 echo "$(id -nu):x:$(id -u):$(id -g)::$(pwd)/t:/bin/sh" >> "$R/etc/passwd"
 echo "root::0:root" > "$R/etc/group"
 echo "$(id -ng)::$(id -g):$(id -nu)" >> "$R/etc/group"
 
-[ -x "$R/bin/busybox" ] || cp /bin/busybox "$R/bin/busybox"
-[ -x "$R/bin/sh" ] || ln -s /bin/busybox "$R/bin/sh"
-[ -x "$R/bin/su" ] || ln -s /bin/busybox "$R/bin/su"
+[ -x "$R$BB" ] || cp $BB "$R/bin/busybox"
+for cmd in sh su ls expr tr basename rm mkdir mv id uname dirname cat true sed diff; do
+       ln -f -s /bin/busybox "$R/bin/$cmd"
+done
 
 mkdir -p "$R$(pwd)"
 rsync --exclude-from t/t1509/excludes -Ha . "$R$(pwd)"
-ldd git | grep '/' | sed 's,.*\s\(/[^ ]*\).*,\1,' | while read i; do
-       mkdir -p "$R$(dirname $i)"
-       cp "$i" "$R/$i"
+# Fake perl to reduce dependency, t1509 does not use perl, but some
+# env might slip through, see test-lib.sh, unset.*PERL_PATH
+sed 's|^PERL_PATH=.*|PERL_PATH=/bin/true|' GIT-BUILD-OPTIONS > "$R$(pwd)/GIT-BUILD-OPTIONS"
+for cmd in git $BB;do 
+       ldd $cmd | grep '/' | sed 's,.*\s\(/[^ ]*\).*,\1,' | while read i; do
+               mkdir -p "$R$(dirname $i)"
+               cp "$i" "$R/$i"
+       done
 done
-echo "Execute this in root: 'chroot $R /bin/su - $(id -nu)'"
+cat <<EOF
+All is set up in $R, execute t1509 with the following commands:
+
+sudo chroot $R /bin/su - $(id -nu)
+IKNOWWHATIAMDOING=YES ./t1509-root-worktree.sh -v -i
+
+When you are done, simply delete $R to clean up
+EOF
index eed76cca55ce655cb51c793f21cd5ebbd363ba85..ac429a0bbbbeb95f96336aa203a5d9726ed6eb1d 100755 (executable)
@@ -1055,4 +1055,51 @@ test_expect_success 'todo count' '
        grep "^# Rebase ..* onto ..* ([0-9]" actual
 '
 
+test_expect_success 'rebase -i commits that overwrite untracked files (pick)' '
+       git checkout --force branch2 &&
+       git clean -f &&
+       set_fake_editor &&
+       FAKE_LINES="edit 1 2" git rebase -i A &&
+       test_cmp_rev HEAD F &&
+       test_path_is_missing file6 &&
+       >file6 &&
+       test_must_fail git rebase --continue &&
+       test_cmp_rev HEAD F &&
+       rm file6 &&
+       git rebase --continue &&
+       test_cmp_rev HEAD I
+'
+
+test_expect_success 'rebase -i commits that overwrite untracked files (squash)' '
+       git checkout --force branch2 &&
+       git clean -f &&
+       git tag original-branch2 &&
+       set_fake_editor &&
+       FAKE_LINES="edit 1 squash 2" git rebase -i A &&
+       test_cmp_rev HEAD F &&
+       test_path_is_missing file6 &&
+       >file6 &&
+       test_must_fail git rebase --continue &&
+       test_cmp_rev HEAD F &&
+       rm file6 &&
+       git rebase --continue &&
+       test $(git cat-file commit HEAD | sed -ne \$p) = I &&
+       git reset --hard original-branch2
+'
+
+test_expect_success 'rebase -i commits that overwrite untracked files (no ff)' '
+       git checkout --force branch2 &&
+       git clean -f &&
+       set_fake_editor &&
+       FAKE_LINES="edit 1 2" git rebase -i --no-ff A &&
+       test $(git cat-file commit HEAD | sed -ne \$p) = F &&
+       test_path_is_missing file6 &&
+       >file6 &&
+       test_must_fail git rebase --continue &&
+       test $(git cat-file commit HEAD | sed -ne \$p) = F &&
+       rm file6 &&
+       git rebase --continue &&
+       test $(git cat-file commit HEAD | sed -ne \$p) = I
+'
+
 test_done
index 4ee47cc9a862cdbe8c63bc9d4ebbe46ff3c344bc..3cb74ca296d141a6bd248e527680f5f217608ca7 100755 (executable)
@@ -118,4 +118,11 @@ test_expect_success 'add -e' '
 
 '
 
+test_expect_success 'add -e notices editor failure' '
+       git reset --hard &&
+       echo change >>file &&
+       test_must_fail env GIT_EDITOR=false git add -e &&
+       test_expect_code 1 git diff --exit-code
+'
+
 test_done
index 1e29962fad30edb263cd07b6586f1b1a962c88cf..0746eeeff70c29c91accacdfa1efa4bc2e612715 100755 (executable)
@@ -10,6 +10,8 @@ test_description='Test git stash'
 test_expect_success 'stash some dirty working directory' '
        echo 1 > file &&
        git add file &&
+       echo unrelated >other-file &&
+       git add other-file &&
        test_tick &&
        git commit -m initial &&
        echo 2 > file &&
@@ -43,10 +45,15 @@ test_expect_success 'applying bogus stash does nothing' '
        test_cmp expect file
 '
 
+test_expect_success 'apply requires a clean index' '
+       test_when_finished "git reset --hard" &&
+       echo changed >other-file &&
+       git add other-file &&
+       test_must_fail git stash apply
+'
+
 test_expect_success 'apply does not need clean working directory' '
        echo 4 >other-file &&
-       git add other-file &&
-       echo 5 >other-file &&
        git stash apply &&
        echo 3 >expect &&
        test_cmp expect file
@@ -695,8 +702,8 @@ test_expect_success 'setup stash with index and worktree changes' '
 '
 
 test_expect_success 'stash list implies --first-parent -m' '
-       cat >expect <<-\EOF &&
-       stash@{0}: WIP on master: b27a2bc subdir
+       cat >expect <<-EOF &&
+       stash@{0}
 
        diff --git a/file b/file
        index 257cc56..d26b33d 100644
@@ -706,13 +713,13 @@ test_expect_success 'stash list implies --first-parent -m' '
        -foo
        +working
        EOF
-       git stash list -p >actual &&
+       git stash list --format=%gd -p >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'stash list --cc shows combined diff' '
        cat >expect <<-\EOF &&
-       stash@{0}: WIP on master: b27a2bc subdir
+       stash@{0}
 
        diff --cc file
        index 257cc56,9015a7a..d26b33d
@@ -723,7 +730,7 @@ test_expect_success 'stash list --cc shows combined diff' '
         -index
        ++working
        EOF
-       git stash list -p --cc >actual &&
+       git stash list --format=%gd -p --cc >actual &&
        test_cmp expect actual
 '
 
index 44d45257da708f25bd0eb2c631bd7e68a38a7354..b345b2ebfa53e51cb0ab32635a9123abdf55f2b1 100644 (file)
@@ -5,7 +5,7 @@ Date:   Mon Jun 26 00:06:00 2006 +0000
 
     Rearranged lines in dir/sub
 
-commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD, refs/heads/master)
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> refs/heads/master)
 Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
index 075ece6db16f9c313504559d95c5b1dc6b4c2e07..6eb83211b593ecea798eba3790b9c76fc7d3aa60 100755 (executable)
@@ -55,4 +55,38 @@ test_expect_success 'git diff --no-index executed outside repo gives correct err
        )
 '
 
+test_expect_success 'diff D F and diff F D' '
+       (
+               cd repo &&
+               echo in-repo >a &&
+               echo non-repo >../non/git/a &&
+               mkdir sub &&
+               echo sub-repo >sub/a &&
+
+               test_must_fail git diff --no-index sub/a ../non/git/a >expect &&
+               test_must_fail git diff --no-index sub/a ../non/git/ >actual &&
+               test_cmp expect actual &&
+
+               test_must_fail git diff --no-index a ../non/git/a >expect &&
+               test_must_fail git diff --no-index a ../non/git/ >actual &&
+               test_cmp expect actual &&
+
+               test_must_fail git diff --no-index ../non/git/a a >expect &&
+               test_must_fail git diff --no-index ../non/git a >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'turning a file into a directory' '
+       (
+               cd non/git &&
+               mkdir d e e/sub &&
+               echo 1 >d/sub &&
+               echo 2 >e/sub/file &&
+               printf "D\td/sub\nA\te/sub/file\n" >expect &&
+               test_must_fail git diff --no-index --name-status d e >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_done
index 0901b3098239863df983faa61d2b00ce2c4d670d..4451127eb24051dcc3d2aebd443d10619c525e27 100755 (executable)
@@ -54,14 +54,14 @@ canned_test "-L 4:a.c -L 8,12:a.c simple" multiple-superset
 canned_test "-L 8,12:a.c -L 4:a.c simple" multiple-superset
 
 test_bad_opts "-L" "switch.*requires a value"
-test_bad_opts "-L b.c" "argument.*not of the form"
-test_bad_opts "-L 1:" "argument.*not of the form"
+test_bad_opts "-L b.c" "argument not .start,end:file"
+test_bad_opts "-L 1:" "argument not .start,end:file"
 test_bad_opts "-L 1:nonexistent" "There is no path"
 test_bad_opts "-L 1:simple" "There is no path"
-test_bad_opts "-L '/foo:b.c'" "argument.*not of the form"
+test_bad_opts "-L '/foo:b.c'" "argument not .start,end:file"
 test_bad_opts "-L 1000:b.c" "has only.*lines"
 test_bad_opts "-L 1,1000:b.c" "has only.*lines"
-test_bad_opts "-L :b.c" "argument.*not of the form"
+test_bad_opts "-L :b.c" "argument not .start,end:file"
 test_bad_opts "-L :foo:b.c" "no match"
 
 test_expect_success '-L X (X == nlines)' '
index 1befc453a31e6ad67c3805eb94bfe00f6b7a5469..bfdaf75966f7b8bb057e4db0b8791306f6e6a44f 100755 (executable)
@@ -296,6 +296,12 @@ setup_ssh_wrapper () {
        '
 }
 
+copy_ssh_wrapper_as () {
+       cp "$TRASH_DIRECTORY/ssh-wrapper" "$1" &&
+       GIT_SSH="$1" &&
+       export GIT_SSH
+}
+
 expect_ssh () {
        test_when_finished '
                (cd "$TRASH_DIRECTORY" && rm -f ssh-expect && >ssh-output)
@@ -332,9 +338,36 @@ test_expect_success !MINGW,!CYGWIN 'clone local path foo:bar' '
 
 test_expect_success 'bracketed hostnames are still ssh' '
        git clone "[myhost:123]:src" ssh-bracket-clone &&
-       expect_ssh myhost '-p 123' src
+       expect_ssh "-p 123" myhost src
+'
+
+test_expect_success 'uplink is not treated as putty' '
+       copy_ssh_wrapper_as "$TRASH_DIRECTORY/uplink" &&
+       git clone "[myhost:123]:src" ssh-bracket-clone-uplink &&
+       expect_ssh "-p 123" myhost src
+'
+
+test_expect_success 'plink is treated specially (as putty)' '
+       copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
+       git clone "[myhost:123]:src" ssh-bracket-clone-plink-0 &&
+       expect_ssh "-P 123" myhost src
 '
 
+test_expect_success 'plink.exe is treated specially (as putty)' '
+       copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink.exe" &&
+       git clone "[myhost:123]:src" ssh-bracket-clone-plink-1 &&
+       expect_ssh "-P 123" myhost src
+'
+
+test_expect_success 'tortoiseplink is like putty, with extra arguments' '
+       copy_ssh_wrapper_as "$TRASH_DIRECTORY/tortoiseplink" &&
+       git clone "[myhost:123]:src" ssh-bracket-clone-plink-2 &&
+       expect_ssh "-batch -P 123" myhost src
+'
+
+# Reset the GIT_SSH environment variable for clone tests.
+setup_ssh_wrapper
+
 counter=0
 # $1 url
 # $2 none|host
index 66643e4bd758aa55f4f58d70675e32f1ad173460..855afda80a1b0dea6d74aa18a96018f59cb2e50b 100755 (executable)
@@ -394,4 +394,14 @@ test_expect_success 'replace submodule revision' '
        test $orig_head != `git show-ref --hash --head HEAD`
 '
 
+test_expect_success 'filter commit message without trailing newline' '
+       git reset --hard original &&
+       commit=$(printf "no newline" | git commit-tree HEAD^{tree}) &&
+       git update-ref refs/heads/no-newline $commit &&
+       git filter-branch -f refs/heads/no-newline &&
+       echo $commit >expect &&
+       git rev-parse refs/heads/no-newline >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 460789b4d85241eb51c0c1a5a1f4be7d4a0d7154..cdc0747bf01b0598a2e864073887896162815019 100755 (executable)
@@ -20,6 +20,15 @@ test_expect_success 'status untracked directory with --ignored' '
        test_cmp expected actual
 '
 
+test_expect_success 'same with gitignore starting with BOM' '
+       printf "\357\273\277ignored\n" >.gitignore &&
+       mkdir -p untracked &&
+       : >untracked/ignored &&
+       : >untracked/uncommitted &&
+       git status --porcelain --ignored >actual &&
+       test_cmp expected actual
+'
+
 cat >expected <<\EOF
 ?? .gitignore
 ?? actual
index 32895e5acba8d246539d3bd97fe7305827f18aa7..16f1442c1e6e4780dc10474a3fbf516841468c9f 100755 (executable)
@@ -191,12 +191,24 @@ test_expect_success 'indent of line numbers, ten lines' '
        test $(grep -c "  " actual) = 9
 '
 
-test_expect_success 'blaming files with CRLF newlines' '
+test_expect_success 'setup file with CRLF newlines' '
        git config core.autocrlf false &&
-       printf "testcase\r\n" >crlffile &&
+       printf "testcase\n" >crlffile &&
        git add crlffile &&
        git commit -m testcase &&
-       git -c core.autocrlf=input blame crlffile >actual &&
+       printf "testcase\r\n" >crlffile
+'
+
+test_expect_success 'blame file with CRLF core.autocrlf true' '
+       git config core.autocrlf true &&
+       git blame crlffile >actual &&
+       grep "A U Thor" actual
+'
+
+test_expect_success 'blame file with CRLF attributes text' '
+       git config core.autocrlf false &&
+       echo "crlffile text" >.gitattributes &&
+       git blame crlffile >actual &&
        grep "A U Thor" actual
 '
 
index 0698ce7908cb958c6e94a8cfa11c1019de692710..8f8858a5f0a4f7a705960cef5b5558064a86ea28 100644 (file)
@@ -478,7 +478,7 @@ test_external_without_stderr () {
 test_path_is_file () {
        if ! test -f "$1"
        then
-               echo "File $1 doesn't exist. $*"
+               echo "File $1 doesn't exist. $2"
                false
        fi
 }
@@ -486,7 +486,7 @@ test_path_is_file () {
 test_path_is_dir () {
        if ! test -d "$1"
        then
-               echo "Directory $1 doesn't exist. $*"
+               echo "Directory $1 doesn't exist. $2"
                false
        fi
 }
diff --git a/utf8.c b/utf8.c
index 520fbb4994ab1bdcfa0bc4589a0ca5131a89c490..28e6d76a425db4c16a8cc32e6f17c7aa72c2b1f0 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -633,3 +633,14 @@ int is_hfs_dotgit(const char *path)
 
        return 1;
 }
+
+const char utf8_bom[] = "\357\273\277";
+
+int skip_utf8_bom(char **text, size_t len)
+{
+       if (len < strlen(utf8_bom) ||
+           memcmp(*text, utf8_bom, strlen(utf8_bom)))
+               return 0;
+       *text += strlen(utf8_bom);
+       return 1;
+}
diff --git a/utf8.h b/utf8.h
index e4d9183c5fec81f8dbc75d1f2aea136f9b397a72..e7b2aa416844a0b2ccb2d2cf8a1bce68c4d60be1 100644 (file)
--- a/utf8.h
+++ b/utf8.h
@@ -13,6 +13,9 @@ int same_encoding(const char *, const char *);
 __attribute__((format (printf, 2, 3)))
 int utf8_fprintf(FILE *, const char *, ...);
 
+extern const char utf8_bom[];
+extern int skip_utf8_bom(char **, size_t);
+
 void strbuf_add_wrapped_text(struct strbuf *buf,
                const char *text, int indent, int indent2, int width);
 void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,