Imported Upstream version 2.2.2 upstream/2.2.2
authorDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 3 Mar 2021 06:14:49 +0000 (15:14 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 3 Mar 2021 06:14:49 +0000 (15:14 +0900)
33 files changed:
Documentation/RelNotes/2.2.2.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/git-am.txt
Documentation/git-push.txt
Documentation/git.txt
GIT-VERSION-GEN
Makefile
RelNotes
builtin/add.c
builtin/checkout.c
builtin/clean.c
builtin/config.c
builtin/init-db.c
builtin/push.c
builtin/receive-pack.c
compat/mingw.c
config.c
date.c
gitweb/gitweb.perl
refs.c
sha1_name.c
t/t0001-init.sh
t/t0006-date.sh
t/t0090-cache-tree.sh
t/t1410-reflog.sh
t/t1450-fsck.sh
t/t2022-checkout-paths.sh
t/t3700-add.sh
t/t4026-color.sh
t/t5528-push-default.sh
t/t9603-cvsimport-patchsets.sh
t/t9604-cvsimport-timestamps.sh
utf8.c

diff --git a/Documentation/RelNotes/2.2.2.txt b/Documentation/RelNotes/2.2.2.txt
new file mode 100644 (file)
index 0000000..b19a35d
--- /dev/null
@@ -0,0 +1,63 @@
+Git v2.2.2 Release Notes
+========================
+
+Fixes since v2.2.1
+------------------
+
+ * "git checkout $treeish $path", when $path in the index and the
+   working tree already matched what is in $treeish at the $path,
+   still overwrote the $path unnecessarily.
+
+ * "git config --get-color" did not parse its command line arguments
+   carefully.
+
+ * open() emulated on Windows platforms did not give EISDIR upon
+   an attempt to open a directory for writing.
+
+ * A few code paths used abs() when they should have used labs() on
+   long integers.
+
+ * "gitweb" used to depend on a behaviour recent CGI.pm deprecated.
+
+ * "git init" (hence "git clone") initialized the per-repository
+   configuration file .git/config with x-bit by mistake.
+
+ * Git 2.0 was supposed to make the "simple" mode for the default of
+   "git push", but it didn't.
+
+ * "Everyday" document had a broken link.
+
+ * The build procedure did not bother fixing perl and python scripts
+   when NO_PERL and NO_PYTHON build-time configuration changed.
+
+ * The code that reads the reflog from the newer to the older entries
+   did not handle an entry that crosses a boundary of block it uses to
+   read them correctly.
+
+ * "git apply" was described in the documentation to take --ignore-date
+   option, which it does not.
+
+ * Traditionally we tried to avoid interpreting date strings given by
+   the user as future dates, e.g. GIT_COMMITTER_DATE=2014-12-10 when
+   used early November 2014 was taken as "October 12, 2014" because it
+   is likely that a date in the future, December 10, is a mistake.
+   This heuristics has been loosened to allow people to express future
+   dates (most notably, --until=<date> may want to be far in the
+   future) and we no longer tiebreak by future-ness of the date when
+
+    (1) ISO-like format is used, and
+    (2) the string can make sense interpreted as both y-m-d and y-d-m.
+
+   Git may still have to use the heuristics to tiebreak between dd/mm/yy
+   and mm/dd/yy, though.
+
+ * The code to abbreviate an object name to its short unique prefix
+   has been optimized when no abbreviation was requested.
+
+ * "git add --ignore-errors ..." did not ignore an error to
+   give a file that did not exist.
+
+ * Git did not correctly read an overlong refname from a packed refs
+   file.
+
+Also contains typofixes, documentation updates and trivial code clean-ups.
index 302d61e..9335ff2 100644 (file)
@@ -850,6 +850,10 @@ accepted are `normal`, `black`, `red`, `green`, `yellow`, `blue`,
 `blink` and `reverse`.  The first color given is the foreground; the
 second is the background.  The position of the attribute, if any,
 doesn't matter.
++
+Colors (foreground and background) may also be given as numbers between
+0 and 255; these use ANSI 256-color mode (but note that not all
+terminals may support this).
 
 color.diff::
        Whether to use ANSI escape sequences to add color to patches.
index 9adce37..d4ef16c 100644 (file)
@@ -83,7 +83,6 @@ default.   You can use `--no-utf8` to override this.
        it is supposed to apply to and we have those blobs
        available locally.
 
---ignore-date::
 --ignore-space-change::
 --ignore-whitespace::
 --whitespace=<option>::
index 21b3f29..b17283a 100644 (file)
@@ -34,7 +34,7 @@ When the command line does not specify what to push with `<refspec>...`
 arguments or `--all`, `--mirror`, `--tags` options, the command finds
 the default `<refspec>` by consulting `remote.*.push` configuration,
 and if it is not found, honors `push.default` configuration to decide
-what to push (See gitlink:git-config[1] for the meaning of `push.default`).
+what to push (See linkgit:git-config[1] for the meaning of `push.default`).
 
 
 OPTIONS[[OPTIONS]]
index db4e407..edceb50 100644 (file)
@@ -43,9 +43,10 @@ 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.2.1/git.html[documentation for release 2.2.1]
+* link:v2.2.2/git.html[documentation for release 2.2.2]
 
 * release notes for
+  link:RelNotes/2.2.2.txt[2.2.2],
   link:RelNotes/2.2.1.txt[2.2.1],
   link:RelNotes/2.2.0.txt[2.2].
 
index 495ddb7..f9f8f32 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.2.1
+DEF_VER=v2.2.2
 
 LF='
 '
index 827006b..7482a4d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1662,7 +1662,7 @@ GIT-SCRIPT-DEFINES: FORCE
             fi
 
 
-$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh GIT-SCRIPT-DEFINES
+$(SCRIPT_SH_GEN) : % : %.sh GIT-SCRIPT-DEFINES
        $(QUIET_GEN)$(cmd_munge_script) && \
        chmod +x $@+ && \
        mv $@+ $@
@@ -1676,8 +1676,11 @@ git.res: git.rc GIT-VERSION-FILE
          $(join -DMAJOR= -DMINOR=, $(wordlist 1,2,$(subst -, ,$(subst ., ,$(GIT_VERSION))))) \
          -DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" $< -o $@
 
+# This makes sure we depend on the NO_PERL setting itself.
+$(SCRIPT_PERL_GEN): GIT-BUILD-OPTIONS
+
 ifndef NO_PERL
-$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
+$(SCRIPT_PERL_GEN): perl/perl.mak
 
 perl/perl.mak: perl/PM.stamp
 
@@ -1690,7 +1693,7 @@ perl/perl.mak: GIT-CFLAGS GIT-PREFIX perl/Makefile perl/Makefile.PL
        $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
 
 PERL_DEFINES = $(PERL_PATH_SQ):$(PERLLIB_EXTRA_SQ)
-$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl perl/perl.mak GIT-PERL-DEFINES GIT-VERSION-FILE
+$(SCRIPT_PERL_GEN): % : %.perl perl/perl.mak GIT-PERL-DEFINES GIT-VERSION-FILE
        $(QUIET_GEN)$(RM) $@ $@+ && \
        INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
        INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \
@@ -1724,7 +1727,7 @@ git-instaweb: git-instaweb.sh gitweb GIT-SCRIPT-DEFINES
        chmod +x $@+ && \
        mv $@+ $@
 else # NO_PERL
-$(patsubst %.perl,%,$(SCRIPT_PERL)) git-instaweb: % : unimplemented.sh
+$(SCRIPT_PERL_GEN) git-instaweb: % : unimplemented.sh
        $(QUIET_GEN)$(RM) $@ $@+ && \
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
            -e 's|@@REASON@@|NO_PERL=$(NO_PERL)|g' \
@@ -1733,6 +1736,9 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)) git-instaweb: % : unimplemented.sh
        mv $@+ $@
 endif # NO_PERL
 
+# This makes sure we depend on the NO_PYTHON setting itself.
+$(SCRIPT_PYTHON_GEN): GIT-BUILD-OPTIONS
+
 ifndef NO_PYTHON
 $(SCRIPT_PYTHON_GEN): GIT-CFLAGS GIT-PREFIX GIT-PYTHON-VARS
 $(SCRIPT_PYTHON_GEN): % : %.py
index d51c518..c5fa3ef 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.2.1.txt
\ No newline at end of file
+Documentation/RelNotes/2.2.2.txt
\ No newline at end of file
index ae6d3e2..1074e32 100644 (file)
@@ -284,7 +284,7 @@ static int add_files(struct dir_struct *dir, int flags)
                for (i = 0; i < dir->ignored_nr; i++)
                        fprintf(stderr, "%s\n", dir->ignored[i]->name);
                fprintf(stderr, _("Use -f if you really want to add them.\n"));
-               die(_("no files added"));
+               exit_status = 1;
        }
 
        for (i = 0; i < dir->nr; i++)
index 5410dac..5a78758 100644 (file)
@@ -67,6 +67,7 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen,
 {
        int len;
        struct cache_entry *ce;
+       int pos;
 
        if (S_ISDIR(mode))
                return READ_TREE_RECURSIVE;
@@ -79,6 +80,23 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen,
        ce->ce_flags = create_ce_flags(0) | CE_UPDATE;
        ce->ce_namelen = len;
        ce->ce_mode = create_ce_mode(mode);
+
+       /*
+        * If the entry is the same as the current index, we can leave the old
+        * entry in place. Whether it is UPTODATE or not, checkout_entry will
+        * do the right thing.
+        */
+       pos = cache_name_pos(ce->name, ce->ce_namelen);
+       if (pos >= 0) {
+               struct cache_entry *old = active_cache[pos];
+               if (ce->ce_mode == old->ce_mode &&
+                   !hashcmp(ce->sha1, old->sha1)) {
+                       old->ce_flags |= CE_UPDATE;
+                       free(ce);
+                       return 0;
+               }
+       }
+
        add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
        return 0;
 }
index 7784676..7e7fdcf 100644 (file)
@@ -321,7 +321,7 @@ static void print_highlight_menu_stuff(struct menu_stuff *stuff, int **chosen)
 
        switch (stuff->type) {
        default:
-               die("Bad type of menu_staff when print menu");
+               die("Bad type of menu_stuff when print menu");
        case MENU_STUFF_TYPE_MENU_ITEM:
                menu_item = (struct menu_item *)stuff->stuff;
                for (i = 0; i < stuff->nr; i++, menu_item++) {
index 8cc2604..fddafbb 100644 (file)
@@ -69,8 +69,8 @@ static struct option builtin_config_options[] = {
        OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
        OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
        OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
-       OPT_STRING(0, "get-color", &get_color_slot, N_("slot"), N_("find the color configured: [default]")),
-       OPT_STRING(0, "get-colorbool", &get_colorbool_slot, N_("slot"), N_("find the color setting: [stdout-is-tty]")),
+       OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
+       OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
        OPT_GROUP(N_("Type")),
        OPT_BIT(0, "bool", &types, N_("value is \"true\" or \"false\""), TYPE_BOOL),
        OPT_BIT(0, "int", &types, N_("value is decimal number"), TYPE_INT),
@@ -303,8 +303,9 @@ static int git_get_color_config(const char *var, const char *value, void *cb)
        return 0;
 }
 
-static void get_color(const char *def_color)
+static void get_color(const char *var, const char *def_color)
 {
+       get_color_slot = var;
        get_color_found = 0;
        parsed_color[0] = '\0';
        git_config_with_options(git_get_color_config, NULL,
@@ -333,8 +334,9 @@ static int git_get_colorbool_config(const char *var, const char *value,
        return 0;
 }
 
-static int get_colorbool(int print)
+static int get_colorbool(const char *var, int print)
 {
+       get_colorbool_slot = var;
        get_colorbool_found = -1;
        get_diff_color_found = -1;
        get_color_ui_found = -1;
@@ -532,12 +534,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                usage_with_options(builtin_config_usage, builtin_config_options);
        }
 
-       if (get_color_slot)
-           actions |= ACTION_GET_COLOR;
-       if (get_colorbool_slot)
-           actions |= ACTION_GET_COLORBOOL;
-
-       if ((get_color_slot || get_colorbool_slot) && types) {
+       if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && types) {
                error("--get-color and variable type are incoherent");
                usage_with_options(builtin_config_usage, builtin_config_options);
        }
@@ -683,12 +680,14 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                        die("No such section!");
        }
        else if (actions == ACTION_GET_COLOR) {
-               get_color(argv[0]);
+               check_argc(argc, 1, 2);
+               get_color(argv[0], argv[1]);
        }
        else if (actions == ACTION_GET_COLORBOOL) {
-               if (argc == 1)
-                       color_stdout_is_tty = git_config_bool("command line", argv[0]);
-               return get_colorbool(argc != 0);
+               check_argc(argc, 1, 2);
+               if (argc == 2)
+                       color_stdout_is_tty = git_config_bool("command line", argv[1]);
+               return get_colorbool(argv[0], argc == 2);
        }
 
        return 0;
index 587a505..aab44d2 100644 (file)
@@ -254,7 +254,8 @@ static int create_default_files(const char *template_path)
                struct stat st2;
                filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
                                !lstat(path, &st2) &&
-                               st1.st_mode != st2.st_mode);
+                               st1.st_mode != st2.st_mode &&
+                               !chmod(path, st1.st_mode));
        }
        git_config_set("core.filemode", filemode ? "true" : "false");
 
index a076b19..7aedf6f 100644 (file)
@@ -161,7 +161,7 @@ static const char message_detached_head_die[] =
           "    git push %s HEAD:<name-of-remote-branch>\n");
 
 static void setup_push_upstream(struct remote *remote, struct branch *branch,
-                               int triangular)
+                               int triangular, int simple)
 {
        struct strbuf refspec = STRBUF_INIT;
 
@@ -184,7 +184,7 @@ static void setup_push_upstream(struct remote *remote, struct branch *branch,
                      "to update which remote branch."),
                    remote->name, branch->name);
 
-       if (push_default == PUSH_DEFAULT_SIMPLE) {
+       if (simple) {
                /* Additional safety */
                if (strcmp(branch->refname, branch->merge[0]->src))
                        die_push_simple(branch, remote);
@@ -257,11 +257,11 @@ static void setup_default_push_refspecs(struct remote *remote)
                if (triangular)
                        setup_push_current(remote, branch);
                else
-                       setup_push_upstream(remote, branch, triangular);
+                       setup_push_upstream(remote, branch, triangular, 1);
                break;
 
        case PUSH_DEFAULT_UPSTREAM:
-               setup_push_upstream(remote, branch, triangular);
+               setup_push_upstream(remote, branch, triangular, 0);
                break;
 
        case PUSH_DEFAULT_CURRENT:
index 32fc540..e908d07 100644 (file)
@@ -431,7 +431,7 @@ static const char *check_nonce(const char *buf, size_t len)
        nonce_stamp_slop = (long)ostamp - (long)stamp;
 
        if (nonce_stamp_slop_limit &&
-           abs(nonce_stamp_slop) <= nonce_stamp_slop_limit) {
+           labs(nonce_stamp_slop) <= nonce_stamp_slop_limit) {
                /*
                 * Pretend as if the received nonce (which passes the
                 * HMAC check, so it is not a forged by third-party)
index c5c37e5..70f3191 100644 (file)
@@ -312,7 +312,7 @@ int mingw_open (const char *filename, int oflags, ...)
                return -1;
        fd = _wopen(wfilename, oflags, mode);
 
-       if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) {
+       if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) {
                DWORD attrs = GetFileAttributesW(wfilename);
                if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
                        errno = EISDIR;
index d5446d2..752e2e2 100644 (file)
--- a/config.c
+++ b/config.c
@@ -506,9 +506,9 @@ static int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
                        errno = EINVAL;
                        return 0;
                }
-               uval = abs(val);
+               uval = labs(val);
                uval *= factor;
-               if (uval > max || abs(val) > uval) {
+               if (uval > max || labs(val) > uval) {
                        errno = ERANGE;
                        return 0;
                }
diff --git a/date.c b/date.c
index 59dfe57..3eba2df 100644 (file)
--- a/date.c
+++ b/date.c
@@ -405,9 +405,9 @@ static int is_date(int year, int month, int day, struct tm *now_tm, time_t now,
        return 0;
 }
 
-static int match_multi_number(unsigned long num, char c, const char *date, char *end, struct tm *tm)
+static int match_multi_number(unsigned long num, char c, const char *date,
+                             char *end, struct tm *tm, time_t now)
 {
-       time_t now;
        struct tm now_tm;
        struct tm *refuse_future;
        long num2, num3;
@@ -433,17 +433,18 @@ static int match_multi_number(unsigned long num, char c, const char *date, char
        case '-':
        case '/':
        case '.':
-               now = time(NULL);
+               if (!now)
+                       now = time(NULL);
                refuse_future = NULL;
                if (gmtime_r(&now, &now_tm))
                        refuse_future = &now_tm;
 
                if (num > 70) {
                        /* yyyy-mm-dd? */
-                       if (is_date(num, num2, num3, refuse_future, now, tm))
+                       if (is_date(num, num2, num3, NULL, now, tm))
                                break;
                        /* yyyy-dd-mm? */
-                       if (is_date(num, num3, num2, refuse_future, now, tm))
+                       if (is_date(num, num3, num2, NULL, now, tm))
                                break;
                }
                /* Our eastern European friends say dd.mm.yy[yy]
@@ -513,7 +514,7 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
        case '/':
        case '-':
                if (isdigit(end[1])) {
-                       int match = match_multi_number(num, *end, date, end, tm);
+                       int match = match_multi_number(num, *end, date, end, tm, 0);
                        if (match)
                                return match;
                }
@@ -1013,7 +1014,8 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm
        return end;
 }
 
-static const char *approxidate_digit(const char *date, struct tm *tm, int *num)
+static const char *approxidate_digit(const char *date, struct tm *tm, int *num,
+                                    time_t now)
 {
        char *end;
        unsigned long number = strtoul(date, &end, 10);
@@ -1024,7 +1026,8 @@ static const char *approxidate_digit(const char *date, struct tm *tm, int *num)
        case '/':
        case '-':
                if (isdigit(end[1])) {
-                       int match = match_multi_number(number, *end, date, end, tm);
+                       int match = match_multi_number(number, *end, date, end,
+                                                      tm, now);
                        if (match)
                                return date + match;
                }
@@ -1087,7 +1090,7 @@ static unsigned long approxidate_str(const char *date,
                date++;
                if (isdigit(c)) {
                        pending_number(&tm, &number);
-                       date = approxidate_digit(date-1, &tm, &number);
+                       date = approxidate_digit(date-1, &tm, &number, time_sec);
                        touched = 1;
                        continue;
                }
index ccf7516..7a5b23a 100755 (executable)
@@ -20,6 +20,10 @@ use File::Basename qw(basename);
 use Time::HiRes qw(gettimeofday tv_interval);
 binmode STDOUT, ':utf8';
 
+if (!defined($CGI::VERSION) || $CGI::VERSION < 4.08) {
+       eval 'sub CGI::multi_param { CGI::param(@_) }'
+}
+
 our $t0 = [ gettimeofday() ];
 our $number_of_git_cmds = 0;
 
@@ -871,7 +875,7 @@ sub evaluate_query_params {
 
        while (my ($name, $symbol) = each %cgi_param_mapping) {
                if ($symbol eq 'opt') {
-                       $input_params{$name} = [ map { decode_utf8($_) } $cgi->param($symbol) ];
+                       $input_params{$name} = [ map { decode_utf8($_) } $cgi->multi_param($symbol) ];
                } else {
                        $input_params{$name} = decode_utf8($cgi->param($symbol));
                }
diff --git a/refs.c b/refs.c
index 5ff457e..6664423 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1068,8 +1068,10 @@ static const char PACKED_REFS_HEADER[] =
  * Return a pointer to the refname within the line (null-terminated),
  * or NULL if there was a problem.
  */
-static const char *parse_ref_line(char *line, unsigned char *sha1)
+static const char *parse_ref_line(struct strbuf *line, unsigned char *sha1)
 {
+       const char *ref;
+
        /*
         * 42: the answer to everything.
         *
@@ -1078,22 +1080,23 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)
         *  +1 (space in between hex and name)
         *  +1 (newline at the end of the line)
         */
-       int len = strlen(line) - 42;
-
-       if (len <= 0)
+       if (line->len <= 42)
                return NULL;
-       if (get_sha1_hex(line, sha1) < 0)
+
+       if (get_sha1_hex(line->buf, sha1) < 0)
                return NULL;
-       if (!isspace(line[40]))
+       if (!isspace(line->buf[40]))
                return NULL;
-       line += 41;
-       if (isspace(*line))
+
+       ref = line->buf + 41;
+       if (isspace(*ref))
                return NULL;
-       if (line[len] != '\n')
+
+       if (line->buf[line->len - 1] != '\n')
                return NULL;
-       line[len] = 0;
+       line->buf[--line->len] = 0;
 
-       return line;
+       return ref;
 }
 
 /*
@@ -1126,16 +1129,15 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)
 static void read_packed_refs(FILE *f, struct ref_dir *dir)
 {
        struct ref_entry *last = NULL;
-       char refline[PATH_MAX];
+       struct strbuf line = STRBUF_INIT;
        enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE;
 
-       while (fgets(refline, sizeof(refline), f)) {
+       while (strbuf_getwholeline(&line, f, '\n') != EOF) {
                unsigned char sha1[20];
                const char *refname;
-               static const char header[] = "# pack-refs with:";
+               const char *traits;
 
-               if (!strncmp(refline, header, sizeof(header)-1)) {
-                       const char *traits = refline + sizeof(header) - 1;
+               if (skip_prefix(line.buf, "# pack-refs with:", &traits)) {
                        if (strstr(traits, " fully-peeled "))
                                peeled = PEELED_FULLY;
                        else if (strstr(traits, " peeled "))
@@ -1144,7 +1146,7 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
                        continue;
                }
 
-               refname = parse_ref_line(refline, sha1);
+               refname = parse_ref_line(&line, sha1);
                if (refname) {
                        int flag = REF_ISPACKED;
 
@@ -1160,10 +1162,10 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
                        continue;
                }
                if (last &&
-                   refline[0] == '^' &&
-                   strlen(refline) == PEELED_LINE_LENGTH &&
-                   refline[PEELED_LINE_LENGTH - 1] == '\n' &&
-                   !get_sha1_hex(refline + 1, sha1)) {
+                   line.buf[0] == '^' &&
+                   line.len == PEELED_LINE_LENGTH &&
+                   line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
+                   !get_sha1_hex(line.buf + 1, sha1)) {
                        hashcpy(last->u.value.peeled, sha1);
                        /*
                         * Regardless of what the file header said,
@@ -1173,6 +1175,8 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
                        last->flag |= REF_KNOWS_PEELED;
                }
        }
+
+       strbuf_release(&line);
 }
 
 /*
@@ -3404,29 +3408,54 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void
 
                        bp = find_beginning_of_line(buf, scanp);
 
-                       if (*bp != '\n') {
-                               strbuf_splice(&sb, 0, 0, buf, endp - buf);
-                               if (pos)
-                                       break; /* need to fill another block */
-                               scanp = buf - 1; /* leave loop */
-                       } else {
+                       if (*bp == '\n') {
                                /*
-                                * (bp + 1) thru endp is the beginning of the
-                                * current line we have in sb
+                                * The newline is the end of the previous line,
+                                * so we know we have complete line starting
+                                * at (bp + 1). Prefix it onto any prior data
+                                * we collected for the line and process it.
                                 */
                                strbuf_splice(&sb, 0, 0, bp + 1, endp - (bp + 1));
                                scanp = bp;
                                endp = bp + 1;
+                               ret = show_one_reflog_ent(&sb, fn, cb_data);
+                               strbuf_reset(&sb);
+                               if (ret)
+                                       break;
+                       } else if (!pos) {
+                               /*
+                                * We are at the start of the buffer, and the
+                                * start of the file; there is no previous
+                                * line, and we have everything for this one.
+                                * Process it, and we can end the loop.
+                                */
+                               strbuf_splice(&sb, 0, 0, buf, endp - buf);
+                               ret = show_one_reflog_ent(&sb, fn, cb_data);
+                               strbuf_reset(&sb);
+                               break;
                        }
-                       ret = show_one_reflog_ent(&sb, fn, cb_data);
-                       strbuf_reset(&sb);
-                       if (ret)
+
+                       if (bp == buf) {
+                               /*
+                                * We are at the start of the buffer, and there
+                                * is more file to read backwards. Which means
+                                * we are in the middle of a line. Note that we
+                                * may get here even if *bp was a newline; that
+                                * just means we are at the exact end of the
+                                * previous line, rather than some spot in the
+                                * middle.
+                                *
+                                * Save away what we have to be combined with
+                                * the data from the next read.
+                                */
+                               strbuf_splice(&sb, 0, 0, buf, endp - buf);
                                break;
+                       }
                }
 
        }
        if (!ret && sb.len)
-               ret = show_one_reflog_ent(&sb, fn, cb_data);
+               die("BUG: reverse reflog parser had leftover data");
 
        fclose(logfp);
        strbuf_release(&sb);
index 5b004f5..cb88170 100644 (file)
@@ -372,10 +372,10 @@ const char *find_unique_abbrev(const unsigned char *sha1, int len)
        int status, exists;
        static char hex[41];
 
-       exists = has_sha1_file(sha1);
        memcpy(hex, sha1_to_hex(sha1), 40);
        if (len == 40 || !len)
                return hex;
+       exists = has_sha1_file(sha1);
        while (len < 40) {
                unsigned char sha1_ret[20];
                status = get_short_sha1(hex, len, sha1_ret, GET_SHA1_QUIETLY);
index e62c0ff..7de8d85 100755 (executable)
@@ -12,6 +12,13 @@ check_config () {
                echo "expected a directory $1, a file $1/config and $1/refs"
                return 1
        fi
+
+       if test_have_prereq POSIXPERM && test -x "$1/config"
+       then
+               echo "$1/config is executable?"
+               return 1
+       fi
+
        bare=$(cd "$1" && git config --bool core.bare)
        worktree=$(cd "$1" && git config core.worktree) ||
        worktree=unset
index e53cf6d..fac0986 100755 (executable)
@@ -82,4 +82,7 @@ check_approxidate 'Jun 6, 5AM' '2009-06-06 05:00:00'
 check_approxidate '5AM Jun 6' '2009-06-06 05:00:00'
 check_approxidate '6AM, June 7, 2009' '2009-06-07 06:00:00'
 
+check_approxidate '2008-12-01' '2008-12-01 19:20:00'
+check_approxidate '2009-12-01' '2009-12-01 19:20:00'
+
 test_done
index 158cf4f..067f4c6 100755 (executable)
@@ -131,7 +131,7 @@ test_expect_success 'second commit has cache-tree' '
        test_cache_tree
 '
 
-test_expect_success 'commit --interactive gives cache-tree on partial commit' '
+test_expect_success PERL 'commit --interactive gives cache-tree on partial commit' '
        cat <<-\EOT >foo.c &&
        int foo()
        {
index 8cf4461..779d4e3 100755 (executable)
@@ -287,4 +287,34 @@ test_expect_success 'stale dirs do not cause d/f conflicts (reflogs off)' '
        test_cmp expect actual
 '
 
+# Triggering the bug detected by this test requires a newline to fall
+# exactly BUFSIZ-1 bytes from the end of the file. We don't know
+# what that value is, since it's platform dependent. However, if
+# we choose some value N, we also catch any D which divides N evenly
+# (since we will read backwards in chunks of D). So we choose 8K,
+# which catches glibc (with an 8K BUFSIZ) and *BSD (1K).
+#
+# Each line is 114 characters, so we need 75 to still have a few before the
+# last 8K. The 89-character padding on the final entry lines up our
+# newline exactly.
+test_expect_success 'parsing reverse reflogs at BUFSIZ boundaries' '
+       git checkout -b reflogskip &&
+       z38=00000000000000000000000000000000000000 &&
+       ident="abc <xyz> 0000000001 +0000" &&
+       for i in $(test_seq 1 75); do
+               printf "$z38%02d $z38%02d %s\t" $i $(($i+1)) "$ident" &&
+               if test $i = 75; then
+                       for j in $(test_seq 1 89); do
+                               printf X
+                       done
+               else
+                       printf X
+               fi &&
+               printf "\n"
+       done >.git/logs/refs/heads/reflogskip &&
+       git rev-parse reflogskip@{73} >actual &&
+       echo ${z38}03 >expect &&
+       test_cmp expect actual
+'
+
 test_done
index d00b70f..793aee9 100755 (executable)
@@ -345,6 +345,21 @@ dot-backslash-case .\\\\.GIT\\\\foobar
 dotgit-case-backslash .git\\\\foobar
 EOF
 
+test_expect_success 'fsck allows .Ňit' '
+       (
+               git init not-dotgit &&
+               cd not-dotgit &&
+               echo content >file &&
+               git add file &&
+               git commit -m base &&
+               blob=$(git rev-parse :file) &&
+               printf "100644 blob $blob\t.\\305\\207it" >tree &&
+               tree=$(git mktree <tree) &&
+               git fsck 2>err &&
+               test_line_count = 0 err
+       )
+'
+
 # create a static test repo which is broken by omitting
 # one particular object ($1, which is looked up via rev-parse
 # in the new repository).
index 8e3545d..f46d049 100755 (executable)
@@ -61,4 +61,21 @@ test_expect_success 'do not touch unmerged entries matching $path but not in $tr
        test_cmp expect.next0 actual.next0
 '
 
+test_expect_success 'do not touch files that are already up-to-date' '
+       git reset --hard &&
+       echo one >file1 &&
+       echo two >file2 &&
+       git add file1 file2 &&
+       git commit -m base &&
+       echo modified >file1 &&
+       test-chmtime =1000000000 file2 &&
+       git update-index -q --refresh &&
+       git checkout HEAD -- file1 file2 &&
+       echo one >expect &&
+       test_cmp expect file1 &&
+       echo "1000000000        file2" >expect &&
+       test-chmtime -v +0 file2 >actual &&
+       test_cmp expect actual
+'
+
 test_done
index fe274e2..f7ff1f5 100755 (executable)
@@ -91,6 +91,13 @@ test_expect_success 'error out when attempting to add ignored ones without -f' '
        ! (git ls-files | grep "\\.ig")
 '
 
+test_expect_success 'error out when attempting to add ignored ones but add others' '
+       touch a.if &&
+       test_must_fail git add a.?? &&
+       ! (git ls-files | grep "\\.ig") &&
+       (git ls-files | grep a.if)
+'
+
 test_expect_success 'add ignored ones with -f' '
        git add -f a.?? &&
        git ls-files --error-unmatch a.ig
@@ -311,7 +318,6 @@ cat >expect.err <<\EOF
 The following paths are ignored by one of your .gitignore files:
 ignored-file
 Use -f if you really want to add them.
-fatal: no files added
 EOF
 cat >expect.out <<\EOF
 add 'track-this'
index 3726a0e..63e4238 100755 (executable)
@@ -53,6 +53,14 @@ test_expect_success '256 colors' '
        color "254 bold 255" "[1;38;5;254;48;5;255m"
 '
 
+test_expect_success '"normal" yields no color at all"' '
+       color "normal black" "[40m"
+'
+
+test_expect_success '-1 is a synonym for "normal"' '
+       color "-1 black" "[40m"
+'
+
 test_expect_success 'color too small' '
        invalid_color "-2"
 '
index 6a5ac3a..cc74519 100755 (executable)
@@ -26,7 +26,7 @@ check_pushed_commit () {
 # $2 = expected target branch for the push
 # $3 = [optional] repo to check for actual output (repo1 by default)
 test_push_success () {
-       git -c push.default="$1" push &&
+       git ${1:+-c push.default="$1"} push &&
        check_pushed_commit HEAD "$2" "$3"
 }
 
@@ -34,7 +34,7 @@ test_push_success () {
 # check that push fails and does not modify any remote branch
 test_push_failure () {
        git --git-dir=repo1 log --no-walk --format='%h %s' --all >expect &&
-       test_must_fail git -c push.default="$1" push &&
+       test_must_fail git ${1:+-c push.default="$1"} push &&
        git --git-dir=repo1 log --no-walk --format='%h %s' --all >actual &&
        test_cmp expect actual
 }
@@ -172,4 +172,32 @@ test_pushdefault_workflow success simple master triangular
 # master is updated (parent2 does not have foo)
 test_pushdefault_workflow success matching master triangular
 
+# default tests, when no push-default is specified. This
+# should behave the same as "simple" in non-triangular
+# settings, and as "current" otherwise.
+
+test_expect_success 'default behavior allows "simple" push' '
+       test_config branch.master.remote parent1 &&
+       test_config branch.master.merge refs/heads/master &&
+       test_config remote.pushdefault parent1 &&
+       test_commit default-master-master &&
+       test_push_success "" master
+'
+
+test_expect_success 'default behavior rejects non-simple push' '
+       test_config branch.master.remote parent1 &&
+       test_config branch.master.merge refs/heads/foo &&
+       test_config remote.pushdefault parent1 &&
+       test_commit default-master-foo &&
+       test_push_failure ""
+'
+
+test_expect_success 'default triangular behavior acts like "current"' '
+       test_config branch.master.remote parent1 &&
+       test_config branch.master.merge refs/heads/foo &&
+       test_config remote.pushdefault parent2 &&
+       test_commit default-triangular &&
+       test_push_success "" master repo2
+'
+
 test_done
index 52034c8..c4c3c49 100755 (executable)
@@ -16,7 +16,7 @@ test_description='git cvsimport testing for correct patchset estimation'
 
 setup_cvs_test_repository t9603
 
-test_expect_failure 'import with criss cross times on revisions' '
+test_expect_failure PERL 'import with criss cross times on revisions' '
 
     git cvsimport -p"-x" -C module-git module &&
     (cd module-git &&
index 1fd5142..a4b3db2 100755 (executable)
@@ -5,7 +5,7 @@ test_description='git cvsimport timestamps'
 
 setup_cvs_test_repository t9604
 
-test_expect_success 'check timestamps are UTC (TZ=CST6CDT)' '
+test_expect_success PERL 'check timestamps are UTC (TZ=CST6CDT)' '
 
        TZ=CST6CDT git cvsimport -p"-x" -C module-1 module &&
        git cvsimport -p"-x" -C module-1 module &&
@@ -34,7 +34,7 @@ test_expect_success 'check timestamps are UTC (TZ=CST6CDT)' '
        test_cmp actual-1 expect-1
 '
 
-test_expect_success 'check timestamps with author-specific timezones' '
+test_expect_success PERL 'check timestamps with author-specific timezones' '
 
        cat >cvs-authors <<-EOF &&
        user1=User One <user1@domain.org>
diff --git a/utf8.c b/utf8.c
index 9a3f4ad..520fbb4 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -563,8 +563,8 @@ int mbs_chrlen(const char **text, size_t *remainder_p, const char *encoding)
 }
 
 /*
- * Pick the next char from the stream, folding as an HFS+ filename comparison
- * would. Note that this is _not_ complete by any means. It's just enough
+ * Pick the next char from the stream, ignoring codepoints an HFS+ would.
+ * Note that this is _not_ complete by any means. It's just enough
  * to make is_hfs_dotgit() work, and should not be used otherwise.
  */
 static ucs_char_t next_hfs_char(const char **in)
@@ -601,12 +601,7 @@ static ucs_char_t next_hfs_char(const char **in)
                        continue;
                }
 
-               /*
-                * there's a great deal of other case-folding that occurs,
-                * but this is enough to catch anything that will convert
-                * to ".git"
-                */
-               return tolower(out);
+               return out;
        }
 }
 
@@ -614,10 +609,23 @@ int is_hfs_dotgit(const char *path)
 {
        ucs_char_t c;
 
-       if (next_hfs_char(&path) != '.' ||
-           next_hfs_char(&path) != 'g' ||
-           next_hfs_char(&path) != 'i' ||
-           next_hfs_char(&path) != 't')
+       c = next_hfs_char(&path);
+       if (c != '.')
+               return 0;
+       c = next_hfs_char(&path);
+
+       /*
+        * there's a great deal of other case-folding that occurs
+        * in HFS+, but this is enough to catch anything that will
+        * convert to ".git"
+        */
+       if (c != 'g' && c != 'G')
+               return 0;
+       c = next_hfs_char(&path);
+       if (c != 'i' && c != 'I')
+               return 0;
+       c = next_hfs_char(&path);
+       if (c != 't' && c != 'T')
                return 0;
        c = next_hfs_char(&path);
        if (c && !is_dir_sep(c))