aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/RelNotes/2.10.0.txt67
-rw-r--r--Documentation/config.txt18
-rw-r--r--Documentation/git-push.txt8
-rw-r--r--Documentation/githooks.txt18
-rw-r--r--Documentation/pretty-formats.txt10
-rw-r--r--Documentation/rev-list-options.txt44
-rw-r--r--Documentation/technical/pack-protocol.txt12
-rw-r--r--Documentation/technical/protocol-capabilities.txt9
-rw-r--r--Makefile1
-rw-r--r--builtin/blame.c3
-rw-r--r--builtin/clone.c12
-rw-r--r--builtin/commit.c2
-rw-r--r--builtin/fetch.c6
-rw-r--r--builtin/index-pack.c10
-rw-r--r--builtin/notes.c5
-rw-r--r--builtin/pack-objects.c40
-rw-r--r--builtin/push.c21
-rw-r--r--builtin/receive-pack.c177
-rw-r--r--builtin/rev-list.c17
-rw-r--r--builtin/revert.c13
-rw-r--r--builtin/submodule--helper.c2
-rw-r--r--cache.h23
-rw-r--r--config.c117
-rw-r--r--connected.c71
-rw-r--r--connected.h37
-rw-r--r--contrib/completion/git-completion.bash1
-rw-r--r--contrib/git-jump/README6
-rwxr-xr-xcontrib/git-jump/git-jump8
-rw-r--r--contrib/persistent-https/Makefile6
-rwxr-xr-xcontrib/subtree/git-subtree.sh678
-rwxr-xr-xcontrib/subtree/t/t7900-subtree.sh32
-rw-r--r--date.c8
-rw-r--r--diff.c7
-rw-r--r--fetch-pack.c19
-rwxr-xr-xgit-difftool.perl86
-rw-r--r--git-rebase--interactive.sh2
-rwxr-xr-xgit-submodule.sh22
-rwxr-xr-xgitweb/gitweb.perl2
-rw-r--r--grep.c22
-rw-r--r--grep.h1
-rw-r--r--http-walker.c55
-rw-r--r--http.c16
-rw-r--r--list.h166
-rw-r--r--mru.c50
-rw-r--r--mru.h45
-rw-r--r--pack-write.c1
-rw-r--r--parse-options-cb.c29
-rw-r--r--parse-options.h2
-rw-r--r--revision.c8
-rw-r--r--send-pack.c27
-rw-r--r--send-pack.h3
-rw-r--r--sha1_file.c66
-rw-r--r--submodule-config.c34
-rw-r--r--t/helper/test-submodule-config.c2
-rw-r--r--t/lib-git-svn.sh91
-rw-r--r--t/lib-httpd.sh8
-rw-r--r--t/lib-httpd/apache.conf4
-rwxr-xr-xt/perf/p5303-many-packs.sh87
-rwxr-xr-xt/t0006-date.sh3
-rwxr-xr-xt/t0021-conversion.sh11
-rwxr-xr-xt/t3700-add.sh65
-rwxr-xr-xt/t4130-apply-criss-cross-rename.sh10
-rwxr-xr-xt/t4202-log.sh14
-rwxr-xr-xt/t4205-log-pretty-formats.sh398
-rwxr-xr-xt/t5510-fetch.sh4
-rwxr-xr-xt/t5545-push-options.sh103
-rwxr-xr-xt/t7060-wtstatus.sh4
-rwxr-xr-xt/t7411-submodule-config.sh11
-rwxr-xr-xt/t7512-status-help.sh1
-rwxr-xr-xt/t7800-difftool.sh14
-rwxr-xr-xt/t9100-git-svn-basic.sh2
-rwxr-xr-xt/t9115-git-svn-dcommit-funky-renames.sh7
-rwxr-xr-xt/t9118-git-svn-funky-branch-names.sh2
-rwxr-xr-xt/t9120-git-svn-clone-with-percent-escapes.sh2
-rwxr-xr-xt/t9142-git-svn-shallow-clone.sh2
-rwxr-xr-xt/t9158-git-svn-mergeinfo.sh2
-rwxr-xr-xt/t9160-git-svn-preserve-empty-dirs.sh1
-rw-r--r--templates/hooks--pre-receive.sample24
-rw-r--r--transport.c1
-rw-r--r--transport.h7
-rw-r--r--wt-status.c7
81 files changed, 2059 insertions, 973 deletions
diff --git a/Documentation/RelNotes/2.10.0.txt b/Documentation/RelNotes/2.10.0.txt
index aa5edb674..a9fb0736d 100644
--- a/Documentation/RelNotes/2.10.0.txt
+++ b/Documentation/RelNotes/2.10.0.txt
@@ -91,6 +91,20 @@ UI, Workflows & Features
* A handful of "git svn" updates.
+ * "git push" learned to accept and pass extra options to the
+ receiving end so that hooks can read and react to them.
+
+ * "git status" learned to suggest "merge --abort" during a conflicted
+ merge, just like it already suggests "rebase --abort" during a
+ conflicted rebase.
+ (merge b0a61ab mm/status-suggest-merge-abort later to maint).
+
+ * "git jump" script (in contrib/) has been updated a bit.
+ (merge a91e692 jk/git-jump later to maint).
+
+ * "git push" and "git clone" learned to give better progress meters
+ to the end user who is waiting on the terminal.
+
Performance, Internal Implementation, Development Support etc.
@@ -214,6 +228,24 @@ Performance, Internal Implementation, Development Support etc.
* A few tests that specifically target "git rebase -i" have been
added.
+ * Dumb http transport on the client side has been optimized.
+ (merge ecba195 ew/http-walker later to maint).
+
+ * Users of the parse_options_concat() API function need to allocate
+ extra slots in advance and fill them with OPT_END() when they want
+ to decide the set of supported options dynamically, which makes the
+ code error-prone and hard to read. This has been corrected by tweaking
+ the API to allocate and return a new copy of "struct option" array.
+ (merge 023ff39 jk/parse-options-concat later to maint).
+
+ * "git fetch" exchanges batched have/ack messages between the sender
+ and the receiver, initially doubling every time and then falling
+ back to enlarge the window size linearly. The "smart http"
+ transport, being an half-duplex protocol, outgrows the preset limit
+ too quickly and becomes inefficient when interacting with a large
+ repository. The internal mechanism learned to grow the window size
+ more aggressively when working with the "smart http" transport.
+
Also contains various documentation updates and code clean-ups.
@@ -430,6 +462,39 @@ notes for details).
too ancient FreeBSD releases.
(merge 259f22a ew/find-perl-on-freebsd-in-local later to maint).
+ * "git commit --help" said "--no-verify" is only about skipping the
+ pre-commit hook, and failed to say that it also skipped the
+ commit-msg hook.
+ (merge def480f os/no-verify-skips-commit-msg-too later to maint).
+
+ * "git merge" in Git v2.9 was taught to forbid merging an unrelated
+ lines of history by default, but that is exactly the kind of thing
+ the "--rejoin" mode of "git subtree" (in contrib/) wants to do.
+ "git subtree" has been taught to use the "--allow-unrelated-histories"
+ option to override the default.
+ (merge 0f12c7d da/subtree-2.9-regression later to maint).
+
+ * The build procedure for "git persistent-https" helper (in contrib/)
+ has been updated so that it can be built with more recent versions
+ of Go.
+ (merge accb613 pm/build-persistent-https-with-recent-go later to maint).
+
+ * There is an optimization used in "git diff $treeA $treeB" to borrow
+ an already checked-out copy in the working tree when it is known to
+ be the same as the blob being compared, expecting that open/mmap of
+ such a file is faster than reading it from the object store, which
+ involves inflating and applying delta. This however kicked in even
+ when the checked-out copy needs to go through the convert-to-git
+ conversion (including the clean filter), which defeats the whole
+ point of the optimization. The optimization has been disabled when
+ the conversion is necessary.
+ (merge 06dec43 jk/diff-do-not-reuse-wtf-needs-cleaning later to maint).
+
+ * "git -c grep.patternType=extended log --basic-regexp" misbehaved
+ because the internal API to access the grep machinery was not
+ designed well.
+ (merge 8465541 jc/grep-commandline-vs-configuration later to maint).
+
* Other minor clean-ups and documentation updates
(merge e51b0df pb/commit-editmsg-path later to maint).
(merge b333d0d jk/send-pack-stdio later to maint).
@@ -441,3 +506,5 @@ notes for details).
(merge 16726cf jc/doc-diff-filter-exclude later to maint).
(merge fd2e7da rs/worktree-use-strbuf-absolute-path later to maint).
(merge 406621f sb/submodule-deinit-all later to maint).
+ (merge 55cbe18 rs/submodule-config-code-cleanup later to maint).
+ (merge 280abfd sb/pack-protocol-doc-nak later to maint).
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 8b1aee4b3..bc1c433c4 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -2427,8 +2427,13 @@ rebase.instructionFormat
receive.advertiseAtomic::
By default, git-receive-pack will advertise the atomic push
- capability to its clients. If you don't want to this capability
- to be advertised, set this variable to false.
+ capability to its clients. If you don't want to advertise this
+ capability, set this variable to false.
+
+receive.advertisePushOptions::
+ By default, git-receive-pack will advertise the push options
+ capability to its clients. If you don't want to advertise this
+ capability, set this variable to false.
receive.autogc::
By default, git-receive-pack will run "git-gc --auto" after
@@ -2483,6 +2488,15 @@ receive.fsck.skipList::
can be safely ignored such as invalid committer email addresses.
Note: corrupt objects cannot be skipped with this setting.
+receive.keepAlive::
+ After receiving the pack from the client, `receive-pack` may
+ produce no output (if `--quiet` was specified) while processing
+ the pack, causing some networks to drop the TCP connection.
+ With this option set, if `receive-pack` does not transmit
+ any data in this phase for `receive.keepAlive` seconds, it will
+ send a short keepalive packet. The default is 5 seconds; set
+ to 0 to disable keepalives entirely.
+
receive.unpackLimit::
If the number of objects received in a push is below this
limit then the objects will be unpacked into loose object
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 93c3527f0..ec514f6cd 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -11,7 +11,7 @@ SYNOPSIS
[verse]
'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
[--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-v | --verbose]
- [-u | --set-upstream]
+ [-u | --set-upstream] [--push-option=<string>]
[--[no-]signed|--sign=(true|false|if-asked)]
[--force-with-lease[=<refname>[:<expect>]]]
[--no-verify] [<repository> [<refspec>...]]
@@ -156,6 +156,12 @@ already exists on the remote side.
Either all refs are updated, or on error, no refs are updated.
If the server does not support atomic pushes the push will fail.
+-o::
+--push-option::
+ Transmit the given string to the server, which passes them to
+ the pre-receive as well as the post-receive hook. The given string
+ must not contain a NUL or LF character.
+
--receive-pack=<git-receive-pack>::
--exec=<git-receive-pack>::
Path to the 'git-receive-pack' program on the remote
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index d82e912e5..9565dc3fd 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -247,6 +247,15 @@ Both standard output and standard error output are forwarded to
'git send-pack' on the other end, so you can simply `echo` messages
for the user.
+The number of push options given on the command line of
+`git push --push-option=...` can be read from the environment
+variable `GIT_PUSH_OPTION_COUNT`, and the options themselves are
+found in `GIT_PUSH_OPTION_0`, `GIT_PUSH_OPTION_1`,...
+If it is negotiated to not use the push options phase, the
+environment variables will not be set. If the client selects
+to use push options, but doesn't transmit any, the count variable
+will be set to zero, `GIT_PUSH_OPTION_COUNT=0`.
+
[[update]]
update
~~~~~~
@@ -322,6 +331,15 @@ a sample script `post-receive-email` provided in the `contrib/hooks`
directory in Git distribution, which implements sending commit
emails.
+The number of push options given on the command line of
+`git push --push-option=...` can be read from the environment
+variable `GIT_PUSH_OPTION_COUNT`, and the options themselves are
+found in `GIT_PUSH_OPTION_0`, `GIT_PUSH_OPTION_1`,...
+If it is negotiated to not use the push options phase, the
+environment variables will not be set. If the client selects
+to use push options, but doesn't transmit any, the count variable
+will be set to zero, `GIT_PUSH_OPTION_COUNT=0`.
+
[[post-update]]
post-update
~~~~~~~~~~~
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 29b19b992..b95d67ec0 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -147,8 +147,14 @@ endif::git-rev-list[]
"U" for a good signature with unknown validity and "N" for no signature
- '%GS': show the name of the signer for a signed commit
- '%GK': show the key used to sign a signed commit
-- '%gD': reflog selector, e.g., `refs/stash@{1}`
-- '%gd': shortened reflog selector, e.g., `stash@{1}`
+- '%gD': reflog selector, e.g., `refs/stash@{1}` or
+ `refs/stash@{2 minutes ago`}; the format follows the rules described
+ for the `-g` option. The portion before the `@` is the refname as
+ given on the command line (so `git log -g refs/heads/master` would
+ yield `refs/heads/master@{0}`).
+- '%gd': shortened reflog selector; same as `%gD`, but the refname
+ portion is shortened for human readability (so `refs/heads/master`
+ becomes just `master`).
- '%gn': reflog identity name
- '%gN': reflog identity name (respecting .mailmap, see
linkgit:git-shortlog[1] or linkgit:git-blame[1])
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index c5bd21812..a779c9dfe 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -252,10 +252,25 @@ list.
+
With `--pretty` format other than `oneline` (for obvious reasons),
this causes the output to have two extra lines of information
-taken from the reflog. By default, 'commit@\{Nth}' notation is
-used in the output. When the starting commit is specified as
-'commit@\{now}', output also uses 'commit@\{timestamp}' notation
-instead. Under `--pretty=oneline`, the commit message is
+taken from the reflog. The reflog designator in the output may be shown
+as `ref@{Nth}` (where `Nth` is the reverse-chronological index in the
+reflog) or as `ref@{timestamp}` (with the timestamp for that entry),
+depending on a few rules:
++
+--
+1. If the starting point is specified as `ref@{Nth}`, show the index
+format.
++
+2. If the starting point was specified as `ref@{now}`, show the
+timestamp format.
++
+3. If neither was used, but `--date` was given on the command line, show
+the timestamp in the format requested by `--date`.
++
+4. Otherwise, show the index format.
+--
++
+Under `--pretty=oneline`, the commit message is
prefixed with this information on the same line.
This option cannot be combined with `--reverse`.
See also linkgit:git-reflog[1].
@@ -274,6 +289,10 @@ ifdef::git-rev-list[]
Try to speed up the traversal using the pack bitmap index (if
one is available). Note that when traversing with `--objects`,
trees and blobs will not have their associated path printed.
+
+--progress=<header>::
+ Show progress reports on stderr as objects are considered. The
+ `<header>` text will be printed with each progress update.
endif::git-rev-list[]
--
@@ -710,8 +729,8 @@ include::pretty-options.txt[]
`iso-local`), the user's local time zone is used instead.
+
`--date=relative` shows dates relative to the current time,
-e.g. ``2 hours ago''. The `-local` option cannot be used with
-`--raw` or `--relative`.
+e.g. ``2 hours ago''. The `-local` option has no effect for
+`--date=relative`.
+
`--date=local` is an alias for `--date=default-local`.
+
@@ -731,7 +750,18 @@ format, often found in email messages.
+
`--date=short` shows only the date, but not the time, in `YYYY-MM-DD` format.
+
-`--date=raw` shows the date in the internal raw Git format `%s %z` format.
+`--date=raw` shows the date as seconds since the epoch (1970-01-01
+00:00:00 UTC), followed by a space, and then the timezone as an offset
+from UTC (a `+` or `-` with four digits; the first two are hours, and
+the second two are minutes). I.e., as if the timestamp were formatted
+with `strftime("%s %z")`).
+Note that the `-local` option does not affect the seconds-since-epoch
+value (which is always measured in UTC), but does switch the accompanying
+timezone value.
++
+`--date=unix` shows the date as a Unix epoch timestamp (seconds since
+1970). As with `--raw`, this is always in UTC and therefore `-local`
+has no effect.
+
`--date=format:...` feeds the format `...` to your system `strftime`.
Use `--date=format:%c` to show the date in your system locale's
diff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt
index 8b3634380..736f3894a 100644
--- a/Documentation/technical/pack-protocol.txt
+++ b/Documentation/technical/pack-protocol.txt
@@ -307,7 +307,7 @@ In multi_ack mode:
ready to make a packfile, it will blindly ACK all 'have' obj-ids
back to the client.
- * the server will then send a 'NACK' and then wait for another response
+ * the server will then send a 'NAK' and then wait for another response
from the client - either a 'done' or another list of 'have' lines.
In multi_ack_detailed mode:
@@ -454,7 +454,8 @@ The reference discovery phase is done nearly the same way as it is in the
fetching protocol. Each reference obj-id and name on the server is sent
in packet-line format to the client, followed by a flush-pkt. The only
real difference is that the capability listing is different - the only
-possible values are 'report-status', 'delete-refs' and 'ofs-delta'.
+possible values are 'report-status', 'delete-refs', 'ofs-delta' and
+'push-options'.
Reference Update Request and Packfile Transfer
----------------------------------------------
@@ -465,9 +466,10 @@ that it wants to update, it sends a line listing the obj-id currently on
the server, the obj-id the client would like to update it to and the name
of the reference.
-This list is followed by a flush-pkt and then the packfile that should
-contain all the objects that the server will need to complete the new
-references.
+This list is followed by a flush-pkt. Then the push options are transmitted
+one per packet followed by another flush-pkt. After that the packfile that
+should contain all the objects that the server will need to complete the new
+references will be sent.
----
update-request = *shallow ( command-list | push-cert ) [packfile]
diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt
index eaab6b4ac..4c28d3a8a 100644
--- a/Documentation/technical/protocol-capabilities.txt
+++ b/Documentation/technical/protocol-capabilities.txt
@@ -253,6 +253,15 @@ atomic pushes. If the pushing client requests this capability, the server
will update the refs in one atomic transaction. Either all refs are
updated or none.
+push-options
+------------
+
+If the server sends the 'push-options' capability it is able to accept
+push options after the update commands have been sent, but before the
+packfile is streamed. If the pushing client requests this capability,
+the server will pass the options to the pre- and post- receive hooks
+that process this push request.
+
allow-tip-sha1-in-want
----------------------
diff --git a/Makefile b/Makefile
index 6a13386c2..ad3624d95 100644
--- a/Makefile
+++ b/Makefile
@@ -755,6 +755,7 @@ LIB_OBJS += merge.o
LIB_OBJS += merge-blobs.o
LIB_OBJS += merge-recursive.o
LIB_OBJS += mergesort.o
+LIB_OBJS += mru.o
LIB_OBJS += name-hash.o
LIB_OBJS += notes.o
LIB_OBJS += notes-cache.o
diff --git a/builtin/blame.c b/builtin/blame.c
index ab66cde2c..2e7b3030c 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -2633,6 +2633,9 @@ parse_done:
case DATE_RAW:
blame_date_width = sizeof("1161298804 -0700");
break;
+ case DATE_UNIX:
+ blame_date_width = sizeof("1161298804");
+ break;
case DATE_SHORT:
blame_date_width = sizeof("2006-10-19");
break;
diff --git a/builtin/clone.c b/builtin/clone.c
index 31ea247e3..f044a8c27 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -624,13 +624,13 @@ static void update_remote_refs(const struct ref *refs,
const struct ref *rm = mapped_refs;
if (check_connectivity) {
- if (transport->progress)
- fprintf(stderr, _("Checking connectivity... "));
- if (check_everything_connected_with_transport(iterate_ref_map,
- 0, &rm, transport))
+ struct check_connected_options opt = CHECK_CONNECTED_INIT;
+
+ opt.transport = transport;
+ opt.progress = transport->progress;
+
+ if (check_connected(iterate_ref_map, &rm, &opt))
die(_("remote did not send all necessary objects"));
- if (transport->progress)
- fprintf(stderr, _("done.\n"));
}
if (refs) {
diff --git a/builtin/commit.c b/builtin/commit.c
index 1f6dbcd0d..77e3dc849 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1617,7 +1617,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")),
OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")),
OPT_BOOL('o', "only", &only, N_("commit only specified files")),
- OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit hook")),
+ OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")),
OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")),
OPT_SET_INT(0, "short", &status_format, N_("show status concisely"),
STATUS_FORMAT_SHORT),
diff --git a/builtin/fetch.c b/builtin/fetch.c
index acd0cf175..164623bb6 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -729,7 +729,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
url = xstrdup("foreign");
rm = ref_map;
- if (check_everything_connected(iterate_ref_map, 0, &rm)) {
+ if (check_connected(iterate_ref_map, &rm, NULL)) {
rc = error(_("%s did not send all necessary objects\n"), url);
goto abort;
}
@@ -866,6 +866,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
static int quickfetch(struct ref *ref_map)
{
struct ref *rm = ref_map;
+ struct check_connected_options opt = CHECK_CONNECTED_INIT;
/*
* If we are deepening a shallow clone we already have these
@@ -876,7 +877,8 @@ static int quickfetch(struct ref *ref_map)
*/
if (depth)
return -1;
- return check_everything_connected(iterate_ref_map, 1, &rm);
+ opt.quiet = 1;
+ return check_connected(iterate_ref_map, &rm, &opt);
}
static int fetch_refs(struct transport *transport, struct ref *ref_map)
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 1008d7f63..1d2ea583a 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -77,6 +77,7 @@ static int strict;
static int do_fsck_object;
static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
static int verbose;
+static int show_resolving_progress;
static int show_stat;
static int check_self_contained_and_connected;
@@ -1191,7 +1192,7 @@ static void resolve_deltas(void)
qsort(ref_deltas, nr_ref_deltas, sizeof(struct ref_delta_entry),
compare_ref_delta_entry);
- if (verbose)
+ if (verbose || show_resolving_progress)
progress = start_progress(_("Resolving deltas"),
nr_ref_deltas + nr_ofs_deltas);
@@ -1626,6 +1627,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
struct pack_idx_option opts;
unsigned char pack_sha1[20];
unsigned foreign_nr = 1; /* zero is a "good" value, assume bad */
+ int report_end_of_input = 0;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(index_pack_usage);
@@ -1695,6 +1697,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
input_len = sizeof(*hdr);
} else if (!strcmp(arg, "-v")) {
verbose = 1;
+ } else if (!strcmp(arg, "--show-resolving-progress")) {
+ show_resolving_progress = 1;
+ } else if (!strcmp(arg, "--report-end-of-input")) {
+ report_end_of_input = 1;
} else if (!strcmp(arg, "-o")) {
if (index_name || (i+1) >= argc)
usage(index_pack_usage);
@@ -1752,6 +1758,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
obj_stat = xcalloc(st_add(nr_objects, 1), sizeof(struct object_stat));
ofs_deltas = xcalloc(nr_objects, sizeof(struct ofs_delta_entry));
parse_pack_objects(pack_sha1);
+ if (report_end_of_input)
+ write_in_full(2, "\0", 1);
resolve_deltas();
conclude_pack(fix_thin_pack, curr_pack, pack_sha1);
free(ofs_deltas);
diff --git a/builtin/notes.c b/builtin/notes.c
index 057205176..f848b8969 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -91,7 +91,7 @@ static const char * const git_notes_get_ref_usage[] = {
};
static const char note_template[] =
- "\nWrite/edit the notes for the following object:\n";
+ N_("Write/edit the notes for the following object:");
struct note_data {
int given;
@@ -179,7 +179,8 @@ static void prepare_note_data(const unsigned char *object, struct note_data *d,
copy_obj_to_fd(fd, old_note);
strbuf_addch(&buf, '\n');
- strbuf_add_commented_lines(&buf, note_template, strlen(note_template));
+ strbuf_add_commented_lines(&buf, "\n", strlen("\n"));
+ strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)));
strbuf_addch(&buf, '\n');
write_or_die(fd, buf.buf, buf.len);
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 92e2e5f7a..4a6339896 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -46,6 +46,7 @@ static int keep_unreachable, unpack_unreachable, include_tag;
static unsigned long unpack_unreachable_expiration;
static int pack_loose_unreachable;
static int local;
+static int have_non_local_packs;
static int incremental;
static int ignore_packed_keep;
static int allow_ofs_delta;
@@ -978,6 +979,23 @@ static int want_object_in_pack(const unsigned char *sha1,
return 1;
if (incremental)
return 0;
+
+ /*
+ * When asked to do --local (do not include an
+ * object that appears in a pack we borrow
+ * from elsewhere) or --honor-pack-keep (do not
+ * include an object that appears in a pack marked
+ * with .keep), we need to make sure no copy of this
+ * object come from in _any_ pack that causes us to
+ * omit it, and need to complete this loop. When
+ * neither option is in effect, we know the object
+ * we just found is going to be packed, so break
+ * out of the loop to return 1 now.
+ */
+ if (!ignore_packed_keep &&
+ (!local || !have_non_local_packs))
+ break;
+
if (local && !p->pack_local)
return 0;
if (ignore_packed_keep && p->pack_local && p->pack_keep)
@@ -2784,6 +2802,28 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
progress = 2;
prepare_packed_git();
+ if (ignore_packed_keep) {
+ struct packed_git *p;
+ for (p = packed_git; p; p = p->next)
+ if (p->pack_local && p->pack_keep)
+ break;
+ if (!p) /* no keep-able packs found */
+ ignore_packed_keep = 0;
+ }
+ if (local) {
+ /*
+ * unlike ignore_packed_keep above, we do not want to
+ * unset "local" based on looking at packs, as it
+ * also covers non-local objects
+ */
+ struct packed_git *p;
+ for (p = packed_git; p; p = p->next) {
+ if (!p->pack_local) {
+ have_non_local_packs = 1;
+ break;
+ }
+ }
+ }
if (progress)
progress_state = start_progress(_("Counting objects"), 0);
diff --git a/builtin/push.c b/builtin/push.c
index 4e9e4dbab..3bb9d6b7e 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -353,7 +353,8 @@ static int push_with_options(struct transport *transport, int flags)
return 1;
}
-static int do_push(const char *repo, int flags)
+static int do_push(const char *repo, int flags,
+ const struct string_list *push_options)
{
int i, errs;
struct remote *remote = pushremote_get(repo);
@@ -376,6 +377,9 @@ static int do_push(const char *repo, int flags)
if (remote->mirror)
flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
+ if (push_options->nr)
+ flags |= TRANSPORT_PUSH_OPTIONS;
+
if ((flags & TRANSPORT_PUSH_ALL) && refspec) {
if (!strcmp(*refspec, "refs/tags/*"))
return error(_("--all and --tags are incompatible"));
@@ -406,13 +410,16 @@ static int do_push(const char *repo, int flags)
for (i = 0; i < url_nr; i++) {
struct transport *transport =
transport_get(remote, url[i]);
+ if (flags & TRANSPORT_PUSH_OPTIONS)
+ transport->push_options = push_options;
if (push_with_options(transport, flags))
errs++;
}
} else {
struct transport *transport =
transport_get(remote, NULL);
-
+ if (flags & TRANSPORT_PUSH_OPTIONS)
+ transport->push_options = push_options;
if (push_with_options(transport, flags))
errs++;
}
@@ -500,6 +507,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)
int push_cert = -1;
int rc;
const char *repo = NULL; /* default repository */
+ static struct string_list push_options = STRING_LIST_INIT_DUP;
+ static struct string_list_item *item;
+
struct option options[] = {
OPT__VERBOSITY(&verbosity),
OPT_STRING( 0 , "repo", &repo, N_("repository"), N_("repository")),
@@ -533,6 +543,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"),
PARSE_OPT_OPTARG, option_parse_push_signed },
OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
+ OPT_STRING_LIST('o', "push-option", &push_options, N_("server-specific"), N_("option to transmit")),
OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
TRANSPORT_FAMILY_IPV4),
OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
@@ -563,7 +574,11 @@ int cmd_push(int argc, const char **argv, const char *prefix)
set_refspecs(argv + 1, argc - 1, repo);
}
- rc = do_push(repo, flags);
+ for_each_string_list_item(item, &push_options)
+ if (strchr(item->string, '\n'))
+ die(_("push options must not have new line characters"));
+
+ rc = do_push(repo, flags, &push_options);
if (rc == -1)
usage_with_options(push_usage, options);
else
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 15c323a7c..92e1213ec 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -44,10 +44,12 @@ static struct strbuf fsck_msg_types = STRBUF_INIT;
static int receive_unpack_limit = -1;
static int transfer_unpack_limit = -1;
static int advertise_atomic_push = 1;
+static int advertise_push_options;
static int unpack_limit = 100;
static int report_status;
static int use_sideband;
static int use_atomic;
+static int use_push_options;
static int quiet;
static int prefer_ofs_delta = 1;
static int auto_update_server_info;
@@ -76,6 +78,13 @@ static long nonce_stamp_slop;
static unsigned long nonce_stamp_slop_limit;
static struct ref_transaction *transaction;
+static enum {
+ KEEPALIVE_NEVER = 0,
+ KEEPALIVE_AFTER_NUL,
+ KEEPALIVE_ALWAYS
+} use_keepalive;
+static int keepalive_in_sec = 5;
+
static enum deny_action parse_deny_action(const char *var, const char *value)
{
if (value) {
@@ -193,6 +202,16 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
return 0;
}
+ if (strcmp(var, "receive.advertisepushoptions") == 0) {
+ advertise_push_options = git_config_bool(var, value);
+ return 0;
+ }
+
+ if (strcmp(var, "receive.keepalive") == 0) {
+ keepalive_in_sec = git_config_int(var, value);
+ return 0;
+ }
+
return git_default_config(var, value, cb);
}
@@ -211,6 +230,8 @@ static void show_ref(const char *path, const unsigned char *sha1)
strbuf_addstr(&cap, " ofs-delta");
if (push_cert_nonce)
strbuf_addf(&cap, " push-cert=%s", push_cert_nonce);
+ if (advertise_push_options)
+ strbuf_addstr(&cap, " push-options");
strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized());
packet_write(1, "%s %s%c%s\n",
sha1_to_hex(sha1), path, 0, cap.buf);
@@ -319,10 +340,60 @@ static void rp_error(const char *err, ...)
static int copy_to_sideband(int in, int out, void *arg)
{
char data[128];
+ int keepalive_active = 0;
+
+ if (keepalive_in_sec <= 0)
+ use_keepalive = KEEPALIVE_NEVER;
+ if (use_keepalive == KEEPALIVE_ALWAYS)
+ keepalive_active = 1;
+
while (1) {
- ssize_t sz = xread(in, data, sizeof(data));
+ ssize_t sz;
+
+ if (keepalive_active) {
+ struct pollfd pfd;
+ int ret;
+
+ pfd.fd = in;
+ pfd.events = POLLIN;
+ ret = poll(&pfd, 1, 1000 * keepalive_in_sec);
+
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ break;
+ } else if (ret == 0) {
+ /* no data; send a keepalive packet */
+ static const char buf[] = "0005\1";
+ write_or_die(1, buf, sizeof(buf) - 1);
+ continue;
+ } /* else there is actual data to read */
+ }
+
+ sz = xread(in, data, sizeof(data));
if (sz <= 0)
break;
+
+ if (use_keepalive == KEEPALIVE_AFTER_NUL && !keepalive_active) {
+ const char *p = memchr(data, '\0', sz);
+ if (p) {
+ /*
+ * The NUL tells us to start sending keepalives. Make
+ * sure we send any other data we read along
+ * with it.
+ */
+ keepalive_active = 1;
+ send_sideband(1, 2, data, p - data, use_sideband);
+ send_sideband(1, 2, p + 1, sz - (p - data + 1), use_sideband);
+ continue;
+ }
+ }
+
+ /*
+ * Either we're not looking for a NUL signal, or we didn't see
+ * it yet; just pass along the data.
+ */
send_sideband(1, 2, data, sz, use_sideband);
}
close(in);
@@ -550,8 +621,16 @@ static void prepare_push_cert_sha1(struct child_process *proc)
}
}
+struct receive_hook_feed_state {
+ struct command *cmd;
+ int skip_broken;
+ struct strbuf buf;
+ const struct string_list *push_options;
+};
+
typedef int (*feed_fn)(void *, const char **, size_t *);
-static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_state)
+static int run_and_feed_hook(const char *hook_name, feed_fn feed,
+ struct receive_hook_feed_state *feed_state)
{
struct child_process proc = CHILD_PROCESS_INIT;
struct async muxer;
@@ -567,6 +646,16 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_sta
proc.argv = argv;
proc.in = -1;
proc.stdout_to_stderr = 1;
+ if (feed_state->push_options) {
+ int i;
+ for (i = 0; i < feed_state->push_options->nr; i++)
+ argv_array_pushf(&proc.env_array,
+ "GIT_PUSH_OPTION_%d=%s", i,
+ feed_state->push_options->items[i].string);
+ argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT=%d",
+ feed_state->push_options->nr);
+ } else
+ argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT");
if (use_sideband) {
memset(&muxer, 0, sizeof(muxer));
@@ -606,12 +695,6 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_sta
return finish_command(&proc);
}
-struct receive_hook_feed_state {
- struct command *cmd;
- int skip_broken;
- struct strbuf buf;
-};
-
static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
{
struct receive_hook_feed_state *state = state_;
@@ -634,8 +717,10 @@ static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
return 0;
}
-static int run_receive_hook(struct command *commands, const char *hook_name,
- int skip_broken)
+static int run_receive_hook(struct command *commands,
+ const char *hook_name,
+ int skip_broken,
+ const struct string_list *push_options)
{
struct receive_hook_feed_state state;
int status;
@@ -646,6 +731,7 @@ static int run_receive_hook(struct command *commands, const char *hook_name,
if (feed_receive_hook(&state, NULL, NULL))
return 0;
state.cmd = commands;
+ state.push_options = push_options;
status = run_and_feed_hook(hook_name, feed_receive_hook, &state);
strbuf_release(&state.buf);
return status;
@@ -737,7 +823,7 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
{
static struct lock_file shallow_lock;
struct sha1_array extra = SHA1_ARRAY_INIT;
- const char *alt_file;
+ struct check_connected_options opt = CHECK_CONNECTED_INIT;
uint32_t mask = 1 << (cmd->index % 32);
int i;
@@ -749,9 +835,8 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
!delayed_reachability_test(si, i))
sha1_array_append(&extra, si->shallow->sha1[i]);
- setup_alternate_shallow(&shallow_lock, &alt_file, &extra);
- if (check_shallow_connected(command_singleton_iterator,
- 0, cmd, alt_file)) {
+ setup_alternate_shallow(&shallow_lock, &opt.shallow_file, &extra);
+ if (check_connected(command_singleton_iterator, cmd, &opt)) {
rollback_lock_file(&shallow_lock);
sha1_array_clear(&extra);
return -1;
@@ -1160,8 +1245,8 @@ static void set_connectivity_errors(struct command *commands,
if (shallow_update && si->shallow_ref[cmd->index])
/* to be checked in update_shallow_ref() */
continue;
- if (!check_everything_connected(command_singleton_iterator,
- 0, &singleton))
+ if (!check_connected(command_singleton_iterator, &singleton,
+ NULL))
continue;
cmd->error_string = "missing necessary objects";
}
@@ -1316,11 +1401,15 @@ cleanup:
static void execute_commands(struct command *commands,
const char *unpacker_error,
- struct shallow_info *si)
+ struct shallow_info *si,
+ const struct string_list *push_options)
{
+ struct check_connected_options opt = CHECK_CONNECTED_INIT;
struct command *cmd;
unsigned char sha1[20];
struct iterate_data data;
+ struct async muxer;
+ int err_fd = 0;
if (unpacker_error) {
for (cmd = commands; cmd; cmd = cmd->next)
@@ -1328,14 +1417,28 @@ static void execute_commands(struct command *commands,
return;
}
+ if (use_sideband) {
+ memset(&muxer, 0, sizeof(muxer));
+ muxer.proc = copy_to_sideband;
+ muxer.in = -1;
+ if (!start_async(&muxer))
+ err_fd = muxer.in;
+ /* ...else, continue without relaying sideband */
+ }
+
data.cmds = commands;
data.si = si;
- if (check_everything_connected(iterate_receive_command_list, 0, &data))
+ opt.err_fd = err_fd;
+ opt.progress = err_fd && !quiet;
+ if (check_connected(iterate_receive_command_list, &data, &opt))
set_connectivity_errors(commands, si);
+ if (use_sideband)
+ finish_async(&muxer);
+
reject_updates_to_hidden(commands);
- if (run_receive_hook(commands, "pre-receive", 0)) {
+ if (run_receive_hook(commands, "pre-receive", 0, push_options)) {
for (cmd = commands; cmd; cmd = cmd->next) {
if (!cmd->error_string)
cmd->error_string = "pre-receive hook declined";
@@ -1439,6 +1542,9 @@ static struct command *read_head_info(struct sha1_array *shallow)
if (advertise_atomic_push
&& parse_feature_request(feature_list, "atomic"))
use_atomic = 1;
+ if (advertise_push_options
+ && parse_feature_request(feature_list, "push-options"))
+ use_push_options = 1;
}
if (!strcmp(line, "push-cert")) {
@@ -1471,6 +1577,21 @@ static struct command *read_head_info(struct sha1_array *shallow)
return commands;
}
+static void read_push_options(struct string_list *options)
+{
+ while (1) {
+ char *line;
+ int len;
+
+ line = packet_read_line(0, &len);
+
+ if (!line)
+ break;
+
+ string_list_append(options, line);
+ }
+}
+
static const char *parse_pack_header(struct pack_header *hdr)
{
switch (read_pack_header(0, hdr)) {
@@ -1548,6 +1669,10 @@ static const char *unpack(int err_fd, struct shallow_info *si)
(uintmax_t)getpid(),
hostname);
+ if (!quiet && err_fd)
+ argv_array_push(&child.args, "--show-resolving-progress");
+ if (use_sideband)
+ argv_array_push(&child.args, "--report-end-of-input");
if (fsck_objects)
argv_array_pushf(&child.args, "--strict%s",
fsck_msg_types.buf);
@@ -1577,6 +1702,7 @@ static const char *unpack_with_sideband(struct shallow_info *si)
if (!use_sideband)
return unpack(0, si);
+ use_keepalive = KEEPALIVE_AFTER_NUL;
memset(&muxer, 0, sizeof(muxer));
muxer.proc = copy_to_sideband;
muxer.in = -1;
@@ -1756,6 +1882,10 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
if ((commands = read_head_info(&shallow)) != NULL) {
const char *unpack_status = NULL;
+ struct string_list push_options = STRING_LIST_INIT_DUP;
+
+ if (use_push_options)
+ read_push_options(&push_options);
prepare_shallow_info(&si, &shallow);
if (!si.nr_ours && !si.nr_theirs)
@@ -1764,13 +1894,18 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
unpack_status = unpack_with_sideband(&si);
update_shallow_info(commands, &si, &ref);
}
- execute_commands(commands, unpack_status, &si);
+ use_keepalive = KEEPALIVE_ALWAYS;
+ execute_commands(commands, unpack_status, &si,
+ &push_options);
if (pack_lockfile)
unlink_or_warn(pack_lockfile);
if (report_status)
report(commands, unpack_status);
- run_receive_hook(commands, "post-receive", 1);
+ run_receive_hook(commands, "post-receive", 1,
+ &push_options);
run_update_post_hook(commands);
+ if (push_options.nr)
+ string_list_clear(&push_options, 0);
if (auto_gc) {
const char *argv_gc_auto[] = {
"gc", "--auto", "--quiet", NULL,
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index b82bcc343..0ba82b163 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -9,6 +9,7 @@
#include "log-tree.h"
#include "graph.h"
#include "bisect.h"
+#include "progress.h"
static const char rev_list_usage[] =
"git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
@@ -49,12 +50,17 @@ static const char rev_list_usage[] =
" --bisect-all"
;
+static struct progress *progress;
+static unsigned progress_counter;
+
static void finish_commit(struct commit *commit, void *data);
static void show_commit(struct commit *commit, void *data)
{
struct rev_list_info *info = data;
struct rev_info *revs = info->revs;
+ display_progress(progress, ++progress_counter);
+
if (info->flags & REV_LIST_QUIET) {
finish_commit(commit, data);
return;
@@ -190,6 +196,7 @@ static void show_object(struct object *obj, const char *name, void *cb_data)
{
struct rev_list_info *info = cb_data;
finish_object(obj, name, cb_data);
+ display_progress(progress, ++progress_counter);
if (info->flags & REV_LIST_QUIET)
return;
show_object_with_name(stdout, obj, name);
@@ -276,6 +283,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
int bisect_show_vars = 0;
int bisect_find_all = 0;
int use_bitmap_index = 0;
+ const char *show_progress = NULL;
git_config(git_default_config, NULL);
init_revisions(&revs, prefix);
@@ -325,6 +333,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
test_bitmap_walk(&revs);
return 0;
}
+ if (skip_prefix(arg, "--progress=", &arg)) {
+ show_progress = arg;
+ continue;
+ }
usage(rev_list_usage);
}
@@ -355,6 +367,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
if (bisect_list)
revs.limited = 1;
+ if (show_progress)
+ progress = start_progress_delay(show_progress, 0, 0, 2);
+
if (use_bitmap_index && !revs.prune) {
if (revs.count && !revs.left_right && !revs.cherry_mark) {
uint32_t commit_count;
@@ -392,6 +407,8 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
traverse_commit_list(&revs, show_commit, show_object, &info);
+ stop_progress(&progress);
+
if (revs.count) {
if (revs.left_right && revs.cherry_mark)
printf("%d\t%d\t%d\n", revs.count_left, revs.count_right, revs.count_same);
diff --git a/builtin/revert.c b/builtin/revert.c
index 56a2c3666..4e693808b 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -76,7 +76,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
const char * const * usage_str = revert_or_cherry_pick_usage(opts);
const char *me = action_name(opts);
int cmd = 0;
- struct option options[] = {
+ struct option base_options[] = {
OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
OPT_CMDMODE(0, "continue", &cmd, N_("resume revert or cherry-pick sequence"), 'c'),
OPT_CMDMODE(0, "abort", &cmd, N_("cancel revert or cherry-pick sequence"), 'a'),
@@ -91,13 +91,9 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
N_("option for merge strategy"), option_parse_x),
{ OPTION_STRING, 'S', "gpg-sign", &opts->gpg_sign, N_("key-id"),
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
- OPT_END(),
- OPT_END(),
- OPT_END(),
- OPT_END(),
- OPT_END(),
- OPT_END(),
+ OPT_END()
};
+ struct option *options = base_options;
if (opts->action == REPLAY_PICK) {
struct option cp_extra[] = {
@@ -108,8 +104,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
OPT_END(),
};
- if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
- die(_("program error"));
+ options = parse_options_concat(options, cp_extra);
}
argc = parse_options(argc, argv, NULL, options, usage_str,
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index b22352b6e..6f6d67a46 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -795,7 +795,7 @@ static int update_clone_task_finished(int result,
suc->failed_clones[suc->failed_clones_nr++] = ce;
return 0;
} else {
- idx = suc->current - suc->list.nr;
+ idx -= suc->list.nr;
ce = suc->failed_clones[idx];
strbuf_addf(err, _("Failed to clone '%s' a second time, aborting"),
ce->name);
diff --git a/cache.h b/cache.h
index b5f76a4cf..7c8051be0 100644
--- a/cache.h
+++ b/cache.h
@@ -1230,7 +1230,8 @@ struct date_mode {
DATE_ISO8601_STRICT,
DATE_RFC2822,
DATE_STRFTIME,
- DATE_RAW
+ DATE_RAW,
+ DATE_UNIX
} type;
const char *strftime_fmt;
int local;
@@ -1377,6 +1378,13 @@ extern struct packed_git {
char pack_name[FLEX_ARRAY]; /* more */
} *packed_git;
+/*
+ * A most-recently-used ordered version of the packed_git list, which can
+ * be iterated instead of packed_git (and marked via mru_mark).
+ */
+struct mru;
+extern struct mru *packed_git_mru;
+
struct pack_entry {
off_t offset;
unsigned char sha1[20];
@@ -1416,7 +1424,6 @@ extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t
extern void close_pack_windows(struct packed_git *);
extern void close_all_packs(void);
extern void unuse_pack(struct pack_window **);
-extern void free_pack_by_name(const char *);
extern void clear_delta_base_cache(void);
extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
@@ -1566,10 +1573,18 @@ struct git_config_source {
const char *blob;
};
+enum config_origin_type {
+ CONFIG_ORIGIN_BLOB,
+ CONFIG_ORIGIN_FILE,
+ CONFIG_ORIGIN_STDIN,
+ CONFIG_ORIGIN_SUBMODULE_BLOB,
+ CONFIG_ORIGIN_CMDLINE
+};
+
typedef int (*config_fn_t)(const char *, const char *, void *);
extern int git_default_config(const char *, const char *, void *);
extern int git_config_from_file(config_fn_t fn, const char *, void *);
-extern int git_config_from_mem(config_fn_t fn, const char *origin_type,
+extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
const char *name, const char *buf, size_t len, void *data);
extern void git_config_push_parameter(const char *text);
extern int git_config_from_parameters(config_fn_t fn, void *data);
@@ -1713,7 +1728,7 @@ extern int ignore_untracked_cache_config;
struct key_value_info {
const char *filename;
int linenr;
- const char *origin_type;
+ enum config_origin_type origin_type;
enum config_scope scope;
};
diff --git a/config.c b/config.c
index bea937e4e..584cacf14 100644
--- a/config.c
+++ b/config.c
@@ -24,7 +24,7 @@ struct config_source {
size_t pos;
} buf;
} u;
- const char *origin_type;
+ enum config_origin_type origin_type;
const char *name;
const char *path;
int die_on_error;
@@ -245,6 +245,7 @@ int git_config_from_parameters(config_fn_t fn, void *data)
memset(&source, 0, sizeof(source));
source.prev = cf;
+ source.origin_type = CONFIG_ORIGIN_CMDLINE;
cf = &source;
/* sq_dequote will write over it */
@@ -453,6 +454,8 @@ static int git_parse_source(config_fn_t fn, void *data)
int comment = 0;
int baselen = 0;
struct strbuf *var = &cf->var;
+ int error_return = 0;
+ char *error_msg = NULL;
/* U+FEFF Byte Order Mark in UTF8 */
const char *bomptr = utf8_bom;
@@ -507,10 +510,40 @@ static int git_parse_source(config_fn_t fn, void *data)
if (get_value(fn, data, var) < 0)
break;
}
+
+ switch (cf->origin_type) {
+ case CONFIG_ORIGIN_BLOB:
+ error_msg = xstrfmt(_("bad config line %d in blob %s"),
+ cf->linenr, cf->name);
+ break;
+ case CONFIG_ORIGIN_FILE:
+ error_msg = xstrfmt(_("bad config line %d in file %s"),
+ cf->linenr, cf->name);
+ break;
+ case CONFIG_ORIGIN_STDIN:
+ error_msg = xstrfmt(_("bad config line %d in standard input"),
+ cf->linenr);
+ break;
+ case CONFIG_ORIGIN_SUBMODULE_BLOB:
+ error_msg = xstrfmt(_("bad config line %d in submodule-blob %s"),
+ cf->linenr, cf->name);
+ break;
+ case CONFIG_ORIGIN_CMDLINE:
+ error_msg = xstrfmt(_("bad config line %d in command line %s"),
+ cf->linenr, cf->name);
+ break;
+ default:
+ error_msg = xstrfmt(_("bad config line %d in %s"),
+ cf->linenr, cf->name);
+ }
+
if (cf->die_on_error)
- die(_("bad config line %d in %s %s"), cf->linenr, cf->origin_type, cf->name);
+ die("%s", error_msg);
else
- return error(_("bad config line %d in %s %s"), cf->linenr, cf->origin_type, cf->name);
+ error_return = error("%s", error_msg);
+
+ free(error_msg);
+ return error_return;
}
static int parse_unit_factor(const char *end, uintmax_t *val)
@@ -619,16 +652,47 @@ int git_parse_ulong(const char *value, unsigned long *ret)
NORETURN
static void die_bad_number(const char *name, const char *value)
{
- const char *reason = errno == ERANGE ?
- "out of range" :
- "invalid unit";
if (!value)
value = "";
- if (cf && cf->origin_type && cf->name)
- die(_("bad numeric config value '%s' for '%s' in %s %s: %s"),
- value, name, cf->origin_type, cf->name, reason);
- die(_("bad numeric config value '%s' for '%s': %s"), value, name, reason);
+ if (!(cf && cf->name))
+ die(errno == ERANGE
+ ? _("bad numeric config value '%s' for '%s': out of range")
+ : _("bad numeric config value '%s' for '%s': invalid unit"),
+ value, name);
+
+ switch (cf->origin_type) {
+ case CONFIG_ORIGIN_BLOB:
+ die(errno == ERANGE
+ ? _("bad numeric config value '%s' for '%s' in blob %s: out of range")
+ : _("bad numeric config value '%s' for '%s' in blob %s: invalid unit"),
+ value, name, cf->name);
+ case CONFIG_ORIGIN_FILE:
+ die(errno == ERANGE
+ ? _("bad numeric config value '%s' for '%s' in file %s: out of range")
+ : _("bad numeric config value '%s' for '%s' in file %s: invalid unit"),
+ value, name, cf->name);
+ case CONFIG_ORIGIN_STDIN:
+ die(errno == ERANGE
+ ? _("bad numeric config value '%s' for '%s' in standard input: out of range")
+ : _("bad numeric config value '%s' for '%s' in standard input: invalid unit"),
+ value, name);
+ case CONFIG_ORIGIN_SUBMODULE_BLOB:
+ die(errno == ERANGE
+ ? _("bad numeric config value '%s' for '%s' in submodule-blob %s: out of range")
+ : _("bad numeric config value '%s' for '%s' in submodule-blob %s: invalid unit"),
+ value, name, cf->name);
+ case CONFIG_ORIGIN_CMDLINE:
+ die(errno == ERANGE
+ ? _("bad numeric config value '%s' for '%s' in command line %s: out of range")
+ : _("bad numeric config value '%s' for '%s' in command line %s: invalid unit"),
+ value, name, cf->name);
+ default:
+ die(errno == ERANGE
+ ? _("bad numeric config value '%s' for '%s' in %s: out of range")
+ : _("bad numeric config value '%s' for '%s' in %s: invalid unit"),
+ value, name, cf->name);
+ }
}
int git_config_int(const char *name, const char *value)
@@ -1105,7 +1169,8 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data)
}
static int do_config_from_file(config_fn_t fn,
- const char *origin_type, const char *name, const char *path, FILE *f,
+ const enum config_origin_type origin_type,
+ const char *name, const char *path, FILE *f,
void *data)
{
struct config_source top;
@@ -1124,7 +1189,7 @@ static int do_config_from_file(config_fn_t fn,
static int git_config_from_stdin(config_fn_t fn, void *data)
{
- return do_config_from_file(fn, "standard input", "", NULL, stdin, data);
+ return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin, data);
}
int git_config_from_file(config_fn_t fn, const char *filename, void *data)
@@ -1135,14 +1200,14 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
f = fopen(filename, "r");
if (f) {
flockfile(f);
- ret = do_config_from_file(fn, "file", filename, filename, f, data);
+ ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename, filename, f, data);
funlockfile(f);
fclose(f);
}
return ret;
}
-int git_config_from_mem(config_fn_t fn, const char *origin_type,
+int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_type,
const char *name, const char *buf, size_t len, void *data)
{
struct config_source top;
@@ -1179,7 +1244,7 @@ static int git_config_from_blob_sha1(config_fn_t fn,
return error("reference '%s' does not point to a blob", name);
}
- ret = git_config_from_mem(fn, "blob", name, buf, size, data);
+ ret = git_config_from_mem(fn, CONFIG_ORIGIN_BLOB, name, buf, size, data);
free(buf);
return ret;
@@ -1390,12 +1455,12 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
if (cf->name) {
kv_info->filename = strintern(cf->name);
kv_info->linenr = cf->linenr;
- kv_info->origin_type = strintern(cf->origin_type);
+ kv_info->origin_type = cf->origin_type;
} else {
/* for values read from `git_config_from_parameters()` */
kv_info->filename = NULL;
kv_info->linenr = -1;
- kv_info->origin_type = NULL;
+ kv_info->origin_type = CONFIG_ORIGIN_CMDLINE;
}
kv_info->scope = current_parsing_scope;
si->util = kv_info;
@@ -2476,14 +2541,28 @@ int parse_config_key(const char *var,
const char *current_config_origin_type(void)
{
- const char *type;
+ int type;
if (current_config_kvi)
type = current_config_kvi->origin_type;
else if(cf)
type = cf->origin_type;
else
die("BUG: current_config_origin_type called outside config callback");
- return type ? type : "command line";
+
+ switch (type) {
+ case CONFIG_ORIGIN_BLOB:
+ return "blob";
+ case CONFIG_ORIGIN_FILE:
+ return "file";
+ case CONFIG_ORIGIN_STDIN:
+ return "standard input";
+ case CONFIG_ORIGIN_SUBMODULE_BLOB:
+ return "submodule-blob";
+ case CONFIG_ORIGIN_CMDLINE:
+ return "command line";
+ default:
+ die("BUG: unknown config origin type");
+ }
}
const char *current_config_name(void)
diff --git a/connected.c b/connected.c
index bf1b12e7e..8e3e4b1dc 100644
--- a/connected.c
+++ b/connected.c
@@ -4,10 +4,6 @@
#include "connected.h"
#include "transport.h"
-int check_everything_connected(sha1_iterate_fn fn, int quiet, void *cb_data)
-{
- return check_everything_connected_with_transport(fn, quiet, cb_data, NULL);
-}
/*
* If we feed all the commits we want to verify to this command
*
@@ -19,22 +15,27 @@ int check_everything_connected(sha1_iterate_fn fn, int quiet, void *cb_data)
*
* Returns 0 if everything is connected, non-zero otherwise.
*/
-static int check_everything_connected_real(sha1_iterate_fn fn,
- int quiet,
- void *cb_data,
- struct transport *transport,
- const char *shallow_file)
+int check_connected(sha1_iterate_fn fn, void *cb_data,
+ struct check_connected_options *opt)
{
struct child_process rev_list = CHILD_PROCESS_INIT;
- const char *argv[9];
+ struct check_connected_options defaults = CHECK_CONNECTED_INIT;
char commit[41];
unsigned char sha1[20];
- int err = 0, ac = 0;
+ int err = 0;
struct packed_git *new_pack = NULL;
+ struct transport *transport;
size_t base_len;
- if (fn(cb_data, sha1))
+ if (!opt)
+ opt = &defaults;
+ transport = opt->transport;
+
+ if (fn(cb_data, sha1)) {
+ if (opt->err_fd)
+ close(opt->err_fd);
return err;
+ }
if (transport && transport->smart_options &&
transport->smart_options->self_contained_and_connected &&
@@ -47,24 +48,28 @@ static int check_everything_connected_real(sha1_iterate_fn fn,
strbuf_release(&idx_file);
}
- if (shallow_file) {
- argv[ac++] = "--shallow-file";
- argv[ac++] = shallow_file;
+ if (opt->shallow_file) {
+ argv_array_push(&rev_list.args, "--shallow-file");
+ argv_array_push(&rev_list.args, opt->shallow_file);
}
- argv[ac++] = "rev-list";
- argv[ac++] = "--objects";
- argv[ac++] = "--stdin";
- argv[ac++] = "--not";
- argv[ac++] = "--all";
- if (quiet)
- argv[ac++] = "--quiet";
- argv[ac] = NULL;
+ argv_array_push(&rev_list.args,"rev-list");
+ argv_array_push(&rev_list.args, "--objects");
+ argv_array_push(&rev_list.args, "--stdin");
+ argv_array_push(&rev_list.args, "--not");
+ argv_array_push(&rev_list.args, "--all");
+ argv_array_push(&rev_list.args, "--quiet");
+ if (opt->progress)
+ argv_array_pushf(&rev_list.args, "--progress=%s",
+ _("Checking connectivity"));
- rev_list.argv = argv;
rev_list.git_cmd = 1;
rev_list.in = -1;
rev_list.no_stdout = 1;
- rev_list.no_stderr = quiet;
+ if (opt->err_fd)
+ rev_list.err = opt->err_fd;
+ else
+ rev_list.no_stderr = opt->quiet;
+
if (start_command(&rev_list))
return error(_("Could not run 'git rev-list'"));
@@ -98,19 +103,3 @@ static int check_everything_connected_real(sha1_iterate_fn fn,
sigchain_pop(SIGPIPE);
return finish_command(&rev_list) || err;
}
-
-int check_everything_connected_with_transport(sha1_iterate_fn fn,
- int quiet,
- void *cb_data,
- struct transport *transport)
-{
- return check_everything_connected_real(fn, quiet, cb_data,
- transport, NULL);
-}
-
-int check_shallow_connected(sha1_iterate_fn fn, int quiet, void *cb_data,
- const char *shallow_file)
-{
- return check_everything_connected_real(fn, quiet, cb_data,
- NULL, shallow_file);
-}
diff --git a/connected.h b/connected.h
index 071d408f3..afa48cc05 100644
--- a/connected.h
+++ b/connected.h
@@ -11,17 +11,42 @@ struct transport;
typedef int (*sha1_iterate_fn)(void *, unsigned char [20]);
/*
+ * Named-arguments struct for check_connected. All arguments are
+ * optional, and can be left to defaults as set by CHECK_CONNECTED_INIT.
+ */
+struct check_connected_options {
+ /* Avoid printing any errors to stderr. */
+ int quiet;
+
+ /* --shallow-file to pass to rev-list sub-process */
+ const char *shallow_file;
+
+ /* Transport whose objects we are checking, if available. */
+ struct transport *transport;
+
+ /*
+ * If non-zero, send error messages to this descriptor rather
+ * than stderr. The descriptor is closed before check_connected
+ * returns.
+ */
+ int err_fd;
+
+ /* If non-zero, show progress as we traverse the objects. */
+ int progress;
+};
+
+#define CHECK_CONNECTED_INIT { 0 }
+
+/*
* Make sure that our object store has all the commits necessary to
* connect the ancestry chain to some of our existing refs, and all
* the trees and blobs that these commits use.
*
* Return 0 if Ok, non zero otherwise (i.e. some missing objects)
+ *
+ * If "opt" is NULL, behaves as if CHECK_CONNECTED_INIT was passed.
*/
-extern int check_everything_connected(sha1_iterate_fn, int quiet, void *cb_data);
-extern int check_shallow_connected(sha1_iterate_fn, int quiet, void *cb_data,
- const char *shallow_file);
-extern int check_everything_connected_with_transport(sha1_iterate_fn, int quiet,
- void *cb_data,
- struct transport *transport);
+int check_connected(sha1_iterate_fn fn, void *cb_data,
+ struct check_connected_options *opt);
#endif /* CONNECTED_H */
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 10f6d5225..6a187bc11 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1136,6 +1136,7 @@ _git_clone ()
--depth
--single-branch
--branch
+ --recurse-submodules
"
return
;;
diff --git a/contrib/git-jump/README b/contrib/git-jump/README
index 1cebc328c..225e3f095 100644
--- a/contrib/git-jump/README
+++ b/contrib/git-jump/README
@@ -29,7 +29,7 @@ Obviously this trivial case isn't that interesting; you could just open
`foo.c` yourself. But when you have many changes scattered across a
project, you can use the editor's support to "jump" from point to point.
-Git-jump can generate three types of interesting lists:
+Git-jump can generate four types of interesting lists:
1. The beginning of any diff hunks.
@@ -37,6 +37,8 @@ Git-jump can generate three types of interesting lists:
3. Any grep matches.
+ 4. Any whitespace errors detected by `git diff --check`.
+
Using git-jump
--------------
@@ -83,7 +85,7 @@ complete list of files and line numbers for each match.
Limitations
-----------
-This scripts was written and tested with vim. Given that the quickfix
+This script was written and tested with vim. Given that the quickfix
format is the same as what gcc produces, I expect emacs users have a
similar feature for iterating through the list, but I know nothing about
how to activate it.
diff --git a/contrib/git-jump/git-jump b/contrib/git-jump/git-jump
index dc90cd637..427f206a4 100755
--- a/contrib/git-jump/git-jump
+++ b/contrib/git-jump/git-jump
@@ -12,6 +12,8 @@ diff: elements are diff hunks. Arguments are given to diff.
merge: elements are merge conflicts. Arguments are ignored.
grep: elements are grep hits. Arguments are given to grep.
+
+ws: elements are whitespace errors. Arguments are given to diff --check.
EOF
}
@@ -25,7 +27,7 @@ mode_diff() {
perl -ne '
if (m{^\+\+\+ (.*)}) { $file = $1; next }
defined($file) or next;
- if (m/^@@ .*\+(\d+)/) { $line = $1; next }
+ if (m/^@@ .*?\+(\d+)/) { $line = $1; next }
defined($line) or next;
if (/^ /) { $line++; next }
if (/^[-+]\s*(.*)/) {
@@ -55,6 +57,10 @@ mode_grep() {
'
}
+mode_ws() {
+ git diff --check "$@"
+}
+
if test $# -lt 1; then
usage >&2
exit 1
diff --git a/contrib/persistent-https/Makefile b/contrib/persistent-https/Makefile
index 92baa3bee..52b84ba3d 100644
--- a/contrib/persistent-https/Makefile
+++ b/contrib/persistent-https/Makefile
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-BUILD_LABEL=$(shell date +"%s")
+BUILD_LABEL=$(shell cut -d" " -f3 ../../GIT-VERSION-FILE)
TAR_OUT=$(shell go env GOOS)_$(shell go env GOARCH).tar.gz
all: git-remote-persistent-https git-remote-persistent-https--proxy \
@@ -25,8 +25,10 @@ git-remote-persistent-http: git-remote-persistent-https
ln -f -s git-remote-persistent-https git-remote-persistent-http
git-remote-persistent-https:
+ case $$(go version) in \
+ "go version go"1.[0-5].*) EQ=" " ;; *) EQ="=" ;; esac && \
go build -o git-remote-persistent-https \
- -ldflags "-X main._BUILD_EMBED_LABEL $(BUILD_LABEL)"
+ -ldflags "-X main._BUILD_EMBED_LABEL$${EQ}$(BUILD_LABEL)"
clean:
rm -f git-remote-persistent-http* *.tar.gz
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index 7a39b30ad..dec085a23 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
@@ -4,8 +4,9 @@
#
# Copyright (C) 2009 Avery Pennarun <apenwarr@gmail.com>
#
-if [ $# -eq 0 ]; then
- set -- -h
+if test $# -eq 0
+then
+ set -- -h
fi
OPTS_SPEC="\
git subtree add --prefix=<prefix> <commit>
@@ -48,89 +49,144 @@ squash=
message=
prefix=
-debug()
-{
- if [ -n "$debug" ]; then
+debug () {
+ if test -n "$debug"
+ then
printf "%s\n" "$*" >&2
fi
}
-say()
-{
- if [ -z "$quiet" ]; then
+say () {
+ if test -z "$quiet"
+ then
printf "%s\n" "$*" >&2
fi
}
-progress()
-{
- if [ -z "$quiet" ]; then
+progress () {
+ if test -z "$quiet"
+ then
printf "%s\r" "$*" >&2
fi
}
-assert()
-{
- if "$@"; then
- :
- else
+assert () {
+ if ! "$@"
+ then
die "assertion failed: " "$@"
fi
}
-#echo "Options: $*"
-
-while [ $# -gt 0 ]; do
+while test $# -gt 0
+do
opt="$1"
shift
+
case "$opt" in
- -q) quiet=1 ;;
- -d) debug=1 ;;
- --annotate) annotate="$1"; shift ;;
- --no-annotate) annotate= ;;
- -b) branch="$1"; shift ;;
- -P) prefix="${1%/}"; shift ;;
- -m) message="$1"; shift ;;
- --no-prefix) prefix= ;;
- --onto) onto="$1"; shift ;;
- --no-onto) onto= ;;
- --rejoin) rejoin=1 ;;
- --no-rejoin) rejoin= ;;
- --ignore-joins) ignore_joins=1 ;;
- --no-ignore-joins) ignore_joins= ;;
- --squash) squash=1 ;;
- --no-squash) squash= ;;
- --) break ;;
- *) die "Unexpected option: $opt" ;;
+ -q)
+ quiet=1
+ ;;
+ -d)
+ debug=1
+ ;;
+ --annotate)
+ annotate="$1"
+ shift
+ ;;
+ --no-annotate)
+ annotate=
+ ;;
+ -b)
+ branch="$1"
+ shift
+ ;;
+ -P)
+ prefix="${1%/}"
+ shift
+ ;;
+ -m)
+ message="$1"
+ shift
+ ;;
+ --no-prefix)
+ prefix=
+ ;;
+ --onto)
+ onto="$1"
+ shift
+ ;;
+ --no-onto)
+ onto=
+ ;;
+ --rejoin)
+ rejoin=1
+ ;;
+ --no-rejoin)
+ rejoin=
+ ;;
+ --ignore-joins)
+ ignore_joins=1
+ ;;
+ --no-ignore-joins)
+ ignore_joins=
+ ;;
+ --squash)
+ squash=1
+ ;;
+ --no-squash)
+ squash=
+ ;;
+ --)
+ break
+ ;;
+ *)
+ die "Unexpected option: $opt"
+ ;;
esac
done
command="$1"
shift
+
case "$command" in
- add|merge|pull) default= ;;
- split|push) default="--default HEAD" ;;
- *) die "Unknown command '$command'" ;;
+add|merge|pull)
+ default=
+ ;;
+split|push)
+ default="--default HEAD"
+ ;;
+*)
+ die "Unknown command '$command'"
+ ;;
esac
-if [ -z "$prefix" ]; then
+if test -z "$prefix"
+then
die "You must provide the --prefix option."
fi
case "$command" in
- add) [ -e "$prefix" ] &&
- die "prefix '$prefix' already exists." ;;
- *) [ -e "$prefix" ] ||
- die "'$prefix' does not exist; use 'git subtree add'" ;;
+add)
+ test -e "$prefix" &&
+ die "prefix '$prefix' already exists."
+ ;;
+*)
+ test -e "$prefix" ||
+ die "'$prefix' does not exist; use 'git subtree add'"
+ ;;
esac
dir="$(dirname "$prefix/.")"
-if [ "$command" != "pull" -a "$command" != "add" -a "$command" != "push" ]; then
+if test "$command" != "pull" &&
+ test "$command" != "add" &&
+ test "$command" != "push"
+then
revs=$(git rev-parse $default --revs-only "$@") || exit $?
- dirs="$(git rev-parse --no-revs --no-flags "$@")" || exit $?
- if [ -n "$dirs" ]; then
+ dirs=$(git rev-parse --no-revs --no-flags "$@") || exit $?
+ if test -n "$dirs"
+ then
die "Error: Use --prefix instead of bare filenames."
fi
fi
@@ -142,78 +198,82 @@ debug "dir: {$dir}"
debug "opts: {$*}"
debug
-cache_setup()
-{
+cache_setup () {
cachedir="$GIT_DIR/subtree-cache/$$"
- rm -rf "$cachedir" || die "Can't delete old cachedir: $cachedir"
- mkdir -p "$cachedir" || die "Can't create new cachedir: $cachedir"
- mkdir -p "$cachedir/notree" || die "Can't create new cachedir: $cachedir/notree"
+ rm -rf "$cachedir" ||
+ die "Can't delete old cachedir: $cachedir"
+ mkdir -p "$cachedir" ||
+ die "Can't create new cachedir: $cachedir"
+ mkdir -p "$cachedir/notree" ||
+ die "Can't create new cachedir: $cachedir/notree"
debug "Using cachedir: $cachedir" >&2
}
-cache_get()
-{
- for oldrev in $*; do
- if [ -r "$cachedir/$oldrev" ]; then
+cache_get () {
+ for oldrev in "$@"
+ do
+ if test -r "$cachedir/$oldrev"
+ then
read newrev <"$cachedir/$oldrev"
echo $newrev
fi
done
}
-cache_miss()
-{
- for oldrev in $*; do
- if [ ! -r "$cachedir/$oldrev" ]; then
+cache_miss () {
+ for oldrev in "$@"
+ do
+ if ! test -r "$cachedir/$oldrev"
+ then
echo $oldrev
fi
done
}
-check_parents()
-{
- missed=$(cache_miss $*)
- for miss in $missed; do
- if [ ! -r "$cachedir/notree/$miss" ]; then
+check_parents () {
+ missed=$(cache_miss "$@")
+ for miss in $missed
+ do
+ if ! test -r "$cachedir/notree/$miss"
+ then
debug " incorrect order: $miss"
fi
done
}
-set_notree()
-{
+set_notree () {
echo "1" > "$cachedir/notree/$1"
}
-cache_set()
-{
+cache_set () {
oldrev="$1"
newrev="$2"
- if [ "$oldrev" != "latest_old" \
- -a "$oldrev" != "latest_new" \
- -a -e "$cachedir/$oldrev" ]; then
+ if test "$oldrev" != "latest_old" &&
+ test "$oldrev" != "latest_new" &&
+ test -e "$cachedir/$oldrev"
+ then
die "cache for $oldrev already exists!"
fi
echo "$newrev" >"$cachedir/$oldrev"
}
-rev_exists()
-{
- if git rev-parse "$1" >/dev/null 2>&1; then
+rev_exists () {
+ if git rev-parse "$1" >/dev/null 2>&1
+ then
return 0
else
return 1
fi
}
-rev_is_descendant_of_branch()
-{
+rev_is_descendant_of_branch () {
newrev="$1"
branch="$2"
- branch_hash=$(git rev-parse $branch)
- match=$(git rev-list -1 $branch_hash ^$newrev)
+ branch_hash=$(git rev-parse "$branch")
+ match=$(git rev-list -1 "$branch_hash" "^$newrev")
- if [ -z "$match" ]; then
+ if test -z "$match"
+ then
return 0
else
return 1
@@ -223,15 +283,14 @@ rev_is_descendant_of_branch()
# if a commit doesn't have a parent, this might not work. But we only want
# to remove the parent from the rev-list, and since it doesn't exist, it won't
# be there anyway, so do nothing in that case.
-try_remove_previous()
-{
- if rev_exists "$1^"; then
+try_remove_previous () {
+ if rev_exists "$1^"
+ then
echo "^$1^"
fi
}
-find_latest_squash()
-{
+find_latest_squash () {
debug "Looking for latest squash ($dir)..."
dir="$1"
sq=
@@ -239,37 +298,43 @@ find_latest_squash()
sub=
git log --grep="^git-subtree-dir: $dir/*\$" \
--pretty=format:'START %H%n%s%n%n%b%nEND%n' HEAD |
- while read a b junk; do
+ while read a b junk
+ do
debug "$a $b $junk"
debug "{{$sq/$main/$sub}}"
case "$a" in
- START) sq="$b" ;;
- git-subtree-mainline:) main="$b" ;;
- git-subtree-split:)
- sub="$(git rev-parse "$b^0")" ||
- die "could not rev-parse split hash $b from commit $sq"
- ;;
- END)
- if [ -n "$sub" ]; then
- if [ -n "$main" ]; then
- # a rejoin commit?
- # Pretend its sub was a squash.
- sq="$sub"
- fi
- debug "Squash found: $sq $sub"
- echo "$sq" "$sub"
- break
+ START)
+ sq="$b"
+ ;;
+ git-subtree-mainline:)
+ main="$b"
+ ;;
+ git-subtree-split:)
+ sub="$(git rev-parse "$b^0")" ||
+ die "could not rev-parse split hash $b from commit $sq"
+ ;;
+ END)
+ if test -n "$sub"
+ then
+ if test -n "$main"
+ then
+ # a rejoin commit?
+ # Pretend its sub was a squash.
+ sq="$sub"
fi
- sq=
- main=
- sub=
- ;;
+ debug "Squash found: $sq $sub"
+ echo "$sq" "$sub"
+ break
+ fi
+ sq=
+ main=
+ sub=
+ ;;
esac
done
}
-find_existing_splits()
-{
+find_existing_splits () {
debug "Looking for prior splits..."
dir="$1"
revs="$2"
@@ -277,37 +342,43 @@ find_existing_splits()
sub=
git log --grep="^git-subtree-dir: $dir/*\$" \
--pretty=format:'START %H%n%s%n%n%b%nEND%n' $revs |
- while read a b junk; do
+ while read a b junk
+ do
case "$a" in
- START) sq="$b" ;;
- git-subtree-mainline:) main="$b" ;;
- git-subtree-split:)
- sub="$(git rev-parse "$b^0")" ||
- die "could not rev-parse split hash $b from commit $sq"
- ;;
- END)
- debug " Main is: '$main'"
- if [ -z "$main" -a -n "$sub" ]; then
- # squash commits refer to a subtree
- debug " Squash: $sq from $sub"
- cache_set "$sq" "$sub"
- fi
- if [ -n "$main" -a -n "$sub" ]; then
- debug " Prior: $main -> $sub"
- cache_set $main $sub
- cache_set $sub $sub
- try_remove_previous "$main"
- try_remove_previous "$sub"
- fi
- main=
- sub=
- ;;
+ START)
+ sq="$b"
+ ;;
+ git-subtree-mainline:)
+ main="$b"
+ ;;
+ git-subtree-split:)
+ sub="$(git rev-parse "$b^0")" ||
+ die "could not rev-parse split hash $b from commit $sq"
+ ;;
+ END)
+ debug " Main is: '$main'"
+ if test -z "$main" -a -n "$sub"
+ then
+ # squash commits refer to a subtree
+ debug " Squash: $sq from $sub"
+ cache_set "$sq" "$sub"
+ fi
+ if test -n "$main" -a -n "$sub"
+ then
+ debug " Prior: $main -> $sub"
+ cache_set $main $sub
+ cache_set $sub $sub
+ try_remove_previous "$main"
+ try_remove_previous "$sub"
+ fi
+ main=
+ sub=
+ ;;
esac
done
}
-copy_commit()
-{
+copy_commit () {
# We're going to set some environment vars here, so
# do it in a subshell to get rid of them safely later
debug copy_commit "{$1}" "{$2}" "{$3}"
@@ -325,66 +396,69 @@ copy_commit()
GIT_COMMITTER_NAME \
GIT_COMMITTER_EMAIL \
GIT_COMMITTER_DATE
- (printf "%s" "$annotate"; cat ) |
+ (
+ printf "%s" "$annotate"
+ cat
+ ) |
git commit-tree "$2" $3 # reads the rest of stdin
) || die "Can't copy commit $1"
}
-add_msg()
-{
+add_msg () {
dir="$1"
latest_old="$2"
latest_new="$3"
- if [ -n "$message" ]; then
+ if test -n "$message"
+ then
commit_message="$message"
else
commit_message="Add '$dir/' from commit '$latest_new'"
fi
cat <<-EOF
$commit_message
-
+
git-subtree-dir: $dir
git-subtree-mainline: $latest_old
git-subtree-split: $latest_new
EOF
}
-add_squashed_msg()
-{
- if [ -n "$message" ]; then
+add_squashed_msg () {
+ if test -n "$message"
+ then
echo "$message"
else
echo "Merge commit '$1' as '$2'"
fi
}
-rejoin_msg()
-{
+rejoin_msg () {
dir="$1"
latest_old="$2"
latest_new="$3"
- if [ -n "$message" ]; then
+ if test -n "$message"
+ then
commit_message="$message"
else
commit_message="Split '$dir/' into commit '$latest_new'"
fi
cat <<-EOF
$commit_message
-
+
git-subtree-dir: $dir
git-subtree-mainline: $latest_old
git-subtree-split: $latest_new
EOF
}
-squash_msg()
-{
+squash_msg () {
dir="$1"
oldsub="$2"
newsub="$3"
newsub_short=$(git rev-parse --short "$newsub")
-
- if [ -n "$oldsub" ]; then
+
+ if test -n "$oldsub"
+ then
oldsub_short=$(git rev-parse --short "$oldsub")
echo "Squashed '$dir/' changes from $oldsub_short..$newsub_short"
echo
@@ -393,41 +467,41 @@ squash_msg()
else
echo "Squashed '$dir/' content from commit $newsub_short"
fi
-
+
echo
echo "git-subtree-dir: $dir"
echo "git-subtree-split: $newsub"
}
-toptree_for_commit()
-{
+toptree_for_commit () {
commit="$1"
git log -1 --pretty=format:'%T' "$commit" -- || exit $?
}
-subtree_for_commit()
-{
+subtree_for_commit () {
commit="$1"
dir="$2"
git ls-tree "$commit" -- "$dir" |
- while read mode type tree name; do
- assert [ "$name" = "$dir" ]
- assert [ "$type" = "tree" -o "$type" = "commit" ]
- [ "$type" = "commit" ] && continue # ignore submodules
+ while read mode type tree name
+ do
+ assert test "$name" = "$dir"
+ assert test "$type" = "tree" -o "$type" = "commit"
+ test "$type" = "commit" && continue # ignore submodules
echo $tree
break
done
}
-tree_changed()
-{
+tree_changed () {
tree=$1
shift
- if [ $# -ne 1 ]; then
+ if test $# -ne 1
+ then
return 0 # weird parents, consider it changed
else
ptree=$(toptree_for_commit $1)
- if [ "$ptree" != "$tree" ]; then
+ if test "$ptree" != "$tree"
+ then
return 0 # changed
else
return 1 # not changed
@@ -435,118 +509,127 @@ tree_changed()
fi
}
-new_squash_commit()
-{
+new_squash_commit () {
old="$1"
oldsub="$2"
newsub="$3"
tree=$(toptree_for_commit $newsub) || exit $?
- if [ -n "$old" ]; then
- squash_msg "$dir" "$oldsub" "$newsub" |
- git commit-tree "$tree" -p "$old" || exit $?
+ if test -n "$old"
+ then
+ squash_msg "$dir" "$oldsub" "$newsub" |
+ git commit-tree "$tree" -p "$old" || exit $?
else
squash_msg "$dir" "" "$newsub" |
- git commit-tree "$tree" || exit $?
+ git commit-tree "$tree" || exit $?
fi
}
-copy_or_skip()
-{
+copy_or_skip () {
rev="$1"
tree="$2"
newparents="$3"
- assert [ -n "$tree" ]
+ assert test -n "$tree"
identical=
nonidentical=
p=
gotparents=
- for parent in $newparents; do
+ for parent in $newparents
+ do
ptree=$(toptree_for_commit $parent) || exit $?
- [ -z "$ptree" ] && continue
- if [ "$ptree" = "$tree" ]; then
+ test -z "$ptree" && continue
+ if test "$ptree" = "$tree"
+ then
# an identical parent could be used in place of this rev.
identical="$parent"
else
nonidentical="$parent"
fi
-
+
# sometimes both old parents map to the same newparent;
# eliminate duplicates
is_new=1
- for gp in $gotparents; do
- if [ "$gp" = "$parent" ]; then
+ for gp in $gotparents
+ do
+ if test "$gp" = "$parent"
+ then
is_new=
break
fi
done
- if [ -n "$is_new" ]; then
+ if test -n "$is_new"
+ then
gotparents="$gotparents $parent"
p="$p -p $parent"
fi
done
copycommit=
- if [ -n "$identical" ] && [ -n "$nonidentical" ]; then
+ if test -n "$identical" && test -n "$nonidentical"
+ then
extras=$(git rev-list --count $identical..$nonidentical)
- if [ "$extras" -ne 0 ]; then
+ if test "$extras" -ne 0
+ then
# we need to preserve history along the other branch
copycommit=1
fi
fi
- if [ -n "$identical" ] && [ -z "$copycommit" ]; then
+ if test -n "$identical" && test -z "$copycommit"
+ then
echo $identical
else
- copy_commit $rev $tree "$p" || exit $?
+ copy_commit "$rev" "$tree" "$p" || exit $?
fi
}
-ensure_clean()
-{
- if ! git diff-index HEAD --exit-code --quiet 2>&1; then
+ensure_clean () {
+ if ! git diff-index HEAD --exit-code --quiet 2>&1
+ then
die "Working tree has modifications. Cannot add."
fi
- if ! git diff-index --cached HEAD --exit-code --quiet 2>&1; then
+ if ! git diff-index --cached HEAD --exit-code --quiet 2>&1
+ then
die "Index has modifications. Cannot add."
fi
}
-ensure_valid_ref_format()
-{
+ensure_valid_ref_format () {
git check-ref-format "refs/heads/$1" ||
- die "'$1' does not look like a ref"
+ die "'$1' does not look like a ref"
}
-cmd_add()
-{
- if [ -e "$dir" ]; then
+cmd_add () {
+ if test -e "$dir"
+ then
die "'$dir' already exists. Cannot add."
fi
ensure_clean
-
- if [ $# -eq 1 ]; then
- git rev-parse -q --verify "$1^{commit}" >/dev/null ||
- die "'$1' does not refer to a commit"
-
- "cmd_add_commit" "$@"
- elif [ $# -eq 2 ]; then
- # Technically we could accept a refspec here but we're
- # just going to turn around and add FETCH_HEAD under the
- # specified directory. Allowing a refspec might be
- # misleading because we won't do anything with any other
- # branches fetched via the refspec.
- ensure_valid_ref_format "$2"
-
- "cmd_add_repository" "$@"
+
+ if test $# -eq 1
+ then
+ git rev-parse -q --verify "$1^{commit}" >/dev/null ||
+ die "'$1' does not refer to a commit"
+
+ cmd_add_commit "$@"
+
+ elif test $# -eq 2
+ then
+ # Technically we could accept a refspec here but we're
+ # just going to turn around and add FETCH_HEAD under the
+ # specified directory. Allowing a refspec might be
+ # misleading because we won't do anything with any other
+ # branches fetched via the refspec.
+ ensure_valid_ref_format "$2"
+
+ cmd_add_repository "$@"
else
- say "error: parameters were '$@'"
- die "Provide either a commit or a repository and commit."
+ say "error: parameters were '$@'"
+ die "Provide either a commit or a repository and commit."
fi
}
-cmd_add_repository()
-{
+cmd_add_repository () {
echo "git fetch" "$@"
repository=$1
refspec=$2
@@ -556,60 +639,63 @@ cmd_add_repository()
cmd_add_commit "$@"
}
-cmd_add_commit()
-{
+cmd_add_commit () {
revs=$(git rev-parse $default --revs-only "$@") || exit $?
set -- $revs
rev="$1"
-
+
debug "Adding $dir as '$rev'..."
git read-tree --prefix="$dir" $rev || exit $?
git checkout -- "$dir" || exit $?
tree=$(git write-tree) || exit $?
-
+
headrev=$(git rev-parse HEAD) || exit $?
- if [ -n "$headrev" -a "$headrev" != "$rev" ]; then
+ if test -n "$headrev" && test "$headrev" != "$rev"
+ then
headp="-p $headrev"
else
headp=
fi
-
- if [ -n "$squash" ]; then
+
+ if test -n "$squash"
+ then
rev=$(new_squash_commit "" "" "$rev") || exit $?
commit=$(add_squashed_msg "$rev" "$dir" |
- git commit-tree $tree $headp -p "$rev") || exit $?
+ git commit-tree "$tree" $headp -p "$rev") || exit $?
else
revp=$(peel_committish "$rev") &&
- commit=$(add_msg "$dir" "$headrev" "$rev" |
- git commit-tree $tree $headp -p "$revp") || exit $?
+ commit=$(add_msg "$dir" $headrev "$rev" |
+ git commit-tree "$tree" $headp -p "$revp") || exit $?
fi
git reset "$commit" || exit $?
-
+
say "Added dir '$dir'"
}
-cmd_split()
-{
+cmd_split () {
debug "Splitting $dir..."
cache_setup || exit $?
-
- if [ -n "$onto" ]; then
+
+ if test -n "$onto"
+ then
debug "Reading history for --onto=$onto..."
git rev-list $onto |
- while read rev; do
+ while read rev
+ do
# the 'onto' history is already just the subdir, so
# any parent we find there can be used verbatim
debug " cache: $rev"
- cache_set $rev $rev
+ cache_set "$rev" "$rev"
done
fi
-
- if [ -n "$ignore_joins" ]; then
+
+ if test -n "$ignore_joins"
+ then
unrevs=
else
unrevs="$(find_existing_splits "$dir" "$revs")"
fi
-
+
# We can't restrict rev-list to only $dir here, because some of our
# parents have the $dir contents the root, and those won't match.
# (and rev-list --follow doesn't seem to solve this)
@@ -618,12 +704,14 @@ cmd_split()
revcount=0
createcount=0
eval "$grl" |
- while read rev parents; do
+ while read rev parents
+ do
revcount=$(($revcount + 1))
progress "$revcount/$revmax ($createcount)"
debug "Processing commit: $rev"
- exists=$(cache_get $rev)
- if [ -n "$exists" ]; then
+ exists=$(cache_get "$rev")
+ if test -n "$exists"
+ then
debug " prior: $exists"
continue
fi
@@ -631,76 +719,89 @@ cmd_split()
debug " parents: $parents"
newparents=$(cache_get $parents)
debug " newparents: $newparents"
-
- tree=$(subtree_for_commit $rev "$dir")
+
+ tree=$(subtree_for_commit "$rev" "$dir")
debug " tree is: $tree"
check_parents $parents
-
+
# ugly. is there no better way to tell if this is a subtree
# vs. a mainline commit? Does it matter?
- if [ -z $tree ]; then
- set_notree $rev
- if [ -n "$newparents" ]; then
- cache_set $rev $rev
+ if test -z "$tree"
+ then
+ set_notree "$rev"
+ if test -n "$newparents"
+ then
+ cache_set "$rev" "$rev"
fi
continue
fi
newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
debug " newrev is: $newrev"
- cache_set $rev $newrev
- cache_set latest_new $newrev
- cache_set latest_old $rev
+ cache_set "$rev" "$newrev"
+ cache_set latest_new "$newrev"
+ cache_set latest_old "$rev"
done || exit $?
+
latest_new=$(cache_get latest_new)
- if [ -z "$latest_new" ]; then
+ if test -z "$latest_new"
+ then
die "No new revisions were found"
fi
-
- if [ -n "$rejoin" ]; then
+
+ if test -n "$rejoin"
+ then
debug "Merging split branch into HEAD..."
latest_old=$(cache_get latest_old)
git merge -s ours \
- -m "$(rejoin_msg "$dir" $latest_old $latest_new)" \
- $latest_new >&2 || exit $?
- fi
- if [ -n "$branch" ]; then
- if rev_exists "refs/heads/$branch"; then
- if ! rev_is_descendant_of_branch $latest_new $branch; then
+ --allow-unrelated-histories \
+ -m "$(rejoin_msg "$dir" "$latest_old" "$latest_new")" \
+ "$latest_new" >&2 || exit $?
+ fi
+ if test -n "$branch"
+ then
+ if rev_exists "refs/heads/$branch"
+ then
+ if ! rev_is_descendant_of_branch "$latest_new" "$branch"
+ then
die "Branch '$branch' is not an ancestor of commit '$latest_new'."
fi
action='Updated'
else
action='Created'
fi
- git update-ref -m 'subtree split' "refs/heads/$branch" $latest_new || exit $?
+ git update-ref -m 'subtree split' \
+ "refs/heads/$branch" "$latest_new" || exit $?
say "$action branch '$branch'"
fi
- echo $latest_new
+ echo "$latest_new"
exit 0
}
-cmd_merge()
-{
+cmd_merge () {
revs=$(git rev-parse $default --revs-only "$@") || exit $?
ensure_clean
-
+
set -- $revs
- if [ $# -ne 1 ]; then
+ if test $# -ne 1
+ then
die "You must provide exactly one revision. Got: '$revs'"
fi
rev="$1"
-
- if [ -n "$squash" ]; then
+
+ if test -n "$squash"
+ then
first_split="$(find_latest_squash "$dir")"
- if [ -z "$first_split" ]; then
+ if test -z "$first_split"
+ then
die "Can't squash-merge: '$dir' was never added."
fi
set $first_split
old=$1
sub=$2
- if [ "$sub" = "$rev" ]; then
+ if test "$sub" = "$rev"
+ then
say "Subtree is already at commit $rev."
exit 0
fi
@@ -710,25 +811,29 @@ cmd_merge()
fi
version=$(git version)
- if [ "$version" \< "git version 1.7" ]; then
- if [ -n "$message" ]; then
- git merge -s subtree --message="$message" $rev
+ if test "$version" \< "git version 1.7"
+ then
+ if test -n "$message"
+ then
+ git merge -s subtree --message="$message" "$rev"
else
- git merge -s subtree $rev
+ git merge -s subtree "$rev"
fi
else
- if [ -n "$message" ]; then
- git merge -Xsubtree="$prefix" --message="$message" $rev
+ if test -n "$message"
+ then
+ git merge -Xsubtree="$prefix" \
+ --message="$message" "$rev"
else
git merge -Xsubtree="$prefix" $rev
fi
fi
}
-cmd_pull()
-{
- if [ $# -ne 2 ]; then
- die "You must provide <repository> <ref>"
+cmd_pull () {
+ if test $# -ne 2
+ then
+ die "You must provide <repository> <ref>"
fi
ensure_clean
ensure_valid_ref_format "$2"
@@ -738,20 +843,21 @@ cmd_pull()
cmd_merge "$@"
}
-cmd_push()
-{
- if [ $# -ne 2 ]; then
- die "You must provide <repository> <ref>"
+cmd_push () {
+ if test $# -ne 2
+ then
+ die "You must provide <repository> <ref>"
fi
ensure_valid_ref_format "$2"
- if [ -e "$dir" ]; then
- repository=$1
- refspec=$2
- echo "git push using: " $repository $refspec
- localrev=$(git subtree split --prefix="$prefix") || die
- git push "$repository" $localrev:refs/heads/$refspec
+ if test -e "$dir"
+ then
+ repository=$1
+ refspec=$2
+ echo "git push using: " "$repository" "$refspec"
+ localrev=$(git subtree split --prefix="$prefix") || die
+ git push "$repository" "$localrev":"refs/heads/$refspec"
else
- die "'$dir' must already exist. Try 'git subtree add'."
+ die "'$dir' must already exist. Try 'git subtree add'."
fi
}
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index 3bf96a9bb..9751cfe9e 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -16,16 +16,16 @@ export TEST_DIRECTORY
subtree_test_create_repo()
{
- test_create_repo "$1"
+ test_create_repo "$1" &&
(
- cd $1
+ cd "$1" &&
git config log.date relative
)
}
create()
{
- echo "$1" >"$1"
+ echo "$1" >"$1" &&
git add "$1"
}
@@ -71,12 +71,12 @@ join_commits()
}
test_create_commit() (
- repo=$1
- commit=$2
- cd "$repo"
- mkdir -p $(dirname "$commit") \
+ repo=$1 &&
+ commit=$2 &&
+ cd "$repo" &&
+ mkdir -p "$(dirname "$commit")" \
|| error "Could not create directory for commit"
- echo "$commit" >"$commit"
+ echo "$commit" >"$commit" &&
git add "$commit" || error "Could not add commit"
git commit -m "$commit" || error "Could not commit"
)
@@ -347,6 +347,22 @@ test_expect_success 'split sub dir/ with --rejoin' '
'
next_test
+test_expect_success 'split sub dir/ with --rejoin from scratch' '
+ subtree_test_create_repo "$subtree_test_count" &&
+ test_create_commit "$subtree_test_count" main1 &&
+ (
+ cd "$subtree_test_count" &&
+ mkdir "sub dir" &&
+ echo file >"sub dir"/file &&
+ git add "sub dir/file" &&
+ git commit -m"sub dir file" &&
+ split_hash=$(git subtree split --prefix="sub dir" --rejoin) &&
+ git subtree split --prefix="sub dir" --rejoin &&
+ check_equal "$(last_commit_message)" "Split '\''sub dir/'\'' into commit '\''$split_hash'\''"
+ )
+ '
+
+next_test
test_expect_success 'split sub dir/ with --rejoin and --message' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
diff --git a/date.c b/date.c
index 4c7aa9ba8..a996331f5 100644
--- a/date.c
+++ b/date.c
@@ -177,6 +177,12 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
struct tm *tm;
static struct strbuf timebuf = STRBUF_INIT;
+ if (mode->type == DATE_UNIX) {
+ strbuf_reset(&timebuf);
+ strbuf_addf(&timebuf, "%lu", time);
+ return timebuf.buf;
+ }
+
if (mode->local)
tz = local_tzoffset(time);
@@ -792,6 +798,8 @@ static enum date_mode_type parse_date_type(const char *format, const char **end)
return DATE_NORMAL;
if (skip_prefix(format, "raw", end))
return DATE_RAW;
+ if (skip_prefix(format, "unix", end))
+ return DATE_UNIX;
if (skip_prefix(format, "format", end))
return DATE_STRFTIME;
diff --git a/diff.c b/diff.c
index 7d0341988..b43d3dd2e 100644
--- a/diff.c
+++ b/diff.c
@@ -2683,6 +2683,13 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int
if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1))
return 0;
+ /*
+ * Similarly, if we'd have to convert the file contents anyway, that
+ * makes the optimization not worthwhile.
+ */
+ if (!want_file && would_convert_to_git(name))
+ return 0;
+
len = strlen(name);
pos = cache_name_pos(name, len);
if (pos < 0)
diff --git a/fetch-pack.c b/fetch-pack.c
index b501d5c32..85e77af61 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -243,16 +243,21 @@ static void insert_one_alternate_ref(const struct ref *ref, void *unused)
#define INITIAL_FLUSH 16
#define PIPESAFE_FLUSH 32
-#define LARGE_FLUSH 1024
+#define LARGE_FLUSH 16384
static int next_flush(struct fetch_pack_args *args, int count)
{
- int flush_limit = args->stateless_rpc ? LARGE_FLUSH : PIPESAFE_FLUSH;
-
- if (count < flush_limit)
- count <<= 1;
- else
- count += flush_limit;
+ if (args->stateless_rpc) {
+ if (count < LARGE_FLUSH)
+ count <<= 1;
+ else
+ count = count * 11 / 10;
+ } else {
+ if (count < PIPESAFE_FLUSH)
+ count <<= 1;
+ else
+ count += PIPESAFE_FLUSH;
+ }
return count;
}
diff --git a/git-difftool.perl b/git-difftool.perl
index ebd13baa6..a5790d03a 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -37,14 +37,6 @@ USAGE
exit($exitcode);
}
-sub find_worktree
-{
- # Git->repository->wc_path() does not honor changes to the working
- # tree location made by $ENV{GIT_WORK_TREE} or the 'core.worktree'
- # config variable.
- return Git::command_oneline('rev-parse', '--show-toplevel');
-}
-
sub print_tool_help
{
# See the comment at the bottom of file_diff() for the reason behind
@@ -67,14 +59,14 @@ sub exit_cleanup
sub use_wt_file
{
- my ($repo, $workdir, $file, $sha1) = @_;
+ my ($workdir, $file, $sha1) = @_;
my $null_sha1 = '0' x 40;
if (-l "$workdir/$file" || ! -e _) {
return (0, $null_sha1);
}
- my $wt_sha1 = $repo->command_oneline('hash-object', "$workdir/$file");
+ my $wt_sha1 = Git::command_oneline('hash-object', "$workdir/$file");
my $use = ($sha1 eq $null_sha1) || ($sha1 eq $wt_sha1);
return ($use, $wt_sha1);
}
@@ -83,20 +75,17 @@ sub changed_files
{
my ($repo_path, $index, $worktree) = @_;
$ENV{GIT_INDEX_FILE} = $index;
- $ENV{GIT_WORK_TREE} = $worktree;
- my $must_unset_git_dir = 0;
- if (not defined($ENV{GIT_DIR})) {
- $must_unset_git_dir = 1;
- $ENV{GIT_DIR} = $repo_path;
- }
- my @refreshargs = qw/update-index --really-refresh -q --unmerged/;
- my @gitargs = qw/diff-files --name-only -z/;
+ my @gitargs = ('--git-dir', $repo_path, '--work-tree', $worktree);
+ my @refreshargs = (
+ @gitargs, 'update-index',
+ '--really-refresh', '-q', '--unmerged');
try {
Git::command_oneline(@refreshargs);
} catch Git::Error::Command with {};
- my $line = Git::command_oneline(@gitargs);
+ my @diffargs = (@gitargs, 'diff-files', '--name-only', '-z');
+ my $line = Git::command_oneline(@diffargs);
my @files;
if (defined $line) {
@files = split('\0', $line);
@@ -105,26 +94,15 @@ sub changed_files
}
delete($ENV{GIT_INDEX_FILE});
- delete($ENV{GIT_WORK_TREE});
- delete($ENV{GIT_DIR}) if ($must_unset_git_dir);
return map { $_ => 1 } @files;
}
sub setup_dir_diff
{
- my ($repo, $workdir, $symlinks) = @_;
-
- # Run the diff; exit immediately if no diff found
- # 'Repository' and 'WorkingCopy' must be explicitly set to insure that
- # if $GIT_DIR and $GIT_WORK_TREE are set in ENV, they are actually used
- # by Git->repository->command*.
- my $repo_path = $repo->repo_path();
- my %repo_args = (Repository => $repo_path, WorkingCopy => $workdir);
- my $diffrepo = Git->repository(%repo_args);
-
+ my ($workdir, $symlinks) = @_;
my @gitargs = ('diff', '--raw', '--no-abbrev', '-z', @ARGV);
- my $diffrtn = $diffrepo->command_oneline(@gitargs);
+ my $diffrtn = Git::command_oneline(@gitargs);
exit(0) unless defined($diffrtn);
# Build index info for left and right sides of the diff
@@ -176,12 +154,12 @@ EOF
if ($lmode eq $symlink_mode) {
$symlink{$src_path}{left} =
- $diffrepo->command_oneline('show', "$lsha1");
+ Git::command_oneline('show', $lsha1);
}
if ($rmode eq $symlink_mode) {
$symlink{$dst_path}{right} =
- $diffrepo->command_oneline('show', "$rsha1");
+ Git::command_oneline('show', $rsha1);
}
if ($lmode ne $null_mode and $status !~ /^C/) {
@@ -193,8 +171,8 @@ EOF
if ($working_tree_dups{$dst_path}++) {
next;
}
- my ($use, $wt_sha1) = use_wt_file($repo, $workdir,
- $dst_path, $rsha1);
+ my ($use, $wt_sha1) =
+ use_wt_file($workdir, $dst_path, $rsha1);
if ($use) {
push @working_tree, $dst_path;
$wtindex .= "$rmode $wt_sha1\t$dst_path\0";
@@ -211,44 +189,34 @@ EOF
mkpath($ldir) or exit_cleanup($tmpdir, 1);
mkpath($rdir) or exit_cleanup($tmpdir, 1);
- # If $GIT_DIR is not set prior to calling 'git update-index' and
- # 'git checkout-index', then those commands will fail if difftool
- # is called from a directory other than the repo root.
- my $must_unset_git_dir = 0;
- if (not defined($ENV{GIT_DIR})) {
- $must_unset_git_dir = 1;
- $ENV{GIT_DIR} = $repo_path;
- }
-
# Populate the left and right directories based on each index file
my ($inpipe, $ctx);
$ENV{GIT_INDEX_FILE} = "$tmpdir/lindex";
($inpipe, $ctx) =
- $repo->command_input_pipe(qw(update-index -z --index-info));
+ Git::command_input_pipe('update-index', '-z', '--index-info');
print($inpipe $lindex);
- $repo->command_close_pipe($inpipe, $ctx);
+ Git::command_close_pipe($inpipe, $ctx);
my $rc = system('git', 'checkout-index', '--all', "--prefix=$ldir/");
exit_cleanup($tmpdir, $rc) if $rc != 0;
$ENV{GIT_INDEX_FILE} = "$tmpdir/rindex";
($inpipe, $ctx) =
- $repo->command_input_pipe(qw(update-index -z --index-info));
+ Git::command_input_pipe('update-index', '-z', '--index-info');
print($inpipe $rindex);
- $repo->command_close_pipe($inpipe, $ctx);
+ Git::command_close_pipe($inpipe, $ctx);
$rc = system('git', 'checkout-index', '--all', "--prefix=$rdir/");
exit_cleanup($tmpdir, $rc) if $rc != 0;
$ENV{GIT_INDEX_FILE} = "$tmpdir/wtindex";
($inpipe, $ctx) =
- $repo->command_input_pipe(qw(update-index --info-only -z --index-info));
+ Git::command_input_pipe('update-index', '--info-only', '-z', '--index-info');
print($inpipe $wtindex);
- $repo->command_close_pipe($inpipe, $ctx);
+ Git::command_close_pipe($inpipe, $ctx);
# If $GIT_DIR was explicitly set just for the update/checkout
# commands, then it should be unset before continuing.
- delete($ENV{GIT_DIR}) if ($must_unset_git_dir);
delete($ENV{GIT_INDEX_FILE});
# Changes in the working tree need special treatment since they are
@@ -415,9 +383,9 @@ sub dir_diff
my $rc;
my $error = 0;
my $repo = Git->repository();
- my $workdir = find_worktree();
- my ($a, $b, $tmpdir, @worktree) =
- setup_dir_diff($repo, $workdir, $symlinks);
+ my $repo_path = $repo->repo_path();
+ my $workdir = $repo->wc_path();
+ my ($a, $b, $tmpdir, @worktree) = setup_dir_diff($workdir, $symlinks);
if (defined($extcmd)) {
$rc = system($extcmd, $a, $b);
@@ -443,10 +411,10 @@ sub dir_diff
next if ! -f "$b/$file";
if (!$indices_loaded) {
- %wt_modified = changed_files($repo->repo_path(),
- "$tmpdir/wtindex", "$workdir");
- %tmp_modified = changed_files($repo->repo_path(),
- "$tmpdir/wtindex", "$b");
+ %wt_modified = changed_files(
+ $repo_path, "$tmpdir/wtindex", $workdir);
+ %tmp_modified = changed_files(
+ $repo_path, "$tmpdir/wtindex", $b);
$indices_loaded = 1;
}
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index ded459563..e2da524f5 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -121,7 +121,7 @@ mark_action_done () {
sed -e 1q < "$todo" >> "$done"
sed -e 1d < "$todo" >> "$todo".new
mv -f "$todo".new "$todo"
- new_count=$(git stripspace --strip-comments <"$done" | wc -l)
+ new_count=$(( $(git stripspace --strip-comments <"$done" | wc -l) ))
echo $new_count >"$msgnum"
total=$(($new_count + $(git stripspace --strip-comments <"$todo" | wc -l)))
echo $total >"$end"
diff --git a/git-submodule.sh b/git-submodule.sh
index 2b23ce6e2..c90dc335d 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -49,7 +49,7 @@ die_if_unmatched ()
{
if test "$1" = "#unmatched"
then
- exit 1
+ exit ${2:-1}
fi
}
@@ -312,11 +312,11 @@ cmd_foreach()
{
git submodule--helper list --prefix "$wt_prefix" ||
- echo "#unmatched"
+ echo "#unmatched" $?
} |
while read mode sha1 stage sm_path
do
- die_if_unmatched "$mode"
+ die_if_unmatched "$mode" "$sha1"
if test -e "$sm_path"/.git
then
displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
@@ -421,11 +421,11 @@ cmd_deinit()
{
git submodule--helper list --prefix "$wt_prefix" "$@" ||
- echo "#unmatched"
+ echo "#unmatched" $?
} |
while read mode sha1 stage sm_path
do
- die_if_unmatched "$mode"
+ die_if_unmatched "$mode" "$sha1"
name=$(git submodule--helper name "$sm_path") || exit
displaypath=$(git submodule--helper relative-path "$sm_path" "$wt_prefix")
@@ -579,12 +579,12 @@ cmd_update()
${depth:+--depth "$depth"} \
${recommend_shallow:+"$recommend_shallow"} \
${jobs:+$jobs} \
- "$@" || echo "#unmatched"
+ "$@" || echo "#unmatched" $?
} | {
err=
while read mode sha1 stage just_cloned sm_path
do
- die_if_unmatched "$mode"
+ die_if_unmatched "$mode" "$sha1"
name=$(git submodule--helper name "$sm_path") || exit
url=$(git config submodule."$name".url)
@@ -992,11 +992,11 @@ cmd_status()
{
git submodule--helper list --prefix "$wt_prefix" "$@" ||
- echo "#unmatched"
+ echo "#unmatched" $?
} |
while read mode sha1 stage sm_path
do
- die_if_unmatched "$mode"
+ die_if_unmatched "$mode" "$sha1"
name=$(git submodule--helper name "$sm_path") || exit
url=$(git config submodule."$name".url)
displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
@@ -1073,11 +1073,11 @@ cmd_sync()
cd_to_toplevel
{
git submodule--helper list --prefix "$wt_prefix" "$@" ||
- echo "#unmatched"
+ echo "#unmatched" $?
} |
while read mode sha1 stage sm_path
do
- die_if_unmatched "$mode"
+ die_if_unmatched "$mode" "$sha1"
name=$(git submodule--helper name "$sm_path")
url=$(git config -f .gitmodules --get submodule."$name".url)
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 2fddf750f..33d701d85 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -2090,7 +2090,7 @@ sub format_ref_marker {
-href => href(
action=>$dest_action,
hash=>$dest
- )}, $name);
+ )}, esc_html($name));
$markers .= " <span class=\"".esc_attr($class)."\" title=\"".esc_attr($ref)."\">" .
$link . "</span>";
diff --git a/grep.c b/grep.c
index 394c8569d..58d599e64 100644
--- a/grep.c
+++ b/grep.c
@@ -163,17 +163,7 @@ void grep_init(struct grep_opt *opt, const char *prefix)
color_set(opt->color_sep, def->color_sep);
}
-void grep_commit_pattern_type(enum grep_pattern_type pattern_type, struct grep_opt *opt)
-{
- if (pattern_type != GREP_PATTERN_TYPE_UNSPECIFIED)
- grep_set_pattern_type_option(pattern_type, opt);
- else if (opt->pattern_type_option != GREP_PATTERN_TYPE_UNSPECIFIED)
- grep_set_pattern_type_option(opt->pattern_type_option, opt);
- else if (opt->extended_regexp_option)
- grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, opt);
-}
-
-void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt)
+static void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt)
{
switch (pattern_type) {
case GREP_PATTERN_TYPE_UNSPECIFIED:
@@ -205,6 +195,16 @@ void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct gr
}
}
+void grep_commit_pattern_type(enum grep_pattern_type pattern_type, struct grep_opt *opt)
+{
+ if (pattern_type != GREP_PATTERN_TYPE_UNSPECIFIED)
+ grep_set_pattern_type_option(pattern_type, opt);
+ else if (opt->pattern_type_option != GREP_PATTERN_TYPE_UNSPECIFIED)
+ grep_set_pattern_type_option(opt->pattern_type_option, opt);
+ else if (opt->extended_regexp_option)
+ grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, opt);
+}
+
static struct grep_pat *create_grep_pat(const char *pat, size_t patlen,
const char *origin, int no,
enum grep_pat_token t,
diff --git a/grep.h b/grep.h
index cee4357b1..5856a23e4 100644
--- a/grep.h
+++ b/grep.h
@@ -145,7 +145,6 @@ struct grep_opt {
extern void init_grep_defaults(void);
extern int grep_config(const char *var, const char *value, void *);
extern void grep_init(struct grep_opt *, const char *prefix);
-void grep_set_pattern_type_option(enum grep_pattern_type, struct grep_opt *opt);
void grep_commit_pattern_type(enum grep_pattern_type, struct grep_opt *opt);
extern void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
diff --git a/http-walker.c b/http-walker.c
index 2c721f0c3..0b2425531 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -2,6 +2,7 @@
#include "commit.h"
#include "walker.h"
#include "http.h"
+#include "list.h"
struct alt_base {
char *base;
@@ -23,7 +24,7 @@ struct object_request {
struct alt_base *repo;
enum object_request_state state;
struct http_object_request *req;
- struct object_request *next;
+ struct list_head node;
};
struct alternates_request {
@@ -41,7 +42,7 @@ struct walker_data {
struct alt_base *alt;
};
-static struct object_request *object_queue_head;
+static LIST_HEAD(object_queue_head);
static void fetch_alternates(struct walker *walker, const char *base);
@@ -110,19 +111,10 @@ static void process_object_response(void *callback_data)
static void release_object_request(struct object_request *obj_req)
{
- struct object_request *entry = object_queue_head;
-
if (obj_req->req !=NULL && obj_req->req->localfile != -1)
error("fd leakage in release: %d", obj_req->req->localfile);
- if (obj_req == object_queue_head) {
- object_queue_head = obj_req->next;
- } else {
- while (entry->next != NULL && entry->next != obj_req)
- entry = entry->next;
- if (entry->next == obj_req)
- entry->next = entry->next->next;
- }
+ list_del(&obj_req->node);
free(obj_req);
}
@@ -130,8 +122,10 @@ static void release_object_request(struct object_request *obj_req)
static int fill_active_slot(struct walker *walker)
{
struct object_request *obj_req;
+ struct list_head *pos, *tmp, *head = &object_queue_head;
- for (obj_req = object_queue_head; obj_req; obj_req = obj_req->next) {
+ list_for_each_safe(pos, tmp, head) {
+ obj_req = list_entry(pos, struct object_request, node);
if (obj_req->state == WAITING) {
if (has_sha1_file(obj_req->sha1))
obj_req->state = COMPLETE;
@@ -148,7 +142,6 @@ static int fill_active_slot(struct walker *walker)
static void prefetch(struct walker *walker, unsigned char *sha1)
{
struct object_request *newreq;
- struct object_request *tail;
struct walker_data *data = walker->data;
newreq = xmalloc(sizeof(*newreq));
@@ -157,18 +150,9 @@ static void prefetch(struct walker *walker, unsigned char *sha1)
newreq->repo = data->alt;
newreq->state = WAITING;
newreq->req = NULL;
- newreq->next = NULL;
http_is_verbose = walker->get_verbosely;
-
- if (object_queue_head == NULL) {
- object_queue_head = newreq;
- } else {
- tail = object_queue_head;
- while (tail->next != NULL)
- tail = tail->next;
- tail->next = newreq;
- }
+ list_add_tail(&newreq->node, &object_queue_head);
#ifdef USE_CURL_MULTI
fill_active_slots();
@@ -447,15 +431,19 @@ static void abort_object_request(struct object_request *obj_req)
release_object_request(obj_req);
}
-static int fetch_object(struct walker *walker, struct alt_base *repo, unsigned char *sha1)
+static int fetch_object(struct walker *walker, unsigned char *sha1)
{
char *hex = sha1_to_hex(sha1);
int ret = 0;
- struct object_request *obj_req = object_queue_head;
+ struct object_request *obj_req = NULL;
struct http_object_request *req;
+ struct list_head *pos, *head = &object_queue_head;
- while (obj_req != NULL && hashcmp(obj_req->sha1, sha1))
- obj_req = obj_req->next;
+ list_for_each(pos, head) {
+ obj_req = list_entry(pos, struct object_request, node);
+ if (!hashcmp(obj_req->sha1, sha1))
+ break;
+ }
if (obj_req == NULL)
return error("Couldn't find request for %s in the queue", hex);
@@ -488,6 +476,15 @@ static int fetch_object(struct walker *walker, struct alt_base *repo, unsigned c
req->localfile = -1;
}
+ /*
+ * we turned off CURLOPT_FAILONERROR to avoid losing a
+ * persistent connection and got CURLE_OK.
+ */
+ if (req->http_code == 404 && req->curl_result == CURLE_OK &&
+ (starts_with(req->url, "http://") ||
+ starts_with(req->url, "https://")))
+ req->curl_result = CURLE_HTTP_RETURNED_ERROR;
+
if (obj_req->state == ABORTED) {
ret = error("Request for %s aborted", hex);
} else if (req->curl_result != CURLE_OK &&
@@ -518,7 +515,7 @@ static int fetch(struct walker *walker, unsigned char *sha1)
struct walker_data *data = walker->data;
struct alt_base *altbase = data->alt;
- if (!fetch_object(walker, altbase, sha1))
+ if (!fetch_object(walker, sha1))
return 0;
while (altbase) {
if (!http_fetch_pack(walker, altbase, sha1))
diff --git a/http.c b/http.c
index d8b2bec86..e81dd132d 100644
--- a/http.c
+++ b/http.c
@@ -1975,8 +1975,19 @@ static size_t fwrite_sha1_file(char *ptr, size_t eltsize, size_t nmemb,
unsigned char expn[4096];
size_t size = eltsize * nmemb;
int posn = 0;
- struct http_object_request *freq =
- (struct http_object_request *)data;
+ struct http_object_request *freq = data;
+ struct active_request_slot *slot = freq->slot;
+
+ if (slot) {
+ CURLcode c = curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE,
+ &slot->http_code);
+ if (c != CURLE_OK)
+ die("BUG: curl_easy_getinfo for HTTP code failed: %s",
+ curl_easy_strerror(c));
+ if (slot->http_code >= 400)
+ return size;
+ }
+
do {
ssize_t retval = xwrite(freq->localfile,
(char *) ptr + posn, size - posn);
@@ -2097,6 +2108,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
freq->slot = get_active_slot();
curl_easy_setopt(freq->slot->curl, CURLOPT_FILE, freq);
+ curl_easy_setopt(freq->slot->curl, CURLOPT_FAILONERROR, 0);
curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);
diff --git a/list.h b/list.h
new file mode 100644
index 000000000..a226a870d
--- /dev/null
+++ b/list.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2002 Free Software Foundation, Inc.
+ * (originally part of the GNU C Library and Userspace RCU)
+ * Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
+ *
+ * Copyright (C) 2009 Pierre-Marc Fournier
+ * Conversion to RCU list.
+ * Copyright (C) 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIST_H
+#define LIST_H 1
+
+/*
+ * The definitions of this file are adopted from those which can be
+ * found in the Linux kernel headers to enable people familiar with the
+ * latter find their way in these sources as well.
+ */
+
+/* Basic type for the double-link list. */
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+/* avoid conflicts with BSD-only sys/queue.h */
+#undef LIST_HEAD
+/* Define a variable with the head and tail of the list. */
+#define LIST_HEAD(name) \
+ struct list_head name = { &(name), &(name) }
+
+/* Initialize a new list head. */
+#define INIT_LIST_HEAD(ptr) \
+ (ptr)->next = (ptr)->prev = (ptr)
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+/* Add new element at the head of the list. */
+static inline void list_add(struct list_head *newp, struct list_head *head)
+{
+ head->next->prev = newp;
+ newp->next = head->next;
+ newp->prev = head;
+ head->next = newp;
+}
+
+/* Add new element at the tail of the list. */
+static inline void list_add_tail(struct list_head *newp, struct list_head *head)
+{
+ head->prev->next = newp;
+ newp->next = head;
+ newp->prev = head->prev;
+ head->prev = newp;
+}
+
+/* Remove element from list. */
+static inline void __list_del(struct list_head *prev, struct list_head *next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/* Remove element from list. */
+static inline void list_del(struct list_head *elem)
+{
+ __list_del(elem->prev, elem->next);
+}
+
+/* Remove element from list, initializing the element's list pointers. */
+static inline void list_del_init(struct list_head *elem)
+{
+ list_del(elem);
+ INIT_LIST_HEAD(elem);
+}
+
+/* Delete from list, add to another list as head. */
+static inline void list_move(struct list_head *elem, struct list_head *head)
+{
+ __list_del(elem->prev, elem->next);
+ list_add(elem, head);
+}
+
+/* Replace an old entry. */
+static inline void list_replace(struct list_head *old, struct list_head *newp)
+{
+ newp->next = old->next;
+ newp->prev = old->prev;
+ newp->prev->next = newp;
+ newp->next->prev = newp;
+}
+
+/* Join two lists. */
+static inline void list_splice(struct list_head *add, struct list_head *head)
+{
+ /* Do nothing if the list which gets added is empty. */
+ if (add != add->next) {
+ add->next->prev = head;
+ add->prev->next = head->next;
+ head->next->prev = add->prev;
+ head->next = add->next;
+ }
+}
+
+/* Get typed element from list at a given position. */
+#define list_entry(ptr, type, member) \
+ ((type *) ((char *) (ptr) - offsetof(type, member)))
+
+/* Get first entry from a list. */
+#define list_first_entry(ptr, type, member) \
+ list_entry((ptr)->next, type, member)
+
+/* Iterate forward over the elements of the list. */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/*
+ * Iterate forward over the elements list. The list elements can be
+ * removed from the list while doing this.
+ */
+#define list_for_each_safe(pos, p, head) \
+ for (pos = (head)->next, p = pos->next; \
+ pos != (head); \
+ pos = p, p = pos->next)
+
+/* Iterate backward over the elements of the list. */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/*
+ * Iterate backwards over the elements list. The list elements can be
+ * removed from the list while doing this.
+ */
+#define list_for_each_prev_safe(pos, p, head) \
+ for (pos = (head)->prev, p = pos->prev; \
+ pos != (head); \
+ pos = p, p = pos->prev)
+
+static inline int list_empty(struct list_head *head)
+{
+ return head == head->next;
+}
+
+static inline void list_replace_init(struct list_head *old,
+ struct list_head *newp)
+{
+ struct list_head *head = old->next;
+
+ list_del(old);
+ list_add_tail(newp, head);
+ INIT_LIST_HEAD(old);
+}
+
+#endif /* LIST_H */
diff --git a/mru.c b/mru.c
new file mode 100644
index 000000000..9dedae028
--- /dev/null
+++ b/mru.c
@@ -0,0 +1,50 @@
+#include "cache.h"
+#include "mru.h"
+
+void mru_append(struct mru *mru, void *item)
+{
+ struct mru_entry *cur = xmalloc(sizeof(*cur));
+ cur->item = item;
+ cur->prev = mru->tail;
+ cur->next = NULL;
+
+ if (mru->tail)
+ mru->tail->next = cur;
+ else
+ mru->head = cur;
+ mru->tail = cur;
+}
+
+void mru_mark(struct mru *mru, struct mru_entry *entry)
+{
+ /* If we're already at the front of the list, nothing to do */
+ if (mru->head == entry)
+ return;
+
+ /* Otherwise, remove us from our current slot... */
+ if (entry->prev)
+ entry->prev->next = entry->next;
+ if (entry->next)
+ entry->next->prev = entry->prev;
+ else
+ mru->tail = entry->prev;
+
+ /* And insert us at the beginning. */
+ entry->prev = NULL;
+ entry->next = mru->head;
+ if (mru->head)
+ mru->head->prev = entry;
+ mru->head = entry;
+}
+
+void mru_clear(struct mru *mru)
+{
+ struct mru_entry *p = mru->head;
+
+ while (p) {
+ struct mru_entry *to_free = p;
+ p = p->next;
+ free(to_free);
+ }
+ mru->head = mru->tail = NULL;
+}
diff --git a/mru.h b/mru.h
new file mode 100644
index 000000000..42e4aeaa1
--- /dev/null
+++ b/mru.h
@@ -0,0 +1,45 @@
+#ifndef MRU_H
+#define MRU_H
+
+/**
+ * A simple most-recently-used cache, backed by a doubly-linked list.
+ *
+ * Usage is roughly:
+ *
+ * // Create a list. Zero-initialization is required.
+ * static struct mru cache;
+ * mru_append(&cache, item);
+ * ...
+ *
+ * // Iterate in MRU order.
+ * struct mru_entry *p;
+ * for (p = cache.head; p; p = p->next) {
+ * if (matches(p->item))
+ * break;
+ * }
+ *
+ * // Mark an item as used, moving it to the front of the list.
+ * mru_mark(&cache, p);
+ *
+ * // Reset the list to empty, cleaning up all resources.
+ * mru_clear(&cache);
+ *
+ * Note that you SHOULD NOT call mru_mark() and then continue traversing the
+ * list; it reorders the marked item to the front of the list, and therefore
+ * you will begin traversing the whole list again.
+ */
+
+struct mru_entry {
+ void *item;
+ struct mru_entry *prev, *next;
+};
+
+struct mru {
+ struct mru_entry *head, *tail;
+};
+
+void mru_append(struct mru *mru, void *item);
+void mru_mark(struct mru *mru, struct mru_entry *entry);
+void mru_clear(struct mru *mru);
+
+#endif /* MRU_H */
diff --git a/pack-write.c b/pack-write.c
index 33293ce2a..ea0b78813 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -354,7 +354,6 @@ void finish_tmp_packfile(struct strbuf *name_buffer,
die_errno("unable to make temporary index file readable");
strbuf_addf(name_buffer, "%s.pack", sha1_to_hex(sha1));
- free_pack_by_name(name_buffer->buf);
if (rename(pack_tmp_name, name_buffer->buf))
die_errno("unable to rename temporary pack file");
diff --git a/parse-options-cb.c b/parse-options-cb.c
index ba5acf311..9667bc75a 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -117,19 +117,24 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
return 0;
}
-int parse_options_concat(struct option *dst, size_t dst_size, struct option *src)
+struct option *parse_options_concat(struct option *a, struct option *b)
{
- int i, j;
-
- for (i = 0; i < dst_size; i++)
- if (dst[i].type == OPTION_END)
- break;
- for (j = 0; i < dst_size; i++, j++) {
- dst[i] = src[j];
- if (src[j].type == OPTION_END)
- return 0;
- }
- return -1;
+ struct option *ret;
+ size_t i, a_len = 0, b_len = 0;
+
+ for (i = 0; a[i].type != OPTION_END; i++)
+ a_len++;
+ for (i = 0; b[i].type != OPTION_END; i++)
+ b_len++;
+
+ ALLOC_ARRAY(ret, st_add3(a_len, b_len, 1));
+ for (i = 0; i < a_len; i++)
+ ret[i] = a[i];
+ for (i = 0; i < b_len; i++)
+ ret[a_len + i] = b[i];
+ ret[a_len + b_len] = b[b_len]; /* final OPTION_END */
+
+ return ret;
}
int parse_opt_string_list(const struct option *opt, const char *arg, int unset)
diff --git a/parse-options.h b/parse-options.h
index ea4af92a5..78f8384c5 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -215,7 +215,7 @@ extern int parse_options_step(struct parse_opt_ctx_t *ctx,
extern int parse_options_end(struct parse_opt_ctx_t *ctx);
-extern int parse_options_concat(struct option *dst, size_t, struct option *src);
+extern struct option *parse_options_concat(struct option *a, struct option *b);
/*----- some often used options -----*/
extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
diff --git a/revision.c b/revision.c
index edba5b79b..15873bf24 100644
--- a/revision.c
+++ b/revision.c
@@ -1973,16 +1973,16 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
} else if (!strcmp(arg, "--grep-debug")) {
revs->grep_filter.debug = 1;
} else if (!strcmp(arg, "--basic-regexp")) {
- grep_set_pattern_type_option(GREP_PATTERN_TYPE_BRE, &revs->grep_filter);
+ revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_BRE;
} else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
- grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, &revs->grep_filter);
+ revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_ERE;
} else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
revs->grep_filter.regflags |= REG_ICASE;
DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
} else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
- grep_set_pattern_type_option(GREP_PATTERN_TYPE_FIXED, &revs->grep_filter);
+ revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_FIXED;
} else if (!strcmp(arg, "--perl-regexp")) {
- grep_set_pattern_type_option(GREP_PATTERN_TYPE_PCRE, &revs->grep_filter);
+ revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_PCRE;
} else if (!strcmp(arg, "--all-match")) {
revs->grep_filter.all_match = 1;
} else if (!strcmp(arg, "--invert-grep")) {
diff --git a/send-pack.c b/send-pack.c
index 299d30384..3a842ac2d 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -260,6 +260,7 @@ static int generate_push_cert(struct strbuf *req_buf,
const char *push_cert_nonce)
{
const struct ref *ref;
+ struct string_list_item *item;
char *signing_key = xstrdup(get_signing_key());
const char *cp, *np;
struct strbuf cert = STRBUF_INIT;
@@ -276,6 +277,9 @@ static int generate_push_cert(struct strbuf *req_buf,
}
if (push_cert_nonce[0])
strbuf_addf(&cert, "nonce %s\n", push_cert_nonce);
+ if (args->push_options)
+ for_each_string_list_item(item, args->push_options)
+ strbuf_addf(&cert, "push-option %s\n", item->string);
strbuf_addstr(&cert, "\n");
for (ref = remote_refs; ref; ref = ref->next) {
@@ -370,6 +374,8 @@ int send_pack(struct send_pack_args *args,
int agent_supported = 0;
int use_atomic = 0;
int atomic_supported = 0;
+ int use_push_options = 0;
+ int push_options_supported = 0;
unsigned cmds_sent = 0;
int ret;
struct async demux;
@@ -392,6 +398,8 @@ int send_pack(struct send_pack_args *args,
args->use_thin_pack = 0;
if (server_supports("atomic"))
atomic_supported = 1;
+ if (server_supports("push-options"))
+ push_options_supported = 1;
if (args->push_cert != SEND_PACK_PUSH_CERT_NEVER) {
int len;
@@ -418,6 +426,11 @@ int send_pack(struct send_pack_args *args,
use_atomic = atomic_supported && args->atomic;
+ if (args->push_options && !push_options_supported)
+ die(_("the receiving end does not support push options"));
+
+ use_push_options = push_options_supported && args->push_options;
+
if (status_report)
strbuf_addstr(&cap_buf, " report-status");
if (use_sideband)
@@ -426,6 +439,8 @@ int send_pack(struct send_pack_args *args,
strbuf_addstr(&cap_buf, " quiet");
if (use_atomic)
strbuf_addstr(&cap_buf, " atomic");
+ if (use_push_options)
+ strbuf_addstr(&cap_buf, " push-options");
if (agent_supported)
strbuf_addf(&cap_buf, " agent=%s", git_user_agent_sanitized());
@@ -512,6 +527,18 @@ int send_pack(struct send_pack_args *args,
strbuf_release(&req_buf);
strbuf_release(&cap_buf);
+ if (use_push_options) {
+ struct string_list_item *item;
+ struct strbuf sb = STRBUF_INIT;
+
+ for_each_string_list_item(item, args->push_options)
+ packet_buf_write(&sb, "%s", item->string);
+
+ write_or_die(out, sb.buf, sb.len);
+ packet_flush(out);
+ strbuf_release(&sb);
+ }
+
if (use_sideband && cmds_sent) {
memset(&demux, 0, sizeof(demux));
demux.proc = sideband_demux;
diff --git a/send-pack.h b/send-pack.h
index 57f222abc..67fc40f4e 100644
--- a/send-pack.h
+++ b/send-pack.h
@@ -1,6 +1,8 @@
#ifndef SEND_PACK_H
#define SEND_PACK_H
+#include "string-list.h"
+
/* Possible values for push_cert field in send_pack_args. */
#define SEND_PACK_PUSH_CERT_NEVER 0
#define SEND_PACK_PUSH_CERT_IF_ASKED 1
@@ -21,6 +23,7 @@ struct send_pack_args {
push_cert:2,
stateless_rpc:1,
atomic:1;
+ const struct string_list *push_options;
};
struct option;
diff --git a/sha1_file.c b/sha1_file.c
index cb571ac6e..3066b5f71 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -23,6 +23,7 @@
#include "bulk-checkin.h"
#include "streaming.h"
#include "dir.h"
+#include "mru.h"
#ifndef O_NOATIME
#if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@ -59,14 +60,6 @@ static struct cached_object empty_tree = {
0
};
-/*
- * A pointer to the last packed_git in which an object was found.
- * When an object is sought, we look in this packfile first, because
- * objects that are looked up at similar times are often in the same
- * packfile as one another.
- */
-static struct packed_git *last_found_pack;
-
static struct cached_object *find_cached_object(const unsigned char *sha1)
{
int i;
@@ -522,6 +515,9 @@ static size_t peak_pack_mapped;
static size_t pack_mapped;
struct packed_git *packed_git;
+static struct mru packed_git_mru_storage;
+struct mru *packed_git_mru = &packed_git_mru_storage;
+
void pack_report(void)
{
fprintf(stderr,
@@ -891,36 +887,6 @@ void close_pack_index(struct packed_git *p)
}
}
-/*
- * This is used by git-repack in case a newly created pack happens to
- * contain the same set of objects as an existing one. In that case
- * the resulting file might be different even if its name would be the
- * same. It is best to close any reference to the old pack before it is
- * replaced on disk. Of course no index pointers or windows for given pack
- * must subsist at this point. If ever objects from this pack are requested
- * again, the new version of the pack will be reinitialized through
- * reprepare_packed_git().
- */
-void free_pack_by_name(const char *pack_name)
-{
- struct packed_git *p, **pp = &packed_git;
-
- while (*pp) {
- p = *pp;
- if (strcmp(pack_name, p->pack_name) == 0) {
- clear_delta_base_cache();
- close_pack(p);
- free(p->bad_object_sha1);
- *pp = p->next;
- if (last_found_pack == p)
- last_found_pack = NULL;
- free(p);
- return;
- }
- pp = &p->next;
- }
-}
-
static unsigned int get_max_fd_limit(void)
{
#ifdef RLIMIT_NOFILE
@@ -1385,6 +1351,15 @@ static void rearrange_packed_git(void)
free(ary);
}
+static void prepare_packed_git_mru(void)
+{
+ struct packed_git *p;
+
+ mru_clear(packed_git_mru);
+ for (p = packed_git; p; p = p->next)
+ mru_append(packed_git_mru, p);
+}
+
static int prepare_packed_git_run_once = 0;
void prepare_packed_git(void)
{
@@ -1400,6 +1375,7 @@ void prepare_packed_git(void)
alt->name[-1] = '/';
}
rearrange_packed_git();
+ prepare_packed_git_mru();
prepare_packed_git_run_once = 1;
}
@@ -2604,21 +2580,15 @@ static int fill_pack_entry(const unsigned char *sha1,
*/
static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
{
- struct packed_git *p;
+ struct mru_entry *p;
prepare_packed_git();
if (!packed_git)
return 0;
- if (last_found_pack && fill_pack_entry(sha1, e, last_found_pack))
- return 1;
-
- for (p = packed_git; p; p = p->next) {
- if (p == last_found_pack)
- continue; /* we already checked this one */
-
- if (fill_pack_entry(sha1, e, p)) {
- last_found_pack = p;
+ for (p = packed_git_mru->head; p; p = p->next) {
+ if (fill_pack_entry(sha1, e, p->item)) {
+ mru_mark(packed_git_mru, p);
return 1;
}
}
diff --git a/submodule-config.c b/submodule-config.c
index 077db4054..d22a851d2 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -371,9 +371,9 @@ static int parse_config(const char *var, const char *value, void *data)
}
static int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
- unsigned char *gitmodules_sha1)
+ unsigned char *gitmodules_sha1,
+ struct strbuf *rev)
{
- struct strbuf rev = STRBUF_INIT;
int ret = 0;
if (is_null_sha1(commit_sha1)) {
@@ -381,11 +381,10 @@ static int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
return 1;
}
- strbuf_addf(&rev, "%s:.gitmodules", sha1_to_hex(commit_sha1));
- if (get_sha1(rev.buf, gitmodules_sha1) >= 0)
+ strbuf_addf(rev, "%s:.gitmodules", sha1_to_hex(commit_sha1));
+ if (get_sha1(rev->buf, gitmodules_sha1) >= 0)
ret = 1;
- strbuf_release(&rev);
return ret;
}
@@ -399,7 +398,7 @@ static const struct submodule *config_from(struct submodule_cache *cache,
{
struct strbuf rev = STRBUF_INIT;
unsigned long config_size;
- char *config;
+ char *config = NULL;
unsigned char sha1[20];
enum object_type type;
const struct submodule *submodule = NULL;
@@ -420,8 +419,8 @@ static const struct submodule *config_from(struct submodule_cache *cache,
return entry->config;
}
- if (!gitmodule_sha1_from_commit(commit_sha1, sha1))
- return NULL;
+ if (!gitmodule_sha1_from_commit(commit_sha1, sha1, &rev))
+ goto out;
switch (lookup_type) {
case lookup_name:
@@ -432,24 +431,20 @@ static const struct submodule *config_from(struct submodule_cache *cache,
break;
}
if (submodule)
- return submodule;
+ goto out;
config = read_sha1_file(sha1, &type, &config_size);
- if (!config)
- return NULL;
-
- if (type != OBJ_BLOB) {
- free(config);
- return NULL;
- }
+ if (!config || type != OBJ_BLOB)
+ goto out;
/* fill the submodule config into the cache */
parameter.cache = cache;
parameter.commit_sha1 = commit_sha1;
parameter.gitmodules_sha1 = sha1;
parameter.overwrite = 0;
- git_config_from_mem(parse_config, "submodule-blob", rev.buf,
+ git_config_from_mem(parse_config, CONFIG_ORIGIN_SUBMODULE_BLOB, rev.buf,
config, config_size, &parameter);
+ strbuf_release(&rev);
free(config);
switch (lookup_type) {
@@ -460,6 +455,11 @@ static const struct submodule *config_from(struct submodule_cache *cache,
default:
return NULL;
}
+
+out:
+ strbuf_release(&rev);
+ free(config);
+ return submodule;
}
static const struct submodule *config_from_path(struct submodule_cache *cache,
diff --git a/t/helper/test-submodule-config.c b/t/helper/test-submodule-config.c
index 61049b87a..2f144d539 100644
--- a/t/helper/test-submodule-config.c
+++ b/t/helper/test-submodule-config.c
@@ -23,7 +23,7 @@ int cmd_main(int argc, const char **argv)
arg++;
my_argc--;
- while (starts_with(arg[0], "--")) {
+ while (arg[0] && starts_with(arg[0], "--")) {
if (!strcmp(arg[0], "--url"))
output_url = 1;
if (!strcmp(arg[0], "--name"))
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index fb8823224..688313ed5 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -65,81 +65,22 @@ svn_cmd () {
svn "$orig_svncmd" --config-dir "$svnconf" "$@"
}
-prepare_httpd () {
- for d in \
- "$SVN_HTTPD_PATH" \
- /usr/sbin/apache2 \
- /usr/sbin/httpd \
- ; do
- if test -f "$d"
- then
- SVN_HTTPD_PATH="$d"
- break
- fi
- done
- if test -z "$SVN_HTTPD_PATH"
- then
- echo >&2 '*** error: Apache not found'
- return 1
- fi
- for d in \
- "$SVN_HTTPD_MODULE_PATH" \
- /usr/lib/apache2/modules \
- /usr/libexec/apache2 \
- ; do
- if test -d "$d"
- then
- SVN_HTTPD_MODULE_PATH="$d"
- break
- fi
- done
- if test -z "$SVN_HTTPD_MODULE_PATH"
- then
- echo >&2 '*** error: Apache module dir not found'
- return 1
- fi
- if test ! -f "$SVN_HTTPD_MODULE_PATH/mod_dav_svn.so"
- then
- echo >&2 '*** error: Apache module "mod_dav_svn" not found'
- return 1
- fi
-
- repo_base_path="${1-svn}"
- mkdir "$GIT_DIR"/logs
-
- cat > "$GIT_DIR/httpd.conf" <<EOF
-ServerName "git svn test"
-ServerRoot "$GIT_DIR"
-DocumentRoot "$GIT_DIR"
-PidFile "$GIT_DIR/httpd.pid"
-LockFile logs/accept.lock
-Listen 127.0.0.1:$SVN_HTTPD_PORT
-LoadModule dav_module $SVN_HTTPD_MODULE_PATH/mod_dav.so
-LoadModule dav_svn_module $SVN_HTTPD_MODULE_PATH/mod_dav_svn.so
-<Location /$repo_base_path>
- DAV svn
- SVNPath "$rawsvnrepo"
-</Location>
-EOF
-}
-
-start_httpd () {
- if test -z "$SVN_HTTPD_PORT"
- then
- echo >&2 'SVN_HTTPD_PORT is not defined!'
- return
- fi
-
- prepare_httpd "$1" || return 1
-
- "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k start
- svnrepo="http://127.0.0.1:$SVN_HTTPD_PORT/$repo_base_path"
-}
-
-stop_httpd () {
- test -z "$SVN_HTTPD_PORT" && return
- test ! -f "$GIT_DIR/httpd.conf" && return
- "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k stop
+maybe_start_httpd () {
+ loc=${1-svn}
+
+ test_tristate GIT_SVN_TEST_HTTPD
+ case $GIT_SVN_TEST_HTTPD in
+ true)
+ . "$TEST_DIRECTORY"/lib-httpd.sh
+ LIB_HTTPD_SVN="$loc"
+ start_httpd
+ ;;
+ *)
+ stop_httpd () {
+ : noop
+ }
+ ;;
+ esac
}
convert_to_rev_db () {
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index ac2cbee25..435a37465 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -24,7 +24,7 @@
# LIB_HTTPD_MODULE_PATH web server modules path
# LIB_HTTPD_PORT listening port
# LIB_HTTPD_DAV enable DAV
-# LIB_HTTPD_SVN enable SVN
+# LIB_HTTPD_SVN enable SVN at given location (e.g. "svn")
# LIB_HTTPD_SSL enable SSL
#
# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
@@ -162,8 +162,10 @@ prepare_httpd() {
if test -n "$LIB_HTTPD_SVN"
then
HTTPD_PARA="$HTTPD_PARA -DSVN"
- rawsvnrepo="$HTTPD_ROOT_PATH/svnrepo"
- svnrepo="http://127.0.0.1:$LIB_HTTPD_PORT/svn"
+ LIB_HTTPD_SVNPATH="$rawsvnrepo"
+ svnrepo="http://127.0.0.1:$LIB_HTTPD_PORT/"
+ svnrepo="$svnrepo$LIB_HTTPD_SVN"
+ export LIB_HTTPD_SVN LIB_HTTPD_SVNPATH
fi
fi
}
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 018a83a5a..c3e631394 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -208,8 +208,8 @@ RewriteRule ^/half-auth-complete/ - [E=AUTHREQUIRED:yes]
<IfDefine SVN>
LoadModule dav_svn_module modules/mod_dav_svn.so
- <Location /svn>
+ <Location /${LIB_HTTPD_SVN}>
DAV svn
- SVNPath svnrepo
+ SVNPath "${LIB_HTTPD_SVNPATH}"
</Location>
</IfDefine>
diff --git a/t/perf/p5303-many-packs.sh b/t/perf/p5303-many-packs.sh
new file mode 100755
index 000000000..377985194
--- /dev/null
+++ b/t/perf/p5303-many-packs.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='performance with large numbers of packs'
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+# A real many-pack situation would probably come from having a lot of pushes
+# over time. We don't know how big each push would be, but we can fake it by
+# just walking the first-parent chain and having every 5 commits be their own
+# "push". This isn't _entirely_ accurate, as real pushes would have some
+# duplicate objects due to thin-pack fixing, but it's a reasonable
+# approximation.
+#
+# And then all of the rest of the objects can go in a single packfile that
+# represents the state before any of those pushes (actually, we'll generate
+# that first because in such a setup it would be the oldest pack, and we sort
+# the packs by reverse mtime inside git).
+repack_into_n () {
+ rm -rf staging &&
+ mkdir staging &&
+
+ git rev-list --first-parent HEAD |
+ sed -n '1~5p' |
+ head -n "$1" |
+ perl -e 'print reverse <>' \
+ >pushes
+
+ # create base packfile
+ head -n 1 pushes |
+ git pack-objects --delta-base-offset --revs staging/pack
+
+ # and then incrementals between each pair of commits
+ last= &&
+ while read rev
+ do
+ if test -n "$last"; then
+ {
+ echo "$rev" &&
+ echo "^$last"
+ } |
+ git pack-objects --delta-base-offset --revs \
+ staging/pack || return 1
+ fi
+ last=$rev
+ done <pushes &&
+
+ # and install the whole thing
+ rm -f .git/objects/pack/* &&
+ mv staging/* .git/objects/pack/
+}
+
+# Pretend we just have a single branch and no reflogs, and that everything is
+# in objects/pack; that makes our fake pack-building via repack_into_n()
+# much simpler.
+test_expect_success 'simplify reachability' '
+ tip=$(git rev-parse --verify HEAD) &&
+ git for-each-ref --format="option no-deref%0adelete %(refname)" |
+ git update-ref --stdin &&
+ rm -rf .git/logs &&
+ git update-ref refs/heads/master $tip &&
+ git symbolic-ref HEAD refs/heads/master &&
+ git repack -ad
+'
+
+for nr_packs in 1 50 1000
+do
+ test_expect_success "create $nr_packs-pack scenario" '
+ repack_into_n $nr_packs
+ '
+
+ test_perf "rev-list ($nr_packs)" '
+ git rev-list --objects --all >/dev/null
+ '
+
+ # This simulates the interesting part of the repack, which is the
+ # actual pack generation, without smudging the on-disk setup
+ # between trials.
+ test_perf "repack ($nr_packs)" '
+ git pack-objects --keep-true-parents \
+ --honor-pack-keep --non-empty --all \
+ --reflog --indexed-objects --delta-base-offset \
+ --stdout </dev/null >/dev/null
+ '
+done
+
+test_done
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 4c8cf5851..c0c910867 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -46,7 +46,10 @@ check_show rfc2822 "$TIME" 'Wed, 15 Jun 2016 16:13:20 +0200'
check_show short "$TIME" '2016-06-15'
check_show default "$TIME" 'Wed Jun 15 16:13:20 2016 +0200'
check_show raw "$TIME" '1466000000 +0200'
+check_show unix "$TIME" '1466000000'
check_show iso-local "$TIME" '2016-06-15 14:13:20 +0000'
+check_show raw-local "$TIME" '1466000000 +0000'
+check_show unix-local "$TIME" '1466000000'
# arbitrary time absurdly far in the future
FUTURE="5758122296 -0400"
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index 7bac2bcf2..e799e5954 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -268,4 +268,15 @@ test_expect_success 'disable filter with empty override' '
test_must_be_empty err
'
+test_expect_success 'diff does not reuse worktree files that need cleaning' '
+ test_config filter.counter.clean "echo . >>count; sed s/^/clean:/" &&
+ echo "file filter=counter" >.gitattributes &&
+ test_commit one file &&
+ test_commit two file &&
+
+ >count &&
+ git diff-tree -p HEAD &&
+ test_line_count = 0 count
+'
+
test_done
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index 4865304eb..2978cb9d6 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -7,6 +7,20 @@ test_description='Test of git add, including the -- option.'
. ./test-lib.sh
+# Test the file mode "$1" of the file "$2" in the index.
+test_mode_in_index () {
+ case "$(git ls-files -s "$2")" in
+ "$1 "*" $2")
+ echo pass
+ ;;
+ *)
+ echo fail
+ git ls-files -s "$2"
+ return 1
+ ;;
+ esac
+}
+
test_expect_success \
'Test of git add' \
'touch foo && git add foo'
@@ -25,18 +39,12 @@ test_expect_success \
echo foo >xfoo1 &&
chmod 755 xfoo1 &&
git add xfoo1 &&
- case "$(git ls-files --stage xfoo1)" in
- 100644" "*xfoo1) echo pass;;
- *) echo fail; git ls-files --stage xfoo1; (exit 1);;
- esac'
+ test_mode_in_index 100644 xfoo1'
test_expect_success 'git add: filemode=0 should not get confused by symlink' '
rm -f xfoo1 &&
test_ln_s_add foo xfoo1 &&
- case "$(git ls-files --stage xfoo1)" in
- 120000" "*xfoo1) echo pass;;
- *) echo fail; git ls-files --stage xfoo1; (exit 1);;
- esac
+ test_mode_in_index 120000 xfoo1
'
test_expect_success \
@@ -45,28 +53,19 @@ test_expect_success \
echo foo >xfoo2 &&
chmod 755 xfoo2 &&
git update-index --add xfoo2 &&
- case "$(git ls-files --stage xfoo2)" in
- 100644" "*xfoo2) echo pass;;
- *) echo fail; git ls-files --stage xfoo2; (exit 1);;
- esac'
+ test_mode_in_index 100644 xfoo2'
test_expect_success 'git add: filemode=0 should not get confused by symlink' '
rm -f xfoo2 &&
test_ln_s_add foo xfoo2 &&
- case "$(git ls-files --stage xfoo2)" in
- 120000" "*xfoo2) echo pass;;
- *) echo fail; git ls-files --stage xfoo2; (exit 1);;
- esac
+ test_mode_in_index 120000 xfoo2
'
test_expect_success \
'git update-index --add: Test that executable bit is not used...' \
'git config core.filemode 0 &&
test_ln_s_add xfoo2 xfoo3 && # runs git update-index --add
- case "$(git ls-files --stage xfoo3)" in
- 120000" "*xfoo3) echo pass;;
- *) echo fail; git ls-files --stage xfoo3; (exit 1);;
- esac'
+ test_mode_in_index 120000 xfoo3'
test_expect_success '.gitignore test setup' '
echo "*.ig" >.gitignore &&
@@ -332,34 +331,22 @@ test_expect_success 'git add --dry-run --ignore-missing of non-existing file out
test_i18ncmp expect.err actual.err
'
-test_expect_success 'git add --chmod=+x stages a non-executable file with +x' '
+test_expect_success 'git add --chmod=[+-]x stages correctly' '
+ rm -f foo1 &&
echo foo >foo1 &&
git add --chmod=+x foo1 &&
- case "$(git ls-files --stage foo1)" in
- 100755" "*foo1) echo pass;;
- *) echo fail; git ls-files --stage foo1; (exit 1);;
- esac
-'
-
-test_expect_success 'git add --chmod=-x stages an executable file with -x' '
- echo foo >xfoo1 &&
- chmod 755 xfoo1 &&
- git add --chmod=-x xfoo1 &&
- case "$(git ls-files --stage xfoo1)" in
- 100644" "*xfoo1) echo pass;;
- *) echo fail; git ls-files --stage xfoo1; (exit 1);;
- esac
+ test_mode_in_index 100755 foo1 &&
+ git add --chmod=-x foo1 &&
+ test_mode_in_index 100644 foo1
'
test_expect_success POSIXPERM,SYMLINKS 'git add --chmod=+x with symlinks' '
git config core.filemode 1 &&
git config core.symlinks 1 &&
+ rm -f foo2 &&
echo foo >foo2 &&
git add --chmod=+x foo2 &&
- case "$(git ls-files --stage foo2)" in
- 100755" "*foo2) echo pass;;
- *) echo fail; git ls-files --stage foo2; (exit 1);;
- esac
+ test_mode_in_index 100755 foo2
'
test_done
diff --git a/t/t4130-apply-criss-cross-rename.sh b/t/t4130-apply-criss-cross-rename.sh
index d173acde0..f8a313bcb 100755
--- a/t/t4130-apply-criss-cross-rename.sh
+++ b/t/t4130-apply-criss-cross-rename.sh
@@ -13,9 +13,13 @@ create_file() {
}
test_expect_success 'setup' '
- create_file file1 "File1 contents" &&
- create_file file2 "File2 contents" &&
- create_file file3 "File3 contents" &&
+ # Ensure that file sizes are different, because on Windows
+ # lstat() does not discover inode numbers, and we need
+ # other properties to discover swapped files
+ # (mtime is not always different, either).
+ create_file file1 "some content" &&
+ create_file file2 "some other content" &&
+ create_file file3 "again something else" &&
git add file1 file2 file3 &&
git commit -m 1
'
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 803e1e6b8..e2db47c36 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -255,6 +255,20 @@ test_expect_success 'log -F -E --grep=<ere> uses ere' '
test_cmp expect actual
'
+test_expect_success 'log with grep.patternType configuration' '
+ >expect &&
+ git -c grep.patterntype=fixed \
+ log -1 --pretty=tformat:%s --grep=s.c.nd >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log with grep.patternType configuration and command line' '
+ echo second >expect &&
+ git -c grep.patterntype=fixed \
+ log -1 --pretty=tformat:%s --basic-regexp --grep=s.c.nd >actual &&
+ test_cmp expect actual
+'
+
cat > expect <<EOF
* Second
* sixth
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index d9f62425b..f5435fd25 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -145,199 +145,199 @@ test_expect_success 'setup more commits' '
test_expect_success 'left alignment formatting' '
git log --pretty="tformat:%<(40)%s" >actual &&
- qz_to_tab_space <<EOF >expected &&
-message two Z
-message one Z
-add bar Z
-$(commit_msg) Z
-EOF
+ qz_to_tab_space <<-EOF >expected &&
+ message two Z
+ message one Z
+ add bar Z
+ $(commit_msg) Z
+ EOF
test_cmp expected actual
'
test_expect_success 'left alignment formatting. i18n.logOutputEncoding' '
git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(40)%s" >actual &&
- qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-message two Z
-message one Z
-add bar Z
-$(commit_msg) Z
-EOF
+ qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+ message two Z
+ message one Z
+ add bar Z
+ $(commit_msg) Z
+ EOF
test_cmp expected actual
'
test_expect_success 'left alignment formatting at the nth column' '
git log --pretty="tformat:%h %<|(40)%s" >actual &&
- qz_to_tab_space <<EOF >expected &&
-$head1 message two Z
-$head2 message one Z
-$head3 add bar Z
-$head4 $(commit_msg) Z
-EOF
+ qz_to_tab_space <<-EOF >expected &&
+ $head1 message two Z
+ $head2 message one Z
+ $head3 add bar Z
+ $head4 $(commit_msg) Z
+ EOF
test_cmp expected actual
'
test_expect_success 'left alignment formatting at the nth column' '
COLUMNS=50 git log --pretty="tformat:%h %<|(-10)%s" >actual &&
- qz_to_tab_space <<EOF >expected &&
-$head1 message two Z
-$head2 message one Z
-$head3 add bar Z
-$head4 $(commit_msg) Z
-EOF
+ qz_to_tab_space <<-EOF >expected &&
+ $head1 message two Z
+ $head2 message one Z
+ $head3 add bar Z
+ $head4 $(commit_msg) Z
+ EOF
test_cmp expected actual
'
test_expect_success 'left alignment formatting at the nth column. i18n.logOutputEncoding' '
git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %<|(40)%s" >actual &&
- qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-$head1 message two Z
-$head2 message one Z
-$head3 add bar Z
-$head4 $(commit_msg) Z
-EOF
+ qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+ $head1 message two Z
+ $head2 message one Z
+ $head3 add bar Z
+ $head4 $(commit_msg) Z
+ EOF
test_cmp expected actual
'
test_expect_success 'left alignment formatting with no padding' '
git log --pretty="tformat:%<(1)%s" >actual &&
- cat <<EOF >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+ cat <<-EOF >expected &&
+ message two
+ message one
+ add bar
+ $(commit_msg)
+ EOF
test_cmp expected actual
'
test_expect_success 'left alignment formatting with no padding. i18n.logOutputEncoding' '
git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(1)%s" >actual &&
- cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+ cat <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+ message two
+ message one
+ add bar
+ $(commit_msg)
+ EOF
test_cmp expected actual
'
test_expect_success 'left alignment formatting with trunc' '
git log --pretty="tformat:%<(10,trunc)%s" >actual &&
- qz_to_tab_space <<EOF >expected &&
-message ..
-message ..
-add bar Z
-initial...
-EOF
+ qz_to_tab_space <<-\EOF >expected &&
+ message ..
+ message ..
+ add bar Z
+ initial...
+ EOF
test_cmp expected actual
'
test_expect_success 'left alignment formatting with trunc. i18n.logOutputEncoding' '
git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,trunc)%s" >actual &&
- qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-message ..
-message ..
-add bar Z
-initial...
-EOF
+ qz_to_tab_space <<-\EOF | iconv -f utf-8 -t $test_encoding >expected &&
+ message ..
+ message ..
+ add bar Z
+ initial...
+ EOF
test_cmp expected actual
'
test_expect_success 'left alignment formatting with ltrunc' '
git log --pretty="tformat:%<(10,ltrunc)%s" >actual &&
- qz_to_tab_space <<EOF >expected &&
-..sage two
-..sage one
-add bar Z
-..${sample_utf8_part}lich
-EOF
+ qz_to_tab_space <<-EOF >expected &&
+ ..sage two
+ ..sage one
+ add bar Z
+ ..${sample_utf8_part}lich
+ EOF
test_cmp expected actual
'
test_expect_success 'left alignment formatting with ltrunc. i18n.logOutputEncoding' '
git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,ltrunc)%s" >actual &&
- qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-..sage two
-..sage one
-add bar Z
-..${sample_utf8_part}lich
-EOF
+ qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+ ..sage two
+ ..sage one
+ add bar Z
+ ..${sample_utf8_part}lich
+ EOF
test_cmp expected actual
'
test_expect_success 'left alignment formatting with mtrunc' '
git log --pretty="tformat:%<(10,mtrunc)%s" >actual &&
- qz_to_tab_space <<EOF >expected &&
-mess.. two
-mess.. one
-add bar Z
-init..lich
-EOF
+ qz_to_tab_space <<-\EOF >expected &&
+ mess.. two
+ mess.. one
+ add bar Z
+ init..lich
+ EOF
test_cmp expected actual
'
test_expect_success 'left alignment formatting with mtrunc. i18n.logOutputEncoding' '
git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,mtrunc)%s" >actual &&
- qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-mess.. two
-mess.. one
-add bar Z
-init..lich
-EOF
+ qz_to_tab_space <<-\EOF | iconv -f utf-8 -t $test_encoding >expected &&
+ mess.. two
+ mess.. one
+ add bar Z
+ init..lich
+ EOF
test_cmp expected actual
'
test_expect_success 'right alignment formatting' '
git log --pretty="tformat:%>(40)%s" >actual &&
- qz_to_tab_space <<EOF >expected &&
-Z message two
-Z message one
-Z add bar
-Z $(commit_msg)
-EOF
+ qz_to_tab_space <<-EOF >expected &&
+ Z message two
+ Z message one
+ Z add bar
+ Z $(commit_msg)
+ EOF
test_cmp expected actual
'
test_expect_success 'right alignment formatting. i18n.logOutputEncoding' '
git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%>(40)%s" >actual &&
- qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-Z message two
-Z message one
-Z add bar
-Z $(commit_msg)
-EOF
+ qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+ Z message two
+ Z message one
+ Z add bar
+ Z $(commit_msg)
+ EOF
test_cmp expected actual
'
test_expect_success 'right alignment formatting at the nth column' '
git log --pretty="tformat:%h %>|(40)%s" >actual &&
- qz_to_tab_space <<EOF >expected &&
-$head1 message two
-$head2 message one
-$head3 add bar
-$head4 $(commit_msg)
-EOF
+ qz_to_tab_space <<-EOF >expected &&
+ $head1 message two
+ $head2 message one
+ $head3 add bar
+ $head4 $(commit_msg)
+ EOF
test_cmp expected actual
'
test_expect_success 'right alignment formatting at the nth column' '
COLUMNS=50 git log --pretty="tformat:%h %>|(-10)%s" >actual &&
- qz_to_tab_space <<EOF >expected &&
-$head1 message two
-$head2 message one
-$head3 add bar
-$head4 $(commit_msg)
-EOF
+ qz_to_tab_space <<-EOF >expected &&
+ $head1 message two
+ $head2 message one
+ $head3 add bar
+ $head4 $(commit_msg)
+ EOF
test_cmp expected actual
'
test_expect_success 'right alignment formatting at the nth column. i18n.logOutputEncoding' '
git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %>|(40)%s" >actual &&
- qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-$head1 message two
-$head2 message one
-$head3 add bar
-$head4 $(commit_msg)
-EOF
+ qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+ $head1 message two
+ $head2 message one
+ $head3 add bar
+ $head4 $(commit_msg)
+ EOF
test_cmp expected actual
'
@@ -345,110 +345,110 @@ EOF
# as in previous test.
test_expect_success 'right alignment formatting at the nth column with --graph. i18n.logOutputEncoding' '
git -c i18n.logOutputEncoding=$test_encoding log --graph --pretty="tformat:%h %>|(40)%s" >actual &&
- iconv -f utf-8 -t $test_encoding >expected <<EOF&&
-* $head1 message two
-* $head2 message one
-* $head3 add bar
-* $head4 $(commit_msg)
-EOF
+ iconv -f utf-8 -t $test_encoding >expected <<-EOF &&
+ * $head1 message two
+ * $head2 message one
+ * $head3 add bar
+ * $head4 $(commit_msg)
+ EOF
test_cmp expected actual
'
test_expect_success 'right alignment formatting with no padding' '
git log --pretty="tformat:%>(1)%s" >actual &&
- cat <<EOF >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+ cat <<-EOF >expected &&
+ message two
+ message one
+ add bar
+ $(commit_msg)
+ EOF
test_cmp expected actual
'
test_expect_success 'right alignment formatting with no padding and with --graph' '
git log --graph --pretty="tformat:%>(1)%s" >actual &&
- cat <<EOF >expected &&
-* message two
-* message one
-* add bar
-* $(commit_msg)
-EOF
+ cat <<-EOF >expected &&
+ * message two
+ * message one
+ * add bar
+ * $(commit_msg)
+ EOF
test_cmp expected actual
'
test_expect_success 'right alignment formatting with no padding. i18n.logOutputEncoding' '
git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%>(1)%s" >actual &&
- cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+ cat <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+ message two
+ message one
+ add bar
+ $(commit_msg)
+ EOF
test_cmp expected actual
'
test_expect_success 'center alignment formatting' '
git log --pretty="tformat:%><(40)%s" >actual &&
- qz_to_tab_space <<EOF >expected &&
-Z message two Z
-Z message one Z
-Z add bar Z
-Z $(commit_msg) Z
-EOF
+ qz_to_tab_space <<-EOF >expected &&
+ Z message two Z
+ Z message one Z
+ Z add bar Z
+ Z $(commit_msg) Z
+ EOF
test_cmp expected actual
'
test_expect_success 'center alignment formatting. i18n.logOutputEncoding' '
git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%><(40)%s" >actual &&
- qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-Z message two Z
-Z message one Z
-Z add bar Z
-Z $(commit_msg) Z
-EOF
+ qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+ Z message two Z
+ Z message one Z
+ Z add bar Z
+ Z $(commit_msg) Z
+ EOF
test_cmp expected actual
'
test_expect_success 'center alignment formatting at the nth column' '
git log --pretty="tformat:%h %><|(40)%s" >actual &&
- qz_to_tab_space <<EOF >expected &&
-$head1 message two Z
-$head2 message one Z
-$head3 add bar Z
-$head4 $(commit_msg) Z
-EOF
+ qz_to_tab_space <<-EOF >expected &&
+ $head1 message two Z
+ $head2 message one Z
+ $head3 add bar Z
+ $head4 $(commit_msg) Z
+ EOF
test_cmp expected actual
'
test_expect_success 'center alignment formatting at the nth column' '
COLUMNS=70 git log --pretty="tformat:%h %><|(-30)%s" >actual &&
- qz_to_tab_space <<EOF >expected &&
-$head1 message two Z
-$head2 message one Z
-$head3 add bar Z
-$head4 $(commit_msg) Z
-EOF
+ qz_to_tab_space <<-EOF >expected &&
+ $head1 message two Z
+ $head2 message one Z
+ $head3 add bar Z
+ $head4 $(commit_msg) Z
+ EOF
test_cmp expected actual
'
test_expect_success 'center alignment formatting at the nth column. i18n.logOutputEncoding' '
git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %><|(40)%s" >actual &&
- qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-$head1 message two Z
-$head2 message one Z
-$head3 add bar Z
-$head4 $(commit_msg) Z
-EOF
+ qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+ $head1 message two Z
+ $head2 message one Z
+ $head3 add bar Z
+ $head4 $(commit_msg) Z
+ EOF
test_cmp expected actual
'
test_expect_success 'center alignment formatting with no padding' '
git log --pretty="tformat:%><(1)%s" >actual &&
- cat <<EOF >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+ cat <<-EOF >expected &&
+ message two
+ message one
+ add bar
+ $(commit_msg)
+ EOF
test_cmp expected actual
'
@@ -457,34 +457,34 @@ EOF
old_head1=$(git rev-parse --verify HEAD~0)
test_expect_success 'center alignment formatting with no padding. i18n.logOutputEncoding' '
git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%><(1)%s" >actual &&
- cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+ cat <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+ message two
+ message one
+ add bar
+ $(commit_msg)
+ EOF
test_cmp expected actual
'
test_expect_success 'left/right alignment formatting with stealing' '
git commit --amend -m short --author "long long long <long@me.com>" &&
git log --pretty="tformat:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual &&
- cat <<EOF >expected &&
-short long long long
-message .. A U Thor
-add bar A U Thor
-initial... A U Thor
-EOF
+ cat <<-\EOF >expected &&
+ short long long long
+ message .. A U Thor
+ add bar A U Thor
+ initial... A U Thor
+ EOF
test_cmp expected actual
'
test_expect_success 'left/right alignment formatting with stealing. i18n.logOutputEncoding' '
git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual &&
- cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-short long long long
-message .. A U Thor
-add bar A U Thor
-initial... A U Thor
-EOF
+ cat <<-\EOF | iconv -f utf-8 -t $test_encoding >expected &&
+ short long long long
+ message .. A U Thor
+ add bar A U Thor
+ initial... A U Thor
+ EOF
test_cmp expected actual
'
@@ -504,8 +504,10 @@ test_expect_success 'ISO and ISO-strict date formats display the same values' '
'
# get new digests (with no abbreviations)
-head1=$(git rev-parse --verify HEAD~0) &&
-head2=$(git rev-parse --verify HEAD~1) &&
+test_expect_success 'set up log decoration tests' '
+ head1=$(git rev-parse --verify HEAD~0) &&
+ head2=$(git rev-parse --verify HEAD~1)
+'
test_expect_success 'log decoration properly follows tag chain' '
git tag -a tag1 -m tag1 &&
@@ -513,22 +515,22 @@ test_expect_success 'log decoration properly follows tag chain' '
git tag -d tag1 &&
git commit --amend -m shorter &&
git log --no-walk --tags --pretty="%H %d" --decorate=full >actual &&
- cat <<EOF >expected &&
-$head1 (tag: refs/tags/tag2)
-$head2 (tag: refs/tags/message-one)
-$old_head1 (tag: refs/tags/message-two)
-EOF
+ cat <<-EOF >expected &&
+ $head1 (tag: refs/tags/tag2)
+ $head2 (tag: refs/tags/message-one)
+ $old_head1 (tag: refs/tags/message-two)
+ EOF
sort actual >actual1 &&
test_cmp expected actual1
'
test_expect_success 'clean log decoration' '
git log --no-walk --tags --pretty="%H %D" --decorate=full >actual &&
- cat >expected <<EOF &&
-$head1 tag: refs/tags/tag2
-$head2 tag: refs/tags/message-one
-$old_head1 tag: refs/tags/message-two
-EOF
+ cat >expected <<-EOF &&
+ $head1 tag: refs/tags/tag2
+ $head2 tag: refs/tags/message-one
+ $old_head1 tag: refs/tags/message-two
+ EOF
sort actual >actual1 &&
test_cmp expected actual1
'
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 6bd485307..668c54be4 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -688,7 +688,7 @@ test_expect_success 'fetching with auto-gc does not lock up' '
)
'
-test_expect_success 'fetch aligned output' '
+test_expect_success C_LOCALE_OUTPUT 'fetch aligned output' '
git clone . full-output &&
test_commit looooooooooooong-tag &&
(
@@ -703,7 +703,7 @@ test_expect_success 'fetch aligned output' '
test_cmp expect actual
'
-test_expect_success 'fetch compact output' '
+test_expect_success C_LOCALE_OUTPUT 'fetch compact output' '
git clone . compact &&
test_commit extraaa &&
(
diff --git a/t/t5545-push-options.sh b/t/t5545-push-options.sh
new file mode 100755
index 000000000..ea813b938
--- /dev/null
+++ b/t/t5545-push-options.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+test_description='pushing to a repository using push options'
+
+. ./test-lib.sh
+
+mk_repo_pair () {
+ rm -rf workbench upstream &&
+ test_create_repo upstream &&
+ test_create_repo workbench &&
+ (
+ cd upstream &&
+ git config receive.denyCurrentBranch warn &&
+ mkdir -p .git/hooks &&
+ cat >.git/hooks/pre-receive <<-'EOF' &&
+ #!/bin/sh
+ if test -n "$GIT_PUSH_OPTION_COUNT"; then
+ i=0
+ >hooks/pre-receive.push_options
+ while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"; do
+ eval "value=\$GIT_PUSH_OPTION_$i"
+ echo $value >>hooks/pre-receive.push_options
+ i=$((i + 1))
+ done
+ fi
+ EOF
+ chmod u+x .git/hooks/pre-receive
+
+ cat >.git/hooks/post-receive <<-'EOF' &&
+ #!/bin/sh
+ if test -n "$GIT_PUSH_OPTION_COUNT"; then
+ i=0
+ >hooks/post-receive.push_options
+ while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"; do
+ eval "value=\$GIT_PUSH_OPTION_$i"
+ echo $value >>hooks/post-receive.push_options
+ i=$((i + 1))
+ done
+ fi
+ EOF
+ chmod u+x .git/hooks/post-receive
+ ) &&
+ (
+ cd workbench &&
+ git remote add up ../upstream
+ )
+}
+
+# Compare the ref ($1) in upstream with a ref value from workbench ($2)
+# i.e. test_refs second HEAD@{2}
+test_refs () {
+ test $# = 2 &&
+ git -C upstream rev-parse --verify "$1" >expect &&
+ git -C workbench rev-parse --verify "$2" >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'one push option works for a single branch' '
+ mk_repo_pair &&
+ git -C upstream config receive.advertisePushOptions true &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git push --mirror up &&
+ test_commit two &&
+ git push --push-option=asdf up master
+ ) &&
+ test_refs master master &&
+ echo "asdf" >expect &&
+ test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
+ test_cmp expect upstream/.git/hooks/post-receive.push_options
+'
+
+test_expect_success 'push option denied by remote' '
+ mk_repo_pair &&
+ git -C upstream config receive.advertisePushOptions false &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git push --mirror up &&
+ test_commit two &&
+ test_must_fail git push --push-option=asdf up master
+ ) &&
+ test_refs master HEAD@{1}
+'
+
+test_expect_success 'two push options work' '
+ mk_repo_pair &&
+ git -C upstream config receive.advertisePushOptions true &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git push --mirror up &&
+ test_commit two &&
+ git push --push-option=asdf --push-option="more structured text" up master
+ ) &&
+ test_refs master master &&
+ printf "asdf\nmore structured text\n" >expect &&
+ test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
+ test_cmp expect upstream/.git/hooks/post-receive.push_options
+'
+
+test_done
diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh
index 44bf1d84a..4d17363a9 100755
--- a/t/t7060-wtstatus.sh
+++ b/t/t7060-wtstatus.sh
@@ -34,6 +34,7 @@ test_expect_success 'M/D conflict does not segfault' '
On branch side
You have unmerged paths.
(fix conflicts and run "git commit")
+ (use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add/rm <file>..." as appropriate to mark resolution)
@@ -138,6 +139,7 @@ test_expect_success 'status when conflicts with add and rm advice (deleted by th
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
+ (use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add/rm <file>..." as appropriate to mark resolution)
@@ -171,6 +173,7 @@ test_expect_success 'status when conflicts with add and rm advice (both deleted)
On branch conflict_second
You have unmerged paths.
(fix conflicts and run "git commit")
+ (use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add/rm <file>..." as appropriate to mark resolution)
@@ -195,6 +198,7 @@ test_expect_success 'status when conflicts with only rm advice (both deleted)' '
On branch conflict_second
You have unmerged paths.
(fix conflicts and run "git commit")
+ (use "git merge --abort" to abort the merge)
Changes to be committed:
diff --git a/t/t7411-submodule-config.sh b/t/t7411-submodule-config.sh
index fc97c3314..400e2b143 100755
--- a/t/t7411-submodule-config.sh
+++ b/t/t7411-submodule-config.sh
@@ -82,6 +82,17 @@ test_expect_success 'error in one submodule config lets continue' '
)
'
+test_expect_success 'error message contains blob reference' '
+ (cd super &&
+ sha1=$(git rev-parse HEAD) &&
+ test-submodule-config \
+ HEAD b \
+ HEAD submodule \
+ 2>actual_err &&
+ grep "submodule-blob $sha1:.gitmodules" actual_err >/dev/null
+ )
+'
+
cat >super/expect_url <<EOF
Submodule url: 'git@somewhere.else.net:a.git' for path 'b'
Submodule url: 'git@somewhere.else.net:submodule.git' for path 'submodule'
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 49d19a3b3..5c3db656d 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -29,6 +29,7 @@ test_expect_success 'status when conflicts unresolved' '
On branch conflicts
You have unmerged paths.
(fix conflicts and run "git commit")
+ (use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index 42a292983..297490057 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -412,6 +412,20 @@ run_dir_diff_test 'difftool --dir-diff from subdirectory' '
)
'
+run_dir_diff_test 'difftool --dir-diff from subdirectory with GIT_DIR set' '
+ (
+ GIT_DIR=$(pwd)/.git &&
+ export GIT_DIR &&
+ GIT_WORK_TREE=$(pwd) &&
+ export GIT_WORK_TREE &&
+ cd sub &&
+ git difftool --dir-diff $symlinks --extcmd ls \
+ branch -- sub >output &&
+ grep sub output &&
+ ! grep file output
+ )
+'
+
run_dir_diff_test 'difftool --dir-diff when worktree file is missing' '
test_when_finished git reset --hard &&
rm file2 &&
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index d29f60108..92a3aa806 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -8,8 +8,6 @@ GIT_SVN_LC_ALL=${LC_ALL:-$LANG}
. ./lib-git-svn.sh
-say 'define NO_SVN_TESTS to skip git svn tests'
-
case "$GIT_SVN_LC_ALL" in
*.UTF-8)
test_set_prereq UTF8
diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
index a87d3d3fc..64bb49583 100755
--- a/t/t9115-git-svn-dcommit-funky-renames.sh
+++ b/t/t9115-git-svn-dcommit-funky-renames.sh
@@ -8,9 +8,10 @@ test_description='git svn dcommit can commit renames of files with ugly names'
. ./lib-git-svn.sh
test_expect_success 'load repository with strange names' '
- svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9115/funky-names.dump &&
- start_httpd gtk+
- '
+ svnadmin load -q "$rawsvnrepo" <"$TEST_DIRECTORY"/t9115/funky-names.dump
+'
+
+maybe_start_httpd gtk+
test_expect_success 'init and fetch repository' '
git svn init "$svnrepo" &&
diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh
index ecb1fed14..41a026637 100755
--- a/t/t9118-git-svn-funky-branch-names.sh
+++ b/t/t9118-git-svn-funky-branch-names.sh
@@ -32,7 +32,7 @@ test_expect_success 'setup svnrepo' '
"$svnrepo/pr ject/branches/trailing_dotlock.lock" &&
svn_cmd cp -m "reflog" "$svnrepo/pr ject/trunk" \
"$svnrepo/pr ject/branches/not-a@{0}reflog@" &&
- start_httpd
+ maybe_start_httpd
'
# SVN 1.7 will truncate "not-a%40{0]" to just "not-a".
diff --git a/t/t9120-git-svn-clone-with-percent-escapes.sh b/t/t9120-git-svn-clone-with-percent-escapes.sh
index 59465b147..b28a1741e 100755
--- a/t/t9120-git-svn-clone-with-percent-escapes.sh
+++ b/t/t9120-git-svn-clone-with-percent-escapes.sh
@@ -15,7 +15,7 @@ test_expect_success 'setup svnrepo' '
svn_cmd cp -m "tag" "$svnrepo/pr ject/trunk" \
"$svnrepo/pr ject/tags/v1" &&
rm -rf project &&
- start_httpd
+ maybe_start_httpd
'
test_expect_success 'test clone with percent escapes' '
diff --git a/t/t9142-git-svn-shallow-clone.sh b/t/t9142-git-svn-shallow-clone.sh
index e21ee5f66..9ee23be64 100755
--- a/t/t9142-git-svn-shallow-clone.sh
+++ b/t/t9142-git-svn-shallow-clone.sh
@@ -18,7 +18,7 @@ test_expect_success 'setup test repository' '
svn_cmd add foo &&
svn_cmd commit -m "add foo"
) &&
- start_httpd
+ maybe_start_httpd
'
test_expect_success 'clone trunk with "-r HEAD"' '
diff --git a/t/t9158-git-svn-mergeinfo.sh b/t/t9158-git-svn-mergeinfo.sh
index 13f78f268..a875b4510 100755
--- a/t/t9158-git-svn-mergeinfo.sh
+++ b/t/t9158-git-svn-mergeinfo.sh
@@ -7,8 +7,6 @@ test_description='git svn mergeinfo propagation'
. ./lib-git-svn.sh
-say 'define NO_SVN_TESTS to skip git svn tests'
-
test_expect_success 'initialize source svn repo' '
svn_cmd mkdir -m x "$svnrepo"/trunk &&
svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
diff --git a/t/t9160-git-svn-preserve-empty-dirs.sh b/t/t9160-git-svn-preserve-empty-dirs.sh
index b4a443460..0ede3cfed 100755
--- a/t/t9160-git-svn-preserve-empty-dirs.sh
+++ b/t/t9160-git-svn-preserve-empty-dirs.sh
@@ -11,7 +11,6 @@ local Git repository with placeholder files.'
. ./lib-git-svn.sh
-say 'define NO_SVN_TESTS to skip git svn tests'
GIT_REPO=git-svn-repo
test_expect_success 'initialize source svn repo containing empty dirs' '
diff --git a/templates/hooks--pre-receive.sample b/templates/hooks--pre-receive.sample
new file mode 100644
index 000000000..a1fd29ec1
--- /dev/null
+++ b/templates/hooks--pre-receive.sample
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# An example hook script to make use of push options.
+# The example simply echoes all push options that start with 'echoback='
+# and rejects all pushes when the "reject" push option is used.
+#
+# To enable this hook, rename this file to "pre-receive".
+
+if test -n "$GIT_PUSH_OPTION_COUNT"
+then
+ i=0
+ while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"
+ do
+ eval "value=\$GIT_PUSH_OPTION_$i"
+ case "$value" in
+ echoback=*)
+ echo "echo from the pre-receive-hook: ${value#*=}" >&2
+ ;;
+ reject)
+ exit 1
+ esac
+ i=$((i + 1))
+ done
+fi
diff --git a/transport.c b/transport.c
index b233e3ee5..4ba48b059 100644
--- a/transport.c
+++ b/transport.c
@@ -513,6 +513,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);
args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC);
+ args.push_options = transport->push_options;
args.url = transport->url;
if (flags & TRANSPORT_PUSH_CERT_ALWAYS)
diff --git a/transport.h b/transport.h
index c68140892..6fe348532 100644
--- a/transport.h
+++ b/transport.h
@@ -48,6 +48,12 @@ struct transport {
*/
unsigned cloning : 1;
+ /*
+ * These strings will be passed to the {pre, post}-receive hook,
+ * on the remote side, if both sides support the push options capability.
+ */
+ const struct string_list *push_options;
+
/**
* Returns 0 if successful, positive if the option is not
* recognized or is inapplicable, and negative if the option
@@ -134,6 +140,7 @@ struct transport {
#define TRANSPORT_PUSH_CERT_ALWAYS 2048
#define TRANSPORT_PUSH_CERT_IF_ASKED 4096
#define TRANSPORT_PUSH_ATOMIC 8192
+#define TRANSPORT_PUSH_OPTIONS 16384
#define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
#define TRANSPORT_SUMMARY(x) (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x)
diff --git a/wt-status.c b/wt-status.c
index 19cbc3939..3175ec6dd 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -948,9 +948,12 @@ static void show_merge_in_progress(struct wt_status *s,
{
if (has_unmerged(s)) {
status_printf_ln(s, color, _("You have unmerged paths."));
- if (s->hints)
+ if (s->hints) {
status_printf_ln(s, color,
- _(" (fix conflicts and run \"git commit\")"));
+ _(" (fix conflicts and run \"git commit\")"));
+ status_printf_ln(s, color,
+ _(" (use \"git merge --abort\" to abort the merge)"));
+ }
} else {
s-> commitable = 1;
status_printf_ln(s, color,