summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mailmap3
-rw-r--r--.travis.yml21
-rw-r--r--Documentation/RelNotes/2.12.1.txt41
-rw-r--r--Documentation/RelNotes/2.12.2.txt83
-rw-r--r--Documentation/config.txt8
-rw-r--r--Documentation/git-commit.txt2
-rw-r--r--Documentation/git-filter-branch.txt14
-rw-r--r--Documentation/git-format-patch.txt2
-rw-r--r--Documentation/git-send-email.txt2
-rw-r--r--Documentation/git.txt4
-rw-r--r--Documentation/gitdiffcore.txt10
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile8
-rw-r--r--README.md6
l---------RelNotes2
-rw-r--r--abspath.c4
-rw-r--r--bisect.c9
-rw-r--r--builtin/blame.c25
-rw-r--r--builtin/branch.c5
-rw-r--r--builtin/checkout.c2
-rw-r--r--builtin/fetch-pack.c7
-rw-r--r--builtin/gc.c57
-rw-r--r--builtin/grep.c82
-rw-r--r--builtin/init-db.c6
-rw-r--r--builtin/merge.c2
-rw-r--r--builtin/pack-objects.c133
-rw-r--r--builtin/receive-pack.c5
-rw-r--r--builtin/remote.c4
-rw-r--r--builtin/show-branch.c42
-rw-r--r--cache.h42
-rwxr-xr-xci/run-linux32-build.sh30
-rw-r--r--commit.c22
-rw-r--r--config.c213
-rw-r--r--config.mak.uname18
-rw-r--r--configure.ac6
-rw-r--r--contrib/coccinelle/array.cocci16
-rw-r--r--contrib/coccinelle/strbuf.cocci6
-rwxr-xr-xcontrib/remote-helpers/git-remote-bzr16
-rwxr-xr-xcontrib/remote-helpers/git-remote-hg16
-rw-r--r--diff.c19
-rw-r--r--diffcore-pickaxe.c7
-rw-r--r--dir.c4
-rw-r--r--environment.c2
-rw-r--r--ewah/ewah_io.c4
-rw-r--r--fetch-pack.c51
-rw-r--r--fetch-pack.h6
-rwxr-xr-xgit-add--interactive.perl8
-rw-r--r--git-compat-util.h5
-rwxr-xr-xgit-filter-branch.sh2
-rwxr-xr-xgit-send-email.perl2
-rw-r--r--http-walker.c21
-rw-r--r--http.c53
-rw-r--r--ident.c49
-rw-r--r--line-log.c18
-rw-r--r--pack-objects.h4
-rw-r--r--progress.c11
-rw-r--r--refs.c8
-rw-r--r--remote.c2
-rw-r--r--remote.h9
-rw-r--r--revision.c2
-rw-r--r--run-command.c2
-rw-r--r--send-pack.c46
-rw-r--r--setup.c6
-rw-r--r--sha1_file.c4
-rw-r--r--sha1_name.c93
-rw-r--r--strbuf.c11
-rw-r--r--strbuf.h35
-rw-r--r--submodule-config.c2
-rw-r--r--submodule.c10
-rw-r--r--t/gitweb-lib.sh7
-rw-r--r--t/interop/.gitignore4
-rw-r--r--t/interop/Makefile16
-rw-r--r--t/interop/README85
-rwxr-xr-xt/interop/i0000-basic.sh27
-rwxr-xr-xt/interop/i5500-git-daemon.sh41
-rw-r--r--t/interop/interop-lib.sh92
-rw-r--r--t/lib-git-daemon.sh3
-rw-r--r--t/lib-httpd/apache.conf9
-rwxr-xr-xt/perf/p0001-rev-list.sh3
-rwxr-xr-xt/perf/p7000-filter-branch.sh5
-rw-r--r--t/perf/perf-lib.sh4
-rwxr-xr-xt/perf/run3
-rwxr-xr-xt/t0100-previous.sh8
-rwxr-xr-xt/t1300-repo-config.sh62
-rwxr-xr-xt/t1501-work-tree.sh8
-rwxr-xr-xt/t3204-branch-name-interpretation.sh133
-rwxr-xr-xt/t3701-add-interactive.sh18
-rwxr-xr-xt/t4035-diff-quiet.sh9
-rwxr-xr-xt/t4062-diff-pickaxe.sh5
-rwxr-xr-xt/t4211-line-log.sh10
-rwxr-xr-xt/t5316-pack-delta-depth.sh93
-rwxr-xr-xt/t5500-fetch-pack.sh6
-rwxr-xr-xt/t5505-remote.sh19
-rwxr-xr-xt/t5512-ls-remote.sh9
-rwxr-xr-xt/t5516-fetch-push.sh3
-rwxr-xr-xt/t5550-http-fetch-dumb.sh18
-rwxr-xr-xt/t6300-for-each-ref.sh2
-rwxr-xr-xt/t6500-gc.sh15
-rwxr-xr-xt/t7003-filter-branch.sh37
-rwxr-xr-xt/t7004-tag.sh12
-rwxr-xr-xt/t7030-verify-tag.sh12
-rwxr-xr-xt/t7518-ident-corner-cases.sh36
-rwxr-xr-xt/t7810-grep.sh66
-rwxr-xr-xt/t9001-send-email.sh7
-rwxr-xr-xt/t9200-git-cvsexportcommit.sh5
-rwxr-xr-xt/t9600-cvsimport.sh5
-rw-r--r--t/test-lib-functions.sh11
-rw-r--r--t/test-lib.sh1
-rw-r--r--tempfile.c9
-rw-r--r--transport-helper.c5
-rw-r--r--transport.c16
-rw-r--r--upload-pack.c6
-rw-r--r--worktree.c4
-rw-r--r--wrapper.c24
-rw-r--r--wt-status.c8
115 files changed, 1912 insertions, 541 deletions
diff --git a/.mailmap b/.mailmap
index ab59b2fac6..c44cf67a48 100644
--- a/.mailmap
+++ b/.mailmap
@@ -151,7 +151,8 @@ Matthias Kestenholz <matthias@spinlock.ch> <mk@spinlock.ch>
Matthias Urlichs <matthias@urlichs.de> <smurf@kiste.(none)>
Matthias Urlichs <matthias@urlichs.de> <smurf@smurf.noris.de>
Michael Coleman <tutufan@gmail.com>
-Michael J Gruber <git@drmicha.warpmail.net> <michaeljgruber+gmane@fastmail.fm>
+Michael J Gruber <git@grubix.eu> <michaeljgruber+gmane@fastmail.fm>
+Michael J Gruber <git@grubix.eu> <git@drmicha.warpmail.net>
Michael S. Tsirkin <mst@kernel.org> <mst@redhat.com>
Michael S. Tsirkin <mst@kernel.org> <mst@mellanox.co.il>
Michael S. Tsirkin <mst@kernel.org> <mst@dev.mellanox.co.il>
diff --git a/.travis.yml b/.travis.yml
index 9c63c8c3f6..591cc57b80 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -39,6 +39,27 @@ env:
matrix:
include:
+ - env: Linux32
+ os: linux
+ services:
+ - docker
+ before_install:
+ - docker pull daald/ubuntu32:xenial
+ before_script:
+ script:
+ - >
+ docker run
+ --interactive
+ --env DEFAULT_TEST_TARGET
+ --env GIT_PROVE_OPTS
+ --env GIT_TEST_OPTS
+ --env GIT_TEST_CLONE_2GB
+ --volume "${PWD}:/usr/src/git"
+ daald/ubuntu32:xenial
+ /usr/src/git/ci/run-linux32-build.sh $(id -u $USER)
+ # Use the following command to debug the docker build locally:
+ # $ docker run -itv "${PWD}:/usr/src/git" --entrypoint /bin/bash daald/ubuntu32:xenial
+ # root@container:/# /usr/src/git/ci/run-linux32-build.sh
- env: Documentation
os: linux
compiler: clang
diff --git a/Documentation/RelNotes/2.12.1.txt b/Documentation/RelNotes/2.12.1.txt
new file mode 100644
index 0000000000..a74f7db747
--- /dev/null
+++ b/Documentation/RelNotes/2.12.1.txt
@@ -0,0 +1,41 @@
+Git v2.12.1 Release Notes
+=========================
+
+Fixes since v2.12
+-----------------
+
+ * Reduce authentication round-trip over HTTP when the server supports
+ just a single authentication method. This also improves the
+ behaviour when Git is misconfigured to enable http.emptyAuth
+ against a server that does not authenticate without a username
+ (i.e. not using Kerberos etc., which makes http.emptyAuth
+ pointless).
+
+ * Windows port wants to use OpenSSL's implementation of SHA-1
+ routines, so let them.
+
+ * Add 32-bit Linux variant to the set of platforms to be tested with
+ Travis CI.
+
+ * When a redirected http transport gets an error during the
+ redirected request, we ignored the error we got from the server,
+ and ended up giving a not-so-useful error message.
+
+ * The patch subcommand of "git add -i" was meant to have paths
+ selection prompt just like other subcommand, unlike "git add -p"
+ directly jumps to hunk selection. Recently, this was broken and
+ "add -i" lost the paths selection dialog, but it now has been
+ fixed.
+
+ * Git v2.12 was shipped with an embarrassing breakage where various
+ operations that verify paths given from the user stopped dying when
+ seeing an issue, and instead later triggering segfault.
+
+ * The code to parse "git log -L..." command line was buggy when there
+ are many ranges specified with -L; overrun of the allocated buffer
+ has been fixed.
+
+ * The command-line parsing of "git log -L" copied internal data
+ structures using incorrect size on ILP32 systems.
+
+Also contains various documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.12.2.txt b/Documentation/RelNotes/2.12.2.txt
new file mode 100644
index 0000000000..441939709c
--- /dev/null
+++ b/Documentation/RelNotes/2.12.2.txt
@@ -0,0 +1,83 @@
+Git v2.12.2 Release Notes
+=========================
+
+Fixes since v2.12.1
+-------------------
+
+ * "git status --porcelain" is supposed to give a stable output, but a
+ few strings were left as translatable by mistake.
+
+ * "Dumb http" transport used to misparse a nonsense http-alternates
+ response, which has been fixed.
+
+ * "git diff --quiet" relies on the size field in diff_filespec to be
+ correctly populated, but diff_populate_filespec() helper function
+ made an incorrect short-cut when asked only to populate the size
+ field for paths that need to go through convert_to_git() (e.g. CRLF
+ conversion).
+
+ * There is no need for Python only to give a few messages to the
+ standard error stream, but we somehow did.
+
+ * A leak in a codepath to read from a packed object in (rare) cases
+ has been plugged.
+
+ * "git upload-pack", which is a counter-part of "git fetch", did not
+ report a request for a ref that was not advertised as invalid.
+ This is generally not a problem (because "git fetch" will stop
+ before making such a request), but is the right thing to do.
+
+ * A "gc.log" file left by a backgrounded "gc --auto" disables further
+ automatic gc; it has been taught to run at least once a day (by
+ default) by ignoring a stale "gc.log" file that is too old.
+
+ * "git remote rm X", when a branch has remote X configured as the
+ value of its branch.*.remote, tried to remove branch.*.remote and
+ branch.*.merge and failed if either is unset.
+
+ * A caller of tempfile API that uses stdio interface to write to
+ files may ignore errors while writing, which is detected when
+ tempfile is closed (with a call to ferror()). By that time, the
+ original errno that may have told us what went wrong is likely to
+ be long gone and was overwritten by an irrelevant value.
+ close_tempfile() now resets errno to EIO to make errno at least
+ predictable.
+
+ * "git show-branch" expected there were only very short branch names
+ in the repository and used a fixed-length buffer to hold them
+ without checking for overflow.
+
+ * The code that parses header fields in the commit object has been
+ updated for (micro)performance and code hygiene.
+
+ * A test that creates a confusing branch whose name is HEAD has been
+ corrected not to do so.
+
+ * "Cc:" on the trailer part does not have to conform to RFC strictly,
+ unlike in the e-mail header. "git send-email" has been updated to
+ ignore anything after '>' when picking addresses, to allow non-address
+ cruft like " # stable 4.4" after the address.
+
+ * "git push" had a handful of codepaths that could lead to a deadlock
+ when unexpected error happened, which has been fixed.
+
+ * Code to read submodule.<name>.ignore config did not state the
+ variable name correctly when giving an error message diagnosing
+ misconfiguration.
+
+ * "git ls-remote" and "git archive --remote" are designed to work
+ without being in a directory under Git's control. However, recent
+ updates revealed that we randomly look into a directory called
+ .git/ without actually doing necessary set-up when working in a
+ repository. Stop doing so.
+
+ * The code to parse the command line "git grep <patterns>... <rev>
+ [[--] <pathspec>...]" has been cleaned up, and a handful of bugs
+ have been fixed (e.g. we used to check "--" if it is a rev).
+
+ * The code to parse "git -c VAR=VAL cmd" and set configuration
+ variable for the duration of cmd had two small bugs, which have
+ been fixed.
+ This supersedes jc/config-case-cmdline topic that has been discarded.
+
+Also contains various documentation updates and code clean-ups.
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 015346c417..953cf69117 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1402,6 +1402,12 @@ gc.autoDetach::
Make `git gc --auto` return immediately and run in background
if the system supports it. Default is true.
+gc.logExpiry::
+ If the file gc.log exists, then `git gc --auto` won't run
+ unless that file is more than 'gc.logExpiry' old. Default is
+ "1.day". See `gc.pruneExpire` for more ways to specify its
+ value.
+
gc.packRefs::
Running `git pack-refs` in a repository renders it
unclonable by Git versions prior to 1.5.1.2 over dumb
@@ -2432,6 +2438,8 @@ push.default::
pushing to the same repository you would normally pull from
(i.e. central workflow).
+* `tracking` - This is a deprecated synonym for `upstream`.
+
* `simple` - in centralized workflow, work like `upstream` with an
added safety to refuse to push if the upstream branch's name is
different from the local one.
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 4f8f20a360..2763edb7a7 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -460,7 +460,7 @@ order). See linkgit:git-var[1] for details.
HOOKS
-----
This command can run `commit-msg`, `prepare-commit-msg`, `pre-commit`,
-and `post-commit` hooks. See linkgit:githooks[5] for more
+`post-commit` and `post-rewrite` hooks. See linkgit:githooks[5] for more
information.
FILES
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index 0a09698c03..6e4bb02204 100644
--- a/Documentation/git-filter-branch.txt
+++ b/Documentation/git-filter-branch.txt
@@ -167,14 +167,12 @@ to other tags will be rewritten to point to the underlying commit.
project root. Implies <<Remap_to_ancestor>>.
--prune-empty::
- Some kind of filters will generate empty commits, that left the tree
- untouched. This switch allow git-filter-branch to ignore such
- commits. Though, this switch only applies for commits that have one
- and only one parent, it will hence keep merges points. Also, this
- option is not compatible with the use of `--commit-filter`. Though you
- just need to use the function 'git_commit_non_empty_tree "$@"' instead
- of the `git commit-tree "$@"` idiom in your commit filter to make that
- happen.
+ Some filters will generate empty commits that leave the tree untouched.
+ This option instructs git-filter-branch to remove such commits if they
+ have exactly one or zero non-pruned parents; merge commits will
+ therefore remain intact. This option cannot be used together with
+ `--commit-filter`, though the same effect can be achieved by using the
+ provided `git_commit_non_empty_tree` function in a commit filter.
--original <namespace>::
Use this option to set the namespace where the original commits
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 9b200b379b..f7a069bb92 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -239,7 +239,7 @@ keeping them as Git notes allows them to be maintained between versions
of the patch series (but see the discussion of the `notes.rewrite`
configuration options in linkgit:git-notes[1] to use this workflow).
---[no]-signature=<signature>::
+--[no-]signature=<signature>::
Add a signature to each message produced. Per RFC 3676 the signature
is separated from the body by a line with '-- ' on it. If the
signature option is omitted the signature defaults to the Git version
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 642d0ef199..9d66166f69 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -89,7 +89,7 @@ See the CONFIGURATION section for `sendemail.multiEdit`.
reply to the given Message-Id, which avoids breaking threads to
provide a new patch series.
The second and subsequent emails will be sent as replies according to
- the `--[no]-chain-reply-to` setting.
+ the `--[no-]chain-reply-to` setting.
+
So for example when `--thread` and `--no-chain-reply-to` are specified, the
second and subsequent patches will be replies to the first one like in the
diff --git a/Documentation/git.txt b/Documentation/git.txt
index aa895da4a5..88c39d3ed5 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -44,9 +44,11 @@ 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.12.0/git.html[documentation for release 2.12.0]
+* link:v2.12.2/git.html[documentation for release 2.12.2]
* release notes for
+ link:RelNotes/2.12.2.txt[2.12.2].
+ link:RelNotes/2.12.1.txt[2.12.1].
link:RelNotes/2.12.0.txt[2.12].
* link:v2.11.1/git.html[documentation for release 2.11.1]
diff --git a/Documentation/gitdiffcore.txt b/Documentation/gitdiffcore.txt
index 46bc6d077c..c0a60f3158 100644
--- a/Documentation/gitdiffcore.txt
+++ b/Documentation/gitdiffcore.txt
@@ -84,8 +84,8 @@ format sections of the manual for 'git diff-{asterisk}' commands) or
diff-patch format.
-diffcore-break: For Splitting Up "Complete Rewrites"
-----------------------------------------------------
+diffcore-break: For Splitting Up Complete Rewrites
+--------------------------------------------------
The second transformation in the chain is diffcore-break, and is
controlled by the -B option to the 'git diff-{asterisk}' commands. This is
@@ -119,7 +119,7 @@ the original is used), and can be customized by giving a number
after "-B" option (e.g. "-B75" to tell it to use 75%).
-diffcore-rename: For Detection Renames and Copies
+diffcore-rename: For Detecting Renames and Copies
-------------------------------------------------
This transformation is used to detect renames and copies, and is
@@ -177,8 +177,8 @@ the expense of making it slower. Without `--find-copies-harder`,
copied happened to have been modified in the same changeset.
-diffcore-merge-broken: For Putting "Complete Rewrites" Back Together
---------------------------------------------------------------------
+diffcore-merge-broken: For Putting Complete Rewrites Back Together
+------------------------------------------------------------------
This transformation is used to merge filepairs broken by
diffcore-break, and not transformed into rename/copy by
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 6a208e92bf..e83f591a7b 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.12.0
+DEF_VER=v2.12.2
LF='
'
diff --git a/Makefile b/Makefile
index 8e4081e061..d5454ca336 100644
--- a/Makefile
+++ b/Makefile
@@ -102,8 +102,6 @@ all::
#
# Define MKDIR_WO_TRAILING_SLASH if your mkdir() can't deal with trailing slash.
#
-# Define NO_MKSTEMPS if you don't have mkstemps in the C library.
-#
# Define NO_GECOS_IN_PWENT if you don't have pw_gecos in struct passwd
# in the C library.
#
@@ -1280,9 +1278,6 @@ ifdef MKDIR_WO_TRAILING_SLASH
COMPAT_CFLAGS += -DMKDIR_WO_TRAILING_SLASH
COMPAT_OBJS += compat/mkdir.o
endif
-ifdef NO_MKSTEMPS
- COMPAT_CFLAGS += -DNO_MKSTEMPS
-endif
ifdef NO_UNSETENV
COMPAT_CFLAGS += -DNO_UNSETENV
COMPAT_OBJS += compat/unsetenv.o
@@ -2254,6 +2249,9 @@ endif
ifdef GIT_PERF_MAKE_OPTS
@echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@+
endif
+ifdef GIT_INTEROP_MAKE_OPTS
+ @echo GIT_INTEROP_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_INTEROP_MAKE_OPTS)))'\' >>$@+
+endif
ifdef TEST_GIT_INDEX_VERSION
@echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@+
endif
diff --git a/README.md b/README.md
index c0cd5580ea..f17af66a97 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ Torvalds with help of a group of hackers around the net.
Please read the file [INSTALL][] for installation instructions.
-Many Git online resources are accessible from http://git-scm.com/
+Many Git online resources are accessible from <https://git-scm.com/>
including full documentation and Git related tools.
See [Documentation/gittutorial.txt][] to get started, then see
@@ -33,8 +33,8 @@ requests, comments and patches to git@vger.kernel.org (read
[Documentation/SubmittingPatches][] for instructions on patch submission).
To subscribe to the list, send an email with just "subscribe git" in
the body to majordomo@vger.kernel.org. The mailing list archives are
-available at https://public-inbox.org/git,
-http://marc.info/?l=git and other archival sites.
+available at <https://public-inbox.org/git/>,
+<http://marc.info/?l=git> and other archival sites.
The maintainer frequently sends the "What's cooking" reports that
list the current status of various development topics to the mailing
diff --git a/RelNotes b/RelNotes
index d09c3d5109..cbee82a294 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.12.0.txt \ No newline at end of file
+Documentation/RelNotes/2.12.2.txt \ No newline at end of file
diff --git a/abspath.c b/abspath.c
index 2f0c26e0e2..b02e068aa3 100644
--- a/abspath.c
+++ b/abspath.c
@@ -214,12 +214,12 @@ const char *real_path_if_valid(const char *path)
return strbuf_realpath(&realpath, path, 0);
}
-char *real_pathdup(const char *path)
+char *real_pathdup(const char *path, int die_on_error)
{
struct strbuf realpath = STRBUF_INIT;
char *retval = NULL;
- if (strbuf_realpath(&realpath, path, 0))
+ if (strbuf_realpath(&realpath, path, die_on_error))
retval = strbuf_detach(&realpath, NULL);
strbuf_release(&realpath);
diff --git a/bisect.c b/bisect.c
index 8e63c40d27..30808cadf7 100644
--- a/bisect.c
+++ b/bisect.c
@@ -940,7 +940,7 @@ int bisect_next_all(const char *prefix, int no_checkout)
struct commit_list *tried;
int reaches = 0, all = 0, nr, steps;
const unsigned char *bisect_rev;
- char steps_msg[32];
+ char *steps_msg;
read_bisect_terms(&term_bad, &term_good);
if (read_bisect_refs())
@@ -990,14 +990,15 @@ int bisect_next_all(const char *prefix, int no_checkout)
nr = all - reaches - 1;
steps = estimate_bisect_steps(all);
- xsnprintf(steps_msg, sizeof(steps_msg),
- Q_("(roughly %d step)", "(roughly %d steps)", steps),
- steps);
+
+ steps_msg = xstrfmt(Q_("(roughly %d step)", "(roughly %d steps)",
+ steps), steps);
/* TRANSLATORS: the last %s will be replaced with
"(roughly %d steps)" translation */
printf(Q_("Bisecting: %d revision left to test after this %s\n",
"Bisecting: %d revisions left to test after this %s\n",
nr), nr, steps_msg);
+ free(steps_msg);
return bisect_checkout(bisect_rev, no_checkout);
}
diff --git a/builtin/blame.c b/builtin/blame.c
index cffc626540..f7aa95f4ba 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -658,8 +658,11 @@ static struct origin *find_rename(struct scoreboard *sb,
/*
* Append a new blame entry to a given output queue.
*/
-static void add_blame_entry(struct blame_entry ***queue, struct blame_entry *e)
+static void add_blame_entry(struct blame_entry ***queue,
+ const struct blame_entry *src)
{
+ struct blame_entry *e = xmalloc(sizeof(*e));
+ memcpy(e, src, sizeof(*e));
origin_incref(e->suspect);
e->next = **queue;
@@ -760,21 +763,15 @@ static void split_blame(struct blame_entry ***blamed,
struct blame_entry *split,
struct blame_entry *e)
{
- struct blame_entry *new_entry;
-
if (split[0].suspect && split[2].suspect) {
/* The first part (reuse storage for the existing entry e) */
dup_entry(unblamed, e, &split[0]);
/* The last part -- me */
- new_entry = xmalloc(sizeof(*new_entry));
- memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
- add_blame_entry(unblamed, new_entry);
+ add_blame_entry(unblamed, &split[2]);
/* ... and the middle part -- parent */
- new_entry = xmalloc(sizeof(*new_entry));
- memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
- add_blame_entry(blamed, new_entry);
+ add_blame_entry(blamed, &split[1]);
}
else if (!split[0].suspect && !split[2].suspect)
/*
@@ -785,18 +782,12 @@ static void split_blame(struct blame_entry ***blamed,
else if (split[0].suspect) {
/* me and then parent */
dup_entry(unblamed, e, &split[0]);
-
- new_entry = xmalloc(sizeof(*new_entry));
- memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
- add_blame_entry(blamed, new_entry);
+ add_blame_entry(blamed, &split[1]);
}
else {
/* parent and then me */
dup_entry(blamed, e, &split[1]);
-
- new_entry = xmalloc(sizeof(*new_entry));
- memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
- add_blame_entry(unblamed, new_entry);
+ add_blame_entry(unblamed, &split[2]);
}
}
diff --git a/builtin/branch.c b/builtin/branch.c
index 9d30f55b0b..babfd0f730 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -190,17 +190,20 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
int ret = 0;
int remote_branch = 0;
struct strbuf bname = STRBUF_INIT;
+ unsigned allowed_interpret;
switch (kinds) {
case FILTER_REFS_REMOTES:
fmt = "refs/remotes/%s";
/* For subsequent UI messages */
remote_branch = 1;
+ allowed_interpret = INTERPRET_BRANCH_REMOTE;
force = 1;
break;
case FILTER_REFS_BRANCHES:
fmt = "refs/heads/%s";
+ allowed_interpret = INTERPRET_BRANCH_LOCAL;
break;
default:
die(_("cannot use -a with -d"));
@@ -215,7 +218,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
char *target = NULL;
int flags = 0;
- strbuf_branchname(&bname, argv[i]);
+ strbuf_branchname(&bname, argv[i], allowed_interpret);
free(name);
name = mkpathdup(fmt, bname.buf);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f174f50303..81f07c3ef2 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -452,7 +452,7 @@ static void setup_branch_path(struct branch_info *branch)
{
struct strbuf buf = STRBUF_INIT;
- strbuf_branchname(&buf, branch->name);
+ strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
if (strcmp(buf.buf, branch->name))
branch->name = xstrdup(buf.buf);
strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index cfe9e447c2..2a1c1c213f 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -219,12 +219,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
* remote no-such-ref' would silently succeed without issuing
* an error.
*/
- for (i = 0; i < nr_sought; i++) {
- if (!sought[i] || sought[i]->matched)
- continue;
- error("no such remote ref %s", sought[i]->name);
- ret = 1;
- }
+ ret |= report_unmatched_refs(sought, nr_sought);
while (ref) {
printf("%s %s\n",
diff --git a/builtin/gc.c b/builtin/gc.c
index 331f219260..a2b9e8924e 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -33,6 +33,8 @@ static int aggressive_window = 250;
static int gc_auto_threshold = 6700;
static int gc_auto_pack_limit = 50;
static int detach_auto = 1;
+static unsigned long gc_log_expire_time;
+static const char *gc_log_expire = "1.day.ago";
static const char *prune_expire = "2.weeks.ago";
static const char *prune_worktrees_expire = "3.months.ago";
@@ -76,10 +78,28 @@ static void git_config_date_string(const char *key, const char **output)
static void process_log_file(void)
{
struct stat st;
- if (!fstat(get_lock_file_fd(&log_lock), &st) && st.st_size)
+ if (fstat(get_lock_file_fd(&log_lock), &st)) {
+ /*
+ * Perhaps there was an i/o error or another
+ * unlikely situation. Try to make a note of
+ * this in gc.log along with any existing
+ * messages.
+ */
+ int saved_errno = errno;
+ fprintf(stderr, _("Failed to fstat %s: %s"),
+ get_tempfile_path(&log_lock.tempfile),
+ strerror(saved_errno));
+ fflush(stderr);
commit_lock_file(&log_lock);
- else
+ errno = saved_errno;
+ } else if (st.st_size) {
+ /* There was some error recorded in the lock file */
+ commit_lock_file(&log_lock);
+ } else {
+ /* No error, clean up any old gc.log */
+ unlink(git_path("gc.log"));
rollback_lock_file(&log_lock);
+ }
}
static void process_log_file_at_exit(void)
@@ -113,6 +133,8 @@ static void gc_config(void)
git_config_get_bool("gc.autodetach", &detach_auto);
git_config_date_string("gc.pruneexpire", &prune_expire);
git_config_date_string("gc.worktreepruneexpire", &prune_worktrees_expire);
+ git_config_date_string("gc.logexpiry", &gc_log_expire);
+
git_config(git_default_config, NULL);
}
@@ -290,19 +312,34 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
static int report_last_gc_error(void)
{
struct strbuf sb = STRBUF_INIT;
- int ret;
+ int ret = 0;
+ struct stat st;
+ char *gc_log_path = git_pathdup("gc.log");
- ret = strbuf_read_file(&sb, git_path("gc.log"), 0);
+ if (stat(gc_log_path, &st)) {
+ if (errno == ENOENT)
+ goto done;
+
+ ret = error_errno(_("Can't stat %s"), gc_log_path);
+ goto done;
+ }
+
+ if (st.st_mtime < gc_log_expire_time)
+ goto done;
+
+ ret = strbuf_read_file(&sb, gc_log_path, 0);
if (ret > 0)
- return error(_("The last gc run reported the following. "
+ ret = error(_("The last gc run reported the following. "
"Please correct the root cause\n"
"and remove %s.\n"
"Automatic cleanup will not be performed "
"until the file is removed.\n\n"
"%s"),
- git_path("gc.log"), sb.buf);
+ gc_log_path, sb.buf);
strbuf_release(&sb);
- return 0;
+done:
+ free(gc_log_path);
+ return ret;
}
static int gc_before_repack(void)
@@ -349,7 +386,10 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
argv_array_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
argv_array_pushl(&rerere, "rerere", "gc", NULL);
+ /* default expiry time, overwritten in gc_config */
gc_config();
+ if (parse_expiry_date(gc_log_expire, &gc_log_expire_time))
+ die(_("Failed to parse gc.logexpiry value %s"), gc_log_expire);
if (pack_refs < 0)
pack_refs = !is_bare_repository();
@@ -448,5 +488,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
warning(_("There are too many unreachable loose objects; "
"run 'git prune' to remove them."));
+ if (!daemonized)
+ unlink(git_path("gc.log"));
+
return 0;
}
diff --git a/builtin/grep.c b/builtin/grep.c
index 2c727ef499..9304c33e75 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -967,6 +967,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
int dummy;
int use_index = 1;
int pattern_type_arg = GREP_PATTERN_TYPE_UNSPECIFIED;
+ int allow_revs;
struct option options[] = {
OPT_BOOL(0, "cached", &cached,
@@ -1149,26 +1150,69 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
compile_grep_patterns(&opt);
- /* Check revs and then paths */
+ /*
+ * We have to find "--" in a separate pass, because its presence
+ * influences how we will parse arguments that come before it.
+ */
+ for (i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], "--")) {
+ seen_dashdash = 1;
+ break;
+ }
+ }
+
+ /*
+ * Resolve any rev arguments. If we have a dashdash, then everything up
+ * to it must resolve as a rev. If not, then we stop at the first
+ * non-rev and assume everything else is a path.
+ */
+ allow_revs = use_index && !untracked;
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
unsigned char sha1[20];
struct object_context oc;
- /* Is it a rev? */
- if (!get_sha1_with_context(arg, 0, sha1, &oc)) {
- struct object *object = parse_object_or_die(sha1, arg);
- if (!seen_dashdash)
- verify_non_filename(prefix, arg);
- add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
- continue;
- }
+ struct object *object;
+
if (!strcmp(arg, "--")) {
i++;
- seen_dashdash = 1;
+ break;
}
- break;
+
+ if (!allow_revs) {
+ if (seen_dashdash)
+ die(_("--no-index or --untracked cannot be used with revs"));
+ break;
+ }
+
+ if (get_sha1_with_context(arg, 0, sha1, &oc)) {
+ if (seen_dashdash)
+ die(_("unable to resolve revision: %s"), arg);
+ break;
+ }
+
+ object = parse_object_or_die(sha1, arg);
+ if (!seen_dashdash)
+ verify_non_filename(prefix, arg);
+ add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
}
+ /*
+ * Anything left over is presumed to be a path. But in the non-dashdash
+ * "do what I mean" case, we verify and complain when that isn't true.
+ */
+ if (!seen_dashdash) {
+ int j;
+ for (j = i; j < argc; j++)
+ verify_filename(prefix, argv[j], j == i && allow_revs);
+ }
+
+ parse_pathspec(&pathspec, 0,
+ PATHSPEC_PREFER_CWD |
+ (opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0),
+ prefix, argv + i);
+ pathspec.max_depth = opt.max_depth;
+ pathspec.recursive = 1;
+
#ifndef NO_PTHREADS
if (list.nr || cached || show_in_pager)
num_threads = 0;
@@ -1190,20 +1234,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
}
#endif
- /* The rest are paths */
- if (!seen_dashdash) {
- int j;
- for (j = i; j < argc; j++)
- verify_filename(prefix, argv[j], j == i);
- }
-
- parse_pathspec(&pathspec, 0,
- PATHSPEC_PREFER_CWD |
- (opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0),
- prefix, argv + i);
- pathspec.max_depth = opt.max_depth;
- pathspec.recursive = 1;
-
if (recurse_submodules) {
gitmodules_config();
compile_submodule_options(&opt, &pathspec, cached, untracked,
@@ -1245,8 +1275,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (!use_index || untracked) {
int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
- if (list.nr)
- die(_("--no-index or --untracked cannot be used with revs."));
hit = grep_directory(&opt, &pathspec, use_exclude, use_index);
} else if (0 <= opt_exclude) {
die(_("--[no-]exclude-standard cannot be used for tracked contents."));
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 1d4d6a0078..8a6acb0ec6 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -338,7 +338,7 @@ int init_db(const char *git_dir, const char *real_git_dir,
{
int reinit;
int exist_ok = flags & INIT_DB_EXIST_OK;
- char *original_git_dir = real_pathdup(git_dir);
+ char *original_git_dir = real_pathdup(git_dir, 1);
if (real_git_dir) {
struct stat st;
@@ -489,7 +489,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
if (real_git_dir && !is_absolute_path(real_git_dir))
- real_git_dir = real_pathdup(real_git_dir);
+ real_git_dir = real_pathdup(real_git_dir, 1);
if (argc == 1) {
int mkdir_tried = 0;
@@ -560,7 +560,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
const char *git_dir_parent = strrchr(git_dir, '/');
if (git_dir_parent) {
char *rel = xstrndup(git_dir, git_dir_parent - git_dir);
- git_work_tree_cfg = real_pathdup(rel);
+ git_work_tree_cfg = real_pathdup(rel, 1);
free(rel);
}
if (!git_work_tree_cfg)
diff --git a/builtin/merge.c b/builtin/merge.c
index a96d4fb501..848a298556 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -438,7 +438,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
char *found_ref;
int len, early;
- strbuf_branchname(&bname, remote);
+ strbuf_branchname(&bname, remote, 0);
remote = bname.buf;
memset(branch_head, 0, sizeof(branch_head));
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 8841f8b366..c7af475485 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -1539,6 +1539,8 @@ static int pack_offset_sort(const void *_a, const void *_b)
* 2. Updating our size/type to the non-delta representation. These were
* either not recorded initially (size) or overwritten with the delta type
* (type) when check_object() decided to reuse the delta.
+ *
+ * 3. Resetting our delta depth, as we are now a base object.
*/
static void drop_reused_delta(struct object_entry *entry)
{
@@ -1552,6 +1554,7 @@ static void drop_reused_delta(struct object_entry *entry)
p = &(*p)->delta_sibling;
}
entry->delta = NULL;
+ entry->depth = 0;
oi.sizep = &entry->size;
oi.typep = &entry->type;
@@ -1570,39 +1573,123 @@ static void drop_reused_delta(struct object_entry *entry)
* Follow the chain of deltas from this entry onward, throwing away any links
* that cause us to hit a cycle (as determined by the DFS state flags in
* the entries).
+ *
+ * We also detect too-long reused chains that would violate our --depth
+ * limit.
*/
static void break_delta_chains(struct object_entry *entry)
{
- /* If it's not a delta, it can't be part of a cycle. */
- if (!entry->delta) {
- entry->dfs_state = DFS_DONE;
- return;
- }
+ /*
+ * The actual depth of each object we will write is stored as an int,
+ * as it cannot exceed our int "depth" limit. But before we break
+ * changes based no that limit, we may potentially go as deep as the
+ * number of objects, which is elsewhere bounded to a uint32_t.
+ */
+ uint32_t total_depth;
+ struct object_entry *cur, *next;
+
+ for (cur = entry, total_depth = 0;
+ cur;
+ cur = cur->delta, total_depth++) {
+ if (cur->dfs_state == DFS_DONE) {
+ /*
+ * We've already seen this object and know it isn't
+ * part of a cycle. We do need to append its depth
+ * to our count.
+ */
+ total_depth += cur->depth;
+ break;
+ }
- switch (entry->dfs_state) {
- case DFS_NONE:
/*
- * This is the first time we've seen the object. We mark it as
- * part of the active potential cycle and recurse.
+ * We break cycles before looping, so an ACTIVE state (or any
+ * other cruft which made its way into the state variable)
+ * is a bug.
*/
- entry->dfs_state = DFS_ACTIVE;
- break_delta_chains(entry->delta);
- entry->dfs_state = DFS_DONE;
- break;
+ if (cur->dfs_state != DFS_NONE)
+ die("BUG: confusing delta dfs state in first pass: %d",
+ cur->dfs_state);
- case DFS_DONE:
- /* object already examined, and not part of a cycle */
- break;
+ /*
+ * Now we know this is the first time we've seen the object. If
+ * it's not a delta, we're done traversing, but we'll mark it
+ * done to save time on future traversals.
+ */
+ if (!cur->delta) {
+ cur->dfs_state = DFS_DONE;
+ break;
+ }
- case DFS_ACTIVE:
/*
- * We found a cycle that needs broken. It would be correct to
- * break any link in the chain, but it's convenient to
- * break this one.
+ * Mark ourselves as active and see if the next step causes
+ * us to cycle to another active object. It's important to do
+ * this _before_ we loop, because it impacts where we make the
+ * cut, and thus how our total_depth counter works.
+ * E.g., We may see a partial loop like:
+ *
+ * A -> B -> C -> D -> B
+ *
+ * Cutting B->C breaks the cycle. But now the depth of A is
+ * only 1, and our total_depth counter is at 3. The size of the
+ * error is always one less than the size of the cycle we
+ * broke. Commits C and D were "lost" from A's chain.
+ *
+ * If we instead cut D->B, then the depth of A is correct at 3.
+ * We keep all commits in the chain that we examined.
*/
- drop_reused_delta(entry);
- entry->dfs_state = DFS_DONE;
- break;
+ cur->dfs_state = DFS_ACTIVE;
+ if (cur->delta->dfs_state == DFS_ACTIVE) {
+ drop_reused_delta(cur);
+ cur->dfs_state = DFS_DONE;
+ break;
+ }
+ }
+
+ /*
+ * And now that we've gone all the way to the bottom of the chain, we
+ * need to clear the active flags and set the depth fields as
+ * appropriate. Unlike the loop above, which can quit when it drops a
+ * delta, we need to keep going to look for more depth cuts. So we need
+ * an extra "next" pointer to keep going after we reset cur->delta.
+ */
+ for (cur = entry; cur; cur = next) {
+ next = cur->delta;
+
+ /*
+ * We should have a chain of zero or more ACTIVE states down to
+ * a final DONE. We can quit after the DONE, because either it
+ * has no bases, or we've already handled them in a previous
+ * call.
+ */
+ if (cur->dfs_state == DFS_DONE)
+ break;
+ else if (cur->dfs_state != DFS_ACTIVE)
+ die("BUG: confusing delta dfs state in second pass: %d",
+ cur->dfs_state);
+
+ /*
+ * If the total_depth is more than depth, then we need to snip
+ * the chain into two or more smaller chains that don't exceed
+ * the maximum depth. Most of the resulting chains will contain
+ * (depth + 1) entries (i.e., depth deltas plus one base), and
+ * the last chain (i.e., the one containing entry) will contain
+ * whatever entries are left over, namely
+ * (total_depth % (depth + 1)) of them.
+ *
+ * Since we are iterating towards decreasing depth, we need to
+ * decrement total_depth as we go, and we need to write to the
+ * entry what its final depth will be after all of the
+ * snipping. Since we're snipping into chains of length (depth
+ * + 1) entries, the final depth of an entry will be its
+ * original depth modulo (depth + 1). Any time we encounter an
+ * entry whose final depth is supposed to be zero, we snip it
+ * from its delta base, thereby making it so.
+ */
+ cur->depth = (total_depth--) % (depth + 1);
+ if (!cur->depth)
+ drop_reused_delta(cur);
+
+ cur->dfs_state = DFS_DONE;
}
}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 1dbb8a0692..8672825e2e 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1664,8 +1664,11 @@ static const char *unpack(int err_fd, struct shallow_info *si)
}
tmp_objdir = tmp_objdir_create();
- if (!tmp_objdir)
+ if (!tmp_objdir) {
+ if (err_fd > 0)
+ close(err_fd);
return "unable to create temporary object directory";
+ }
child.env = tmp_objdir_env(tmp_objdir);
/*
diff --git a/builtin/remote.c b/builtin/remote.c
index 5339ed6ad1..7682206c1e 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -769,7 +769,9 @@ static int rm(int argc, const char **argv)
strbuf_reset(&buf);
strbuf_addf(&buf, "branch.%s.%s",
item->string, *k);
- git_config_set(buf.buf, NULL);
+ result = git_config_set_gently(buf.buf, NULL);
+ if (result && result != CONFIG_NOTHING_SET)
+ die(_("could not unset '%s'"), buf.buf);
}
}
}
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 974f3403ab..19756595d5 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -275,8 +275,7 @@ static void show_one_commit(struct commit *commit, int no_name)
pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty);
pretty_str = pretty.buf;
}
- if (starts_with(pretty_str, "[PATCH] "))
- pretty_str += 8;
+ skip_prefix(pretty_str, "[PATCH] ", &pretty_str);
if (!no_name) {
if (name && name->head_name) {
@@ -470,18 +469,14 @@ static void snarf_refs(int head, int remotes)
}
}
-static int rev_is_head(char *head, int headlen, char *name,
+static int rev_is_head(const char *head, const char *name,
unsigned char *head_sha1, unsigned char *sha1)
{
- if ((!head[0]) ||
- (head_sha1 && sha1 && hashcmp(head_sha1, sha1)))
+ if (!head || (head_sha1 && sha1 && hashcmp(head_sha1, sha1)))
return 0;
- if (starts_with(head, "refs/heads/"))
- head += 11;
- if (starts_with(name, "refs/heads/"))
- name += 11;
- else if (starts_with(name, "heads/"))
- name += 6;
+ skip_prefix(head, "refs/heads/", &head);
+ if (!skip_prefix(name, "refs/heads/", &name))
+ skip_prefix(name, "heads/", &name);
return !strcmp(head, name);
}
@@ -620,9 +615,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
int all_heads = 0, all_remotes = 0;
int all_mask, all_revs;
enum rev_sort_order sort_order = REV_SORT_IN_GRAPH_ORDER;
- char head[128];
- const char *head_p;
- int head_len;
+ char *head;
struct object_id head_oid;
int merge_base = 0;
int independent = 0;
@@ -787,32 +780,24 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
snarf_refs(all_heads, all_remotes);
}
- head_p = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
- head_oid.hash, NULL);
- if (head_p) {
- head_len = strlen(head_p);
- memcpy(head, head_p, head_len + 1);
- }
- else {
- head_len = 0;
- head[0] = 0;
- }
+ head = resolve_refdup("HEAD", RESOLVE_REF_READING,
+ head_oid.hash, NULL);
- if (with_current_branch && head_p) {
+ if (with_current_branch && head) {
int has_head = 0;
for (i = 0; !has_head && i < ref_name_cnt; i++) {
/* We are only interested in adding the branch
* HEAD points at.
*/
if (rev_is_head(head,
- head_len,
ref_name[i],
head_oid.hash, NULL))
has_head++;
}
if (!has_head) {
- int offset = starts_with(head, "refs/heads/") ? 11 : 0;
- append_one_rev(head + offset);
+ const char *name = head;
+ skip_prefix(name, "refs/heads/", &name);
+ append_one_rev(name);
}
}
@@ -866,7 +851,6 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
for (i = 0; i < num_rev; i++) {
int j;
int is_head = rev_is_head(head,
- head_len,
ref_name[i],
head_oid.hash,
rev[i]->object.oid.hash);
diff --git a/cache.h b/cache.h
index 61fc86e6d7..b3cf851b59 100644
--- a/cache.h
+++ b/cache.h
@@ -1045,9 +1045,6 @@ static inline int is_empty_tree_oid(const struct object_id *oid)
return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN);
}
-
-int git_mkstemp(char *path, size_t n, const char *template);
-
/* set default permissions by passing mode arguments to open(2) */
int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
int git_mkstemp_mode(char *pattern, int mode);
@@ -1109,7 +1106,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,
int die_on_error);
const char *real_path(const char *path);
const char *real_path_if_valid(const char *path);
-char *real_pathdup(const char *path);
+char *real_pathdup(const char *path, int die_on_error);
const char *absolute_path(const char *path);
char *absolute_pathdup(const char *path);
const char *remove_leading_path(const char *in, const char *prefix);
@@ -1319,7 +1316,37 @@ extern char *oid_to_hex_r(char *out, const struct object_id *oid);
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
-extern int interpret_branch_name(const char *str, int len, struct strbuf *);
+/*
+ * This reads short-hand syntax that not only evaluates to a commit
+ * object name, but also can act as if the end user spelled the name
+ * of the branch from the command line.
+ *
+ * - "@{-N}" finds the name of the Nth previous branch we were on, and
+ * places the name of the branch in the given buf and returns the
+ * number of characters parsed if successful.
+ *
+ * - "<branch>@{upstream}" finds the name of the other ref that
+ * <branch> is configured to merge with (missing <branch> defaults
+ * to the current branch), and places the name of the branch in the
+ * given buf and returns the number of characters parsed if
+ * successful.
+ *
+ * If the input is not of the accepted format, it returns a negative
+ * number to signal an error.
+ *
+ * If the input was ok but there are not N branch switches in the
+ * reflog, it returns 0.
+ *
+ * If "allowed" is non-zero, it is a treated as a bitfield of allowable
+ * expansions: local branches ("refs/heads/"), remote branches
+ * ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is
+ * allowed, even ones to refs outside of those namespaces.
+ */
+#define INTERPRET_BRANCH_LOCAL (1<<0)
+#define INTERPRET_BRANCH_REMOTE (1<<1)
+#define INTERPRET_BRANCH_HEAD (1<<2)
+extern int interpret_branch_name(const char *str, int len, struct strbuf *,
+ unsigned allowed);
extern int get_oid_mb(const char *str, struct object_id *oid);
extern int validate_headref(const char *ref);
@@ -1819,8 +1846,11 @@ extern int git_config_include(const char *name, const char *value, void *data);
*
* (i.e., what gets handed to a config_fn_t). The caller provides the section;
* we return -1 if it does not match, 0 otherwise. The subsection and key
- * out-parameters are filled by the function (and subsection is NULL if it is
+ * out-parameters are filled by the function (and *subsection is NULL if it is
* missing).
+ *
+ * If the subsection pointer-to-pointer passed in is NULL, returns 0 only if
+ * there is no subsection at all.
*/
extern int parse_config_key(const char *var,
const char *section,
diff --git a/ci/run-linux32-build.sh b/ci/run-linux32-build.sh
new file mode 100755
index 0000000000..e30fb2cddc
--- /dev/null
+++ b/ci/run-linux32-build.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# Build and test Git in a 32-bit environment
+#
+# Usage:
+# run-linux32-build.sh [host-user-id]
+#
+
+# Update packages to the latest available versions
+linux32 --32bit i386 sh -c '
+ apt update >/dev/null &&
+ apt install -y build-essential libcurl4-openssl-dev libssl-dev \
+ libexpat-dev gettext python >/dev/null
+' &&
+
+# If this script runs inside a docker container, then all commands are
+# usually executed as root. Consequently, the host user might not be
+# able to access the test output files.
+# If a host user id is given, then create a user "ci" with the host user
+# id to make everything accessible to the host user.
+HOST_UID=$1 &&
+CI_USER=$USER &&
+test -z $HOST_UID || (CI_USER="ci" && useradd -u $HOST_UID $CI_USER) &&
+
+# Build and test
+linux32 --32bit i386 su -m -l $CI_USER -c '
+ cd /usr/src/git &&
+ make --jobs=2 &&
+ make --quiet test
+'
diff --git a/commit.c b/commit.c
index 2cf85158b4..fab8269731 100644
--- a/commit.c
+++ b/commit.c
@@ -1308,11 +1308,11 @@ void for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data)
static inline int standard_header_field(const char *field, size_t len)
{
- return ((len == 4 && !memcmp(field, "tree ", 5)) ||
- (len == 6 && !memcmp(field, "parent ", 7)) ||
- (len == 6 && !memcmp(field, "author ", 7)) ||
- (len == 9 && !memcmp(field, "committer ", 10)) ||
- (len == 8 && !memcmp(field, "encoding ", 9)));
+ return ((len == 4 && !memcmp(field, "tree", 4)) ||
+ (len == 6 && !memcmp(field, "parent", 6)) ||
+ (len == 6 && !memcmp(field, "author", 6)) ||
+ (len == 9 && !memcmp(field, "committer", 9)) ||
+ (len == 8 && !memcmp(field, "encoding", 8)));
}
static int excluded_header_field(const char *field, size_t len, const char **exclude)
@@ -1322,8 +1322,7 @@ static int excluded_header_field(const char *field, size_t len, const char **exc
while (*exclude) {
size_t xlen = strlen(*exclude);
- if (len == xlen &&
- !memcmp(field, *exclude, xlen) && field[xlen] == ' ')
+ if (len == xlen && !memcmp(field, *exclude, xlen))
return 1;
exclude++;
}
@@ -1354,12 +1353,11 @@ static struct commit_extra_header *read_commit_extra_header_lines(
strbuf_reset(&buf);
it = NULL;
- eof = strchr(line, ' ');
- if (next <= eof)
+ eof = memchr(line, ' ', next - line);
+ if (!eof)
eof = next;
-
- if (standard_header_field(line, eof - line) ||
- excluded_header_field(line, eof - line, exclude))
+ else if (standard_header_field(line, eof - line) ||
+ excluded_header_field(line, eof - line, exclude))
continue;
it = xcalloc(1, sizeof(*it));
diff --git a/config.c b/config.c
index c6b874a7bf..0e9e1ebefc 100644
--- a/config.c
+++ b/config.c
@@ -201,11 +201,105 @@ void git_config_push_parameter(const char *text)
strbuf_release(&env);
}
+static inline int iskeychar(int c)
+{
+ return isalnum(c) || c == '-';
+}
+
+/*
+ * Auxiliary function to sanity-check and split the key into the section
+ * identifier and variable name.
+ *
+ * Returns 0 on success, -1 when there is an invalid character in the key and
+ * -2 if there is no section name in the key.
+ *
+ * store_key - pointer to char* which will hold a copy of the key with
+ * lowercase section and variable name
+ * baselen - pointer to int which will hold the length of the
+ * section + subsection part, can be NULL
+ */
+static int git_config_parse_key_1(const char *key, char **store_key, int *baselen_, int quiet)
+{
+ int i, dot, baselen;
+ const char *last_dot = strrchr(key, '.');
+
+ /*
+ * Since "key" actually contains the section name and the real
+ * key name separated by a dot, we have to know where the dot is.
+ */
+
+ if (last_dot == NULL || last_dot == key) {
+ if (!quiet)
+ error("key does not contain a section: %s", key);
+ return -CONFIG_NO_SECTION_OR_NAME;
+ }
+
+ if (!last_dot[1]) {
+ if (!quiet)
+ error("key does not contain variable name: %s", key);
+ return -CONFIG_NO_SECTION_OR_NAME;
+ }
+
+ baselen = last_dot - key;
+ if (baselen_)
+ *baselen_ = baselen;
+
+ /*
+ * Validate the key and while at it, lower case it for matching.
+ */
+ if (store_key)
+ *store_key = xmallocz(strlen(key));
+
+ dot = 0;
+ for (i = 0; key[i]; i++) {
+ unsigned char c = key[i];
+ if (c == '.')
+ dot = 1;
+ /* Leave the extended basename untouched.. */
+ if (!dot || i > baselen) {
+ if (!iskeychar(c) ||
+ (i == baselen + 1 && !isalpha(c))) {
+ if (!quiet)
+ error("invalid key: %s", key);
+ goto out_free_ret_1;
+ }
+ c = tolower(c);
+ } else if (c == '\n') {
+ if (!quiet)
+ error("invalid key (newline): %s", key);
+ goto out_free_ret_1;
+ }
+ if (store_key)
+ (*store_key)[i] = c;
+ }
+
+ return 0;
+
+out_free_ret_1:
+ if (store_key) {
+ free(*store_key);
+ *store_key = NULL;
+ }
+ return -CONFIG_INVALID_KEY;
+}
+
+int git_config_parse_key(const char *key, char **store_key, int *baselen)
+{
+ return git_config_parse_key_1(key, store_key, baselen, 0);
+}
+
+int git_config_key_is_valid(const char *key)
+{
+ return !git_config_parse_key_1(key, NULL, NULL, 1);
+}
+
int git_config_parse_parameter(const char *text,
config_fn_t fn, void *data)
{
const char *value;
+ char *canonical_name;
struct strbuf **pair;
+ int ret;
pair = strbuf_split_str(text, '=', 2);
if (!pair[0])
@@ -223,13 +317,15 @@ int git_config_parse_parameter(const char *text,
strbuf_list_free(pair);
return error("bogus config parameter: %s", text);
}
- strbuf_tolower(pair[0]);
- if (fn(pair[0]->buf, value, data) < 0) {
- strbuf_list_free(pair);
- return -1;
+
+ if (git_config_parse_key(pair[0]->buf, &canonical_name, NULL)) {
+ ret = -1;
+ } else {
+ ret = (fn(canonical_name, value, data) < 0) ? -1 : 0;
+ free(canonical_name);
}
strbuf_list_free(pair);
- return 0;
+ return ret;
}
int git_config_from_parameters(config_fn_t fn, void *data)
@@ -356,11 +452,6 @@ static char *parse_value(void)
}
}
-static inline int iskeychar(int c)
-{
- return isalnum(c) || c == '-';
-}
-
static int get_value(config_fn_t fn, void *data, struct strbuf *name)
{
int c;
@@ -1990,93 +2081,6 @@ void git_config_set(const char *key, const char *value)
}
/*
- * Auxiliary function to sanity-check and split the key into the section
- * identifier and variable name.
- *
- * Returns 0 on success, -1 when there is an invalid character in the key and
- * -2 if there is no section name in the key.
- *
- * store_key - pointer to char* which will hold a copy of the key with
- * lowercase section and variable name
- * baselen - pointer to int which will hold the length of the
- * section + subsection part, can be NULL
- */
-static int git_config_parse_key_1(const char *key, char **store_key, int *baselen_, int quiet)
-{
- int i, dot, baselen;
- const char *last_dot = strrchr(key, '.');
-
- /*
- * Since "key" actually contains the section name and the real
- * key name separated by a dot, we have to know where the dot is.
- */
-
- if (last_dot == NULL || last_dot == key) {
- if (!quiet)
- error("key does not contain a section: %s", key);
- return -CONFIG_NO_SECTION_OR_NAME;
- }
-
- if (!last_dot[1]) {
- if (!quiet)
- error("key does not contain variable name: %s", key);
- return -CONFIG_NO_SECTION_OR_NAME;
- }
-
- baselen = last_dot - key;
- if (baselen_)
- *baselen_ = baselen;
-
- /*
- * Validate the key and while at it, lower case it for matching.
- */
- if (store_key)
- *store_key = xmallocz(strlen(key));
-
- dot = 0;
- for (i = 0; key[i]; i++) {
- unsigned char c = key[i];
- if (c == '.')
- dot = 1;
- /* Leave the extended basename untouched.. */
- if (!dot || i > baselen) {
- if (!iskeychar(c) ||
- (i == baselen + 1 && !isalpha(c))) {
- if (!quiet)
- error("invalid key: %s", key);
- goto out_free_ret_1;
- }
- c = tolower(c);
- } else if (c == '\n') {
- if (!quiet)
- error("invalid key (newline): %s", key);
- goto out_free_ret_1;
- }
- if (store_key)
- (*store_key)[i] = c;
- }
-
- return 0;
-
-out_free_ret_1:
- if (store_key) {
- free(*store_key);
- *store_key = NULL;
- }
- return -CONFIG_INVALID_KEY;
-}
-
-int git_config_parse_key(const char *key, char **store_key, int *baselen)
-{
- return git_config_parse_key_1(key, store_key, baselen, 0);
-}
-
-int git_config_key_is_valid(const char *key)
-{
- return !git_config_parse_key_1(key, NULL, NULL, 1);
-}
-
-/*
* If value==NULL, unset in (remove from) config,
* if value_regex!=NULL, disregard key/value pairs where value does not match.
* if value_regex==CONFIG_REGEX_NONE, do not match any existing values
@@ -2536,11 +2540,10 @@ int parse_config_key(const char *var,
const char **subsection, int *subsection_len,
const char **key)
{
- int section_len = strlen(section);
const char *dot;
/* Does it start with "section." ? */
- if (!starts_with(var, section) || var[section_len] != '.')
+ if (!skip_prefix(var, section, &var) || *var != '.')
return -1;
/*
@@ -2552,12 +2555,16 @@ int parse_config_key(const char *var,
*key = dot + 1;
/* Did we have a subsection at all? */
- if (dot == var + section_len) {
- *subsection = NULL;
- *subsection_len = 0;
+ if (dot == var) {
+ if (subsection) {
+ *subsection = NULL;
+ *subsection_len = 0;
+ }
}
else {
- *subsection = var + section_len + 1;
+ if (!subsection)
+ return -1;
+ *subsection = var + 1;
*subsection_len = dot - *subsection;
}
diff --git a/config.mak.uname b/config.mak.uname
index 447f36ac2e..399fe19271 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -27,7 +27,6 @@ endif
ifeq ($(uname_S),Linux)
HAVE_ALLOCA_H = YesPlease
NO_STRLCPY = YesPlease
- NO_MKSTEMPS = YesPlease
HAVE_PATHS_H = YesPlease
LIBC_CONTAINS_LIBINTL = YesPlease
HAVE_DEV_TTY = YesPlease
@@ -41,7 +40,6 @@ endif
ifeq ($(uname_S),GNU/kFreeBSD)
HAVE_ALLOCA_H = YesPlease
NO_STRLCPY = YesPlease
- NO_MKSTEMPS = YesPlease
HAVE_PATHS_H = YesPlease
DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
LIBC_CONTAINS_LIBINTL = YesPlease
@@ -55,7 +53,6 @@ ifeq ($(uname_S),UnixWare)
SHELL_PATH = /usr/local/bin/bash
NO_IPV6 = YesPlease
NO_HSTRERROR = YesPlease
- NO_MKSTEMPS = YesPlease
BASIC_CFLAGS += -Kthread
BASIC_CFLAGS += -I/usr/local/include
BASIC_LDFLAGS += -L/usr/local/lib
@@ -79,7 +76,6 @@ ifeq ($(uname_S),SCO_SV)
SHELL_PATH = /usr/bin/bash
NO_IPV6 = YesPlease
NO_HSTRERROR = YesPlease
- NO_MKSTEMPS = YesPlease
BASIC_CFLAGS += -I/usr/local/include
BASIC_LDFLAGS += -L/usr/local/lib
NO_STRCASESTR = YesPlease
@@ -122,7 +118,6 @@ ifeq ($(uname_S),SunOS)
NO_STRCASESTR = YesPlease
NO_MEMMEM = YesPlease
NO_MKDTEMP = YesPlease
- NO_MKSTEMPS = YesPlease
NO_REGEX = YesPlease
NO_MSGFMT_EXTENDED_OPTIONS = YesPlease
HAVE_DEV_TTY = YesPlease
@@ -168,7 +163,6 @@ ifeq ($(uname_O),Cygwin)
NO_D_TYPE_IN_DIRENT = YesPlease
NO_STRCASESTR = YesPlease
NO_MEMMEM = YesPlease
- NO_MKSTEMPS = YesPlease
NO_SYMLINK_HEAD = YesPlease
NO_IPV6 = YesPlease
OLD_ICONV = UnfortunatelyYes
@@ -233,7 +227,6 @@ ifeq ($(uname_S),NetBSD)
BASIC_CFLAGS += -I/usr/pkg/include
BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib
USE_ST_TIMESPEC = YesPlease
- NO_MKSTEMPS = YesPlease
HAVE_PATHS_H = YesPlease
HAVE_BSD_SYSCTL = YesPlease
endif
@@ -242,7 +235,6 @@ ifeq ($(uname_S),AIX)
NO_STRCASESTR = YesPlease
NO_MEMMEM = YesPlease
NO_MKDTEMP = YesPlease
- NO_MKSTEMPS = YesPlease
NO_STRLCPY = YesPlease
NO_NSEC = YesPlease
FREAD_READS_DIRECTORIES = UnfortunatelyYes
@@ -263,7 +255,6 @@ ifeq ($(uname_S),GNU)
# GNU/Hurd
HAVE_ALLOCA_H = YesPlease
NO_STRLCPY = YesPlease
- NO_MKSTEMPS = YesPlease
HAVE_PATHS_H = YesPlease
LIBC_CONTAINS_LIBINTL = YesPlease
endif
@@ -272,7 +263,6 @@ ifeq ($(uname_S),IRIX)
NO_UNSETENV = YesPlease
NO_STRCASESTR = YesPlease
NO_MEMMEM = YesPlease
- NO_MKSTEMPS = YesPlease
NO_MKDTEMP = YesPlease
# When compiled with the MIPSpro 7.4.4m compiler, and without pthreads
# (i.e. NO_PTHREADS is set), and _with_ MMAP (i.e. NO_MMAP is not set),
@@ -291,7 +281,6 @@ ifeq ($(uname_S),IRIX64)
NO_UNSETENV = YesPlease
NO_STRCASESTR = YesPlease
NO_MEMMEM = YesPlease
- NO_MKSTEMPS = YesPlease
NO_MKDTEMP = YesPlease
# When compiled with the MIPSpro 7.4.4m compiler, and without pthreads
# (i.e. NO_PTHREADS is set), and _with_ MMAP (i.e. NO_MMAP is not set),
@@ -311,7 +300,6 @@ ifeq ($(uname_S),HP-UX)
NO_SETENV = YesPlease
NO_STRCASESTR = YesPlease
NO_MEMMEM = YesPlease
- NO_MKSTEMPS = YesPlease
NO_STRLCPY = YesPlease
NO_MKDTEMP = YesPlease
NO_UNSETENV = YesPlease
@@ -352,7 +340,6 @@ ifeq ($(uname_S),Windows)
NO_ICONV = YesPlease
NO_STRTOUMAX = YesPlease
NO_MKDTEMP = YesPlease
- NO_MKSTEMPS = YesPlease
SNPRINTF_RETURNS_BOGUS = YesPlease
NO_SVN_TESTS = YesPlease
RUNTIME_PREFIX = YesPlease
@@ -402,7 +389,6 @@ ifeq ($(uname_S),Interix)
NO_MKDTEMP = YesPlease
NO_STRTOUMAX = YesPlease
NO_NSEC = YesPlease
- NO_MKSTEMPS = YesPlease
ifeq ($(uname_R),3.5)
NO_INET_NTOP = YesPlease
NO_INET_PTON = YesPlease
@@ -461,7 +447,6 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
NO_SETENV = YesPlease
NO_UNSETENV = YesPlease
NO_MKDTEMP = YesPlease
- NO_MKSTEMPS = YesPlease
# Currently libiconv-1.9.1.
OLD_ICONV = UnfortunatelyYes
NO_REGEX = YesPlease
@@ -503,7 +488,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))
NEEDS_LIBICONV = YesPlease
NO_STRTOUMAX = YesPlease
NO_MKDTEMP = YesPlease
- NO_MKSTEMPS = YesPlease
NO_SVN_TESTS = YesPlease
NO_PERL_MAKEMAKER = YesPlease
RUNTIME_PREFIX = YesPlease
@@ -515,7 +499,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))
OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
NO_REGEX = YesPlease
NO_PYTHON = YesPlease
- BLK_SHA1 = YesPlease
ETAGS_TARGET = ETAGS
NO_INET_PTON = YesPlease
NO_INET_NTOP = YesPlease
@@ -584,7 +567,6 @@ ifeq ($(uname_S),QNX)
NO_ICONV = YesPlease
NO_MEMMEM = YesPlease
NO_MKDTEMP = YesPlease
- NO_MKSTEMPS = YesPlease
NO_NSEC = YesPlease
NO_PTHREADS = YesPlease
NO_R_TO_GCC_LINKER = YesPlease
diff --git a/configure.ac b/configure.ac
index 0b15f04b10..128165529f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1050,12 +1050,6 @@ GIT_CHECK_FUNC(mkdtemp,
[NO_MKDTEMP=YesPlease])
GIT_CONF_SUBST([NO_MKDTEMP])
#
-# Define NO_MKSTEMPS if you don't have mkstemps in the C library.
-GIT_CHECK_FUNC(mkstemps,
-[NO_MKSTEMPS=],
-[NO_MKSTEMPS=YesPlease])
-GIT_CONF_SUBST([NO_MKSTEMPS])
-#
# Define NO_INITGROUPS if you don't have initgroups in the C library.
GIT_CHECK_FUNC(initgroups,
[NO_INITGROUPS=],
diff --git a/contrib/coccinelle/array.cocci b/contrib/coccinelle/array.cocci
index 2d7f25d99f..4ba98b7eaf 100644
--- a/contrib/coccinelle/array.cocci
+++ b/contrib/coccinelle/array.cocci
@@ -24,3 +24,19 @@ expression n;
@@
- memcpy(dst, src, n * sizeof(T));
+ COPY_ARRAY(dst, src, n);
+
+@@
+type T;
+T *ptr;
+expression n;
+@@
+- ptr = xmalloc(n * sizeof(*ptr));
++ ALLOC_ARRAY(ptr, n);
+
+@@
+type T;
+T *ptr;
+expression n;
+@@
+- ptr = xmalloc(n * sizeof(T));
++ ALLOC_ARRAY(ptr, n);
diff --git a/contrib/coccinelle/strbuf.cocci b/contrib/coccinelle/strbuf.cocci
index 63995f22ff..1d580e49b0 100644
--- a/contrib/coccinelle/strbuf.cocci
+++ b/contrib/coccinelle/strbuf.cocci
@@ -38,3 +38,9 @@ expression E1, E2, E3;
@@
- strbuf_addstr(E1, find_unique_abbrev(E2, E3));
+ strbuf_add_unique_abbrev(E1, E2, E3);
+
+@@
+expression E1, E2;
+@@
+- strbuf_addstr(E1, real_path(E2));
++ strbuf_add_real_path(E1, E2);
diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr
index 712a1377e2..1c3d87f861 100755
--- a/contrib/remote-helpers/git-remote-bzr
+++ b/contrib/remote-helpers/git-remote-bzr
@@ -1,13 +1,11 @@
-#!/usr/bin/env python
+#!/bin/sh
-import sys
-
-sys.stderr.write('WARNING: git-remote-bzr is now maintained independently.\n')
-sys.stderr.write('WARNING: For more information visit https://github.com/felipec/git-remote-bzr\n')
-
-sys.stderr.write('''WARNING:
+cat >&2 <<'EOT'
+WARNING: git-remote-bzr is now maintained independently.
+WARNING: For more information visit https://github.com/felipec/git-remote-bzr
+WARNING:
WARNING: You can pick a directory on your $PATH and download it, e.g.:
-WARNING: $ wget -O $HOME/bin/git-remote-bzr \\
+WARNING: $ wget -O $HOME/bin/git-remote-bzr \
WARNING: https://raw.github.com/felipec/git-remote-bzr/master/git-remote-bzr
WARNING: $ chmod +x $HOME/bin/git-remote-bzr
-''')
+EOT
diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg
index 4255ad6312..8e9188364c 100755
--- a/contrib/remote-helpers/git-remote-hg
+++ b/contrib/remote-helpers/git-remote-hg
@@ -1,13 +1,11 @@
-#!/usr/bin/env python
+#!/bin/sh
-import sys
-
-sys.stderr.write('WARNING: git-remote-hg is now maintained independently.\n')
-sys.stderr.write('WARNING: For more information visit https://github.com/felipec/git-remote-hg\n')
-
-sys.stderr.write('''WARNING:
+cat >&2 <<'EOT'
+WARNING: git-remote-hg is now maintained independently.
+WARNING: For more information visit https://github.com/felipec/git-remote-hg
+WARNING:
WARNING: You can pick a directory on your $PATH and download it, e.g.:
-WARNING: $ wget -O $HOME/bin/git-remote-hg \\
+WARNING: $ wget -O $HOME/bin/git-remote-hg \
WARNING: https://raw.github.com/felipec/git-remote-hg/master/git-remote-hg
WARNING: $ chmod +x $HOME/bin/git-remote-hg
-''')
+EOT
diff --git a/diff.c b/diff.c
index 051761be40..a628ac3a95 100644
--- a/diff.c
+++ b/diff.c
@@ -2870,8 +2870,25 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
s->should_free = 1;
return 0;
}
- if (size_only)
+
+ /*
+ * Even if the caller would be happy with getting
+ * only the size, we cannot return early at this
+ * point if the path requires us to run the content
+ * conversion.
+ */
+ if (size_only && !would_convert_to_git(s->path))
return 0;
+
+ /*
+ * Note: this check uses xsize_t(st.st_size) that may
+ * not be the true size of the blob after it goes
+ * through convert_to_git(). This may not strictly be
+ * correct, but the whole point of big_file_threshold
+ * and is_binary check being that we want to avoid
+ * opening the file and inspecting the contents, this
+ * is probably fine.
+ */
if ((flags & CHECK_BINARY) &&
s->size > big_file_threshold && s->is_binary == -1) {
s->is_binary = 1;
diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
index 9795ca1c15..341529b5a8 100644
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -81,12 +81,15 @@ static unsigned int contains(mmfile_t *mf, regex_t *regexp, kwset_t kws)
regmatch_t regmatch;
int flags = 0;
- while (*data &&
+ while (sz && *data &&
!regexec_buf(regexp, data, sz, 1, &regmatch, flags)) {
flags |= REG_NOTBOL;
data += regmatch.rm_eo;
- if (*data && regmatch.rm_so == regmatch.rm_eo)
+ sz -= regmatch.rm_eo;
+ if (sz && *data && regmatch.rm_so == regmatch.rm_eo) {
data++;
+ sz--;
+ }
cnt++;
}
diff --git a/dir.c b/dir.c
index 4541f9e146..aeeb5ce104 100644
--- a/dir.c
+++ b/dir.c
@@ -2730,8 +2730,8 @@ void connect_work_tree_and_git_dir(const char *work_tree_, const char *git_dir_)
{
struct strbuf file_name = STRBUF_INIT;
struct strbuf rel_path = STRBUF_INIT;
- char *git_dir = real_pathdup(git_dir_);
- char *work_tree = real_pathdup(work_tree_);
+ char *git_dir = real_pathdup(git_dir_, 1);
+ char *work_tree = real_pathdup(work_tree_, 1);
/* Update gitfile */
strbuf_addf(&file_name, "%s/.git", work_tree);
diff --git a/environment.c b/environment.c
index c07fb17fb7..42dc3106d2 100644
--- a/environment.c
+++ b/environment.c
@@ -259,7 +259,7 @@ void set_git_work_tree(const char *new_work_tree)
return;
}
git_work_tree_initialized = 1;
- work_tree = real_pathdup(new_work_tree);
+ work_tree = real_pathdup(new_work_tree, 1);
}
const char *get_git_work_tree(void)
diff --git a/ewah/ewah_io.c b/ewah/ewah_io.c
index 61f6a43579..f73210973f 100644
--- a/ewah/ewah_io.c
+++ b/ewah/ewah_io.c
@@ -142,8 +142,8 @@ int ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len)
* the endianness conversion in a separate pass to ensure
* we're loading 8-byte aligned words.
*/
- memcpy(self->buffer, ptr, self->buffer_size * sizeof(uint64_t));
- ptr += self->buffer_size * sizeof(uint64_t);
+ memcpy(self->buffer, ptr, self->buffer_size * sizeof(eword_t));
+ ptr += self->buffer_size * sizeof(eword_t);
for (i = 0; i < self->buffer_size; ++i)
self->buffer[i] = ntohll(self->buffer[i]);
diff --git a/fetch-pack.c b/fetch-pack.c
index 601f0779a1..f12bfcdbb1 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -578,7 +578,7 @@ static void filter_refs(struct fetch_pack_args *args,
break; /* definitely do not have it */
else if (cmp == 0) {
keep = 1; /* definitely have it */
- sought[i]->matched = 1;
+ sought[i]->match_status = REF_MATCHED;
}
i++;
}
@@ -598,22 +598,24 @@ static void filter_refs(struct fetch_pack_args *args,
}
/* Append unmatched requests to the list */
- if ((allow_unadvertised_object_request &
- (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) {
- for (i = 0; i < nr_sought; i++) {
- unsigned char sha1[20];
+ for (i = 0; i < nr_sought; i++) {
+ unsigned char sha1[20];
- ref = sought[i];
- if (ref->matched)
- continue;
- if (get_sha1_hex(ref->name, sha1) ||
- ref->name[40] != '\0' ||
- hashcmp(sha1, ref->old_oid.hash))
- continue;
+ ref = sought[i];
+ if (ref->match_status != REF_NOT_MATCHED)
+ continue;
+ if (get_sha1_hex(ref->name, sha1) ||
+ ref->name[40] != '\0' ||
+ hashcmp(sha1, ref->old_oid.hash))
+ continue;
- ref->matched = 1;
+ if ((allow_unadvertised_object_request &
+ (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) {
+ ref->match_status = REF_MATCHED;
*newtail = copy_ref(ref);
newtail = &(*newtail)->next;
+ } else {
+ ref->match_status = REF_UNADVERTISED_NOT_ALLOWED;
}
}
*refs = newlist;
@@ -1094,3 +1096,26 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
clear_shallow_info(&si);
return ref_cpy;
}
+
+int report_unmatched_refs(struct ref **sought, int nr_sought)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < nr_sought; i++) {
+ if (!sought[i])
+ continue;
+ switch (sought[i]->match_status) {
+ case REF_MATCHED:
+ continue;
+ case REF_NOT_MATCHED:
+ error(_("no such remote ref %s"), sought[i]->name);
+ break;
+ case REF_UNADVERTISED_NOT_ALLOWED:
+ error(_("Server does not allow request for unadvertised object %s"),
+ sought[i]->name);
+ break;
+ }
+ ret = 1;
+ }
+ return ret;
+}
diff --git a/fetch-pack.h b/fetch-pack.h
index c912e3d321..a2d46e6e75 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -45,4 +45,10 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
struct sha1_array *shallow,
char **pack_lockfile);
+/*
+ * Print an appropriate error message for each sought ref that wasn't
+ * matched. Return 0 if all sought refs were matched, otherwise 1.
+ */
+int report_unmatched_refs(struct ref **sought, int nr_sought);
+
#endif
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 982593c89e..f5c816e273 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -92,7 +92,7 @@ sub colored {
}
# command line options
-my $cmd;
+my $patch_mode_only;
my $patch_mode;
my $patch_mode_revision;
@@ -1299,7 +1299,7 @@ sub patch_update_cmd {
}
return 0;
}
- if ($patch_mode) {
+ if ($patch_mode_only) {
@them = @mods;
}
else {
@@ -1721,7 +1721,7 @@ sub process_args {
die sprintf(__("invalid argument %s, expecting --"),
$arg) unless $arg eq "--";
%patch_mode_flavour = %{$patch_modes{$patch_mode}};
- $cmd = 1;
+ $patch_mode_only = 1;
}
elsif ($arg ne "--") {
die sprintf(__("invalid argument %s, expecting --"), $arg);
@@ -1758,7 +1758,7 @@ sub main_loop {
process_args();
refresh();
-if ($cmd) {
+if ($patch_mode_only) {
patch_update_cmd();
}
else {
diff --git a/git-compat-util.h b/git-compat-util.h
index ef6d560e15..e626851fe9 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -639,11 +639,6 @@ extern int gitsetenv(const char *, const char *, int);
extern char *gitmkdtemp(char *);
#endif
-#ifdef NO_MKSTEMPS
-#define mkstemps gitmkstemps
-extern int gitmkstemps(char *, int);
-#endif
-
#ifdef NO_UNSETENV
#define unsetenv gitunsetenv
extern void gitunsetenv(const char *);
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index 86b2ff1e07..2b8cdba157 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -46,6 +46,8 @@ git_commit_non_empty_tree()
{
if test $# = 3 && test "$1" = $(git rev-parse "$3^{tree}"); then
map "$3"
+ elif test $# = 1 && test "$1" = 4b825dc642cb6eb9a060e54bf8d69288fbee4904; then
+ :
else
git commit-tree "$@"
fi
diff --git a/git-send-email.perl b/git-send-email.perl
index 068d60b3e6..eea0a517f7 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -1563,7 +1563,7 @@ foreach my $t (@files) {
# Now parse the message body
while(<$fh>) {
$message .= $_;
- if (/^(Signed-off-by|Cc): (.*)$/i) {
+ if (/^(Signed-off-by|Cc): ([^>]*>?)/i) {
chomp;
my ($what, $c) = ($1, $2);
chomp $c;
diff --git a/http-walker.c b/http-walker.c
index b34b6ace7c..ee049cb13d 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -168,6 +168,11 @@ static int is_alternate_allowed(const char *url)
};
int i;
+ if (http_follow_config != HTTP_FOLLOW_ALWAYS) {
+ warning("alternate disabled by http.followRedirects: %s", url);
+ return 0;
+ }
+
for (i = 0; i < ARRAY_SIZE(protocols); i++) {
const char *end;
if (skip_prefix(url, protocols[i], &end) &&
@@ -296,13 +301,16 @@ static void process_alternates_response(void *callback_data)
okay = 1;
}
}
- /* skip "objects\n" at end */
if (okay) {
struct strbuf target = STRBUF_INIT;
strbuf_add(&target, base, serverlen);
- strbuf_add(&target, data + i, posn - i - 7);
-
- if (is_alternate_allowed(target.buf)) {
+ strbuf_add(&target, data + i, posn - i);
+ if (!strbuf_strip_suffix(&target, "objects")) {
+ warning("ignoring alternate that does"
+ " not end in 'objects': %s",
+ target.buf);
+ strbuf_release(&target);
+ } else if (is_alternate_allowed(target.buf)) {
warning("adding alternate object store: %s",
target.buf);
newalt = xmalloc(sizeof(*newalt));
@@ -314,6 +322,8 @@ static void process_alternates_response(void *callback_data)
while (tail->next != NULL)
tail = tail->next;
tail->next = newalt;
+ } else {
+ strbuf_release(&target);
}
}
}
@@ -331,9 +341,6 @@ static void fetch_alternates(struct walker *walker, const char *base)
struct alternates_request alt_req;
struct walker_data *cdata = walker->data;
- if (http_follow_config != HTTP_FOLLOW_ALWAYS)
- return;
-
/*
* If another request has already started fetching alternates,
* wait for them to arrive and return to processing this request's
diff --git a/http.c b/http.c
index 90a1c0f113..96d84bbed3 100644
--- a/http.c
+++ b/http.c
@@ -109,7 +109,7 @@ static int curl_save_cookies;
struct credential http_auth = CREDENTIAL_INIT;
static int http_proactive_auth;
static const char *user_agent;
-static int curl_empty_auth;
+static int curl_empty_auth = -1;
enum http_follow_config http_follow_config = HTTP_FOLLOW_INITIAL;
@@ -125,6 +125,14 @@ static struct credential cert_auth = CREDENTIAL_INIT;
static int ssl_cert_password_required;
#ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
static unsigned long http_auth_methods = CURLAUTH_ANY;
+static int http_auth_methods_restricted;
+/* Modes for which empty_auth cannot actually help us. */
+static unsigned long empty_auth_useless =
+ CURLAUTH_BASIC
+#ifdef CURLAUTH_DIGEST_IE
+ | CURLAUTH_DIGEST_IE
+#endif
+ | CURLAUTH_DIGEST;
#endif
static struct curl_slist *pragma_header;
@@ -333,7 +341,10 @@ static int http_options(const char *var, const char *value, void *cb)
return git_config_string(&user_agent, var, value);
if (!strcmp("http.emptyauth", var)) {
- curl_empty_auth = git_config_bool(var, value);
+ if (value && !strcmp("auto", value))
+ curl_empty_auth = -1;
+ else
+ curl_empty_auth = git_config_bool(var, value);
return 0;
}
@@ -382,10 +393,37 @@ static int http_options(const char *var, const char *value, void *cb)
return git_default_config(var, value, cb);
}
+static int curl_empty_auth_enabled(void)
+{
+ if (curl_empty_auth >= 0)
+ return curl_empty_auth;
+
+#ifndef LIBCURL_CAN_HANDLE_AUTH_ANY
+ /*
+ * Our libcurl is too old to do AUTH_ANY in the first place;
+ * just default to turning the feature off.
+ */
+#else
+ /*
+ * In the automatic case, kick in the empty-auth
+ * hack as long as we would potentially try some
+ * method more exotic than "Basic" or "Digest".
+ *
+ * But only do this when this is our second or
+ * subsequent request, as by then we know what
+ * methods are available.
+ */
+ if (http_auth_methods_restricted &&
+ (http_auth_methods & ~empty_auth_useless))
+ return 1;
+#endif
+ return 0;
+}
+
static void init_curl_http_auth(CURL *result)
{
if (!http_auth.username || !*http_auth.username) {
- if (curl_empty_auth)
+ if (curl_empty_auth_enabled())
curl_easy_setopt(result, CURLOPT_USERPWD, ":");
return;
}
@@ -1079,7 +1117,7 @@ struct active_request_slot *get_active_slot(void)
#ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods);
#endif
- if (http_auth.password || curl_empty_auth)
+ if (http_auth.password || curl_empty_auth_enabled())
init_curl_http_auth(slot->curl);
return slot;
@@ -1347,6 +1385,10 @@ static int handle_curl_result(struct slot_results *results)
} else {
#ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE;
+ if (results->auth_avail) {
+ http_auth_methods &= results->auth_avail;
+ http_auth_methods_restricted = 1;
+ }
#endif
return HTTP_REAUTH;
}
@@ -1727,6 +1769,9 @@ static int http_request_reauth(const char *url,
{
int ret = http_request(url, result, target, options);
+ if (ret != HTTP_OK && ret != HTTP_REAUTH)
+ return ret;
+
if (options && options->effective_url && options->base_url) {
if (update_url_from_redirect(options->base_url,
url, options->effective_url)) {
diff --git a/ident.c b/ident.c
index ac4ae02b48..c0364fe3a1 100644
--- a/ident.c
+++ b/ident.c
@@ -153,7 +153,7 @@ static void copy_email(const struct passwd *pw, struct strbuf *email,
const char *ident_default_name(void)
{
- if (!git_default_name.len) {
+ if (!(ident_config_given & IDENT_NAME_GIVEN) && !git_default_name.len) {
copy_gecos(xgetpwuid_self(&default_name_is_bogus), &git_default_name);
strbuf_trim(&git_default_name);
}
@@ -162,7 +162,7 @@ const char *ident_default_name(void)
const char *ident_default_email(void)
{
- if (!git_default_email.len) {
+ if (!(ident_config_given & IDENT_MAIL_GIVEN) && !git_default_email.len) {
const char *email = getenv("EMAIL");
if (email && email[0]) {
@@ -203,6 +203,15 @@ static int crud(unsigned char c)
c == '\'';
}
+static int has_non_crud(const char *str)
+{
+ for (; *str; str++) {
+ if (!crud(*str))
+ return 1;
+ }
+ return 0;
+}
+
/*
* Copy over a string to the destination, but avoid special
* characters ('\n', '<' and '>') and remove crud at the end
@@ -351,19 +360,32 @@ const char *fmt_ident(const char *name, const char *email,
int want_date = !(flag & IDENT_NO_DATE);
int want_name = !(flag & IDENT_NO_NAME);
+ if (!email) {
+ if (strict && ident_use_config_only
+ && !(ident_config_given & IDENT_MAIL_GIVEN)) {
+ fputs(_(env_hint), stderr);
+ die(_("no email was given and auto-detection is disabled"));
+ }
+ email = ident_default_email();
+ if (strict && default_email_is_bogus) {
+ fputs(_(env_hint), stderr);
+ die(_("unable to auto-detect email address (got '%s')"), email);
+ }
+ }
+
if (want_name) {
int using_default = 0;
if (!name) {
if (strict && ident_use_config_only
&& !(ident_config_given & IDENT_NAME_GIVEN)) {
fputs(_(env_hint), stderr);
- die("no name was given and auto-detection is disabled");
+ die(_("no name was given and auto-detection is disabled"));
}
name = ident_default_name();
using_default = 1;
if (strict && default_name_is_bogus) {
fputs(_(env_hint), stderr);
- die("unable to auto-detect name (got '%s')", name);
+ die(_("unable to auto-detect name (got '%s')"), name);
}
}
if (!*name) {
@@ -371,24 +393,13 @@ const char *fmt_ident(const char *name, const char *email,
if (strict) {
if (using_default)
fputs(_(env_hint), stderr);
- die("empty ident name (for <%s>) not allowed", email);
+ die(_("empty ident name (for <%s>) not allowed"), email);
}
pw = xgetpwuid_self(NULL);
name = pw->pw_name;
}
- }
-
- if (!email) {
- if (strict && ident_use_config_only
- && !(ident_config_given & IDENT_MAIL_GIVEN)) {
- fputs(_(env_hint), stderr);
- die("no email was given and auto-detection is disabled");
- }
- email = ident_default_email();
- if (strict && default_email_is_bogus) {
- fputs(_(env_hint), stderr);
- die("unable to auto-detect email address (got '%s')", email);
- }
+ if (strict && !has_non_crud(name))
+ die(_("name consists only of disallowed characters: %s"), name);
}
strbuf_reset(&ident);
@@ -403,7 +414,7 @@ const char *fmt_ident(const char *name, const char *email,
strbuf_addch(&ident, ' ');
if (date_str && date_str[0]) {
if (parse_date(date_str, &ident) < 0)
- die("invalid date format: %s", date_str);
+ die(_("invalid date format: %s"), date_str);
}
else
strbuf_addstr(&ident, ident_default_date());
diff --git a/line-log.c b/line-log.c
index 65f3558b3b..a23b910471 100644
--- a/line-log.c
+++ b/line-log.c
@@ -43,9 +43,10 @@ void range_set_release(struct range_set *rs)
static void range_set_copy(struct range_set *dst, struct range_set *src)
{
range_set_init(dst, src->nr);
- memcpy(dst->ranges, src->ranges, src->nr*sizeof(struct range_set));
+ COPY_ARRAY(dst->ranges, src->ranges, src->nr);
dst->nr = src->nr;
}
+
static void range_set_move(struct range_set *dst, struct range_set *src)
{
range_set_release(dst);
@@ -144,7 +145,7 @@ void sort_and_merge_range_set(struct range_set *rs)
static void range_set_union(struct range_set *out,
struct range_set *a, struct range_set *b)
{
- int i = 0, j = 0, o = 0;
+ int i = 0, j = 0;
struct range *ra = a->ranges;
struct range *rb = b->ranges;
/* cannot make an alias of out->ranges: it may change during grow */
@@ -167,16 +168,15 @@ static void range_set_union(struct range_set *out,
new = &rb[j++];
if (new->start == new->end)
; /* empty range */
- else if (!o || out->ranges[o-1].end < new->start) {
+ else if (!out->nr || out->ranges[out->nr-1].end < new->start) {
range_set_grow(out, 1);
- out->ranges[o].start = new->start;
- out->ranges[o].end = new->end;
- o++;
- } else if (out->ranges[o-1].end < new->end) {
- out->ranges[o-1].end = new->end;
+ out->ranges[out->nr].start = new->start;
+ out->ranges[out->nr].end = new->end;
+ out->nr++;
+ } else if (out->ranges[out->nr-1].end < new->end) {
+ out->ranges[out->nr-1].end = new->end;
}
}
- out->nr = o;
}
/*
diff --git a/pack-objects.h b/pack-objects.h
index cc9b9a9b90..03f1191659 100644
--- a/pack-objects.h
+++ b/pack-objects.h
@@ -30,12 +30,16 @@ struct object_entry {
/*
* State flags for depth-first search used for analyzing delta cycles.
+ *
+ * The depth is measured in delta-links to the base (so if A is a delta
+ * against B, then A has a depth of 1, and B a depth of 0).
*/
enum {
DFS_NONE = 0,
DFS_ACTIVE,
DFS_DONE
} dfs_state;
+ int depth;
};
struct packing_data {
diff --git a/progress.c b/progress.c
index 76a88c573f..29378caa05 100644
--- a/progress.c
+++ b/progress.c
@@ -243,21 +243,18 @@ void stop_progress_msg(struct progress **p_progress, const char *msg)
*p_progress = NULL;
if (progress->last_value != -1) {
/* Force the last update */
- char buf[128], *bufp;
- size_t len = strlen(msg) + 5;
+ char *buf;
struct throughput *tp = progress->throughput;
- bufp = (len < sizeof(buf)) ? buf : xmallocz(len);
if (tp) {
unsigned int rate = !tp->avg_misecs ? 0 :
tp->avg_bytes / tp->avg_misecs;
throughput_string(&tp->display, tp->curr_total, rate);
}
progress_update = 1;
- xsnprintf(bufp, len + 1, ", %s.\n", msg);
- display(progress, progress->last_value, bufp);
- if (buf != bufp)
- free(bufp);
+ buf = xstrfmt(", %s.\n", msg);
+ display(progress, progress->last_value, buf);
+ free(buf);
}
clear_progress_signal();
if (progress->throughput)
diff --git a/refs.c b/refs.c
index cd36b64ed9..2d717742d6 100644
--- a/refs.c
+++ b/refs.c
@@ -404,7 +404,7 @@ int refname_match(const char *abbrev_name, const char *full_name)
static char *substitute_branch_name(const char **string, int *len)
{
struct strbuf buf = STRBUF_INIT;
- int ret = interpret_branch_name(*string, *len, &buf);
+ int ret = interpret_branch_name(*string, *len, &buf, 0);
if (ret == *len) {
size_t size;
@@ -1034,10 +1034,10 @@ static struct string_list *hide_refs;
int parse_hide_refs_config(const char *var, const char *value, const char *section)
{
+ const char *key;
if (!strcmp("transfer.hiderefs", var) ||
- /* NEEDSWORK: use parse_config_key() once both are merged */
- (starts_with(var, section) && var[strlen(section)] == '.' &&
- !strcmp(var + strlen(section), ".hiderefs"))) {
+ (!parse_config_key(var, section, NULL, NULL, &key) &&
+ !strcmp(key, "hiderefs"))) {
char *ref;
int len;
diff --git a/remote.c b/remote.c
index bf1bf23091..9f83fe2c4c 100644
--- a/remote.c
+++ b/remote.c
@@ -693,7 +693,7 @@ static struct remote *remote_get_1(const char *name,
name = get_default(current_branch, &name_given);
ret = make_remote(name, 0);
- if (valid_remote_nick(name)) {
+ if (valid_remote_nick(name) && have_git_dir()) {
if (!valid_remote(ret))
read_remotes_file(ret);
if (!valid_remote(ret))
diff --git a/remote.h b/remote.h
index a5bbbe0ef9..dd8c517577 100644
--- a/remote.h
+++ b/remote.h
@@ -89,8 +89,13 @@ struct ref {
force:1,
forced_update:1,
expect_old_sha1:1,
- deletion:1,
- matched:1;
+ deletion:1;
+
+ enum {
+ REF_NOT_MATCHED = 0, /* initial value */
+ REF_MATCHED,
+ REF_UNADVERTISED_NOT_ALLOWED
+ } match_status;
/*
* Order is important here, as we write to FETCH_HEAD
diff --git a/revision.c b/revision.c
index b37dbec378..771d079f6e 100644
--- a/revision.c
+++ b/revision.c
@@ -147,7 +147,7 @@ static void add_pending_object_with_path(struct rev_info *revs,
revs->no_walk = 0;
if (revs->reflog_info && obj->type == OBJ_COMMIT) {
struct strbuf buf = STRBUF_INIT;
- int len = interpret_branch_name(name, 0, &buf);
+ int len = interpret_branch_name(name, 0, &buf, 0);
int st;
if (0 < len && name[len] && buf.len)
diff --git a/run-command.c b/run-command.c
index 5227f78aea..574b81d3e8 100644
--- a/run-command.c
+++ b/run-command.c
@@ -48,7 +48,7 @@ static void cleanup_children(int sig, int in_signal)
kill(p->pid, sig);
- if (p->process->wait_after_clean) {
+ if (p->process && p->process->wait_after_clean) {
p->next = children_to_wait_for;
children_to_wait_for = p;
} else {
diff --git a/send-pack.c b/send-pack.c
index 6195b43e9a..d2d2a49a02 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -72,6 +72,7 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru
struct child_process po = CHILD_PROCESS_INIT;
FILE *po_in;
int i;
+ int rc;
i = 4;
if (args->use_thin_pack)
@@ -125,27 +126,44 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru
po.out = -1;
}
- if (finish_command(&po))
+ rc = finish_command(&po);
+ if (rc) {
+ /*
+ * For a normal non-zero exit, we assume pack-objects wrote
+ * something useful to stderr. For death by signal, though,
+ * we should mention it to the user. The exception is SIGPIPE
+ * (141), because that's a normal occurence if the remote end
+ * hangs up (and we'll report that by trying to read the unpack
+ * status).
+ */
+ if (rc > 128 && rc != 141)
+ error("pack-objects died of signal %d", rc - 128);
return -1;
+ }
+ return 0;
+}
+
+static int receive_unpack_status(int in)
+{
+ const char *line = packet_read_line(in, NULL);
+ if (!skip_prefix(line, "unpack ", &line))
+ return error(_("unable to parse remote unpack status: %s"), line);
+ if (strcmp(line, "ok"))
+ return error(_("remote unpack failed: %s"), line);
return 0;
}
static int receive_status(int in, struct ref *refs)
{
struct ref *hint;
- int ret = 0;
- char *line = packet_read_line(in, NULL);
- if (!starts_with(line, "unpack "))
- return error("did not receive remote status");
- if (strcmp(line, "unpack ok")) {
- error("unpack failed: %s", line + 7);
- ret = -1;
- }
+ int ret;
+
hint = NULL;
+ ret = receive_unpack_status(in);
while (1) {
char *refname;
char *msg;
- line = packet_read_line(in, NULL);
+ char *line = packet_read_line(in, NULL);
if (!line)
break;
if (!starts_with(line, "ok ") && !starts_with(line, "ng ")) {
@@ -557,6 +575,14 @@ int send_pack(struct send_pack_args *args,
close(out);
if (git_connection_is_socket(conn))
shutdown(fd[0], SHUT_WR);
+
+ /*
+ * Do not even bother with the return value; we know we
+ * are failing, and just want the error() side effects.
+ */
+ if (status_report)
+ receive_unpack_status(in);
+
if (use_sideband) {
close(demux.out);
finish_async(&demux);
diff --git a/setup.c b/setup.c
index 967f289f1e..8f64fbdfb2 100644
--- a/setup.c
+++ b/setup.c
@@ -254,7 +254,7 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)
if (!is_absolute_path(data.buf))
strbuf_addf(&path, "%s/", gitdir);
strbuf_addbuf(&path, &data);
- strbuf_addstr(sb, real_path(path.buf));
+ strbuf_add_real_path(sb, path.buf);
ret = 1;
} else {
strbuf_addstr(sb, gitdir);
@@ -698,7 +698,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,
/* --work-tree is set without --git-dir; use discovered one */
if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
if (offset != cwd->len && !is_absolute_path(gitdir))
- gitdir = real_pathdup(gitdir);
+ gitdir = real_pathdup(gitdir, 1);
if (chdir(cwd->buf))
die_errno("Could not come back to cwd");
return setup_explicit_git_dir(gitdir, cwd, nongit_ok);
@@ -806,7 +806,7 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,
/* Keep entry but do not canonicalize it */
return 1;
} else {
- char *real_path = real_pathdup(ceil);
+ char *real_path = real_pathdup(ceil, 0);
if (!real_path) {
return 0;
}
diff --git a/sha1_file.c b/sha1_file.c
index ec957db5e1..8ce80d4481 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -2532,6 +2532,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
while (delta_stack_nr) {
void *delta_data;
void *base = data;
+ void *external_base = NULL;
unsigned long delta_size, base_size = size;
int i;
@@ -2558,6 +2559,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
p->pack_name);
mark_bad_packed_object(p, base_sha1);
base = read_object(base_sha1, &type, &base_size);
+ external_base = base;
}
}
@@ -2576,6 +2578,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
"at offset %"PRIuMAX" from %s",
(uintmax_t)curpos, p->pack_name);
data = NULL;
+ free(external_base);
continue;
}
@@ -2595,6 +2598,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
error("failed to apply delta");
free(delta_data);
+ free(external_base);
}
*final_type = type;
diff --git a/sha1_name.c b/sha1_name.c
index 73a915ff1b..26ceec1d79 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -1176,7 +1176,8 @@ static int interpret_empty_at(const char *name, int namelen, int len, struct str
return 1;
}
-static int reinterpret(const char *name, int namelen, int len, struct strbuf *buf)
+static int reinterpret(const char *name, int namelen, int len,
+ struct strbuf *buf, unsigned allowed)
{
/* we have extra data, which might need further processing */
struct strbuf tmp = STRBUF_INIT;
@@ -1184,7 +1185,7 @@ static int reinterpret(const char *name, int namelen, int len, struct strbuf *bu
int ret;
strbuf_add(buf, name + len, namelen - len);
- ret = interpret_branch_name(buf->buf, buf->len, &tmp);
+ ret = interpret_branch_name(buf->buf, buf->len, &tmp, allowed);
/* that data was not interpreted, remove our cruft */
if (ret < 0) {
strbuf_setlen(buf, used);
@@ -1205,11 +1206,27 @@ static void set_shortened_ref(struct strbuf *buf, const char *ref)
free(s);
}
+static int branch_interpret_allowed(const char *refname, unsigned allowed)
+{
+ if (!allowed)
+ return 1;
+
+ if ((allowed & INTERPRET_BRANCH_LOCAL) &&
+ starts_with(refname, "refs/heads/"))
+ return 1;
+ if ((allowed & INTERPRET_BRANCH_REMOTE) &&
+ starts_with(refname, "refs/remotes/"))
+ return 1;
+
+ return 0;
+}
+
static int interpret_branch_mark(const char *name, int namelen,
int at, struct strbuf *buf,
int (*get_mark)(const char *, int),
const char *(*get_data)(struct branch *,
- struct strbuf *))
+ struct strbuf *),
+ unsigned allowed)
{
int len;
struct branch *branch;
@@ -1234,64 +1251,55 @@ static int interpret_branch_mark(const char *name, int namelen,
if (!value)
die("%s", err.buf);
+ if (!branch_interpret_allowed(value, allowed))
+ return -1;
+
set_shortened_ref(buf, value);
return len + at;
}
-/*
- * This reads short-hand syntax that not only evaluates to a commit
- * object name, but also can act as if the end user spelled the name
- * of the branch from the command line.
- *
- * - "@{-N}" finds the name of the Nth previous branch we were on, and
- * places the name of the branch in the given buf and returns the
- * number of characters parsed if successful.
- *
- * - "<branch>@{upstream}" finds the name of the other ref that
- * <branch> is configured to merge with (missing <branch> defaults
- * to the current branch), and places the name of the branch in the
- * given buf and returns the number of characters parsed if
- * successful.
- *
- * If the input is not of the accepted format, it returns a negative
- * number to signal an error.
- *
- * If the input was ok but there are not N branch switches in the
- * reflog, it returns 0.
- */
-int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)
+int interpret_branch_name(const char *name, int namelen, struct strbuf *buf,
+ unsigned allowed)
{
char *at;
const char *start;
- int len = interpret_nth_prior_checkout(name, namelen, buf);
+ int len;
if (!namelen)
namelen = strlen(name);
- if (!len) {
- return len; /* syntax Ok, not enough switches */
- } else if (len > 0) {
- if (len == namelen)
- return len; /* consumed all */
- else
- return reinterpret(name, namelen, len, buf);
+ if (!allowed || (allowed & INTERPRET_BRANCH_LOCAL)) {
+ len = interpret_nth_prior_checkout(name, namelen, buf);
+ if (!len) {
+ return len; /* syntax Ok, not enough switches */
+ } else if (len > 0) {
+ if (len == namelen)
+ return len; /* consumed all */
+ else
+ return reinterpret(name, namelen, len, buf, allowed);
+ }
}
for (start = name;
(at = memchr(start, '@', namelen - (start - name)));
start = at + 1) {
- len = interpret_empty_at(name, namelen, at - name, buf);
- if (len > 0)
- return reinterpret(name, namelen, len, buf);
+ if (!allowed || (allowed & INTERPRET_BRANCH_HEAD)) {
+ len = interpret_empty_at(name, namelen, at - name, buf);
+ if (len > 0)
+ return reinterpret(name, namelen, len, buf,
+ allowed);
+ }
len = interpret_branch_mark(name, namelen, at - name, buf,
- upstream_mark, branch_get_upstream);
+ upstream_mark, branch_get_upstream,
+ allowed);
if (len > 0)
return len;
len = interpret_branch_mark(name, namelen, at - name, buf,
- push_mark, branch_get_push);
+ push_mark, branch_get_push,
+ allowed);
if (len > 0)
return len;
}
@@ -1299,22 +1307,19 @@ int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)
return -1;
}
-int strbuf_branchname(struct strbuf *sb, const char *name)
+void strbuf_branchname(struct strbuf *sb, const char *name, unsigned allowed)
{
int len = strlen(name);
- int used = interpret_branch_name(name, len, sb);
+ int used = interpret_branch_name(name, len, sb, allowed);
- if (used == len)
- return 0;
if (used < 0)
used = 0;
strbuf_add(sb, name + used, len - used);
- return len;
}
int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
{
- strbuf_branchname(sb, name);
+ strbuf_branchname(sb, name, INTERPRET_BRANCH_LOCAL);
if (name[0] == '-')
return -1;
strbuf_splice(sb, 0, 0, "refs/heads/", 11);
diff --git a/strbuf.c b/strbuf.c
index 8fec6579f7..ace58e7367 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -707,6 +707,17 @@ void strbuf_add_absolute_path(struct strbuf *sb, const char *path)
strbuf_addstr(sb, path);
}
+void strbuf_add_real_path(struct strbuf *sb, const char *path)
+{
+ if (sb->len) {
+ struct strbuf resolved = STRBUF_INIT;
+ strbuf_realpath(&resolved, path, 1);
+ strbuf_addbuf(sb, &resolved);
+ strbuf_release(&resolved);
+ } else
+ strbuf_realpath(sb, path, 1);
+}
+
int printf_ln(const char *fmt, ...)
{
int ret;
diff --git a/strbuf.h b/strbuf.h
index cf1b5409e7..80047b1bb7 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -441,6 +441,20 @@ extern int strbuf_getcwd(struct strbuf *sb);
*/
extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
+/**
+ * Canonize `path` (make it absolute, resolve symlinks, remove extra
+ * slashes) and append it to `sb`. Die with an informative error
+ * message if there is a problem.
+ *
+ * The directory part of `path` (i.e., everything up to the last
+ * dir_sep) must denote a valid, existing directory, but the last
+ * component need not exist.
+ *
+ * Callers that don't mind links should use the more lightweight
+ * strbuf_add_absolute_path() instead.
+ */
+extern void strbuf_add_real_path(struct strbuf *sb, const char *path);
+
/**
* Normalize in-place the path contained in the strbuf. See
@@ -560,7 +574,26 @@ static inline void strbuf_complete_line(struct strbuf *sb)
strbuf_complete(sb, '\n');
}
-extern int strbuf_branchname(struct strbuf *sb, const char *name);
+/*
+ * Copy "name" to "sb", expanding any special @-marks as handled by
+ * interpret_branch_name(). The result is a non-qualified branch name
+ * (so "foo" or "origin/master" instead of "refs/heads/foo" or
+ * "refs/remotes/origin/master").
+ *
+ * Note that the resulting name may not be a syntactically valid refname.
+ *
+ * If "allowed" is non-zero, restrict the set of allowed expansions. See
+ * interpret_branch_name() for details.
+ */
+extern void strbuf_branchname(struct strbuf *sb, const char *name,
+ unsigned allowed);
+
+/*
+ * Like strbuf_branchname() above, but confirm that the result is
+ * syntactically valid to be used as a local branch name in refs/heads/.
+ *
+ * The return value is "0" if the result is valid, and "-1" otherwise.
+ */
extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
extern void strbuf_addstr_urlencode(struct strbuf *, const char *,
diff --git a/submodule-config.c b/submodule-config.c
index 93453909cf..bb069bc097 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -333,7 +333,7 @@ static int parse_config(const char *var, const char *value, void *data)
strcmp(value, "all") &&
strcmp(value, "none"))
warning("Invalid parameter '%s' for config option "
- "'submodule.%s.ignore'", value, var);
+ "'submodule.%s.ignore'", value, name.buf);
else {
free((void *) submodule->ignore);
submodule->ignore = xstrdup(value);
diff --git a/submodule.c b/submodule.c
index 3b98766a6b..0a2831d846 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1403,7 +1403,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
/* If it is an actual gitfile, it doesn't need migration. */
return;
- real_old_git_dir = real_pathdup(old_git_dir);
+ real_old_git_dir = real_pathdup(old_git_dir, 1);
sub = submodule_from_path(null_sha1, path);
if (!sub)
@@ -1412,7 +1412,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
new_git_dir = git_path("modules/%s", sub->name);
if (safe_create_leading_directories_const(new_git_dir) < 0)
die(_("could not create directory '%s'"), new_git_dir);
- real_new_git_dir = real_pathdup(new_git_dir);
+ real_new_git_dir = real_pathdup(new_git_dir, 1);
if (!prefix)
prefix = get_super_prefix();
@@ -1472,14 +1472,14 @@ void absorb_git_dir_into_superproject(const char *prefix,
new_git_dir = git_path("modules/%s", sub->name);
if (safe_create_leading_directories_const(new_git_dir) < 0)
die(_("could not create directory '%s'"), new_git_dir);
- real_new_git_dir = real_pathdup(new_git_dir);
+ real_new_git_dir = real_pathdup(new_git_dir, 1);
connect_work_tree_and_git_dir(path, real_new_git_dir);
free(real_new_git_dir);
} else {
/* Is it already absorbed into the superprojects git dir? */
- char *real_sub_git_dir = real_pathdup(sub_git_dir);
- char *real_common_git_dir = real_pathdup(get_git_common_dir());
+ char *real_sub_git_dir = real_pathdup(sub_git_dir, 1);
+ char *real_common_git_dir = real_pathdup(get_git_common_dir(), 1);
if (!starts_with(real_sub_git_dir, real_common_git_dir))
relocate_single_git_dir_into_superproject(prefix, path);
diff --git a/t/gitweb-lib.sh b/t/gitweb-lib.sh
index d5dab5a94f..006d2a8152 100644
--- a/t/gitweb-lib.sh
+++ b/t/gitweb-lib.sh
@@ -110,7 +110,12 @@ perl -MEncode -e '$e="";decode_utf8($e, Encode::FB_CROAK)' >/dev/null 2>&1 || {
}
perl -MCGI -MCGI::Util -MCGI::Carp -e 0 >/dev/null 2>&1 || {
- skip_all='skipping gitweb tests, CGI module unusable'
+ skip_all='skipping gitweb tests, CGI & CGI::Util & CGI::Carp modules not available'
+ test_done
+}
+
+perl -mTime::HiRes -e 0 >/dev/null 2>&1 || {
+ skip_all='skipping gitweb tests, Time::HiRes module not available'
test_done
}
diff --git a/t/interop/.gitignore b/t/interop/.gitignore
new file mode 100644
index 0000000000..49c78d3dba
--- /dev/null
+++ b/t/interop/.gitignore
@@ -0,0 +1,4 @@
+/trash directory*/
+/test-results/
+/.prove/
+/build/
diff --git a/t/interop/Makefile b/t/interop/Makefile
new file mode 100644
index 0000000000..31a4bbc716
--- /dev/null
+++ b/t/interop/Makefile
@@ -0,0 +1,16 @@
+-include ../../config.mak
+export GIT_TEST_OPTIONS
+
+SHELL_PATH ?= $(SHELL)
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+T = $(sort $(wildcard i[0-9][0-9][0-9][0-9]-*.sh))
+
+all: $(T)
+
+$(T):
+ @echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+
+clean:
+ rm -rf build "trash directory".* test-results
+
+.PHONY: all clean $(T)
diff --git a/t/interop/README b/t/interop/README
new file mode 100644
index 0000000000..72d42bd856
--- /dev/null
+++ b/t/interop/README
@@ -0,0 +1,85 @@
+Git version interoperability tests
+==================================
+
+This directory has interoperability tests for git. Each script is
+similar to the normal test scripts found in t/, but with the added twist
+that two special versions of git, "git.a" and "git.b", are available in
+the PATH. Individual tests can then check the interaction between the
+two versions.
+
+When you add a feature that handles backwards compatibility between git
+versions, it's encouraged to add a test here to make sure it behaves as
+you expect.
+
+
+Running Tests
+-------------
+
+The easiest way to run tests is to say "make". This runs all
+the tests against their default versions.
+
+You can run a single test like:
+
+ $ ./i0000-basic.sh
+ ok 1 - bare git is forbidden
+ ok 2 - git.a version (v1.6.6.3)
+ ok 3 - git.b version (v2.11.1)
+ # passed all 3 test(s)
+ 1..3
+
+Each test contains default versions to run against. You may override
+these by setting `GIT_TEST_VERSION_A` and `GIT_TEST_VERSION_B` in the
+environment. Note that not all combinations will give sensible outcomes
+for all tests (e.g., a test checking for a specific old/new interaction
+may want something "old" enough" and something "new" enough; see
+individual tests for details).
+
+Version names should be resolvable as revisions in the current
+repository. They will be exported and built as needed using the
+config.mak files found at the root of your working tree.
+
+The exception is the special version "." which uses the currently-built
+contents of your working tree.
+
+You can set the following variables (in the environment or in your config.mak):
+
+ GIT_INTEROP_MAKE_OPTS
+ Options to pass to `make` when building a git version (e.g.,
+ `-j8`).
+
+You can also pass any command-line options taken by ordinary git tests (e.g.,
+"-v").
+
+
+Naming Tests
+------------
+
+The interop test files are named like:
+
+ iNNNN-short-description.sh
+
+where N is a decimal digit. The same conventions for choosing NNNN as
+for normal tests apply.
+
+
+Writing Tests
+-------------
+
+An interop test script starts like a normal script, declaring a few
+variables and then including interop-lib.sh (which includes test-lib.sh).
+Besides test_description, you should also set the $VERSION_A and $VERSION_B
+variables to give the default versions to test against. See t0000-basic.sh for
+an example.
+
+You can then use test_expect_success as usual, with a few differences:
+
+ 1. The special commands "git.a" and "git.b" correspond to the
+ two versions.
+
+ 2. You cannot call a bare "git". This is to prevent accidents where
+ you meant "git.a" or "git.b".
+
+ 3. The trash directory is _not_ a git repository by default. You
+ should create one with the appropriate version of git.
+
+At the end of the script, call test_done as usual.
diff --git a/t/interop/i0000-basic.sh b/t/interop/i0000-basic.sh
new file mode 100755
index 0000000000..903e9193f8
--- /dev/null
+++ b/t/interop/i0000-basic.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# Note that this test only works on real version numbers,
+# as it depends on matching the output to "git version".
+VERSION_A=v1.6.6.3
+VERSION_B=v2.11.1
+
+test_description='sanity test interop library'
+. ./interop-lib.sh
+
+test_expect_success 'bare git is forbidden' '
+ test_must_fail git version
+'
+
+test_expect_success "git.a version ($VERSION_A)" '
+ echo git version ${VERSION_A#v} >expect &&
+ git.a version >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success "git.b version ($VERSION_B)" '
+ echo git version ${VERSION_B#v} >expect &&
+ git.b version >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/interop/i5500-git-daemon.sh b/t/interop/i5500-git-daemon.sh
new file mode 100755
index 0000000000..1daf69420b
--- /dev/null
+++ b/t/interop/i5500-git-daemon.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+VERSION_A=.
+VERSION_B=v1.0.0
+
+: ${LIB_GIT_DAEMON_PORT:=5500}
+LIB_GIT_DAEMON_COMMAND='git.a daemon'
+
+test_description='clone and fetch by older client'
+. ./interop-lib.sh
+. "$TEST_DIRECTORY"/lib-git-daemon.sh
+
+start_git_daemon --export-all
+
+repo=$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo
+
+test_expect_success "create repo served by $VERSION_A" '
+ git.a init "$repo" &&
+ git.a -C "$repo" commit --allow-empty -m one
+'
+
+test_expect_success "clone with $VERSION_B" '
+ git.b clone "$GIT_DAEMON_URL/repo" child &&
+ echo one >expect &&
+ git.a -C child log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success "fetch with $VERSION_B" '
+ git.a -C "$repo" commit --allow-empty -m two &&
+ (
+ cd child &&
+ git.b fetch
+ ) &&
+ echo two >expect &&
+ git.a -C child log -1 --format=%s FETCH_HEAD >actual &&
+ test_cmp expect actual
+'
+
+stop_git_daemon
+test_done
diff --git a/t/interop/interop-lib.sh b/t/interop/interop-lib.sh
new file mode 100644
index 0000000000..3e0a2911d4
--- /dev/null
+++ b/t/interop/interop-lib.sh
@@ -0,0 +1,92 @@
+# Interoperability testing framework. Each script should source
+# this after setting default $VERSION_A and $VERSION_B variables.
+
+. ../../GIT-BUILD-OPTIONS
+INTEROP_ROOT=$(pwd)
+BUILD_ROOT=$INTEROP_ROOT/build
+
+build_version () {
+ if test -z "$1"
+ then
+ echo >&2 "error: test script did not set default versions"
+ return 1
+ fi
+
+ if test "$1" = "."
+ then
+ git rev-parse --show-toplevel
+ return 0
+ fi
+
+ sha1=$(git rev-parse "$1^{tree}") || return 1
+ dir=$BUILD_ROOT/$sha1
+
+ if test -e "$dir/.built"
+ then
+ echo "$dir"
+ return 0
+ fi
+
+ echo >&2 "==> Building $1..."
+
+ mkdir -p "$dir" || return 1
+
+ (cd "$(git rev-parse --show-cdup)" && git archive --format=tar "$sha1") |
+ (cd "$dir" && tar x) ||
+ return 1
+
+ for config in config.mak config.mak.autogen config.status
+ do
+ if test -e "$INTEROP_ROOT/../../$config"
+ then
+ cp "$INTEROP_ROOT/../../$config" "$dir/" || return 1
+ fi
+ done
+
+ (
+ cd "$dir" &&
+ make $GIT_INTEROP_MAKE_OPTS >&2 &&
+ touch .built
+ ) || return 1
+
+ echo "$dir"
+}
+
+# Old versions of git don't have bin-wrappers, so let's give a rough emulation.
+wrap_git () {
+ write_script "$1" <<-EOF
+ GIT_EXEC_PATH="$2"
+ export GIT_EXEC_PATH
+ PATH="$2:\$PATH"
+ export GIT_EXEC_PATH
+ exec git "\$@"
+ EOF
+}
+
+generate_wrappers () {
+ mkdir -p .bin &&
+ wrap_git .bin/git.a "$DIR_A" &&
+ wrap_git .bin/git.b "$DIR_B" &&
+ write_script .bin/git <<-\EOF &&
+ echo >&2 fatal: test tried to run generic git
+ exit 1
+ EOF
+ PATH=$(pwd)/.bin:$PATH
+}
+
+VERSION_A=${GIT_TEST_VERSION_A:-$VERSION_A}
+VERSION_B=${GIT_TEST_VERSION_B:-$VERSION_B}
+
+if ! DIR_A=$(build_version "$VERSION_A") ||
+ ! DIR_B=$(build_version "$VERSION_B")
+then
+ echo >&2 "fatal: unable to build git versions"
+ exit 1
+fi
+
+TEST_DIRECTORY=$INTEROP_ROOT/..
+TEST_OUTPUT_DIRECTORY=$INTEROP_ROOT
+TEST_NO_CREATE_REPO=t
+. "$TEST_DIRECTORY"/test-lib.sh
+
+generate_wrappers || die "unable to set up interop test environment"
diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh
index f9cbd47931..987d40680b 100644
--- a/t/lib-git-daemon.sh
+++ b/t/lib-git-daemon.sh
@@ -46,7 +46,8 @@ start_git_daemon() {
say >&3 "Starting git daemon ..."
mkfifo git_daemon_output
- git daemon --listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \
+ ${LIB_GIT_DAEMON_COMMAND:-git daemon} \
+ --listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \
--reuseaddr --verbose \
--base-path="$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
"$@" "$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 69174c6e31..0642ae7e6e 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -133,6 +133,15 @@ RewriteRule ^/ftp-redir/(.*)$ ftp://localhost:1000/$1 [R=302]
RewriteRule ^/loop-redir/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-(.*) /$1 [R=302]
RewriteRule ^/loop-redir/(.*)$ /loop-redir/x-$1 [R=302]
+# redir-to/502/x?y -> really-redir-to?path=502/x&qs=y which returns 502
+# redir-to/x?y -> really-redir-to?path=x&qs=y -> x?y
+RewriteCond %{QUERY_STRING} ^(.*)$
+RewriteRule ^/redir-to/(.*)$ /really-redir-to?path=$1&qs=%1 [R=302]
+RewriteCond %{QUERY_STRING} ^path=502/(.*)&qs=(.*)$
+RewriteRule ^/really-redir-to$ - [R=502,L]
+RewriteCond %{QUERY_STRING} ^path=(.*)&qs=(.*)$
+RewriteRule ^/really-redir-to$ /%1?%2 [R=302]
+
# The first rule issues a client-side redirect to something
# that _doesn't_ look like a git repo. The second rule is a
# server-side rewrite, so that it turns out the odd-looking
diff --git a/t/perf/p0001-rev-list.sh b/t/perf/p0001-rev-list.sh
index 16359d51ae..ebf172401b 100755
--- a/t/perf/p0001-rev-list.sh
+++ b/t/perf/p0001-rev-list.sh
@@ -15,7 +15,8 @@ test_perf 'rev-list --all --objects' '
'
test_expect_success 'create new unreferenced commit' '
- commit=$(git commit-tree HEAD^{tree} -p HEAD)
+ commit=$(git commit-tree HEAD^{tree} -p HEAD) &&
+ test_export commit
'
test_perf 'rev-list $commit --not --all' '
diff --git a/t/perf/p7000-filter-branch.sh b/t/perf/p7000-filter-branch.sh
index 15ee5d1d53..b029586ccb 100755
--- a/t/perf/p7000-filter-branch.sh
+++ b/t/perf/p7000-filter-branch.sh
@@ -16,4 +16,9 @@ test_perf 'noop filter' '
git filter-branch -f base..HEAD
'
+test_perf 'noop prune-empty' '
+ git checkout --detach tip &&
+ git filter-branch -f --prune-empty base..HEAD
+'
+
test_done
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
index 46f08ee087..ab4b8b06ae 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -83,7 +83,7 @@ test_perf_create_repo_from () {
error "bug in the test script: not 2 parameters to test-create-repo"
repo="$1"
source="$2"
- source_git="$(git -C "$source" rev-parse --git-dir)"
+ source_git="$("$MODERN_GIT" -C "$source" rev-parse --git-dir)"
objects_dir="$("$MODERN_GIT" -C "$source" rev-parse --git-path objects)"
mkdir -p "$repo/.git"
(
@@ -102,7 +102,7 @@ test_perf_create_repo_from () {
) &&
(
cd "$repo" &&
- git init -q && {
+ "$MODERN_GIT" init -q && {
test_have_prereq SYMLINKS ||
git config core.symlinks false
} &&
diff --git a/t/perf/run b/t/perf/run
index e8adedadfd..c788d713ae 100755
--- a/t/perf/run
+++ b/t/perf/run
@@ -63,6 +63,9 @@ run_dirs_helper () {
unset GIT_TEST_INSTALLED
else
GIT_TEST_INSTALLED="$mydir/bin-wrappers"
+ # Older versions of git lacked bin-wrappers; fallback to the
+ # files in the root.
+ test -d "$GIT_TEST_INSTALLED" || GIT_TEST_INSTALLED=$mydir
export GIT_TEST_INSTALLED
fi
run_one_dir "$@"
diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh
index e0a6940232..58c0b7e9b6 100755
--- a/t/t0100-previous.sh
+++ b/t/t0100-previous.sh
@@ -56,5 +56,13 @@ test_expect_success 'merge @{-100} before checking out that many branches yet' '
test_must_fail git merge @{-100}
'
+test_expect_success 'log -g @{-1}' '
+ git checkout -b last_branch &&
+ git checkout -b new_branch &&
+ echo "last_branch@{0}" >expect &&
+ git log -g --format=%gd @{-1} >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index 923bfc5a26..ea371020fa 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -1097,6 +1097,68 @@ test_expect_success 'multiple git -c appends config' '
test_cmp expect actual
'
+test_expect_success 'last one wins: two level vars' '
+
+ # sec.var and sec.VAR are the same variable, as the first
+ # and the last level of a configuration variable name is
+ # case insensitive.
+
+ echo VAL >expect &&
+
+ git -c sec.var=val -c sec.VAR=VAL config --get sec.var >actual &&
+ test_cmp expect actual &&
+ git -c SEC.var=val -c sec.var=VAL config --get sec.var >actual &&
+ test_cmp expect actual &&
+
+ git -c sec.var=val -c sec.VAR=VAL config --get SEC.var >actual &&
+ test_cmp expect actual &&
+ git -c SEC.var=val -c sec.var=VAL config --get sec.VAR >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'last one wins: three level vars' '
+
+ # v.a.r and v.A.r are not the same variable, as the middle
+ # level of a three-level configuration variable name is
+ # case sensitive.
+
+ echo val >expect &&
+ git -c v.a.r=val -c v.A.r=VAL config --get v.a.r >actual &&
+ test_cmp expect actual &&
+ git -c v.a.r=val -c v.A.r=VAL config --get V.a.R >actual &&
+ test_cmp expect actual &&
+
+ # v.a.r and V.a.R are the same variable, as the first
+ # and the last level of a configuration variable name is
+ # case insensitive.
+
+ echo VAL >expect &&
+ git -c v.a.r=val -c v.a.R=VAL config --get v.a.r >actual &&
+ test_cmp expect actual &&
+ git -c v.a.r=val -c V.a.r=VAL config --get v.a.r >actual &&
+ test_cmp expect actual &&
+ git -c v.a.r=val -c v.a.R=VAL config --get V.a.R >actual &&
+ test_cmp expect actual &&
+ git -c v.a.r=val -c V.a.r=VAL config --get V.a.R >actual &&
+ test_cmp expect actual
+'
+
+for VAR in a .a a. a.0b a."b c". a."b c".0d
+do
+ test_expect_success "git -c $VAR=VAL rejects invalid '$VAR'" '
+ test_must_fail git -c "$VAR=VAL" config -l
+ '
+done
+
+for VAR in a.b a."b c".d
+do
+ test_expect_success "git -c $VAR=VAL works with valid '$VAR'" '
+ echo VAL >expect &&
+ git -c "$VAR=VAL" config --get "$VAR" >actual &&
+ test_cmp expect actual
+ '
+done
+
test_expect_success 'git -c is not confused by empty environment' '
GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list
'
diff --git a/t/t1501-work-tree.sh b/t/t1501-work-tree.sh
index cc5b870e58..b06210ec5e 100755
--- a/t/t1501-work-tree.sh
+++ b/t/t1501-work-tree.sh
@@ -423,4 +423,12 @@ test_expect_success '$GIT_WORK_TREE overrides $GIT_DIR/common' '
)
'
+test_expect_success 'error out gracefully on invalid $GIT_WORK_TREE' '
+ (
+ GIT_WORK_TREE=/.invalid/work/tree &&
+ export GIT_WORK_TREE &&
+ test_expect_code 128 git rev-parse
+ )
+'
+
test_done
diff --git a/t/t3204-branch-name-interpretation.sh b/t/t3204-branch-name-interpretation.sh
new file mode 100755
index 0000000000..698d9cc4f3
--- /dev/null
+++ b/t/t3204-branch-name-interpretation.sh
@@ -0,0 +1,133 @@
+#!/bin/sh
+
+test_description='interpreting exotic branch name arguments
+
+Branch name arguments are usually names which are taken to be inside of
+refs/heads/, but we interpret some magic syntax like @{-1}, @{upstream}, etc.
+This script aims to check the behavior of those corner cases.
+'
+. ./test-lib.sh
+
+expect_branch() {
+ git log -1 --format=%s "$1" >actual &&
+ echo "$2" >expect &&
+ test_cmp expect actual
+}
+
+expect_deleted() {
+ test_must_fail git rev-parse --verify "$1"
+}
+
+test_expect_success 'set up repo' '
+ test_commit one &&
+ test_commit two &&
+ git remote add origin foo.git
+'
+
+test_expect_success 'update branch via @{-1}' '
+ git branch previous one &&
+
+ git checkout previous &&
+ git checkout master &&
+
+ git branch -f @{-1} two &&
+ expect_branch previous two
+'
+
+test_expect_success 'update branch via local @{upstream}' '
+ git branch local one &&
+ git branch --set-upstream-to=local &&
+
+ git branch -f @{upstream} two &&
+ expect_branch local two
+'
+
+test_expect_success 'disallow updating branch via remote @{upstream}' '
+ git update-ref refs/remotes/origin/remote one &&
+ git branch --set-upstream-to=origin/remote &&
+
+ test_must_fail git branch -f @{upstream} two
+'
+
+test_expect_success 'create branch with pseudo-qualified name' '
+ git branch refs/heads/qualified two &&
+ expect_branch refs/heads/refs/heads/qualified two
+'
+
+test_expect_success 'delete branch via @{-1}' '
+ git branch previous-del &&
+
+ git checkout previous-del &&
+ git checkout master &&
+
+ git branch -D @{-1} &&
+ expect_deleted previous-del
+'
+
+test_expect_success 'delete branch via local @{upstream}' '
+ git branch local-del &&
+ git branch --set-upstream-to=local-del &&
+
+ git branch -D @{upstream} &&
+ expect_deleted local-del
+'
+
+test_expect_success 'delete branch via remote @{upstream}' '
+ git update-ref refs/remotes/origin/remote-del two &&
+ git branch --set-upstream-to=origin/remote-del &&
+
+ git branch -r -D @{upstream} &&
+ expect_deleted origin/remote-del
+'
+
+# Note that we create two oddly named local branches here. We want to make
+# sure that we do not accidentally delete either of them, even if
+# shorten_unambiguous_ref() tweaks the name to avoid ambiguity.
+test_expect_success 'delete @{upstream} expansion matches -r option' '
+ git update-ref refs/remotes/origin/remote-del two &&
+ git branch --set-upstream-to=origin/remote-del &&
+ git update-ref refs/heads/origin/remote-del two &&
+ git update-ref refs/heads/remotes/origin/remote-del two &&
+
+ test_must_fail git branch -D @{upstream} &&
+ expect_branch refs/heads/origin/remote-del two &&
+ expect_branch refs/heads/remotes/origin/remote-del two
+'
+
+test_expect_success 'disallow deleting remote branch via @{-1}' '
+ git update-ref refs/remotes/origin/previous one &&
+
+ git checkout -b origin/previous two &&
+ git checkout master &&
+
+ test_must_fail git branch -r -D @{-1} &&
+ expect_branch refs/remotes/origin/previous one &&
+ expect_branch refs/heads/origin/previous two
+'
+
+# The thing we are testing here is that "@" is the real branch refs/heads/@,
+# and not refs/heads/HEAD. These tests should not imply that refs/heads/@ is a
+# sane thing, but it _is_ technically allowed for now. If we disallow it, these
+# can be switched to test_must_fail.
+test_expect_success 'create branch named "@"' '
+ git branch -f @ one &&
+ expect_branch refs/heads/@ one
+'
+
+test_expect_success 'delete branch named "@"' '
+ git update-ref refs/heads/@ two &&
+ git branch -D @ &&
+ expect_deleted refs/heads/@
+'
+
+test_expect_success 'checkout does not treat remote @{upstream} as a branch' '
+ git update-ref refs/remotes/origin/checkout one &&
+ git branch --set-upstream-to=origin/checkout &&
+ git update-ref refs/heads/origin/checkout two &&
+ git update-ref refs/heads/remotes/origin/checkout two &&
+
+ git checkout @{upstream} &&
+ expect_branch HEAD one
+'
+
+test_done
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 5ffe78e920..aaa258daa3 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -394,4 +394,22 @@ test_expect_success 'diffs can be colorized' '
grep "$(printf "\\033")" output
'
+test_expect_success 'patch-mode via -i prompts for files' '
+ git reset --hard &&
+
+ echo one >file &&
+ echo two >test &&
+ git add -i <<-\EOF &&
+ patch
+ test
+
+ y
+ quit
+ EOF
+
+ echo test >expect &&
+ git diff --cached --name-only >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4035-diff-quiet.sh b/t/t4035-diff-quiet.sh
index 461f4bb583..2f1737fcef 100755
--- a/t/t4035-diff-quiet.sh
+++ b/t/t4035-diff-quiet.sh
@@ -152,4 +152,13 @@ test_expect_success 'git diff --quiet ignores stat-change only entries' '
test_expect_code 1 git diff --quiet
'
+test_expect_success 'git diff --quiet on a path that need conversion' '
+ echo "crlf.txt text=auto" >.gitattributes &&
+ printf "Hello\r\nWorld\r\n" >crlf.txt &&
+ git add .gitattributes crlf.txt &&
+
+ printf "Hello\r\nWorld\n" >crlf.txt &&
+ git diff --quiet crlf.txt
+'
+
test_done
diff --git a/t/t4062-diff-pickaxe.sh b/t/t4062-diff-pickaxe.sh
index f0bf50bda7..7c4903f497 100755
--- a/t/t4062-diff-pickaxe.sh
+++ b/t/t4062-diff-pickaxe.sh
@@ -19,4 +19,9 @@ test_expect_success '-G matches' '
test 4096-zeroes.txt = "$(cat out)"
'
+test_expect_success '-S --pickaxe-regex' '
+ git diff --name-only -S0 --pickaxe-regex HEAD^ >out &&
+ verbose test 4096-zeroes.txt = "$(cat out)"
+'
+
test_done
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
index 9d87777b59..d0377fae5c 100755
--- a/t/t4211-line-log.sh
+++ b/t/t4211-line-log.sh
@@ -106,4 +106,14 @@ test_expect_success '-L with --output' '
test_line_count = 70 log
'
+test_expect_success 'range_set_union' '
+ test_seq 500 > c.c &&
+ git add c.c &&
+ git commit -m "many lines" &&
+ test_seq 1000 > c.c &&
+ git add c.c &&
+ git commit -m "modify many lines" &&
+ git log $(for x in $(test_seq 200); do echo -L $((2*x)),+1:c.c; done)
+'
+
test_done
diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh
new file mode 100755
index 0000000000..37143ea0ac
--- /dev/null
+++ b/t/t5316-pack-delta-depth.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+test_description='pack-objects breaks long cross-pack delta chains'
+. ./test-lib.sh
+
+# This mirrors a repeated push setup:
+#
+# 1. A client repeatedly modifies some files, makes a
+# commit, and pushes the result. It does this N times
+# before we get around to repacking.
+#
+# 2. Each push generates a thin pack with the new version of
+# various objects. Let's consider some file in the root tree
+# which is updated in each commit.
+#
+# When generating push number X, we feed commit X-1 (and
+# thus blob X-1) as a preferred base. The resulting pack has
+# blob X as a thin delta against blob X-1.
+#
+# On the receiving end, "index-pack --fix-thin" will
+# complete the pack with a base copy of blob X-1.
+#
+# 3. In older versions of git, if we used the delta from
+# pack X, then we'd always find blob X-1 as a base in the
+# same pack (and generate a fresh delta).
+#
+# But with the pack mru, we jump from delta to delta
+# following the traversal order:
+#
+# a. We grab blob X from pack X as a delta, putting it at
+# the tip of our mru list.
+#
+# b. Eventually we move onto commit X-1. We need other
+# objects which are only in pack X-1 (in the test code
+# below, it's the containing tree). That puts pack X-1
+# at the tip of our mru list.
+#
+# c. Eventually we look for blob X-1, and we find the
+# version in pack X-1 (because it's the mru tip).
+#
+# Now we have blob X as a delta against X-1, which is a delta
+# against X-2, and so forth.
+#
+# In the real world, these small pushes would get exploded by
+# unpack-objects rather than "index-pack --fix-thin", but the
+# same principle applies to larger pushes (they only need one
+# repeatedly-modified file to generate the delta chain).
+
+test_expect_success 'create series of packs' '
+ test-genrandom foo 4096 >content &&
+ prev= &&
+ for i in $(test_seq 1 10)
+ do
+ cat content >file &&
+ echo $i >>file &&
+ git add file &&
+ git commit -m $i &&
+ cur=$(git rev-parse HEAD^{tree}) &&
+ {
+ test -n "$prev" && echo "-$prev"
+ echo $cur
+ echo "$(git rev-parse :file) file"
+ } | git pack-objects --stdout >tmp &&
+ git index-pack --stdin --fix-thin <tmp || return 1
+ prev=$cur
+ done
+'
+
+max_chain() {
+ git index-pack --verify-stat-only "$1" >output &&
+ perl -lne '
+ /chain length = (\d+)/ and $len = $1;
+ END { print $len }
+ ' output
+}
+
+# Note that this whole setup is pretty reliant on the current
+# packing heuristics. We double-check that our test case
+# actually produces a long chain. If it doesn't, it should be
+# adjusted (or scrapped if the heuristics have become too unreliable)
+test_expect_success 'packing produces a long delta' '
+ # Use --window=0 to make sure we are seeing reused deltas,
+ # not computing a new long chain.
+ pack=$(git pack-objects --all --window=0 </dev/null pack) &&
+ test 9 = "$(max_chain pack-$pack.pack)"
+'
+
+test_expect_success '--depth limits depth' '
+ pack=$(git pack-objects --all --depth=5 </dev/null pack) &&
+ test 5 = "$(max_chain pack-$pack.pack)"
+'
+
+test_done
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 505e1b4a7f..b5865b385d 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -484,7 +484,7 @@ test_expect_success 'test lonely missing ref' '
cd client &&
test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy
) >/dev/null 2>error-m &&
- test_cmp expect-error error-m
+ test_i18ncmp expect-error error-m
'
test_expect_success 'test missing ref after existing' '
@@ -492,7 +492,7 @@ test_expect_success 'test missing ref after existing' '
cd client &&
test_must_fail git fetch-pack --no-progress .. refs/heads/A refs/heads/xyzzy
) >/dev/null 2>error-em &&
- test_cmp expect-error error-em
+ test_i18ncmp expect-error error-em
'
test_expect_success 'test missing ref before existing' '
@@ -500,7 +500,7 @@ test_expect_success 'test missing ref before existing' '
cd client &&
test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy refs/heads/A
) >/dev/null 2>error-me &&
- test_cmp expect-error error-me
+ test_i18ncmp expect-error error-me
'
test_expect_success 'test --all, --depth, and explicit head' '
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index ba46e86de0..3ea27107c2 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -153,6 +153,25 @@ test_expect_success 'remove errors out early when deleting non-existent branch'
)
'
+test_expect_success 'remove remote with a branch without configured merge' '
+ test_when_finished "(
+ git -C test checkout master;
+ git -C test branch -D two;
+ git -C test config --remove-section remote.two;
+ git -C test config --remove-section branch.second;
+ true
+ )" &&
+ (
+ cd test &&
+ git remote add two ../two &&
+ git fetch two &&
+ git checkout -b second two/master^0 &&
+ git config branch.second.remote two &&
+ git checkout master &&
+ git remote rm two
+ )
+'
+
test_expect_success 'rename errors out early when deleting non-existent branch' '
(
cd test &&
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
index 55fc83fc06..94fc9be9ce 100755
--- a/t/t5512-ls-remote.sh
+++ b/t/t5512-ls-remote.sh
@@ -248,4 +248,13 @@ test_expect_success PIPE,JGIT,GIT_DAEMON 'indicate no refs in standards-complian
test_expect_code 2 git ls-remote --exit-code git://localhost:$JGIT_DAEMON_PORT/empty.git
'
+test_expect_success 'ls-remote works outside repository' '
+ # It is important for this repo to be inside the nongit
+ # area, as we want a repo name that does not include
+ # slashes (because those inhibit some of our configuration
+ # lookups).
+ nongit git init --bare dst.git &&
+ nongit git ls-remote dst.git
+'
+
test_done
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 0fc5a7c596..177897ea0b 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -1098,7 +1098,8 @@ test_expect_success 'fetch exact SHA1' '
test_must_fail git cat-file -t $the_commit &&
# fetching the hidden object should fail by default
- test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy &&
+ test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy 2>err &&
+ test_i18ngrep "Server does not allow request for unadvertised object" err &&
test_must_fail git rev-parse --verify refs/heads/copy &&
# the server side can allow it to succeed
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index aeb3a63f7c..87308cdced 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -34,6 +34,15 @@ test_expect_success 'clone http repository' '
test_cmp file clone/file
'
+test_expect_success 'list refs from outside any repository' '
+ cat >expect <<-EOF &&
+ $(git rev-parse master) HEAD
+ $(git rev-parse master) refs/heads/master
+ EOF
+ nongit git ls-remote "$HTTPD_URL/dumb/repo.git" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'create password-protected repository' '
mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" &&
cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
@@ -378,5 +387,14 @@ test_expect_success 'http-alternates triggers not-from-user protocol check' '
clone $HTTPD_URL/dumb/evil.git evil-user
'
+test_expect_success 'can redirect through non-"info/refs?service=git-upload-pack" URL' '
+ git clone "$HTTPD_URL/redir-to/dumb/repo.git"
+'
+
+test_expect_success 'print HTTP error when any intermediate redirect throws error' '
+ test_must_fail git clone "$HTTPD_URL/redir-to/502" 2> stderr &&
+ test_i18ngrep "unable to access.*/redir-to/502" stderr
+'
+
stop_httpd
test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index aea1dfc714..a468041c50 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -558,7 +558,7 @@ test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '
test_when_finished "git checkout master" &&
git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual &&
sed -e "s/^\* / /" actual >expect &&
- git checkout --orphan HEAD &&
+ git checkout --orphan orphaned-branch &&
git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual &&
test_cmp expect actual
'
diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh
index 1762dfa6a3..08de2e8ab0 100755
--- a/t/t6500-gc.sh
+++ b/t/t6500-gc.sh
@@ -67,5 +67,20 @@ test_expect_success 'auto gc with too many loose objects does not attempt to cre
test_line_count = 2 new # There is one new pack and its .idx
'
+test_expect_success 'background auto gc does not run if gc.log is present and recent but does if it is old' '
+ test_commit foo &&
+ test_commit bar &&
+ git repack &&
+ test_config gc.autopacklimit 1 &&
+ test_config gc.autodetach true &&
+ echo fleem >.git/gc.log &&
+ test_must_fail git gc --auto 2>err &&
+ test_i18ngrep "^error:" err &&
+ test_config gc.logexpiry 5.days &&
+ test-chmtime =-345600 .git/gc.log &&
+ test_must_fail git gc --auto &&
+ test_config gc.logexpiry 2.days &&
+ git gc --auto
+'
test_done
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index cb8fbd8e5e..7cb60799be 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -313,6 +313,27 @@ test_expect_success 'Tag name filtering allows slashes in tag names' '
git cat-file tag X/2 > actual &&
test_cmp expect actual
'
+test_expect_success 'setup --prune-empty comparisons' '
+ git checkout --orphan master-no-a &&
+ git rm -rf . &&
+ unset test_tick &&
+ test_tick &&
+ GIT_COMMITTER_DATE="@0 +0000" GIT_AUTHOR_DATE="@0 +0000" &&
+ test_commit --notick B B.t B Bx &&
+ git checkout -b branch-no-a Bx &&
+ test_commit D D.t D Dx &&
+ mkdir dir &&
+ test_commit dir/D dir/D.t dir/D dir/Dx &&
+ test_commit E E.t E Ex &&
+ git checkout master-no-a &&
+ test_commit C C.t C Cx &&
+ git checkout branch-no-a &&
+ git merge Cx -m "Merge tag '\''C'\'' into branch" &&
+ git tag Fx &&
+ test_commit G G.t G Gx &&
+ test_commit H H.t H Hx &&
+ git checkout branch
+'
test_expect_success 'Prune empty commits' '
git rev-list HEAD > expect &&
@@ -341,6 +362,22 @@ test_expect_success 'prune empty works even without index/tree filters' '
test_cmp expect actual
'
+test_expect_success '--prune-empty is able to prune root commit' '
+ git rev-list branch-no-a >expect &&
+ git branch testing H &&
+ git filter-branch -f --prune-empty --index-filter "git update-index --remove A.t" testing &&
+ git rev-list testing >actual &&
+ git branch -D testing &&
+ test_cmp expect actual
+'
+
+test_expect_success '--prune-empty is able to prune entire branch' '
+ git branch prune-entire B &&
+ git filter-branch -f --prune-empty --index-filter "git update-index --remove A.t B.t" prune-entire &&
+ test_path_is_missing .git/refs/heads/prune-entire &&
+ test_must_fail git reflog exists refs/heads/prune-entire
+'
+
test_expect_success '--remap-to-ancestor with filename filters' '
git checkout master &&
git reset --hard A &&
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 072e6c6b88..b8ad076c25 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -879,18 +879,16 @@ test_expect_success GPG 'verifying a forged tag should fail' '
test_must_fail git tag -v forged-tag
'
-test_expect_success 'verifying a proper tag with --format pass and format accordingly' '
- cat >expect <<-\EOF
+test_expect_success GPG 'verifying a proper tag with --format pass and format accordingly' '
+ cat >expect <<-\EOF &&
tagname : signed-tag
- EOF &&
+ EOF
git tag -v --format="tagname : %(tag)" "signed-tag" >actual &&
test_cmp expect actual
'
-test_expect_success 'verifying a forged tag with --format fail and format accordingly' '
- cat >expect <<-\EOF
- tagname : forged-tag
- EOF &&
+test_expect_success GPG 'verifying a forged tag with --format should fail silently' '
+ >expect &&
test_must_fail git tag -v --format="tagname : %(tag)" "forged-tag" >actual &&
test_cmp expect actual
'
diff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh
index d62ccbb98e..b4b49eeb08 100755
--- a/t/t7030-verify-tag.sh
+++ b/t/t7030-verify-tag.sh
@@ -125,18 +125,16 @@ test_expect_success GPG 'verify multiple tags' '
test_cmp expect.stderr actual.stderr
'
-test_expect_success 'verifying tag with --format' '
- cat >expect <<-\EOF
+test_expect_success GPG 'verifying tag with --format' '
+ cat >expect <<-\EOF &&
tagname : fourth-signed
- EOF &&
+ EOF
git verify-tag --format="tagname : %(tag)" "fourth-signed" >actual &&
test_cmp expect actual
'
-test_expect_success 'verifying a forged tag with --format fail and format accordingly' '
- cat >expect <<-\EOF
- tagname : 7th forged-signed
- EOF &&
+test_expect_success GPG 'verifying a forged tag with --format should fail silently' '
+ >expect &&
test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged &&
test_cmp expect actual-forged
'
diff --git a/t/t7518-ident-corner-cases.sh b/t/t7518-ident-corner-cases.sh
new file mode 100755
index 0000000000..b22f631261
--- /dev/null
+++ b/t/t7518-ident-corner-cases.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description='corner cases in ident strings'
+. ./test-lib.sh
+
+# confirm that we do not segfault _and_ that we do not say "(null)", as
+# glibc systems will quietly handle our NULL pointer
+#
+# Note also that we can't use "env" here because we need to unset a variable,
+# and "-u" is not portable.
+test_expect_success 'empty name and missing email' '
+ (
+ sane_unset GIT_AUTHOR_EMAIL &&
+ GIT_AUTHOR_NAME= &&
+ test_must_fail git commit --allow-empty -m foo 2>err &&
+ test_i18ngrep ! null err
+ )
+'
+
+test_expect_success 'commit rejects all-crud name' '
+ test_must_fail env GIT_AUTHOR_NAME=" .;<>" \
+ git commit --allow-empty -m foo
+'
+
+# We must test the actual error message here, as an unwanted
+# auto-detection could fail for other reasons.
+test_expect_success 'empty configured name does not auto-detect' '
+ (
+ sane_unset GIT_AUTHOR_NAME &&
+ test_must_fail \
+ git -c user.name= commit --allow-empty -m foo 2>err &&
+ test_i18ngrep "empty ident name" err
+ )
+'
+
+test_done
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index 19f0108f8a..cee42097b0 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -982,6 +982,72 @@ test_expect_success 'grep -e -- -- path' '
test_cmp expected actual
'
+test_expect_success 'dashdash disambiguates rev as rev' '
+ test_when_finished "rm -f master" &&
+ echo content >master &&
+ echo master:hello.c >expect &&
+ git grep -l o master -- hello.c >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'dashdash disambiguates pathspec as pathspec' '
+ test_when_finished "git rm -f master" &&
+ echo content >master &&
+ git add master &&
+ echo master:content >expect &&
+ git grep o -- master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'report bogus arg without dashdash' '
+ test_must_fail git grep o does-not-exist
+'
+
+test_expect_success 'report bogus rev with dashdash' '
+ test_must_fail git grep o hello.c --
+'
+
+test_expect_success 'allow non-existent path with dashdash' '
+ # We need a real match so grep exits with success.
+ tree=$(git ls-tree HEAD |
+ sed s/hello.c/not-in-working-tree/ |
+ git mktree) &&
+ git grep o "$tree" -- not-in-working-tree
+'
+
+test_expect_success 'grep --no-index pattern -- path' '
+ rm -fr non &&
+ mkdir -p non/git &&
+ (
+ GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
+ export GIT_CEILING_DIRECTORIES &&
+ cd non/git &&
+ echo hello >hello &&
+ echo goodbye >goodbye &&
+ echo hello:hello >expect &&
+ git grep --no-index o -- hello >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'grep --no-index complains of revs' '
+ test_must_fail git grep --no-index o master -- 2>err &&
+ test_i18ngrep "cannot be used with revs" err
+'
+
+test_expect_success 'grep --no-index prefers paths to revs' '
+ test_when_finished "rm -f master" &&
+ echo content >master &&
+ echo master:content >expect &&
+ git grep --no-index o master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'grep --no-index does not "diagnose" revs' '
+ test_must_fail git grep --no-index o :1:hello.c 2>err &&
+ test_i18ngrep ! -i "did you mean" err
+'
+
cat >expected <<EOF
hello.c:int main(int argc, const char **argv)
hello.c: printf("Hello world.\n");
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 0f398dd160..60a80f60b2 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -148,7 +148,6 @@ cat >expected-cc <<\EOF
!two@example.com!
!three@example.com!
!four@example.com!
-!five@example.com!
EOF
"
@@ -159,9 +158,9 @@ test_expect_success $PREREQ 'cc trailer with various syntax' '
Test Cc: trailers.
Cc: one@example.com
- Cc: <two@example.com> # this is part of the name
- Cc: <three@example.com>, <four@example.com> # not.five@example.com
- Cc: "Some # Body" <five@example.com> [part.of.name.too]
+ Cc: <two@example.com> # trailing comments are ignored
+ Cc: <three@example.com>, <not.four@example.com> one address per line
+ Cc: "Some # Body" <four@example.com> [ <also.a.comment> ]
EOF
clean_fake_sendmail &&
git send-email -1 --to=recipient@example.com \
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index bb879a527d..1319415ba8 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -18,6 +18,11 @@ then
test_done
fi
+if ! test_have_prereq NOT_ROOT; then
+ skip_all='When cvs is compiled with CVS_BADROOT commits as root fail'
+ test_done
+fi
+
CVSROOT=$PWD/tmpcvsroot
CVSWORK=$PWD/cvswork
GIT_DIR=$PWD/.git
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
index 4c384ff023..804ce3850f 100755
--- a/t/t9600-cvsimport.sh
+++ b/t/t9600-cvsimport.sh
@@ -3,6 +3,11 @@
test_description='git cvsimport basic tests'
. ./lib-cvs.sh
+if ! test_have_prereq NOT_ROOT; then
+ skip_all='When cvs is compiled with CVS_BADROOT commits as root fail'
+ test_done
+fi
+
test_expect_success PERL 'setup cvsroot environment' '
CVSROOT=$(pwd)/cvsroot &&
export CVSROOT
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index bd357704cc..5ee124332a 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -136,17 +136,12 @@ test_tick () {
export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
}
-# Stop execution and start a shell. This is useful for debugging tests and
-# only makes sense together with "-v".
+# Stop execution and start a shell. This is useful for debugging tests.
#
# Be sure to remove all invocations of this command before submitting.
test_pause () {
- if test "$verbose" = t; then
- "$SHELL_PATH" <&6 >&3 2>&4
- else
- error >&5 "test_pause requires --verbose"
- fi
+ "$SHELL_PATH" <&6 >&5 2>&7
}
# Wrap git in gdb. Adding this to a command can make it easier to
@@ -154,7 +149,7 @@ test_pause () {
#
# Example: "debug git checkout master".
debug () {
- GIT_TEST_GDB=1 "$@"
+ GIT_TEST_GDB=1 "$@" <&6 >&5 2>&7
}
# Call test_commit with the arguments
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 86d77c16dd..23c29bce6e 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -342,6 +342,7 @@ fi
exec 5>&1
exec 6<&0
+exec 7>&2
if test "$verbose_log" = "t"
then
exec 3>>"$GIT_TEST_TEE_OUTPUT_FILE" 4>&3
diff --git a/tempfile.c b/tempfile.c
index ffcc272375..6843710670 100644
--- a/tempfile.c
+++ b/tempfile.c
@@ -247,8 +247,13 @@ int close_tempfile(struct tempfile *tempfile)
tempfile->fd = -1;
if (fp) {
tempfile->fp = NULL;
- err = ferror(fp);
- err |= fclose(fp);
+ if (ferror(fp)) {
+ err = -1;
+ if (!fclose(fp))
+ errno = EIO;
+ } else {
+ err = fclose(fp);
+ }
} else {
err = close(fd);
}
diff --git a/transport-helper.c b/transport-helper.c
index 1258d6aedd..dc90a1fb76 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -124,8 +124,9 @@ static struct child_process *get_helper(struct transport *transport)
helper->git_cmd = 0;
helper->silent_exec_failure = 1;
- argv_array_pushf(&helper->env_array, "%s=%s", GIT_DIR_ENVIRONMENT,
- get_git_dir());
+ if (have_git_dir())
+ argv_array_pushf(&helper->env_array, "%s=%s",
+ GIT_DIR_ENVIRONMENT, get_git_dir());
code = start_command(helper);
if (code < 0 && errno == ENOENT)
diff --git a/transport.c b/transport.c
index d72e089484..b58f5c8cdc 100644
--- a/transport.c
+++ b/transport.c
@@ -204,6 +204,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
static int fetch_refs_via_pack(struct transport *transport,
int nr_heads, struct ref **to_fetch)
{
+ int ret = 0;
struct git_transport_data *data = transport->data;
struct ref *refs;
char *dest = xstrdup(transport->url);
@@ -241,19 +242,22 @@ static int fetch_refs_via_pack(struct transport *transport,
&transport->pack_lockfile);
close(data->fd[0]);
close(data->fd[1]);
- if (finish_connect(data->conn)) {
- free_refs(refs);
- refs = NULL;
- }
+ if (finish_connect(data->conn))
+ ret = -1;
data->conn = NULL;
data->got_remote_heads = 0;
data->options.self_contained_and_connected =
args.self_contained_and_connected;
+ if (refs == NULL)
+ ret = -1;
+ if (report_unmatched_refs(to_fetch, nr_heads))
+ ret = -1;
+
free_refs(refs_tmp);
free_refs(refs);
free(dest);
- return (refs ? 0 : -1);
+ return ret;
}
static int push_had_errors(struct ref *ref)
@@ -1221,7 +1225,7 @@ static int refs_from_alternate_cb(struct alternate_object_database *e,
const struct ref *extra;
struct alternate_refs_data *cb = data;
- other = real_pathdup(e->path);
+ other = real_pathdup(e->path, 1);
len = strlen(other);
while (other[len-1] == '/')
diff --git a/upload-pack.c b/upload-pack.c
index 7597ba3405..ffb028d623 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -822,9 +822,13 @@ static void receive_needs(void)
use_include_tag = 1;
o = parse_object(sha1_buf);
- if (!o)
+ if (!o) {
+ packet_write_fmt(1,
+ "ERR upload-pack: not our ref %s",
+ sha1_to_hex(sha1_buf));
die("git upload-pack: not our ref %s",
sha1_to_hex(sha1_buf));
+ }
if (!(o->flags & WANTED)) {
o->flags |= WANTED;
if (!((allow_unadvertised_object_request & ALLOW_ANY_SHA1) == ALLOW_ANY_SHA1
diff --git a/worktree.c b/worktree.c
index d633761575..fa7bc67a50 100644
--- a/worktree.c
+++ b/worktree.c
@@ -175,7 +175,7 @@ struct worktree **get_worktrees(unsigned flags)
struct dirent *d;
int counter = 0, alloc = 2;
- list = xmalloc(alloc * sizeof(struct worktree *));
+ ALLOC_ARRAY(list, alloc);
list[counter++] = get_main_worktree();
@@ -255,7 +255,7 @@ struct worktree *find_worktree(struct worktree **list,
return wt;
arg = prefix_filename(prefix, strlen(prefix), arg);
- path = real_pathdup(arg);
+ path = real_pathdup(arg, 1);
for (; *list; list++)
if (!fspathcmp(path, real_path((*list)->path)))
break;
diff --git a/wrapper.c b/wrapper.c
index e7f1979968..0542fc7582 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -440,23 +440,6 @@ int xmkstemp(char *template)
return fd;
}
-/* git_mkstemp() - create tmp file honoring TMPDIR variable */
-int git_mkstemp(char *path, size_t len, const char *template)
-{
- const char *tmp;
- size_t n;
-
- tmp = getenv("TMPDIR");
- if (!tmp)
- tmp = "/tmp";
- n = snprintf(path, len, "%s/%s", tmp, template);
- if (len <= n) {
- errno = ENAMETOOLONG;
- return -1;
- }
- return mkstemp(path);
-}
-
/* Adapted from libiberty's mkstemp.c. */
#undef TMP_MAX
@@ -531,13 +514,6 @@ int git_mkstemp_mode(char *pattern, int mode)
return git_mkstemps_mode(pattern, 0, mode);
}
-#ifdef NO_MKSTEMPS
-int gitmkstemps(char *pattern, int suffix_len)
-{
- return git_mkstemps_mode(pattern, suffix_len, 0600);
-}
-#endif
-
int xmkstemp_mode(char *template, int mode)
{
int fd;
diff --git a/wt-status.c b/wt-status.c
index d47012048f..234e77a6d6 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1730,12 +1730,14 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
return;
branch_name = s->branch;
+#define LABEL(string) (s->no_gettext ? (string) : _(string))
+
if (s->is_initial)
- color_fprintf(s->fp, header_color, _("Initial commit on "));
+ color_fprintf(s->fp, header_color, LABEL(N_("Initial commit on ")));
if (!strcmp(s->branch, "HEAD")) {
color_fprintf(s->fp, color(WT_STATUS_NOBRANCH, s), "%s",
- _("HEAD (no branch)"));
+ LABEL(N_("HEAD (no branch)")));
goto conclude;
}
@@ -1760,8 +1762,6 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
if (!upstream_is_gone && !num_ours && !num_theirs)
goto conclude;
-#define LABEL(string) (s->no_gettext ? (string) : _(string))
-
color_fprintf(s->fp, header_color, " [");
if (upstream_is_gone) {
color_fprintf(s->fp, header_color, LABEL(N_("gone")));