Git development
 help / color / mirror / Atom feed
* Re: [PATCH 02/10] pack-objects: add --partial-by-size=n --partial-special
From: Jeff Hostetler @ 2017-03-08 20:21 UTC (permalink / raw)
  To: Junio C Hamano, Jeff Hostetler; +Cc: git, peff, markbt, benpeart, jonathantanmy
In-Reply-To: <xmqqh93338s2.fsf@gitster.mtv.corp.google.com>



On 3/8/2017 1:47 PM, Junio C Hamano wrote:
> Jeff Hostetler <jeffhost@microsoft.com> writes:
>
>> From: Jeff Hostetler <git@jeffhostetler.com>
>>
>> Teach pack-objects to omit blobs from the generated packfile.
>>
>> When the --partial-by-size=n[kmg] argument is used, only blobs
>> smaller than the requested size are included.  When n is zero,
>> no blobs are included.
>
> Does this interact with a more traditional way of feeding output of
> an external "rev-list --objects" to pack-objects via its standard
> input, and if so, should it (and if not, shouldn't it)?
>
> It is perfectly OK if the answer is "this applies only to the case
> where we generate the list of objects with internal traversal." but
> that needs to be documented and discussed in the proposed log
> message.
>

Let me study that and see.  I'm still thinking thru ways and
options for doing the sparse-checkout like filtering.


>> When the --partial-special argument is used, git special files,
>> such as ".gitattributes" and ".gitignores" are included.
>
> And not ."gitmodules"?
>
> What happens when we later add ".gitsomethingelse"?
>
> Do we have to worry about the case where the set of git "special
> files" (can we have a better name for them please, by the way?)
> understood by the sending side and the receiving end is different?
>
> I have a feeling that a mode that makes anything whose name begins
> with ".git" excempt from the size based cutoff may generally be
> easier to handle.

I forgot about ".gitmodules".  The more I think about it, maybe
we should always include them (or anything starting with ".git*")
and ignore the size, since they are important for correct behavior.


> I am not sure how "back-filling" of a resulting narrow clone would
> safely be done and how this impacts "git fsck" at this point, but if
> they are solved within this effort, that would be a very welcome
> change.
>
> Thanks.
>

^ permalink raw reply

* Re: [PATCH] submodule--helper.c: remove duplicate code
From: Stefan Beller @ 2017-03-08 20:25 UTC (permalink / raw)
  To: Valery Tolstov; +Cc: git@vger.kernel.org
In-Reply-To: <20170308195916.7349-1-me@vtolstov.org>

On Wed, Mar 8, 2017 at 11:59 AM, Valery Tolstov <me@vtolstov.org> wrote:
>> Maybe we need to have 2293f77a081
>> (connect_work_tree_and_git_dir: safely create leading directories,
>> part of origin/sb/checkout-recurse-submodules, also found at
>> https://public-inbox.org/git/20170306205919.9713-8-sbeller@google.com/ )
>> first before we can apply this patch.
>
> Thank you for your detailed responses. Yes, we difenitely need this
> patch first. All tests passed when I applied it.
>

Thanks for testing!

Then the next step (as outlined by Documentation/SubmittingPatches)
is to figure out how to best present this to the mailing list; I think the best
way is to send out a patch series consisting of both of these 2 patches,
the "connect_work_tree_and_git_dir: safely create leading directories,"
first and then your deduplication patch.

When applied in that order, then git passes the test suite  at all commits
(which is very nice when using e.g. git-bisect on git).

Thanks,
Stefan

^ permalink raw reply

* [PATCH 08/10] fetch: add partial-by-size and partial-special arguments
From: git @ 2017-03-08 18:50 UTC (permalink / raw)
  To: git
  Cc: jeffhost, peff, gitster, markbt, benpeart, jonathantanmy,
	Jeff Hostetler
In-Reply-To: <1488999039-37631-1-git-send-email-git@jeffhostetler.com>

From: Jeff Hostetler <git@jeffhostetler.com>

Teach fetch to accept --partial-by-size=n and --partial-special
arguments and pass them to fetch-patch to request that the
server certain blobs from the generated packfile.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
---
 builtin/fetch.c | 26 +++++++++++++++++++++++++-
 connected.c     |  3 +++
 connected.h     |  3 +++
 3 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/builtin/fetch.c b/builtin/fetch.c
index b5ad09d..3d47107 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -52,6 +52,8 @@ static const char *recurse_submodules_default;
 static int shown_url = 0;
 static int refmap_alloc, refmap_nr;
 static const char **refmap_array;
+static const char *partial_by_size;
+static int partial_special;
 
 static int option_parse_recurse_submodules(const struct option *opt,
 				   const char *arg, int unset)
@@ -141,6 +143,11 @@ static struct option builtin_fetch_options[] = {
 			TRANSPORT_FAMILY_IPV4),
 	OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
 			TRANSPORT_FAMILY_IPV6),
+	OPT_STRING(0, "partial-by-size", &partial_by_size,
+			   N_("size"),
+			   N_("only include blobs smaller than this")),
+	OPT_BOOL(0, "partial-special", &partial_special,
+			 N_("only include blobs for git special files")),
 	OPT_END()
 };
 
@@ -731,6 +738,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
 	const char *filename = dry_run ? "/dev/null" : git_path_fetch_head();
 	int want_status;
 	int summary_width = transport_summary_width(ref_map);
+	struct check_connected_options opt = CHECK_CONNECTED_INIT;
+
+	if (partial_by_size || partial_special)
+		opt.allow_partial = 1;
 
 	fp = fopen(filename, "a");
 	if (!fp)
@@ -742,7 +753,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
 		url = xstrdup("foreign");
 
 	rm = ref_map;
-	if (check_connected(iterate_ref_map, &rm, NULL)) {
+	if (check_connected(iterate_ref_map, &rm, &opt)) {
 		rc = error(_("%s did not send all necessary objects\n"), url);
 		goto abort;
 	}
@@ -882,6 +893,9 @@ static int quickfetch(struct ref *ref_map)
 	struct ref *rm = ref_map;
 	struct check_connected_options opt = CHECK_CONNECTED_INIT;
 
+	if (partial_by_size || partial_special)
+		opt.allow_partial = 1;
+
 	/*
 	 * If we are deepening a shallow clone we already have these
 	 * objects reachable.  Running rev-list here will return with
@@ -1020,6 +1034,10 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
 		set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, "yes");
 	if (update_shallow)
 		set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
+	if (partial_by_size)
+		set_option(transport, TRANS_OPT_PARTIAL_BY_SIZE, partial_by_size);
+	if (partial_special)
+		set_option(transport, TRANS_OPT_PARTIAL_SPECIAL, "yes");
 	return transport;
 }
 
@@ -1314,6 +1332,12 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix,
 			     builtin_fetch_options, builtin_fetch_usage, 0);
 
+	if (partial_by_size) {
+		unsigned long s;
+		if (!git_parse_ulong(partial_by_size, &s))
+			die(_("invalid partial-by-size value"));
+	}
+
 	if (deepen_relative) {
 		if (deepen_relative < 0)
 			die(_("Negative depth in --deepen is not supported"));
diff --git a/connected.c b/connected.c
index 136c2ac..b07cbb5 100644
--- a/connected.c
+++ b/connected.c
@@ -62,6 +62,9 @@ int check_connected(sha1_iterate_fn fn, void *cb_data,
 		argv_array_pushf(&rev_list.args, "--progress=%s",
 				 _("Checking connectivity"));
 
+	if (opt->allow_partial)
+		argv_array_push(&rev_list.args, "--allow-partial");
+
 	rev_list.git_cmd = 1;
 	rev_list.env = opt->env;
 	rev_list.in = -1;
diff --git a/connected.h b/connected.h
index 4ca325f..756259e 100644
--- a/connected.h
+++ b/connected.h
@@ -34,6 +34,9 @@ struct check_connected_options {
 	/* If non-zero, show progress as we traverse the objects. */
 	int progress;
 
+	/* A previous partial clone/fetch may have omitted some blobs. */
+	int allow_partial;
+
 	/*
 	 * Insert these variables into the environment of the child process.
 	 */
-- 
2.7.4


^ permalink raw reply related

* [PATCH 03/10] pack-objects: test for --partial-by-size --partial-special
From: git @ 2017-03-08 18:50 UTC (permalink / raw)
  To: git
  Cc: jeffhost, peff, gitster, markbt, benpeart, jonathantanmy,
	Jeff Hostetler
In-Reply-To: <1488999039-37631-1-git-send-email-git@jeffhostetler.com>

From: Jeff Hostetler <git@jeffhostetler.com>

Some simple tests for pack-objects with the new --partial-by-size
and --partial-special options.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
---
 t/5316-pack-objects-partial.sh | 72 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 72 insertions(+)
 create mode 100644 t/5316-pack-objects-partial.sh

diff --git a/t/5316-pack-objects-partial.sh b/t/5316-pack-objects-partial.sh
new file mode 100644
index 0000000..352de34
--- /dev/null
+++ b/t/5316-pack-objects-partial.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+test_description='pack-object partial'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	perl -e "print \"a\" x 11;"      > a &&
+	perl -e "print \"a\" x 1100;"    > b &&
+	perl -e "print \"a\" x 1100000;" > c &&
+	echo "ignored"                   > .gitignore &&
+	git add a b c .gitignore &&
+	git commit -m test
+	'
+
+test_expect_success 'all blobs' '
+	git pack-objects --revs --thin --stdout >all.pack <<-EOF &&
+	master
+	
+	EOF
+	git index-pack all.pack &&
+	test 4 = $(git verify-pack -v all.pack | grep blob | wc -l)
+	'
+
+test_expect_success 'no blobs' '
+	git pack-objects --revs --thin --stdout --partial-by-size=0 >none.pack <<-EOF &&
+	master
+	
+	EOF
+	git index-pack none.pack &&
+	test 0 = $(git verify-pack -v none.pack | grep blob | wc -l)
+	'
+
+test_expect_success 'small blobs' '
+	git pack-objects --revs --thin --stdout --partial-by-size=1M >small.pack <<-EOF &&
+	master
+	
+	EOF
+	git index-pack small.pack &&
+	test 3 = $(git verify-pack -v small.pack | grep blob | wc -l)
+	'
+
+test_expect_success 'tiny blobs' '
+	git pack-objects --revs --thin --stdout --partial-by-size=100 >tiny.pack <<-EOF &&
+	master
+	
+	EOF
+	git index-pack tiny.pack &&
+	test 2 = $(git verify-pack -v tiny.pack | grep blob | wc -l)
+	'
+
+test_expect_success 'special' '
+	git pack-objects --revs --thin --stdout --partial-special >spec.pack <<-EOF &&
+	master
+	
+	EOF
+	git index-pack spec.pack &&
+	test 1 = $(git verify-pack -v spec.pack | grep blob | wc -l)
+	'
+
+test_expect_success 'union' '
+	git pack-objects --revs --thin --stdout --partial-by-size=0 --partial-special >union.pack <<-EOF &&
+	master
+	
+	EOF
+	git index-pack union.pack &&
+	test 1 = $(git verify-pack -v union.pack | grep blob | wc -l)
+	'
+
+test_done
+
+
-- 
2.7.4


^ permalink raw reply related

* [PATCH 09/10] clone: add partial-by-size and partial-special arguments
From: git @ 2017-03-08 18:50 UTC (permalink / raw)
  To: git
  Cc: jeffhost, peff, gitster, markbt, benpeart, jonathantanmy,
	Jeff Hostetler
In-Reply-To: <1488999039-37631-1-git-send-email-git@jeffhostetler.com>

From: Jeff Hostetler <git@jeffhostetler.com>

Teach clone to accept --partial-by-size=n and --partial-special
arguments to request that the server omit certain blobs from
the generated packfile.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
---
 builtin/clone.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/builtin/clone.c b/builtin/clone.c
index 3f63edb..e5a5904 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -56,6 +56,8 @@ static struct string_list option_required_reference = STRING_LIST_INIT_NODUP;
 static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP;
 static int option_dissociate;
 static int max_jobs = -1;
+static const char *partial_by_size;
+static int partial_special;
 
 static struct option builtin_clone_options[] = {
 	OPT__VERBOSITY(&option_verbosity),
@@ -112,6 +114,11 @@ static struct option builtin_clone_options[] = {
 			TRANSPORT_FAMILY_IPV4),
 	OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
 			TRANSPORT_FAMILY_IPV6),
+	OPT_STRING(0, "partial-by-size", &partial_by_size,
+			   N_("size"),
+			   N_("only include blobs smaller than this")),
+	OPT_BOOL(0, "partial-special", &partial_special,
+			 N_("only include blobs for git special files")),
 	OPT_END()
 };
 
@@ -625,6 +632,9 @@ static void update_remote_refs(const struct ref *refs,
 	if (check_connectivity) {
 		struct check_connected_options opt = CHECK_CONNECTED_INIT;
 
+		if (partial_by_size || partial_special)
+			opt.allow_partial = 1;
+
 		opt.transport = transport;
 		opt.progress = transport->progress;
 
@@ -1021,6 +1031,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 			warning(_("--shallow-since is ignored in local clones; use file:// instead."));
 		if (option_not.nr)
 			warning(_("--shallow-exclude is ignored in local clones; use file:// instead."));
+		if (partial_by_size)
+			warning(_("--partial-by-size is ignored in local clones; use file:// instead."));
+		if (partial_special)
+			warning(_("--partial-special is ignored in local clones; use file:// instead."));
 		if (!access(mkpath("%s/shallow", path), F_OK)) {
 			if (option_local > 0)
 				warning(_("source repository is shallow, ignoring --local"));
@@ -1052,6 +1066,18 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		transport_set_option(transport, TRANS_OPT_UPLOADPACK,
 				     option_upload_pack);
 
+	if (partial_by_size) {
+		transport_set_option(transport, TRANS_OPT_PARTIAL_BY_SIZE,
+				     partial_by_size);
+		if (transport->smart_options)
+			transport->smart_options->partial_by_size = partial_by_size;
+	}
+	if (partial_special) {
+		transport_set_option(transport, TRANS_OPT_PARTIAL_SPECIAL, "yes");
+		if (transport->smart_options)
+			transport->smart_options->partial_special = 1;
+	}
+
 	if (transport->smart_options && !deepen)
 		transport->smart_options->check_self_contained_and_connected = 1;
 
-- 
2.7.4


^ permalink raw reply related

* [PATCH 02/10] pack-objects: add --partial-by-size=n --partial-special
From: git @ 2017-03-08 18:50 UTC (permalink / raw)
  To: git
  Cc: jeffhost, peff, gitster, markbt, benpeart, jonathantanmy,
	Jeff Hostetler
In-Reply-To: <1488999039-37631-1-git-send-email-git@jeffhostetler.com>

From: Jeff Hostetler <git@jeffhostetler.com>

Teach pack-objects to omit blobs from the generated packfile.

When the --partial-by-size=n[kmg] argument is used, only blobs
smaller than the requested size are included.  When n is zero,
no blobs are included.

When the --partial-special argument is used, git special files,
such as ".gitattributes" and ".gitignores" are included.

When both are given, the union of two are included.

This is intended to be used in a partial clone or fetch.
(This has also been called sparse- or lazy-clone.)

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
---
 builtin/pack-objects.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 61 insertions(+), 1 deletion(-)

diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 7e052bb..2df2f49 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -77,6 +77,10 @@ static unsigned long cache_max_small_delta_size = 1000;
 
 static unsigned long window_memory_limit = 0;
 
+static signed long partial_by_size = -1;
+static int partial_special = 0;
+static struct trace_key trace_partial = TRACE_KEY_INIT(PARTIAL);
+
 /*
  * stats
  */
@@ -2532,6 +2536,54 @@ static void show_object(struct object *obj, const char *name, void *data)
 	obj->flags |= OBJECT_ADDED;
 }
 
+/*
+ * If ANY --partial-* option was given, we want to OMIT all
+ * blobs UNLESS they match one of our patterns.  We treat
+ * the options as OR's so that we get the resulting UNION.
+ */
+static void show_object_partial(struct object *obj, const char *name, void *data)
+{
+	unsigned long s = 0;
+
+	if (obj->type != OBJ_BLOB)
+		goto include_it;
+
+	/*
+	 * When (partial_by_size == 0), we want to OMIT all blobs.
+	 * When (partial_by_size >  0), we want blobs smaller than that.
+	 */
+	if (partial_by_size > 0) {
+		enum object_type t = sha1_object_info(obj->oid.hash, &s);
+		assert(t == OBJ_BLOB);
+		if (s < partial_by_size)
+			goto include_it;
+	}
+
+	/*
+	 * When (partial_special), we want the .git* special files.
+	 */
+	if (partial_special) {
+		if (strcmp(name, GITATTRIBUTES_FILE) == 0 ||
+			strcmp(name, ".gitignore") == 0)
+			goto include_it;
+		else {
+			const char *last_slash = strrchr(name, '/');
+			if (last_slash)
+				if (strcmp(last_slash+1, GITATTRIBUTES_FILE) == 0 ||
+					strcmp(last_slash+1, ".gitignore") == 0)
+					goto include_it;
+		}
+	}
+
+	trace_printf_key(
+		&trace_partial, "omitting blob '%s' %"PRIuMAX" '%s'\n",
+		oid_to_hex(&obj->oid), (uintmax_t)s, name);
+	return;
+
+include_it:
+	show_object(obj, name, data);
+}
+
 static void show_edge(struct commit *commit)
 {
 	add_preferred_base(commit->object.oid.hash);
@@ -2794,7 +2846,11 @@ static void get_object_list(int ac, const char **av)
 	if (prepare_revision_walk(&revs))
 		die("revision walk setup failed");
 	mark_edges_uninteresting(&revs, show_edge);
-	traverse_commit_list(&revs, show_commit, show_object, NULL);
+
+	if (partial_by_size >= 0 || partial_special)
+		traverse_commit_list(&revs, show_commit, show_object_partial, NULL);
+	else
+		traverse_commit_list(&revs, show_commit, show_object, NULL);
 
 	if (unpack_unreachable_expiration) {
 		revs.ignore_missing_links = 1;
@@ -2930,6 +2986,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 			 N_("use a bitmap index if available to speed up counting objects")),
 		OPT_BOOL(0, "write-bitmap-index", &write_bitmap_index,
 			 N_("write a bitmap index together with the pack index")),
+		OPT_MAGNITUDE(0, "partial-by-size", (unsigned long *)&partial_by_size,
+			 N_("only include blobs smaller than size in result")),
+		OPT_BOOL(0, "partial-special", &partial_special,
+			 N_("only include blobs for git special files")),
 		OPT_END(),
 	};
 
-- 
2.7.4


^ permalink raw reply related

* [PATCH 01/10] pack-objects: eat CR in addition to LF after fgets.
From: git @ 2017-03-08 18:50 UTC (permalink / raw)
  To: git
  Cc: jeffhost, peff, gitster, markbt, benpeart, jonathantanmy,
	Jeff Hostetler
In-Reply-To: <1488999039-37631-1-git-send-email-git@jeffhostetler.com>

From: Jeff Hostetler <git@jeffhostetler.com>

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
---
 builtin/pack-objects.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index f294dcf..7e052bb 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -2764,6 +2764,8 @@ static void get_object_list(int ac, const char **av)
 		int len = strlen(line);
 		if (len && line[len - 1] == '\n')
 			line[--len] = 0;
+		if (len && line[len - 1] == '\r')
+			line[--len] = 0;
 		if (!len)
 			break;
 		if (*line == '-') {
-- 
2.7.4


^ permalink raw reply related

* [PATCH 07/10] index-pack: add --allow-partial option to relax blob existence checks
From: git @ 2017-03-08 18:50 UTC (permalink / raw)
  To: git
  Cc: jeffhost, peff, gitster, markbt, benpeart, jonathantanmy,
	Jeff Hostetler
In-Reply-To: <1488999039-37631-1-git-send-email-git@jeffhostetler.com>

From: Jeff Hostetler <git@jeffhostetler.com>

Teach index-pack to optionally not complain when there are missing
blobs.  This is for use following a partial clone or fetch when
the server omitted certain blobs.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
---
 builtin/index-pack.c | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index f4b87c6..8f99408 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -13,7 +13,7 @@
 #include "thread-utils.h"
 
 static const char index_pack_usage[] =
-"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
+"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--allow-partial] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
 
 struct object_entry {
 	struct pack_idx_entry idx;
@@ -81,6 +81,9 @@ static int show_resolving_progress;
 static int show_stat;
 static int check_self_contained_and_connected;
 
+static int allow_partial;
+static struct trace_key trace_partial = TRACE_KEY_INIT(PARTIAL);
+
 static struct progress *progress;
 
 /* We always read in 4kB chunks. */
@@ -220,9 +223,18 @@ static unsigned check_object(struct object *obj)
 	if (!(obj->flags & FLAG_CHECKED)) {
 		unsigned long size;
 		int type = sha1_object_info(obj->oid.hash, &size);
-		if (type <= 0)
+		if (type <= 0) {
+			if (allow_partial > 0 && obj->type == OBJ_BLOB) {
+				/* Assume a previous partial clone/fetch omitted it. */
+				trace_printf_key(
+					&trace_partial, "omitted blob '%s'\n",
+					oid_to_hex(&obj->oid));
+				obj->flags |= FLAG_CHECKED;
+				return 0;
+			}
 			die(_("did not receive expected object %s"),
 			      oid_to_hex(&obj->oid));
+		}
 		if (type != obj->type)
 			die(_("object %s: expected type %s, found %s"),
 			    oid_to_hex(&obj->oid),
@@ -1718,6 +1730,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
 					die(_("bad %s"), arg);
 			} else if (skip_prefix(arg, "--max-input-size=", &arg)) {
 				max_input_size = strtoumax(arg, NULL, 10);
+			} else if (!strcmp(arg, "--allow-partial")) {
+				allow_partial = 1;
+			} else if (!strcmp(arg, "--no-allow-partial")) {
+				allow_partial = 0;
 			} else
 				usage(index_pack_usage);
 			continue;
-- 
2.7.4


^ permalink raw reply related

* [PATCH 10/10] ls-partial: created command to list missing blobs
From: git @ 2017-03-08 18:50 UTC (permalink / raw)
  To: git
  Cc: jeffhost, peff, gitster, markbt, benpeart, jonathantanmy,
	Jeff Hostetler
In-Reply-To: <1488999039-37631-1-git-send-email-git@jeffhostetler.com>

From: Jeff Hostetler <git@jeffhostetler.com>

Added a command to list the missing blobs for a commit.
This can be used after a partial clone or fetch to list
the omitted blobs that the client would need to checkout
the given commit/branch.  Optionally respecting or ignoring
the current sparse-checkout definition.

This command prints a simple list of blob SHAs.  It is
expected that this would be piped into another command
with knowledge of the transport and/or blob store.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
---
 Makefile             |   2 +
 builtin.h            |   1 +
 builtin/ls-partial.c | 110 ++++++++++++++++++++
 git.c                |   1 +
 partial-utils.c      | 279 +++++++++++++++++++++++++++++++++++++++++++++++++++
 partial-utils.h      |  93 +++++++++++++++++
 6 files changed, 486 insertions(+)
 create mode 100644 builtin/ls-partial.c
 create mode 100644 partial-utils.c
 create mode 100644 partial-utils.h

diff --git a/Makefile b/Makefile
index 9ec6065..96e9e1e 100644
--- a/Makefile
+++ b/Makefile
@@ -791,6 +791,7 @@ LIB_OBJS += pack-write.o
 LIB_OBJS += pager.o
 LIB_OBJS += parse-options.o
 LIB_OBJS += parse-options-cb.o
+LIB_OBJS += partial-utils.o
 LIB_OBJS += patch-delta.o
 LIB_OBJS += patch-ids.o
 LIB_OBJS += path.o
@@ -908,6 +909,7 @@ BUILTIN_OBJS += builtin/init-db.o
 BUILTIN_OBJS += builtin/interpret-trailers.o
 BUILTIN_OBJS += builtin/log.o
 BUILTIN_OBJS += builtin/ls-files.o
+BUILTIN_OBJS += builtin/ls-partial.o
 BUILTIN_OBJS += builtin/ls-remote.o
 BUILTIN_OBJS += builtin/ls-tree.o
 BUILTIN_OBJS += builtin/mailinfo.o
diff --git a/builtin.h b/builtin.h
index 9e4a898..df00c4b 100644
--- a/builtin.h
+++ b/builtin.h
@@ -79,6 +79,7 @@ extern int cmd_interpret_trailers(int argc, const char **argv, const char *prefi
 extern int cmd_log(int argc, const char **argv, const char *prefix);
 extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
+extern int cmd_ls_partial(int argc, const char **argv, const char *prefix);
 extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
diff --git a/builtin/ls-partial.c b/builtin/ls-partial.c
new file mode 100644
index 0000000..8ebf045
--- /dev/null
+++ b/builtin/ls-partial.c
@@ -0,0 +1,110 @@
+#include "cache.h"
+#include "blob.h"
+#include "tree.h"
+#include "commit.h"
+#include "quote.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "pathspec.h"
+#include "dir.h"
+#include "partial-utils.h"
+
+static struct trace_key trace_partial = TRACE_KEY_INIT(PARTIAL);
+
+static int verbose;
+static int ignore_sparse;
+struct exclude_list el;
+
+static const char * const ls_partial_usage[] = {
+	N_("git ls-partial [<options>] <tree-ish>"),
+	NULL
+};
+
+/*
+ * map <tree-ish> arg into SHA1 and get the root treenode.
+ */
+static struct tree *lookup_tree_from_treeish(const char *arg)
+{
+	unsigned char sha1[20];
+	struct tree *tree;
+
+	if (get_sha1(arg, sha1))
+		die("not a valid object name '%s'", arg);
+
+	trace_printf_key(
+		&trace_partial,
+		"ls-partial: treeish '%s' '%s'\n",
+		arg, sha1_to_hex(sha1));
+
+	if (verbose) {
+		printf("commit\t%s\n", sha1_to_hex(sha1));
+		printf("branch\t%s\n", arg);
+	}
+	
+	tree = parse_tree_indirect(sha1);
+	if (!tree)
+		die("not a tree object '%s'", arg);
+
+	return tree;
+}
+
+static void print_results(const struct pu_vec *vec)
+{
+	int k;
+
+	for (k = 0; k < vec->data_nr; k++)
+		printf("%s\n", oid_to_hex(&vec->data[k]->oid));
+}
+
+static void print_results_verbose(const struct pu_vec *vec)
+{
+	int k;
+
+	/* TODO Consider -z version */
+
+	for (k = 0; k < vec->data_nr; k++)
+		printf("%s\t%s\n", oid_to_hex(&vec->data[k]->oid), vec->data[k]->fullpath.buf);
+}
+
+int cmd_ls_partial(int argc, const char **argv, const char *prefix)
+{
+	struct exclude_list el;
+	struct tree *tree;
+	struct pu_vec *vec;
+	struct pu_vec *vec_all = NULL;
+	struct pu_vec *vec_sparse = NULL;
+	struct pu_vec *vec_missing = NULL;
+	
+	const struct option ls_partial_options[] = {
+		OPT__VERBOSE(&verbose, N_("show verbose blob details")),
+		OPT_BOOL(0, "ignore-sparse", &ignore_sparse,
+				 N_("ignore sparse-checkout settings (scan whole tree)")),
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix,
+						 ls_partial_options, ls_partial_usage, 0);
+	if (argc < 1)
+		usage_with_options(ls_partial_usage, ls_partial_options);
+
+	tree = lookup_tree_from_treeish(argv[0]);
+
+	vec_all = pu_vec_ls_tree(tree, prefix, argv + 1);
+	if (ignore_sparse || pu_load_sparse_definitions("info/sparse-checkout", &el) < 0)
+		vec = vec_all;
+	else {
+		vec_sparse = pu_vec_filter_sparse(vec_all, &el);
+		vec = vec_sparse;
+	}
+
+	vec_missing = pu_vec_filter_missing(vec);
+	vec = vec_missing;
+
+	if (verbose)
+		print_results_verbose(vec);
+	else
+		print_results(vec);
+
+	return 0;
+}
diff --git a/git.c b/git.c
index 33f52ac..ef1e019 100644
--- a/git.c
+++ b/git.c
@@ -444,6 +444,7 @@ static struct cmd_struct commands[] = {
 	{ "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY },
 	{ "log", cmd_log, RUN_SETUP },
 	{ "ls-files", cmd_ls_files, RUN_SETUP | SUPPORT_SUPER_PREFIX },
+	{ "ls-partial", cmd_ls_partial, RUN_SETUP },
 	{ "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
 	{ "ls-tree", cmd_ls_tree, RUN_SETUP },
 	{ "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY },
diff --git a/partial-utils.c b/partial-utils.c
new file mode 100644
index 0000000..b75e91e
--- /dev/null
+++ b/partial-utils.c
@@ -0,0 +1,279 @@
+#include "cache.h"
+#include "blob.h"
+#include "tree.h"
+#include "commit.h"
+#include "quote.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "pathspec.h"
+#include "dir.h"
+#include "partial-utils.h"
+
+static struct trace_key trace_partial_utils = TRACE_KEY_INIT(PARTIAL_UTILS);
+
+void pu_row_trace(
+	const struct pu_row *row,
+	const char *label)
+{
+	trace_printf_key(
+		&trace_partial_utils,
+		"%s: %06o %s %.*s\n",
+		label,
+		row->mode,
+		oid_to_hex(&row->oid),
+		(int)row->fullpath.len,
+		row->fullpath.buf);
+}
+
+struct pu_row *pu_row_alloc(
+	const unsigned char *sha1,
+	const struct strbuf *base,
+	const char *entryname,
+	unsigned mode)
+{
+	struct pu_row *row = xcalloc(1, sizeof(struct pu_row));
+
+	hashcpy(row->oid.hash, sha1);
+	strbuf_init(&row->fullpath, base->len + strlen(entryname) + 1);
+	if (base->len)
+		strbuf_addbuf(&row->fullpath, base);
+	strbuf_addstr(&row->fullpath, entryname);
+	row->mode = mode;
+	row->entryname_offset = base->len;
+
+	pu_row_trace(row, "alloc");
+
+	return row;
+}
+
+struct pu_vec *pu_vec_alloc(
+	unsigned int nr_pre_alloc)
+{
+	struct pu_vec *vec = xcalloc(1, sizeof(struct pu_vec));
+
+	vec->data = xcalloc(nr_pre_alloc, sizeof(struct pu_row *));
+	vec->data_alloc = nr_pre_alloc;
+
+	return vec;
+}
+
+void pu_vec_append(
+	struct pu_vec *vec,
+	struct pu_row *row)
+{
+	ALLOC_GROW(vec->data, vec->data_nr + 1, vec->data_alloc);
+	vec->data[vec->data_nr++] = row;
+}
+
+static int ls_tree_cb(
+	const unsigned char *sha1,
+	struct strbuf *base,
+	const char *pathname,
+	unsigned mode,
+	int stage,
+	void *context)
+{
+	struct pu_vec *vec = (struct pu_vec *)context;
+
+	/* omit submodules */
+	if (S_ISGITLINK(mode))
+		return 0;
+
+	pu_vec_append(vec, pu_row_alloc(sha1, base, pathname, mode));
+
+	if (S_ISDIR(mode))
+		return READ_TREE_RECURSIVE;
+
+	return 0;
+}
+
+struct pu_vec *pu_vec_ls_tree(
+	struct tree *tree,
+	const char *prefix,
+	const char **argv)
+{
+	struct pu_vec *vec;
+	struct pathspec pathspec;
+	int k;
+
+	vec = pu_vec_alloc(PU_VEC_DEFAULT_SIZE);
+
+	parse_pathspec(
+		&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE | PATHSPEC_EXCLUDE,
+		PATHSPEC_PREFER_CWD, prefix, argv);
+	for (k = 0; k < pathspec.nr; k++)
+		pathspec.items[k].nowildcard_len = pathspec.items[k].len;
+	pathspec.has_wildcard = 0;
+
+	if (read_tree_recursive(tree, "", 0, 0, &pathspec, ls_tree_cb, vec) != 0)
+		die("Could not read tree");
+
+	return vec;
+}
+
+int pu_load_sparse_definitions(
+	const char *path,
+	struct exclude_list *pel)
+{
+	int result;
+	char *sparse = git_pathdup("info/sparse-checkout");
+	memset(pel, 0, sizeof(*pel));
+	result = add_excludes_from_file_to_list(sparse, "", 0, pel, 0);
+	free(sparse);
+	return result;
+}
+
+static int mode_to_dtype(unsigned mode)
+{
+	if (S_ISREG(mode))
+		return DT_REG;
+	if (S_ISDIR(mode) || S_ISGITLINK(mode))
+		return DT_DIR;
+	if (S_ISLNK(mode))
+		return DT_LNK;
+	return DT_UNKNOWN;
+}
+
+static int apply_excludes_1(
+	struct pu_row **subset,
+	unsigned int nr,
+	struct strbuf *prefix,
+	struct exclude_list *pel,
+	int defval,
+	struct pu_vec *vec_out);
+
+/* apply directory rules. based on clear_ce_flags_dir() */
+static int apply_excludes_dir(
+	struct pu_row **subset,
+	unsigned int nr,
+	struct strbuf *prefix,
+	char *basename,
+	struct exclude_list *pel,
+	int defval,
+	struct pu_vec *vec_out)
+{
+	struct pu_row **subset_end;
+	int dtype = DT_DIR;
+	int ret = is_excluded_from_list(
+		prefix->buf, prefix->len, basename, &dtype, pel);
+	int rc;
+
+	strbuf_addch(prefix, '/');
+
+	if (ret < 0)
+		ret = defval;
+
+	for (subset_end = subset; subset_end != subset + nr; subset_end++) {
+		struct pu_row *row = *subset_end;
+		if (strncmp(row->fullpath.buf, prefix->buf, prefix->len))
+			break;
+	}
+
+	rc = apply_excludes_1(
+		subset, subset_end - subset,
+		prefix, pel, ret,
+		vec_out);
+	strbuf_setlen(prefix, prefix->len - 1);
+	return rc;
+}
+
+/* apply sparse rules to subset[0..nr). based on clear_ce_flags_1() */
+static int apply_excludes_1(
+	struct pu_row **subset,
+	unsigned int nr,
+	struct strbuf *prefix,
+	struct exclude_list *pel,
+	int defval,
+	struct pu_vec *vec_out)
+{
+	struct pu_row **subset_end = subset + nr;
+
+	while (subset != subset_end) {
+		struct pu_row *row = *subset;
+		const char *name, *slash;
+		int len, dtype, val;
+
+		if (prefix->len && strncmp(row->fullpath.buf, prefix->buf, prefix->len))
+			break;
+
+		name = row->fullpath.buf + prefix->len;
+		slash = strchr(name, '/');
+
+		if (slash) {
+			int processed;
+
+			len = slash - name;
+			strbuf_add(prefix, name, len);
+
+			processed = apply_excludes_dir(
+				subset, subset_end - subset,
+				prefix, prefix->buf + prefix->len - len,
+				pel, defval,
+				vec_out);
+
+			if (processed) {
+				subset += processed;
+				strbuf_setlen(prefix, prefix->len - len);
+				continue;
+			}
+
+			strbuf_addch(prefix, '/');
+			subset += apply_excludes_1(
+				subset, subset_end - subset,
+				prefix, pel, defval,
+				vec_out);
+			strbuf_setlen(prefix, prefix->len - len - 1);
+			continue;
+		}
+
+		dtype = mode_to_dtype(row->mode);
+		val = is_excluded_from_list(
+			row->fullpath.buf, row->fullpath.len, name, &dtype, pel);
+		if (val < 0)
+			val = defval;
+		if (val > 0) {
+			pu_row_trace(row, "sparse");
+			pu_vec_append(vec_out, row);
+		}
+		subset++;
+	}
+
+	return nr - (subset_end - subset);
+}
+
+struct pu_vec *pu_vec_filter_sparse(
+	const struct pu_vec *vec_in,
+	struct exclude_list *pel)
+{
+	struct pu_vec *vec_out;
+	struct strbuf prefix = STRBUF_INIT;
+	int defval = 0;
+
+	vec_out = pu_vec_alloc(vec_in->data_nr);
+
+	apply_excludes_1(
+		vec_in->data, vec_in->data_nr,
+		&prefix, pel, defval,
+		vec_out);
+
+	return vec_out;
+}
+
+struct pu_vec *pu_vec_filter_missing(
+	const struct pu_vec *vec_in)
+{
+	struct pu_vec *vec_out;
+	int k;
+
+	vec_out = pu_vec_alloc(vec_in->data_nr);
+
+	for (k = 0; k < vec_in->data_nr; k++) {
+		struct pu_row *row = vec_in->data[k];
+		if (!has_sha1_file(row->oid.hash)) {
+			pu_row_trace(row, "missing");
+			pu_vec_append(vec_out, row);
+		}
+	}
+
+	return vec_out;
+}
diff --git a/partial-utils.h b/partial-utils.h
new file mode 100644
index 0000000..3bdf2e4
--- /dev/null
+++ b/partial-utils.h
@@ -0,0 +1,93 @@
+#ifndef PARTIAL_UTILS_H
+#define PARTIAL_UTILS_H
+
+/*
+ * A 'partial-utils row' represents a single item in the tree.
+ * This is conceptually equivalent to a cache_entry, but does
+ * not require an index_state and lets us operate on any commit
+ * and not be tied to the current worktree.
+ */
+struct pu_row
+{
+	struct strbuf fullpath;
+	struct object_id oid;
+	unsigned mode;
+	unsigned entryname_offset;
+};
+
+/*
+ * A 'partial-utils vec' represents a vector of 'pu row'
+ * values using the normal vector machinery.
+ */
+struct pu_vec
+{
+	struct pu_row **data;
+	unsigned int data_nr;
+	unsigned int data_alloc;
+};
+
+#define PU_VEC_DEFAULT_SIZE (1024*1024)
+
+
+void pu_row_trace(
+	const struct pu_row *row,
+	const char *label);
+
+struct pu_row *pu_row_alloc(
+	const unsigned char *sha1,
+	const struct strbuf *base,
+	const char *entryname,
+	unsigned mode);
+
+struct pu_vec *pu_vec_alloc(
+	unsigned int nr_pre_alloc);
+
+/*
+ * Append the given row onto the vector WITHOUT
+ * assuming ownership of the pointer.
+ */
+void pu_vec_append(
+	struct pu_vec *vec,
+	struct pu_row *row);
+
+/*
+ * Enumerate the contents of the tree (recursively) into
+ * a vector of rows.  This is essentially "ls-tree -r -t"
+ * into a vector.
+ */ 
+struct pu_vec *pu_vec_ls_tree(
+	struct tree *tree,
+	const char *prefix,
+	const char **argv);
+
+/*
+ * Load a sparse-checkout file into (*pel).
+ * Returns -1 if none or error.
+ */
+int pu_load_sparse_definitions(
+	const char *path,
+	struct exclude_list *pel);
+
+/*
+ * Filter the given vector using the sparse-checkout
+ * definitions and return new vector of just the paths
+ * that WOULD BE populated.
+ *
+ * The returned vector BORROWS rows from the input vector.
+ *
+ * This is loosely based upon clear_ce_flags() in unpack-trees.c
+ */
+struct pu_vec *pu_vec_filter_sparse(
+	const struct pu_vec *vec_in,
+	struct exclude_list *pel);
+
+/*
+ * Filter the given vector and return the list of blobs
+ * missing from the local ODB.
+ *
+ * The returned vector BORROWS rows from the input vector.
+ */
+struct pu_vec *pu_vec_filter_missing(
+	const struct pu_vec *vec_in);
+
+#endif /* PARTIAL_UTILS_H */
-- 
2.7.4


^ permalink raw reply related

* [PATCH 06/10] rev-list: add --allow-partial option to relax connectivity checks
From: git @ 2017-03-08 18:50 UTC (permalink / raw)
  To: git
  Cc: jeffhost, peff, gitster, markbt, benpeart, jonathantanmy,
	Jeff Hostetler
In-Reply-To: <1488999039-37631-1-git-send-email-git@jeffhostetler.com>

From: Jeff Hostetler <git@jeffhostetler.com>

Teach rev-list to optionally not complain when there are missing
blobs.  This is for use following a partial clone or fetch when
the server omitted certain blobs.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
---
 builtin/rev-list.c | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 0aa93d5..50c49ba 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -45,6 +45,7 @@ static const char rev_list_usage[] =
 "    --left-right\n"
 "    --count\n"
 "  special purpose:\n"
+"    --allow-partial\n"
 "    --bisect\n"
 "    --bisect-vars\n"
 "    --bisect-all"
@@ -53,6 +54,9 @@ static const char rev_list_usage[] =
 static struct progress *progress;
 static unsigned progress_counter;
 
+static int allow_partial;
+static struct trace_key trace_partial = TRACE_KEY_INIT(PARTIAL);
+
 static void finish_commit(struct commit *commit, void *data);
 static void show_commit(struct commit *commit, void *data)
 {
@@ -178,8 +182,16 @@ static void finish_commit(struct commit *commit, void *data)
 static void finish_object(struct object *obj, const char *name, void *cb_data)
 {
 	struct rev_list_info *info = cb_data;
-	if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid))
+	if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid)) {
+		if (allow_partial) {
+			/* Assume a previous partial clone/fetch omitted it. */
+			trace_printf_key(
+				&trace_partial, "omitted blob '%s' '%s'\n",
+				oid_to_hex(&obj->oid), name);
+			return;
+		}
 		die("missing blob object '%s'", oid_to_hex(&obj->oid));
+	}
 	if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT)
 		parse_object(obj->oid.hash);
 }
@@ -329,6 +341,14 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 			show_progress = arg;
 			continue;
 		}
+		if (!strcmp(arg, "--allow-partial")) {
+			allow_partial = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--no-allow-partial")) {
+			allow_partial = 0;
+			continue;
+		}
 		usage(rev_list_usage);
 
 	}
-- 
2.7.4


^ permalink raw reply related

* [PATCH 05/10] fetch-pack: add partial-by-size and partial-special
From: git @ 2017-03-08 18:50 UTC (permalink / raw)
  To: git
  Cc: jeffhost, peff, gitster, markbt, benpeart, jonathantanmy,
	Jeff Hostetler
In-Reply-To: <1488999039-37631-1-git-send-email-git@jeffhostetler.com>

From: Jeff Hostetler <git@jeffhostetler.com>

Teach fetch-pack to take --partial-by-size and --partial-special
arguments and pass them via the transport to upload-pack to
request that certain blobs be omitted from the resulting packfile.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
---
 builtin/fetch-pack.c |  9 +++++++++
 fetch-pack.c         | 17 +++++++++++++++++
 fetch-pack.h         |  2 ++
 transport.c          |  8 ++++++++
 transport.h          |  8 ++++++++
 5 files changed, 44 insertions(+)

diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index cfe9e44..324d7b2 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -8,6 +8,7 @@
 static const char fetch_pack_usage[] =
 "git fetch-pack [--all] [--stdin] [--quiet | -q] [--keep | -k] [--thin] "
 "[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
+"[--partial-by-size=<n>] [--partial-special] "
 "[--no-progress] [--diag-url] [-v] [<host>:]<directory> [<refs>...]";
 
 static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
@@ -143,6 +144,14 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 			args.update_shallow = 1;
 			continue;
 		}
+		if (skip_prefix(arg, "--partial-by-size=", &arg)) {
+			args.partial_by_size = xstrdup(arg);
+			continue;
+		}
+		if (!strcmp("--partial-special", arg)) {
+			args.partial_special = 1;
+			continue;
+		}
 		usage(fetch_pack_usage);
 	}
 	if (deepen_not.nr)
diff --git a/fetch-pack.c b/fetch-pack.c
index e0f5d5c..e355c38 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -372,6 +372,8 @@ static int find_common(struct fetch_pack_args *args,
 			if (prefer_ofs_delta)   strbuf_addstr(&c, " ofs-delta");
 			if (deepen_since_ok)    strbuf_addstr(&c, " deepen-since");
 			if (deepen_not_ok)      strbuf_addstr(&c, " deepen-not");
+			if (args->partial_by_size || args->partial_special)
+				strbuf_addstr(&c, " partial");
 			if (agent_supported)    strbuf_addf(&c, " agent=%s",
 							    git_user_agent_sanitized());
 			packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
@@ -402,6 +404,12 @@ static int find_common(struct fetch_pack_args *args,
 			packet_buf_write(&req_buf, "deepen-not %s", s->string);
 		}
 	}
+
+	if (args->partial_by_size)
+		packet_buf_write(&req_buf, "partial-by-size %s", args->partial_by_size);
+	if (args->partial_special)
+		packet_buf_write(&req_buf, "partial-special");
+
 	packet_buf_flush(&req_buf);
 	state_len = req_buf.len;
 
@@ -807,6 +815,10 @@ static int get_pack(struct fetch_pack_args *args,
 					"--keep=fetch-pack %"PRIuMAX " on %s",
 					(uintmax_t)getpid(), hostname);
 		}
+
+		if (args->partial_by_size || args->partial_special)
+			argv_array_push(&cmd.args, "--allow-partial");
+
 		if (args->check_self_contained_and_connected)
 			argv_array_push(&cmd.args, "--check-self-contained-and-connected");
 	}
@@ -920,6 +932,11 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 	else
 		prefer_ofs_delta = 0;
 
+	if (server_supports("partial"))
+		print_verbose(args, _("Server supports partial"));
+	else if (args->partial_by_size || args->partial_special)
+		die(_("Server does not support 'partial'"));
+
 	if ((agent_feature = server_feature_value("agent", &agent_len))) {
 		agent_supported = 1;
 		if (agent_len)
diff --git a/fetch-pack.h b/fetch-pack.h
index c912e3d..b8a26e0 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -12,6 +12,7 @@ struct fetch_pack_args {
 	int depth;
 	const char *deepen_since;
 	const struct string_list *deepen_not;
+	const char *partial_by_size;
 	unsigned deepen_relative:1;
 	unsigned quiet:1;
 	unsigned keep_pack:1;
@@ -29,6 +30,7 @@ struct fetch_pack_args {
 	unsigned cloning:1;
 	unsigned update_shallow:1;
 	unsigned deepen:1;
+	unsigned partial_special:1;
 };
 
 /*
diff --git a/transport.c b/transport.c
index 5828e06..45f35a4 100644
--- a/transport.c
+++ b/transport.c
@@ -160,6 +160,12 @@ static int set_git_option(struct git_transport_options *opts,
 	} else if (!strcmp(name, TRANS_OPT_DEEPEN_RELATIVE)) {
 		opts->deepen_relative = !!value;
 		return 0;
+	} else if (!strcmp(name, TRANS_OPT_PARTIAL_BY_SIZE)) {
+		opts->partial_by_size = xstrdup(value);
+		return 0;
+	} else if (!strcmp(name, TRANS_OPT_PARTIAL_SPECIAL)) {
+		opts->partial_special = !!value;
+		return 0;
 	}
 	return 1;
 }
@@ -227,6 +233,8 @@ static int fetch_refs_via_pack(struct transport *transport,
 		data->options.check_self_contained_and_connected;
 	args.cloning = transport->cloning;
 	args.update_shallow = data->options.update_shallow;
+	args.partial_by_size = data->options.partial_by_size;
+	args.partial_special = data->options.partial_special;
 
 	if (!data->got_remote_heads) {
 		connect_setup(transport, 0);
diff --git a/transport.h b/transport.h
index bc55715..c3f2d52 100644
--- a/transport.h
+++ b/transport.h
@@ -15,12 +15,14 @@ struct git_transport_options {
 	unsigned self_contained_and_connected : 1;
 	unsigned update_shallow : 1;
 	unsigned deepen_relative : 1;
+	unsigned partial_special : 1;
 	int depth;
 	const char *deepen_since;
 	const struct string_list *deepen_not;
 	const char *uploadpack;
 	const char *receivepack;
 	struct push_cas_option *cas;
+	const char *partial_by_size;
 };
 
 enum transport_family {
@@ -210,6 +212,12 @@ void transport_check_allowed(const char *type);
 /* Send push certificates */
 #define TRANS_OPT_PUSH_CERT "pushcert"
 
+/* Partial fetch to only include small files */
+#define TRANS_OPT_PARTIAL_BY_SIZE "partial-by-size"
+
+/* Partial fetch to only include special files, like ".gitignore" */
+#define TRANS_OPT_PARTIAL_SPECIAL "partial-special"
+
 /**
  * Returns 0 if the option was used, non-zero otherwise. Prints a
  * message to stderr if the option is not used.
-- 
2.7.4


^ permalink raw reply related

* [PATCH 04/10] upload-pack: add partial (sparse) fetch
From: git @ 2017-03-08 18:50 UTC (permalink / raw)
  To: git
  Cc: jeffhost, peff, gitster, markbt, benpeart, jonathantanmy,
	Jeff Hostetler
In-Reply-To: <1488999039-37631-1-git-send-email-git@jeffhostetler.com>

From: Jeff Hostetler <git@jeffhostetler.com>

Teach upload-pack to advertise the "partial" capability
in the fetch-pack/upload-pack protocol header and to pass
the value of partial-by-size and partial-special on to
pack-objects.

Update protocol documentation.

This might be used in conjunction with a partial (sparse) clone
or fetch to omit various blobs from the generated packfile.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
---
 Documentation/technical/pack-protocol.txt         | 14 ++++++++++
 Documentation/technical/protocol-capabilities.txt |  7 +++++
 upload-pack.c                                     | 32 ++++++++++++++++++++++-
 3 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt
index c59ac99..0032729 100644
--- a/Documentation/technical/pack-protocol.txt
+++ b/Documentation/technical/pack-protocol.txt
@@ -212,6 +212,7 @@ out of what the server said it could do with the first 'want' line.
   upload-request    =  want-list
 		       *shallow-line
 		       *1depth-request
+		       *partial
 		       flush-pkt
 
   want-list         =  first-want
@@ -223,10 +224,15 @@ out of what the server said it could do with the first 'want' line.
 		       PKT-LINE("deepen-since" SP timestamp) /
 		       PKT-LINE("deepen-not" SP ref)
 
+  partial           =  PKT-LINE("partial-by-size" SP magnitude) /
+		       PKT-LINE("partial-special)  
+
   first-want        =  PKT-LINE("want" SP obj-id SP capability-list)
   additional-want   =  PKT-LINE("want" SP obj-id)
 
   depth             =  1*DIGIT
+
+  magnitude         =  1*DIGIT [ "k" | "m" | "g" ]
 ----
 
 Clients MUST send all the obj-ids it wants from the reference
@@ -249,6 +255,14 @@ complete those commits. Commits whose parents are not received as a
 result are defined as shallow and marked as such in the server. This
 information is sent back to the client in the next step.
 
+The client can optionally request a partial packfile that omits
+various blobs.  The value of "partial-by-size" is a non-negative
+integer with optional units and requests blobs smaller than this
+value.  The "partial-special" command requests git-special files,
+such as ".gitignore".  Using both requests the union of the two.
+These requests are only valid if the server advertises the "partial"
+capability.
+
 Once all the 'want's and 'shallow's (and optional 'deepen') are
 transferred, clients MUST send a flush-pkt, to tell the server side
 that it is done sending the list.
diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt
index 26dcc6f..9aa2123 100644
--- a/Documentation/technical/protocol-capabilities.txt
+++ b/Documentation/technical/protocol-capabilities.txt
@@ -309,3 +309,10 @@ to accept a signed push certificate, and asks the <nonce> to be
 included in the push certificate.  A send-pack client MUST NOT
 send a push-cert packet unless the receive-pack server advertises
 this capability.
+
+partial
+-------
+
+If the upload-pack server advertises this capability, fetch-pack
+may send various "partial-*" commands to request a partial clone
+or fetch where the server omits certain blobs from the packfile.
diff --git a/upload-pack.c b/upload-pack.c
index 7597ba3..74f9dfa 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -63,6 +63,11 @@ static int advertise_refs;
 static int stateless_rpc;
 static const char *pack_objects_hook;
 
+static struct strbuf partial_by_size = STRBUF_INIT;
+static int client_requested_partial_capability;
+static int have_partial_by_size;
+static int have_partial_special;
+
 static void reset_timeout(void)
 {
 	alarm(timeout);
@@ -130,6 +135,10 @@ static void create_pack_file(void)
 		argv_array_push(&pack_objects.args, "--delta-base-offset");
 	if (use_include_tag)
 		argv_array_push(&pack_objects.args, "--include-tag");
+	if (have_partial_by_size)
+		argv_array_push(&pack_objects.args, partial_by_size.buf);
+	if (have_partial_special)
+		argv_array_push(&pack_objects.args, "--partial-special");
 
 	pack_objects.in = -1;
 	pack_objects.out = -1;
@@ -793,6 +802,23 @@ static void receive_needs(void)
 			deepen_rev_list = 1;
 			continue;
 		}
+		if (skip_prefix(line, "partial-by-size ", &arg)) {
+			unsigned long s;
+			if (!client_requested_partial_capability)
+				die("git upload-pack: 'partial-by-size' option requires 'partial' capability");
+			if (!git_parse_ulong(arg, &s))
+				die("git upload-pack: invalid partial-by-size value: %s", line);
+			strbuf_addstr(&partial_by_size, "--partial-by-size=");
+			strbuf_addstr(&partial_by_size, arg);
+			have_partial_by_size = 1;
+			continue;
+		}
+		if (skip_prefix(line, "partial-special", &arg)) {
+			if (!client_requested_partial_capability)
+				die("git upload-pack: 'partial-special' option requires 'partial' capability");
+			have_partial_special = 1;
+			continue;
+		}
 		if (!skip_prefix(line, "want ", &arg) ||
 		    get_sha1_hex(arg, sha1_buf))
 			die("git upload-pack: protocol error, "
@@ -820,6 +846,8 @@ static void receive_needs(void)
 			no_progress = 1;
 		if (parse_feature_request(features, "include-tag"))
 			use_include_tag = 1;
+		if (parse_feature_request(features, "partial"))
+			client_requested_partial_capability = 1;
 
 		o = parse_object(sha1_buf);
 		if (!o)
@@ -924,7 +952,9 @@ static int send_ref(const char *refname, const struct object_id *oid,
 {
 	static const char *capabilities = "multi_ack thin-pack side-band"
 		" side-band-64k ofs-delta shallow deepen-since deepen-not"
-		" deepen-relative no-progress include-tag multi_ack_detailed";
+		" deepen-relative no-progress include-tag multi_ack_detailed"
+		" partial"
+		;
 	const char *refname_nons = strip_namespace(refname);
 	struct object_id peeled;
 
-- 
2.7.4


^ permalink raw reply related

* [PATCH 3/4] Updated de-Translator
From: Florian Schüller @ 2017-03-08 20:52 UTC (permalink / raw)
  To: paulus, git; +Cc: Florian Schüller
In-Reply-To: <20170308205255.18976-1-florian.schueller@gmail.com>

---
 po/de.po | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/po/de.po b/po/de.po
index ab90c34..193059b 100644
--- a/po/de.po
+++ b/po/de.po
@@ -4,13 +4,14 @@
 #
 # Christian Stimming <stimming@tuhh.de>, 2007.
 # Frederik Schwarzer <schwarzerf@gmail.com>, 2008.
+# Florian Schüller <florian.schueller@gmail.com>, 2017.
 msgid ""
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2017-01-02 10:08+0100\n"
 "PO-Revision-Date: 2015-10-20 14:20+0200\n"
-"Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
+"Last-Translator: Florian Schüller <florian.schueller@gmail.com>\n"
 "Language-Team: German\n"
 "Language: \n"
 "MIME-Version: 1.0\n"
-- 
2.9.3


^ permalink raw reply related

* [PATCH 1/4] Inotify Support
From: Florian Schüller @ 2017-03-08 20:52 UTC (permalink / raw)
  To: paulus, git; +Cc: Florian Schüller
In-Reply-To: <20170308205255.18976-1-florian.schueller@gmail.com>

Just automatically update gitk when working in a terminal on the same repo

Features:
* Detects inotify support
  if inotify is not detected the options is not available
  in the preferences
* Enable/Disable auto update in the preferences
* Select "debounce" time for redraw
  i.e. the redraw will be postponed for the given time.
  if a new change is detected in this time the redraw is postponed
  even more
* Automatically scroll to the new HEAD after redrawing
* Depending on the type of change the UI is "Updated" or "Reloaded"

Open points for now:
* release watches for deleted directories seems to
  cause problems in tcl-inotify (so I don't)
  I'm not sure how often that happens in ".git/"

Signed-off-by: Florian Schüller <florian.schueller@gmail.com>
---
 gitk | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 170 insertions(+), 1 deletion(-)

diff --git a/gitk b/gitk
index a14d7a1..a2850d7 100755
--- a/gitk
+++ b/gitk
@@ -8,6 +8,12 @@ exec wish "$0" -- "$@"
 # either version 2, or (at your option) any later version.
 
 package require Tk
+try {
+    package require inotify
+    set have_inotify true
+} on error {em} {
+    set have_inotify false
+}
 
 proc hasworktree {} {
     return [expr {[exec git rev-parse --is-bare-repository] == "false" &&
@@ -11489,6 +11495,7 @@ proc prefspage_general {notebook} {
     global NS maxwidth maxgraphpct showneartags showlocalchanges
     global tabstop limitdiffs autoselect autosellen extdifftool perfile_attrs
     global hideremotes want_ttk have_ttk maxrefs
+    global autoupdate have_inotify autoupdatedebounce
 
     set page [create_prefs_page $notebook.general]
 
@@ -11505,13 +11512,21 @@ proc prefspage_general {notebook} {
     ${NS}::checkbutton $page.showlocal -text [mc "Show local changes"] \
 	-variable showlocalchanges
     grid x $page.showlocal -sticky w
+
     ${NS}::checkbutton $page.autoselect -text [mc "Auto-select SHA1 (length)"] \
 	-variable autoselect
     spinbox $page.autosellen -from 1 -to 40 -width 4 -textvariable autosellen
     grid x $page.autoselect $page.autosellen -sticky w
+
     ${NS}::checkbutton $page.hideremotes -text [mc "Hide remote refs"] \
 	-variable hideremotes
     grid x $page.hideremotes -sticky w
+    if { $have_inotify } {
+        ${NS}::checkbutton $page.autoupdate -text [mc "Auto-update upon change (ms)"] \
+            -variable autoupdate
+        spinbox $page.autoupdatedebounce -from 10 -to 60000 -width 7 -textvariable autoupdatedebounce
+        grid x $page.autoupdate $page.autoupdatedebounce -sticky w
+    }
 
     ${NS}::label $page.ddisp -text [mc "Diff display options"]
     grid $page.ddisp - -sticky w -pady 10
@@ -11765,7 +11780,8 @@ proc prefsok {} {
     global oldprefs prefstop showneartags showlocalchanges
     global fontpref mainfont textfont uifont
     global limitdiffs treediffs perfile_attrs
-    global hideremotes
+    global hideremotes autoupdate
+    global gitdir
 
     catch {destroy $prefstop}
     unset prefstop
@@ -11814,6 +11830,8 @@ proc prefsok {} {
     if {$hideremotes != $oldprefs(hideremotes)} {
 	rereadrefs
     }
+
+    handle_inotify $gitdir true
 }
 
 proc formatdate {d} {
@@ -12295,6 +12313,13 @@ set autoselect 1
 set autosellen 40
 set perfile_attrs 0
 set want_ttk 1
+set autoupdate 1
+set autoupdatedebounce 100
+#timer id for inotify reloading
+set reload_id -1
+#timer id for inotify updating (less than reload)
+set update_id -1
+set inotify_instance -1
 
 if {[tk windowingsystem] eq "aqua"} {
     set extdifftool "opendiff"
@@ -12390,6 +12415,7 @@ set config_variables {
     filesepbgcolor filesepfgcolor linehoverbgcolor linehoverfgcolor
     linehoveroutlinecolor mainheadcirclecolor workingfilescirclecolor
     indexcirclecolor circlecolors linkfgcolor circleoutlinecolor
+    autoupdate autoupdatedebounce
 }
 foreach var $config_variables {
     config_init_trace $var
@@ -12477,6 +12503,149 @@ if {$i >= [llength $argv] && $revtreeargs ne {}} {
     }
 }
 
+#function to be called after inotify reload-timeout
+proc reload_helper {} {
+    #puts "RELOAD"
+    global reload_id
+    set reload_id -1
+    reloadcommits
+    set head [exec git rev-parse HEAD]
+    selbyid $head
+}
+
+#function to be called after inotify update-timeout
+proc update_helper {} {
+    #puts "UPDATE"
+    global update_id
+    set update_id -1
+    updatecommits
+    set head [exec git rev-parse HEAD]
+    selbyid $head
+}
+
+proc inotify_handler { fd } {
+    global autoupdate reload_id update_id autoupdatedebounce
+    set events [inotify_watch read]
+    set watch_info [inotify_watch info]
+    set update_view false
+    set reloadcommits false
+
+    #cancel pending timer
+    if { $reload_id ne -1 } {
+        #puts "cancel a reload"
+        after cancel $reload_id
+        set reload_id -1
+        set update_view true
+        set reloadcommits true
+    }
+
+    if { $update_id ne -1 } {
+        #puts "cancel an update"
+        after cancel $update_id
+        set update_id -1
+        set update_view true
+    }
+
+    foreach {event} $events {
+        set current_watchid [dict get $event watchid]
+        set flags [dict get $event flags]
+        set event_filename [dict get $event filename]
+
+        foreach {path watchid watch_flags} $watch_info {
+            if {$watchid eq $current_watchid} {
+                set watch_path $path
+            }
+        }
+
+        set full_filename [file join $watch_path $event_filename]
+        #check wether we should do update or reload below
+        #puts "Got: $full_filename / $event_filename ($flags)"
+
+        if {$flags eq "nD"} {
+            inotify_watch add $full_filename "nwds"
+        }
+        if {![string match *.lock $event_filename]} {
+            if { $flags eq "d" } {
+                #stuff like deleting branches should result in reloading
+                set reloadcommits true
+            }
+            set update_view true
+        }
+
+        #simple commit just needs updating right?
+        #if { $event_filename eq "COMMIT_EDITMSG" } {
+        #    set reloadcommits true
+        #}
+    }
+
+    #reloadcommits or updatecommits - depending on file and operation?
+    if { $update_view } {
+        if { $reloadcommits } {
+            #puts "schedule reload"
+            set reload_id [after $autoupdatedebounce reload_helper]
+        } else {
+            #puts "schedule update"
+            set update_id [after $autoupdatedebounce update_helper]
+        }
+    }
+}
+
+proc watch_recursive { dir } {
+    inotify_watch add $dir "nwaCmMds"
+
+    foreach i [glob -nocomplain -dir $dir *] {
+        if {[file type $i] eq {directory}} {
+            watch_recursive $i
+        }
+    }
+}
+
+proc enable_inotify { dir redraw} {
+    global inotify_instance autoupdatedebounce reload_id
+
+    if { $inotify_instance ne -1 } {
+        updatecommits
+    } else {
+        set inotify_instance [inotify create "inotify_watch" "::inotify_handler"]
+        watch_recursive $dir
+        if { $redraw } {
+            set reload_id [after $autoupdatedebounce reload_helper]
+        }
+    }
+}
+
+proc disable_inotify {} {
+    global inotify_instance reload_id update_id
+
+    if { $inotify_instance ne -1 } {
+        rename inotify_watch {}
+        set inotify_instance -1
+    }
+
+    if { $reload_id ne -1 } {
+        after cancel $reload_id
+        set reload_id -1
+    }
+
+    if { $update_id ne -1 } {
+        after cancel $update_id
+        set update_id -1
+    }
+}
+
+proc handle_inotify { dir redraw } {
+    global have_inotify autoupdate
+    if { $have_inotify } {
+        if { $autoupdate } {
+            enable_inotify $dir $redraw
+        } else {
+            disable_inotify
+        }
+    }
+}
+
+handle_inotify $gitdir false
+
 set nullid "0000000000000000000000000000000000000000"
 set nullid2 "0000000000000000000000000000000000000001"
 set nullfile "/dev/null"
-- 
2.9.3


^ permalink raw reply related

* [PATCH 0/4] Gitk Inotify support
From: Florian Schüller @ 2017-03-08 20:52 UTC (permalink / raw)
  To: paulus, git; +Cc: Florian Schüller

Inotify Support
    
    Just automatically update gitk when working in a terminal on the same repo
    
    Features:
    * Detects inotify support
      if inotify is not detected the options is not available
      in the preferences
    * Enable/Disable auto update in the preferences
    * Select "debounce" time for redraw
      i.e. the redraw will be postponed for the given time.
      if a new change is detected in this time the redraw is postponed
      even more
    * Automatically scroll to the new HEAD after redrawing
    * Depending on the type of change the UI is "Updated" or "Reloaded"
    
    Open points for now:
    * release watches for deleted directories seems to
      cause problems in tcl-inotify (so I don't)
      I'm not sure how often that happens in ".git/"
    
Signed-off-by: Florian Schüller <florian.schueller@gmail.com>

Florian Schüller (4):
  Inotify Support
  Update of German translation
  Updated de-Translator
  ignore backup files

 .gitignore |   2 +
 gitk       | 171 +++++++++++++++-
 po/de.po   | 684 +++++++++++++++++++++++++++++++++----------------------------
 3 files changed, 540 insertions(+), 317 deletions(-)

-- 
2.9.3


^ permalink raw reply

* [PATCH 2/4] Update of German translation
From: Florian Schüller @ 2017-03-08 20:52 UTC (permalink / raw)
  To: paulus, git; +Cc: Florian Schüller
In-Reply-To: <20170308205255.18976-1-florian.schueller@gmail.com>

---
 po/de.po | 681 ++++++++++++++++++++++++++++++++++-----------------------------
 1 file changed, 366 insertions(+), 315 deletions(-)

diff --git a/po/de.po b/po/de.po
index 5db3824..ab90c34 100644
--- a/po/de.po
+++ b/po/de.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-05-17 14:32+1000\n"
+"POT-Creation-Date: 2017-01-02 10:08+0100\n"
 "PO-Revision-Date: 2015-10-20 14:20+0200\n"
 "Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
 "Language-Team: German\n"
@@ -17,33 +17,33 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: gitk:140
+#: gitk:146
 msgid "Couldn't get list of unmerged files:"
 msgstr "Liste der nicht zusammengeführten Dateien nicht gefunden:"
 
-#: gitk:212 gitk:2381
+#: gitk:218 gitk:2409
 msgid "Color words"
 msgstr "Wörter einfärben"
 
-#: gitk:217 gitk:2381 gitk:8220 gitk:8253
+#: gitk:223 gitk:2409 gitk:8255 gitk:8288
 msgid "Markup words"
 msgstr "Wörter kennzeichnen"
 
-#: gitk:324
+#: gitk:330
 msgid "Error parsing revisions:"
 msgstr "Fehler beim Laden der Versionen:"
 
-#: gitk:380
+#: gitk:386
 msgid "Error executing --argscmd command:"
 msgstr "Fehler beim Ausführen des --argscmd-Kommandos:"
 
-#: gitk:393
+#: gitk:399
 msgid "No files selected: --merge specified but no files are unmerged."
 msgstr ""
 "Keine Dateien ausgewählt: Es wurde --merge angegeben, aber es existieren "
 "keine nicht zusammengeführten Dateien."
 
-#: gitk:396
+#: gitk:402
 msgid ""
 "No files selected: --merge specified but no unmerged files are within file "
 "limit."
@@ -51,314 +51,327 @@ msgstr ""
 "Keine Dateien ausgewählt: Es wurde --merge angegeben, aber es sind keine "
 "nicht zusammengeführten Dateien in der Dateiauswahl."
 
-#: gitk:418 gitk:566
+#: gitk:424 gitk:572
 msgid "Error executing git log:"
 msgstr "Fehler beim Ausführen von »git log«:"
 
-#: gitk:436 gitk:582
+#: gitk:442 gitk:588
 msgid "Reading"
 msgstr "Lesen"
 
-#: gitk:496 gitk:4525
+#: gitk:502 gitk:4555
 msgid "Reading commits..."
 msgstr "Versionen werden gelesen ..."
 
-#: gitk:499 gitk:1637 gitk:4528
+#: gitk:505 gitk:1647 gitk:4558
 msgid "No commits selected"
 msgstr "Keine Versionen ausgewählt"
 
-#: gitk:1445 gitk:4045 gitk:12432
+#: gitk:1455 gitk:4075 gitk:12752
 msgid "Command line"
 msgstr "Kommandozeile"
 
-#: gitk:1511
+#: gitk:1521
 msgid "Can't parse git log output:"
 msgstr "Ausgabe von »git log« kann nicht erkannt werden:"
 
-#: gitk:1740
+#: gitk:1750
 msgid "No commit information available"
 msgstr "Keine Versionsinformation verfügbar"
 
-#: gitk:1903 gitk:1932 gitk:4315 gitk:9669 gitk:11241 gitk:11521
+#: gitk:1913 gitk:1942 gitk:4345 gitk:9795 gitk:11394 gitk:11683
 msgid "OK"
 msgstr "Ok"
 
-#: gitk:1934 gitk:4317 gitk:9196 gitk:9275 gitk:9391 gitk:9440 gitk:9671
-#: gitk:11242 gitk:11522
+#: gitk:1944 gitk:4347 gitk:9231 gitk:9310 gitk:9440 gitk:9526 gitk:9797
+#: gitk:11395 gitk:11684
 msgid "Cancel"
 msgstr "Abbrechen"
 
-#: gitk:2069
+#: gitk:2093
 msgid "&Update"
 msgstr "&Aktualisieren"
 
-#: gitk:2070
+#: gitk:2094
 msgid "&Reload"
 msgstr "&Neu laden"
 
-#: gitk:2071
+#: gitk:2095
 msgid "Reread re&ferences"
 msgstr "&Zweige neu laden"
 
-#: gitk:2072
+#: gitk:2096
 msgid "&List references"
 msgstr "Zweige/Markierungen auf&listen"
 
-#: gitk:2074
+#: gitk:2098
 msgid "Start git &gui"
 msgstr "»git &gui« starten"
 
-#: gitk:2076
+#: gitk:2100
 msgid "&Quit"
 msgstr "&Beenden"
 
-#: gitk:2068
+#: gitk:2092
 msgid "&File"
 msgstr "&Datei"
 
-#: gitk:2080
+#: gitk:2104
 msgid "&Preferences"
 msgstr "&Einstellungen"
 
-#: gitk:2079
+#: gitk:2103
 msgid "&Edit"
 msgstr "&Bearbeiten"
 
-#: gitk:2084
+#: gitk:2108
 msgid "&New view..."
 msgstr "&Neue Ansicht ..."
 
-#: gitk:2085
+#: gitk:2109
 msgid "&Edit view..."
 msgstr "Ansicht &bearbeiten ..."
 
-#: gitk:2086
+#: gitk:2110
 msgid "&Delete view"
 msgstr "Ansicht &entfernen"
 
-#: gitk:2088 gitk:4043
+#: gitk:2112
 msgid "&All files"
 msgstr "&Alle Dateien"
 
-#: gitk:2083 gitk:4067
+#: gitk:2107
 msgid "&View"
 msgstr "&Ansicht"
 
-#: gitk:2093 gitk:2103 gitk:3012
+#: gitk:2117 gitk:2127
 msgid "&About gitk"
 msgstr "Über &gitk"
 
-#: gitk:2094 gitk:2108
+#: gitk:2118 gitk:2132
 msgid "&Key bindings"
 msgstr "&Tastenkürzel"
 
-#: gitk:2092 gitk:2107
+#: gitk:2116 gitk:2131
 msgid "&Help"
 msgstr "&Hilfe"
 
-#: gitk:2185 gitk:8652
+#: gitk:2209 gitk:8687
 msgid "SHA1 ID:"
 msgstr "SHA1 ID:"
 
-#: gitk:2229
+#: gitk:2253
 msgid "Row"
 msgstr "Zeile"
 
-#: gitk:2267
+#: gitk:2291
 msgid "Find"
 msgstr "Suche"
 
-#: gitk:2295
+#: gitk:2319
 msgid "commit"
 msgstr "Version nach"
 
-#: gitk:2299 gitk:2301 gitk:4687 gitk:4710 gitk:4734 gitk:6755 gitk:6827
-#: gitk:6912
+#: gitk:2323 gitk:2325 gitk:4717 gitk:4740 gitk:4764 gitk:6785 gitk:6857
+#: gitk:6942
 msgid "containing:"
 msgstr "Beschreibung:"
 
-#: gitk:2302 gitk:3526 gitk:3531 gitk:4763
+#: gitk:2326 gitk:3556 gitk:3561 gitk:4793
 msgid "touching paths:"
 msgstr "Dateien:"
 
-#: gitk:2303 gitk:4777
+#: gitk:2327 gitk:4807
 msgid "adding/removing string:"
 msgstr "Änderungen:"
 
-#: gitk:2304 gitk:4779
+#: gitk:2328 gitk:4809
 msgid "changing lines matching:"
 msgstr "Geänderte Zeilen entsprechen:"
 
-#: gitk:2313 gitk:2315 gitk:4766
+#: gitk:2337 gitk:2339 gitk:4796
 msgid "Exact"
 msgstr "Exakt"
 
-#: gitk:2315 gitk:4854 gitk:6723
+#: gitk:2339 gitk:4884 gitk:6753
 msgid "IgnCase"
 msgstr "Kein Groß/Klein"
 
-#: gitk:2315 gitk:4736 gitk:4852 gitk:6719
+#: gitk:2339 gitk:4766 gitk:4882 gitk:6749
 msgid "Regexp"
 msgstr "Regexp"
 
-#: gitk:2317 gitk:2318 gitk:4874 gitk:4904 gitk:4911 gitk:6848 gitk:6916
+#: gitk:2341 gitk:2342 gitk:4904 gitk:4934 gitk:4941 gitk:6878 gitk:6946
 msgid "All fields"
 msgstr "Alle Felder"
 
-#: gitk:2318 gitk:4871 gitk:4904 gitk:6786
+#: gitk:2342 gitk:4901 gitk:4934 gitk:6816
 msgid "Headline"
 msgstr "Überschrift"
 
-#: gitk:2319 gitk:4871 gitk:6786 gitk:6916 gitk:7389
+#: gitk:2343 gitk:4901 gitk:6816 gitk:6946 gitk:7419
 msgid "Comments"
 msgstr "Beschreibung"
 
-#: gitk:2319 gitk:4871 gitk:4876 gitk:4911 gitk:6786 gitk:7324 gitk:8830
-#: gitk:8845
+#: gitk:2343 gitk:4901 gitk:4906 gitk:4941 gitk:6816 gitk:7354 gitk:8865
+#: gitk:8880
 msgid "Author"
 msgstr "Autor"
 
-#: gitk:2319 gitk:4871 gitk:6786 gitk:7326
+#: gitk:2343 gitk:4901 gitk:6816 gitk:7356
 msgid "Committer"
 msgstr "Eintragender"
 
-#: gitk:2350
+#: gitk:2377
 msgid "Search"
 msgstr "Suchen"
 
-#: gitk:2358
+#: gitk:2385
 msgid "Diff"
 msgstr "Vergleich"
 
-#: gitk:2360
+#: gitk:2387
 msgid "Old version"
 msgstr "Alte Version"
 
-#: gitk:2362
+#: gitk:2389
 msgid "New version"
 msgstr "Neue Version"
 
-#: gitk:2364
+#: gitk:2392
 msgid "Lines of context"
 msgstr "Kontextzeilen"
 
-#: gitk:2374
+#: gitk:2402
 msgid "Ignore space change"
 msgstr "Leerzeichenänderungen ignorieren"
 
-#: gitk:2378 gitk:2380 gitk:7959 gitk:8206
+#: gitk:2406 gitk:2408 gitk:7989 gitk:8241
 msgid "Line diff"
 msgstr "Zeilenunterschied"
 
-#: gitk:2445
+#: gitk:2473
 msgid "Patch"
 msgstr "Patch"
 
-#: gitk:2447
+#: gitk:2475
 msgid "Tree"
 msgstr "Baum"
 
-#: gitk:2617 gitk:2637
+#: gitk:2645 gitk:2666
 msgid "Diff this -> selected"
 msgstr "Vergleich: diese -> gewählte"
 
-#: gitk:2618 gitk:2638
+#: gitk:2646 gitk:2667
 msgid "Diff selected -> this"
 msgstr "Vergleich: gewählte -> diese"
 
-#: gitk:2619 gitk:2639
+#: gitk:2647 gitk:2668
 msgid "Make patch"
 msgstr "Patch erstellen"
 
-#: gitk:2620 gitk:9254
+#: gitk:2648 gitk:9289
 msgid "Create tag"
 msgstr "Markierung erstellen"
 
-#: gitk:2621 gitk:9371
+#: gitk:2649
+msgid "Copy commit summary"
+msgstr "Übersicht der Version kopieren"
+
+#: gitk:2650 gitk:9420
 msgid "Write commit to file"
 msgstr "Version in Datei schreiben"
 
-#: gitk:2622 gitk:9428
+#: gitk:2651
 msgid "Create new branch"
 msgstr "Neuen Zweig erstellen"
 
-#: gitk:2623
+#: gitk:2652
 msgid "Cherry-pick this commit"
 msgstr "Diese Version pflücken"
 
-#: gitk:2624
+#: gitk:2653
 msgid "Reset HEAD branch to here"
 msgstr "HEAD-Zweig auf diese Version zurücksetzen"
 
-#: gitk:2625
+#: gitk:2654
 msgid "Mark this commit"
 msgstr "Lesezeichen setzen"
 
-#: gitk:2626
+#: gitk:2655
 msgid "Return to mark"
 msgstr "Zum Lesezeichen"
 
-#: gitk:2627
+#: gitk:2656
 msgid "Find descendant of this and mark"
 msgstr "Abkömmling von Lesezeichen und dieser Version finden"
 
-#: gitk:2628
+#: gitk:2657
 msgid "Compare with marked commit"
 msgstr "Mit Lesezeichen vergleichen"
 
-#: gitk:2629 gitk:2640
+#: gitk:2658 gitk:2669
 msgid "Diff this -> marked commit"
 msgstr "Vergleich: diese -> gewählte Version"
 
-#: gitk:2630 gitk:2641
+#: gitk:2659 gitk:2670
 msgid "Diff marked commit -> this"
 msgstr "Vergleich: gewählte -> diese Version"
 
-#: gitk:2631
+#: gitk:2660
 msgid "Revert this commit"
 msgstr "Version umkehren"
 
-#: gitk:2647
+#: gitk:2676
 msgid "Check out this branch"
 msgstr "Auf diesen Zweig umstellen"
 
-#: gitk:2648
+#: gitk:2677
+msgid "Rename this branch"
+msgstr "Zweig umbenennen"
+
+#: gitk:2678
 msgid "Remove this branch"
 msgstr "Zweig löschen"
 
-#: gitk:2649
+#: gitk:2679
 msgid "Copy branch name"
 msgstr "Zweigname kopieren"
 
-#: gitk:2656
+#: gitk:2686
 msgid "Highlight this too"
 msgstr "Diesen auch hervorheben"
 
-#: gitk:2657
+#: gitk:2687
 msgid "Highlight this only"
 msgstr "Nur diesen hervorheben"
 
-#: gitk:2658
+#: gitk:2688
 msgid "External diff"
 msgstr "Externes Diff-Programm"
 
-#: gitk:2659
+#: gitk:2689
 msgid "Blame parent commit"
 msgstr "Annotieren der Elternversion"
 
-#: gitk:2660
+#: gitk:2690
 msgid "Copy path"
 msgstr "Pfad kopieren"
 
-#: gitk:2667
+#: gitk:2697
 msgid "Show origin of this line"
 msgstr "Herkunft dieser Zeile anzeigen"
 
-#: gitk:2668
+#: gitk:2698
 msgid "Run git gui blame on this line"
 msgstr "Diese Zeile annotieren (»git gui blame«)"
 
-#: gitk:3014
+#: gitk:3042
+#, fuzzy
+msgid "About gitk"
+msgstr "Über &gitk"
+
+#: gitk:3044
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
@@ -370,522 +383,532 @@ msgstr ""
 "\n"
 "Gitk - eine Visualisierung der Git-Historie\n"
 "\n"
-"Copyright \\u00a9 2005-2016 Paul Mackerras\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
 "\n"
 "Benutzung und Weiterverbreitung gemäß den Bedingungen der GNU General Public "
 "License"
 
-#: gitk:3022 gitk:3089 gitk:9857
+#: gitk:3052 gitk:3119 gitk:10010
 msgid "Close"
 msgstr "Schließen"
 
-#: gitk:3043
+#: gitk:3073
 msgid "Gitk key bindings"
 msgstr "Gitk-Tastaturbelegung"
 
-#: gitk:3046
+#: gitk:3076
 msgid "Gitk key bindings:"
 msgstr "Gitk-Tastaturbelegung:"
 
-#: gitk:3048
+#: gitk:3078
 #, tcl-format
 msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tBeenden"
 
-#: gitk:3049
+#: gitk:3079
 #, tcl-format
 msgid "<%s-W>\t\tClose window"
 msgstr "<%s-F>\t\tFenster schließen"
 
-#: gitk:3050
+#: gitk:3080
 msgid "<Home>\t\tMove to first commit"
 msgstr "<Pos1>\t\tZur neuesten Version springen"
 
-#: gitk:3051
+#: gitk:3081
 msgid "<End>\t\tMove to last commit"
 msgstr "<Ende>\t\tZur ältesten Version springen"
 
-#: gitk:3052
+#: gitk:3082
 msgid "<Up>, p, k\tMove up one commit"
 msgstr "<Hoch>, p, k\tNächste neuere Version"
 
-#: gitk:3053
+#: gitk:3083
 msgid "<Down>, n, j\tMove down one commit"
 msgstr "<Runter>, n, j\tNächste ältere Version"
 
-#: gitk:3054
+#: gitk:3084
 msgid "<Left>, z, h\tGo back in history list"
 msgstr "<Links>, z, h\tEine Version zurückgehen"
 
-#: gitk:3055
+#: gitk:3085
 msgid "<Right>, x, l\tGo forward in history list"
 msgstr "<Rechts>, x, l\tEine Version weitergehen"
 
-#: gitk:3056
+#: gitk:3086
 #, tcl-format
 msgid "<%s-n>\tGo to n-th parent of current commit in history list"
 msgstr "<%s-n>\tZu n-ter Elternversion in Versionshistorie springen"
 
-#: gitk:3057
+#: gitk:3087
 msgid "<PageUp>\tMove up one page in commit list"
 msgstr "<BildHoch>\tEine Seite nach oben blättern"
 
-#: gitk:3058
+#: gitk:3088
 msgid "<PageDown>\tMove down one page in commit list"
 msgstr "<BildRunter>\tEine Seite nach unten blättern"
 
-#: gitk:3059
+#: gitk:3089
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
 msgstr "<%s-Pos1>\tZum oberen Ende der Versionsliste blättern"
 
-#: gitk:3060
+#: gitk:3090
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
 msgstr "<%s-Ende>\tZum unteren Ende der Versionsliste blättern"
 
-#: gitk:3061
+#: gitk:3091
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
 msgstr "<%s-Hoch>\tVersionsliste eine Zeile nach oben blättern"
 
-#: gitk:3062
+#: gitk:3092
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
 msgstr "<%s-Runter>\tVersionsliste eine Zeile nach unten blättern"
 
-#: gitk:3063
+#: gitk:3093
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
 msgstr "<%s-BildHoch>\tVersionsliste eine Seite nach oben blättern"
 
-#: gitk:3064
+#: gitk:3094
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
 msgstr "<%s-BildRunter>\tVersionsliste eine Seite nach unten blättern"
 
-#: gitk:3065
+#: gitk:3095
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
 msgstr "<Umschalt-Hoch>\tRückwärts suchen (nach oben; neuere Versionen)"
 
-#: gitk:3066
+#: gitk:3096
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
 msgstr "<Umschalt-Runter> Suchen (nach unten; ältere Versionen)"
 
-#: gitk:3067
+#: gitk:3097
 msgid "<Delete>, b\tScroll diff view up one page"
 msgstr "<Entf>, b\t\tVergleich eine Seite nach oben blättern"
 
-#: gitk:3068
+#: gitk:3098
 msgid "<Backspace>\tScroll diff view up one page"
 msgstr "<Löschtaste>\tVergleich eine Seite nach oben blättern"
 
-#: gitk:3069
+#: gitk:3099
 msgid "<Space>\t\tScroll diff view down one page"
 msgstr "<Leertaste>\tVergleich eine Seite nach unten blättern"
 
-#: gitk:3070
+#: gitk:3100
 msgid "u\t\tScroll diff view up 18 lines"
 msgstr "u\t\tVergleich um 18 Zeilen nach oben blättern"
 
-#: gitk:3071
+#: gitk:3101
 msgid "d\t\tScroll diff view down 18 lines"
 msgstr "d\t\tVergleich um 18 Zeilen nach unten blättern"
 
-#: gitk:3072
+#: gitk:3102
 #, tcl-format
 msgid "<%s-F>\t\tFind"
 msgstr "<%s-F>\t\tSuchen"
 
-#: gitk:3073
+#: gitk:3103
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
 msgstr "<%s-G>\t\tWeitersuchen"
 
-#: gitk:3074
+#: gitk:3104
 msgid "<Return>\tMove to next find hit"
 msgstr "<Eingabetaste>\tWeitersuchen"
 
-#: gitk:3075
+#: gitk:3105
 msgid "g\t\tGo to commit"
 msgstr "g\t\tZu Version springen"
 
-#: gitk:3076
+#: gitk:3106
 msgid "/\t\tFocus the search box"
 msgstr "/\t\tTastaturfokus ins Suchfeld"
 
-#: gitk:3077
+#: gitk:3107
 msgid "?\t\tMove to previous find hit"
 msgstr "?\t\tRückwärts weitersuchen"
 
-#: gitk:3078
+#: gitk:3108
 msgid "f\t\tScroll diff view to next file"
 msgstr "f\t\tVergleich zur nächsten Datei blättern"
 
-#: gitk:3079
+#: gitk:3109
 #, tcl-format
 msgid "<%s-S>\t\tSearch for next hit in diff view"
 msgstr "<%s-S>\t\tWeitersuchen im Vergleich"
 
-#: gitk:3080
+#: gitk:3110
 #, tcl-format
 msgid "<%s-R>\t\tSearch for previous hit in diff view"
 msgstr "<%s-R>\t\tRückwärts weitersuchen im Vergleich"
 
-#: gitk:3081
+#: gitk:3111
 #, tcl-format
 msgid "<%s-KP+>\tIncrease font size"
 msgstr "<%s-Nummerblock-Plus>\tSchrift vergrößern"
 
-#: gitk:3082
+#: gitk:3112
 #, tcl-format
 msgid "<%s-plus>\tIncrease font size"
 msgstr "<%s-Plus>\tSchrift vergrößern"
 
-#: gitk:3083
+#: gitk:3113
 #, tcl-format
 msgid "<%s-KP->\tDecrease font size"
 msgstr "<%s-Nummernblock-Minus> Schrift verkleinern"
 
-#: gitk:3084
+#: gitk:3114
 #, tcl-format
 msgid "<%s-minus>\tDecrease font size"
 msgstr "<%s-Minus>\tSchrift verkleinern"
 
-#: gitk:3085
+#: gitk:3115
 msgid "<F5>\t\tUpdate"
 msgstr "<F5>\t\tAktualisieren"
 
-#: gitk:3550 gitk:3559
+#: gitk:3580 gitk:3589
 #, tcl-format
 msgid "Error creating temporary directory %s:"
 msgstr "Fehler beim Erzeugen des temporären Verzeichnisses »%s«:"
 
-#: gitk:3572
+#: gitk:3602
 #, tcl-format
 msgid "Error getting \"%s\" from %s:"
 msgstr "Fehler beim Holen von »%s« von »%s«:"
 
-#: gitk:3635
+#: gitk:3665
 msgid "command failed:"
 msgstr "Kommando fehlgeschlagen:"
 
-#: gitk:3784
+#: gitk:3814
 msgid "No such commit"
 msgstr "Version nicht gefunden"
 
-#: gitk:3798
+#: gitk:3828
 msgid "git gui blame: command failed:"
 msgstr "git gui blame: Kommando fehlgeschlagen:"
 
-#: gitk:3829
+#: gitk:3859
 #, tcl-format
 msgid "Couldn't read merge head: %s"
 msgstr "Zusammenführungs-Spitze konnte nicht gelesen werden: %s"
 
-#: gitk:3837
+#: gitk:3867
 #, tcl-format
 msgid "Error reading index: %s"
 msgstr "Fehler beim Lesen der Bereitstellung (»index«): %s"
 
-#: gitk:3862
+#: gitk:3892
 #, tcl-format
 msgid "Couldn't start git blame: %s"
 msgstr "»git blame« konnte nicht gestartet werden: %s"
 
-#: gitk:3865 gitk:6754
+#: gitk:3895 gitk:6784
 msgid "Searching"
 msgstr "Suchen"
 
-#: gitk:3897
+#: gitk:3927
 #, tcl-format
 msgid "Error running git blame: %s"
 msgstr "Fehler beim Ausführen von »git blame«: %s"
 
-#: gitk:3925
+#: gitk:3955
 #, tcl-format
 msgid "That line comes from commit %s,  which is not in this view"
 msgstr ""
 "Diese Zeile stammt aus Version %s, die nicht in dieser Ansicht gezeigt wird"
 
-#: gitk:3939
+#: gitk:3969
 msgid "External diff viewer failed:"
 msgstr "Externes Diff-Programm fehlgeschlagen:"
 
-#: gitk:4070
+#: gitk:4073
+#, fuzzy
+msgid "All files"
+msgstr "&Alle Dateien"
+
+#: gitk:4097
+#, fuzzy
+msgid "View"
+msgstr "&Ansicht"
+
+#: gitk:4100
 msgid "Gitk view definition"
 msgstr "Gitk-Ansichten"
 
-#: gitk:4074
+#: gitk:4104
 msgid "Remember this view"
 msgstr "Diese Ansicht speichern"
 
-#: gitk:4075
+#: gitk:4105
 msgid "References (space separated list):"
 msgstr "Zweige/Markierungen (durch Leerzeichen getrennte Liste):"
 
-#: gitk:4076
+#: gitk:4106
 msgid "Branches & tags:"
 msgstr "Zweige/Markierungen:"
 
-#: gitk:4077
+#: gitk:4107
 msgid "All refs"
 msgstr "Alle Markierungen und Zweige"
 
-#: gitk:4078
+#: gitk:4108
 msgid "All (local) branches"
 msgstr "Alle (lokalen) Zweige"
 
-#: gitk:4079
+#: gitk:4109
 msgid "All tags"
 msgstr "Alle Markierungen"
 
-#: gitk:4080
+#: gitk:4110
 msgid "All remote-tracking branches"
 msgstr "Alle Übernahmezweige"
 
-#: gitk:4081
+#: gitk:4111
 msgid "Commit Info (regular expressions):"
 msgstr "Versionsinformationen (reguläre Ausdrücke):"
 
-#: gitk:4082
+#: gitk:4112
 msgid "Author:"
 msgstr "Autor:"
 
-#: gitk:4083
+#: gitk:4113
 msgid "Committer:"
 msgstr "Eintragender:"
 
-#: gitk:4084
+#: gitk:4114
 msgid "Commit Message:"
 msgstr "Versionsbeschreibung:"
 
-#: gitk:4085
+#: gitk:4115
 msgid "Matches all Commit Info criteria"
 msgstr "Alle Versionsinformationen-Kriterien erfüllen"
 
-#: gitk:4086
+#: gitk:4116
 msgid "Matches no Commit Info criteria"
 msgstr "keine Versionsinformationen-Kriterien erfüllen"
 
-#: gitk:4087
+#: gitk:4117
 msgid "Changes to Files:"
 msgstr "Dateien:"
 
-#: gitk:4088
+#: gitk:4118
 msgid "Fixed String"
 msgstr "Zeichenkette"
 
-#: gitk:4089
+#: gitk:4119
 msgid "Regular Expression"
 msgstr "Regulärer Ausdruck"
 
-#: gitk:4090
+#: gitk:4120
 msgid "Search string:"
 msgstr "Suchausdruck:"
 
-#: gitk:4091
+#: gitk:4121
 msgid ""
 "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
 "15:27:38\"):"
 msgstr ""
 "Datum (»2 weeks ago«, »2009-03-17 15:27:38«, »March 17, 2009 15:27:38«)"
 
-#: gitk:4092
+#: gitk:4122
 msgid "Since:"
 msgstr "Von:"
 
-#: gitk:4093
+#: gitk:4123
 msgid "Until:"
 msgstr "Bis:"
 
-#: gitk:4094
+#: gitk:4124
 msgid "Limit and/or skip a number of revisions (positive integer):"
 msgstr "Versionsanzahl begrenzen oder einige überspringen (ganzzahliger Wert):"
 
-#: gitk:4095
+#: gitk:4125
 msgid "Number to show:"
 msgstr "Anzeigen:"
 
-#: gitk:4096
+#: gitk:4126
 msgid "Number to skip:"
 msgstr "Überspringen:"
 
-#: gitk:4097
+#: gitk:4127
 msgid "Miscellaneous options:"
 msgstr "Sonstiges:"
 
-#: gitk:4098
+#: gitk:4128
 msgid "Strictly sort by date"
 msgstr "Streng nach Datum sortieren"
 
-#: gitk:4099
+#: gitk:4129
 msgid "Mark branch sides"
 msgstr "Zweig-Seiten markieren"
 
-#: gitk:4100
+#: gitk:4130
 msgid "Limit to first parent"
 msgstr "Auf erste Elternversion beschränken"
 
-#: gitk:4101
+#: gitk:4131
 msgid "Simple history"
 msgstr "Einfache Historie"
 
-#: gitk:4102
+#: gitk:4132
 msgid "Additional arguments to git log:"
 msgstr "Zusätzliche Argumente für »git log«:"
 
-#: gitk:4103
+#: gitk:4133
 msgid "Enter files and directories to include, one per line:"
 msgstr "Folgende Dateien und Verzeichnisse anzeigen (eine pro Zeile):"
 
-#: gitk:4104
+#: gitk:4134
 msgid "Command to generate more commits to include:"
 msgstr "Versionsliste durch folgendes Kommando erzeugen lassen:"
 
-#: gitk:4228
+#: gitk:4258
 msgid "Gitk: edit view"
 msgstr "Gitk: Ansicht bearbeiten"
 
-#: gitk:4236
+#: gitk:4266
 msgid "-- criteria for selecting revisions"
 msgstr "-- Auswahl der angezeigten Versionen"
 
-#: gitk:4241
+#: gitk:4271
 msgid "View Name"
 msgstr "Ansichtsname"
 
-#: gitk:4316
+#: gitk:4346
 msgid "Apply (F5)"
 msgstr "Anwenden (F5)"
 
-#: gitk:4354
+#: gitk:4384
 msgid "Error in commit selection arguments:"
 msgstr "Fehler in den ausgewählten Versionen:"
 
-#: gitk:4409 gitk:4462 gitk:4924 gitk:4938 gitk:6208 gitk:12373 gitk:12374
+#: gitk:4439 gitk:4492 gitk:4954 gitk:4968 gitk:6238 gitk:12693 gitk:12694
 msgid "None"
 msgstr "Keine"
 
-#: gitk:5021 gitk:5026
+#: gitk:5051 gitk:5056
 msgid "Descendant"
 msgstr "Abkömmling"
 
-#: gitk:5022
+#: gitk:5052
 msgid "Not descendant"
 msgstr "Kein Abkömmling"
 
-#: gitk:5029 gitk:5034
+#: gitk:5059 gitk:5064
 msgid "Ancestor"
 msgstr "Vorgänger"
 
-#: gitk:5030
+#: gitk:5060
 msgid "Not ancestor"
 msgstr "Kein Vorgänger"
 
-#: gitk:5324
+#: gitk:5354
 msgid "Local changes checked in to index but not committed"
 msgstr "Lokale Änderungen bereitgestellt, aber nicht eingetragen"
 
-#: gitk:5360
+#: gitk:5390
 msgid "Local uncommitted changes, not checked in to index"
 msgstr "Lokale Änderungen, nicht bereitgestellt"
 
-#: gitk:7134
+#: gitk:7164
 msgid "and many more"
 msgstr "und weitere"
 
-#: gitk:7137
+#: gitk:7167
 msgid "many"
 msgstr "viele"
 
-#: gitk:7328
+#: gitk:7358
 msgid "Tags:"
 msgstr "Markierungen:"
 
-#: gitk:7345 gitk:7351 gitk:8825
+#: gitk:7375 gitk:7381 gitk:8860
 msgid "Parent"
 msgstr "Eltern"
 
-#: gitk:7356
+#: gitk:7386
 msgid "Child"
 msgstr "Kind"
 
-#: gitk:7365
+#: gitk:7395
 msgid "Branch"
 msgstr "Zweig"
 
-#: gitk:7368
+#: gitk:7398
 msgid "Follows"
 msgstr "Folgt auf"
 
-#: gitk:7371
+#: gitk:7401
 msgid "Precedes"
 msgstr "Vorgänger von"
 
-#: gitk:7966
+#: gitk:7996
 #, tcl-format
 msgid "Error getting diffs: %s"
 msgstr "Fehler beim Laden des Vergleichs: %s"
 
-#: gitk:8650
+#: gitk:8685
 msgid "Goto:"
 msgstr "Gehe zu:"
 
-#: gitk:8671
+#: gitk:8706
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
 msgstr "Kurzer SHA1-Hashwert »%s« ist mehrdeutig"
 
-#: gitk:8678
+#: gitk:8713
 #, tcl-format
 msgid "Revision %s is not known"
 msgstr "Version »%s« ist unbekannt"
 
-#: gitk:8688
+#: gitk:8723
 #, tcl-format
 msgid "SHA1 id %s is not known"
 msgstr "SHA1-Hashwert »%s« ist unbekannt"
 
-#: gitk:8690
+#: gitk:8725
 #, tcl-format
 msgid "Revision %s is not in the current view"
 msgstr "Version »%s« wird in der aktuellen Ansicht nicht angezeigt"
 
-#: gitk:8832 gitk:8847
+#: gitk:8867 gitk:8882
 msgid "Date"
 msgstr "Datum"
 
-#: gitk:8835
+#: gitk:8870
 msgid "Children"
 msgstr "Kinder"
 
-#: gitk:8898
+#: gitk:8933
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Zweig »%s« hierher zurücksetzen"
 
-#: gitk:8900
+#: gitk:8935
 msgid "Detached head: can't reset"
 msgstr "Zweigspitze ist abgetrennt: Zurücksetzen nicht möglich"
 
-#: gitk:9005 gitk:9011
+#: gitk:9040 gitk:9046
 msgid "Skipping merge commit "
 msgstr "Überspringe Zusammenführungs-Version "
 
-#: gitk:9020 gitk:9025
+#: gitk:9055 gitk:9060
 msgid "Error getting patch ID for "
 msgstr "Fehler beim Holen der Patch-ID für "
 
-#: gitk:9021 gitk:9026
+#: gitk:9056 gitk:9061
 msgid " - stopping\n"
 msgstr " - Abbruch.\n"
 
-#: gitk:9031 gitk:9034 gitk:9042 gitk:9056 gitk:9065
+#: gitk:9066 gitk:9069 gitk:9077 gitk:9091 gitk:9100
 msgid "Commit "
 msgstr "Version "
 
-#: gitk:9035
+#: gitk:9070
 msgid ""
 " is the same patch as\n"
 "       "
@@ -893,7 +916,7 @@ msgstr ""
 " ist das gleiche Patch wie\n"
 "       "
 
-#: gitk:9043
+#: gitk:9078
 msgid ""
 " differs from\n"
 "       "
@@ -901,7 +924,7 @@ msgstr ""
 " ist unterschiedlich von\n"
 "       "
 
-#: gitk:9045
+#: gitk:9080
 msgid ""
 "Diff of commits:\n"
 "\n"
@@ -909,131 +932,150 @@ msgstr ""
 "Vergleich der Versionen:\n"
 "\n"
 
-#: gitk:9057 gitk:9066
+#: gitk:9092 gitk:9101
 #, tcl-format
 msgid " has %s children - stopping\n"
 msgstr " hat %s Kinder. Abbruch\n"
 
-#: gitk:9085
+#: gitk:9120
 #, tcl-format
 msgid "Error writing commit to file: %s"
 msgstr "Fehler beim Schreiben der Version in Datei: %s"
 
-#: gitk:9091
+#: gitk:9126
 #, tcl-format
 msgid "Error diffing commits: %s"
 msgstr "Fehler beim Vergleichen der Versionen: %s"
 
-#: gitk:9137
+#: gitk:9172
 msgid "Top"
 msgstr "Oben"
 
-#: gitk:9138
+#: gitk:9173
 msgid "From"
 msgstr "Von"
 
-#: gitk:9143
+#: gitk:9178
 msgid "To"
 msgstr "bis"
 
-#: gitk:9167
+#: gitk:9202
 msgid "Generate patch"
 msgstr "Patch erstellen"
 
-#: gitk:9169
+#: gitk:9204
 msgid "From:"
 msgstr "Von:"
 
-#: gitk:9178
+#: gitk:9213
 msgid "To:"
 msgstr "bis:"
 
-#: gitk:9187
+#: gitk:9222
 msgid "Reverse"
 msgstr "Umgekehrt"
 
-#: gitk:9189 gitk:9385
+#: gitk:9224 gitk:9434
 msgid "Output file:"
 msgstr "Ausgabedatei:"
 
-#: gitk:9195
+#: gitk:9230
 msgid "Generate"
 msgstr "Erzeugen"
 
-#: gitk:9233
+#: gitk:9268
 msgid "Error creating patch:"
 msgstr "Fehler beim Erzeugen des Patches:"
 
-#: gitk:9256 gitk:9373 gitk:9430
+#: gitk:9291 gitk:9422 gitk:9510
 msgid "ID:"
 msgstr "ID:"
 
-#: gitk:9265
+#: gitk:9300
 msgid "Tag name:"
 msgstr "Markierungsname:"
 
-#: gitk:9268
+#: gitk:9303
 msgid "Tag message is optional"
 msgstr "Eine Markierungsbeschreibung ist optional"
 
-#: gitk:9270
+#: gitk:9305
 msgid "Tag message:"
 msgstr "Markierungsbeschreibung:"
 
-#: gitk:9274 gitk:9439
+#: gitk:9309 gitk:9480
 msgid "Create"
 msgstr "Erstellen"
 
-#: gitk:9292
+#: gitk:9327
 msgid "No tag name specified"
 msgstr "Kein Markierungsname angegeben"
 
-#: gitk:9296
+#: gitk:9331
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "Markierung »%s« existiert bereits."
 
-#: gitk:9306
+#: gitk:9341
 msgid "Error creating tag:"
 msgstr "Fehler beim Erstellen der Markierung:"
 
-#: gitk:9382
+#: gitk:9431
 msgid "Command:"
 msgstr "Kommando:"
 
-#: gitk:9390
+#: gitk:9439
 msgid "Write"
 msgstr "Schreiben"
 
-#: gitk:9408
+#: gitk:9457
 msgid "Error writing commit:"
 msgstr "Fehler beim Schreiben der Version:"
 
-#: gitk:9435
+#: gitk:9479
+#, fuzzy
+msgid "Create branch"
+msgstr "Neuen Zweig erstellen"
+
+#: gitk:9495
+#, tcl-format
+msgid "Rename branch %s"
+msgstr "Zweig »%s« umbenennen?"
+
+#: gitk:9496
+msgid "Rename"
+msgstr "Umbenennen"
+
+#: gitk:9520
 msgid "Name:"
 msgstr "Name:"
 
-#: gitk:9458
+#: gitk:9544
 msgid "Please specify a name for the new branch"
 msgstr "Bitte geben Sie einen Namen für den neuen Zweig an."
 
-#: gitk:9463
+#: gitk:9549
 #, tcl-format
 msgid "Branch '%s' already exists. Overwrite?"
 msgstr "Zweig »%s« existiert bereits. Soll er überschrieben werden?"
 
-#: gitk:9530
+#: gitk:9593
+#, fuzzy
+msgid "Please specify a new name for the branch"
+msgstr "Bitte geben Sie einen Namen für den neuen Zweig an."
+
+#: gitk:9656
 #, tcl-format
 msgid "Commit %s is already included in branch %s -- really re-apply it?"
 msgstr ""
 "Version »%s« ist bereits im Zweig »%s« enthalten -- trotzdem erneut "
 "eintragen?"
 
-#: gitk:9535
+#: gitk:9661
 msgid "Cherry-picking"
 msgstr "Version pflücken"
 
-#: gitk:9544
+#: gitk:9670
 #, tcl-format
 msgid ""
 "Cherry-pick failed because of local changes to file '%s'.\n"
@@ -1043,7 +1085,7 @@ msgstr ""
 "vorliegen. Bitte diese Änderungen eintragen, zurücksetzen oder\n"
 "zwischenspeichern (»git stash«) und dann erneut versuchen."
 
-#: gitk:9550
+#: gitk:9676
 msgid ""
 "Cherry-pick failed because of merge conflict.\n"
 "Do you wish to run git citool to resolve it?"
@@ -1052,21 +1094,20 @@ msgstr ""
 "ist. Soll das Zusammenführungs-Werkzeug (»git citool«) aufgerufen\n"
 "werden, um diesen Konflikt aufzulösen?"
 
-#: gitk:9566 gitk:9624
+#: gitk:9692 gitk:9750
 msgid "No changes committed"
 msgstr "Keine Änderungen eingetragen"
 
-#: gitk:9593
+#: gitk:9719
 #, tcl-format
 msgid "Commit %s is not included in branch %s -- really revert it?"
-msgstr ""
-"Version »%s« ist nicht im Zweig »%s« enthalten -- trotzdem umkehren?"
+msgstr "Version »%s« ist nicht im Zweig »%s« enthalten -- trotzdem umkehren?"
 
-#: gitk:9598
+#: gitk:9724
 msgid "Reverting"
 msgstr "Umkehren"
 
-#: gitk:9606
+#: gitk:9732
 #, tcl-format
 msgid ""
 "Revert failed because of local changes to the following files:%s Please "
@@ -1076,7 +1117,7 @@ msgstr ""
 "vorliegen. Bitte diese Änderungen eintragen, zurücksetzen oder\n"
 "zwischenspeichern (»git stash«) und dann erneut versuchen."
 
-#: gitk:9610
+#: gitk:9736
 msgid ""
 "Revert failed because of merge conflict.\n"
 " Do you wish to run git citool to resolve it?"
@@ -1085,30 +1126,30 @@ msgstr ""
 "ist. Soll das Zusammenführungs-Werkzeug (»git citool«) aufgerufen\n"
 "werden, um diesen Konflikt aufzulösen?"
 
-#: gitk:9653
+#: gitk:9779
 msgid "Confirm reset"
 msgstr "Zurücksetzen bestätigen"
 
-#: gitk:9655
+#: gitk:9781
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "Zweig »%s« auf »%s« zurücksetzen?"
 
-#: gitk:9657
+#: gitk:9783
 msgid "Reset type:"
 msgstr "Art des Zurücksetzens:"
 
-#: gitk:9660
+#: gitk:9786
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Harmlos: Arbeitskopie und Bereitstellung unverändert"
 
-#: gitk:9663
+#: gitk:9789
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr ""
 "Gemischt: Arbeitskopie unverändert,\n"
 "Bereitstellung zurückgesetzt"
 
-#: gitk:9666
+#: gitk:9792
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -1116,21 +1157,26 @@ msgstr ""
 "Hart: Arbeitskopie und Bereitstellung\n"
 "(Alle lokalen Änderungen werden gelöscht)"
 
-#: gitk:9683
+#: gitk:9809
 msgid "Resetting"
 msgstr "Zurücksetzen"
 
-#: gitk:9743
+#: gitk:9882
+#, tcl-format
+msgid "A local branch named %s exists already"
+msgstr ""
+
+#: gitk:9890
 msgid "Checking out"
 msgstr "Umstellen"
 
-#: gitk:9796
+#: gitk:9949
 msgid "Cannot delete the currently checked-out branch"
 msgstr ""
 "Der Zweig, auf den die Arbeitskopie momentan umgestellt ist, kann nicht "
 "gelöscht werden."
 
-#: gitk:9802
+#: gitk:9955
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -1139,16 +1185,16 @@ msgstr ""
 "Die Versionen auf Zweig »%s« existieren auf keinem anderen Zweig.\n"
 "Zweig »%s« trotzdem löschen?"
 
-#: gitk:9833
+#: gitk:9986
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Markierungen und Zweige: %s"
 
-#: gitk:9850
+#: gitk:10003
 msgid "Filter"
 msgstr "Filtern"
 
-#: gitk:10146
+#: gitk:10299
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
@@ -1156,218 +1202,223 @@ msgstr ""
 "Fehler beim Lesen der Strukturinformationen; Zweige und Informationen zu "
 "Vorgänger/Nachfolger werden unvollständig sein."
 
-#: gitk:11123
+#: gitk:11276
 msgid "Tag"
 msgstr "Markierung"
 
-#: gitk:11127
+#: gitk:11280
 msgid "Id"
 msgstr "Id"
 
-#: gitk:11210
+#: gitk:11363
 msgid "Gitk font chooser"
 msgstr "Gitk-Schriften wählen"
 
-#: gitk:11227
+#: gitk:11380
 msgid "B"
 msgstr "F"
 
-#: gitk:11230
+#: gitk:11383
 msgid "I"
 msgstr "K"
 
-#: gitk:11348
+#: gitk:11502
 msgid "Commit list display options"
 msgstr "Anzeige der Versionsliste"
 
-#: gitk:11351
+#: gitk:11505
 msgid "Maximum graph width (lines)"
 msgstr "Maximale Graphenbreite (Zeilen)"
 
-#: gitk:11355
+#: gitk:11509
 #, no-tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Maximale Graphenbreite (% des Fensters)"
 
-#: gitk:11358
+#: gitk:11512
 msgid "Show local changes"
 msgstr "Lokale Änderungen anzeigen"
 
-#: gitk:11361
+#: gitk:11516
 msgid "Auto-select SHA1 (length)"
 msgstr "SHA1-Hashwert (Länge) automatisch auswählen"
 
-#: gitk:11365
+#: gitk:11521
 msgid "Hide remote refs"
 msgstr "Entfernte Zweige/Markierungen ausblenden"
 
-#: gitk:11369
+#: gitk:11525
+msgid "Auto-update upon change (ms)"
+msgstr "Automatisches Neuladen bei Änderung (ms)"
+
+#: gitk:11531
 msgid "Diff display options"
 msgstr "Anzeige des Vergleichs"
 
-#: gitk:11371
+#: gitk:11533
 msgid "Tab spacing"
 msgstr "Tabulatorbreite"
 
-#: gitk:11374
+#: gitk:11536
 msgid "Display nearby tags/heads"
 msgstr "Naheliegende Markierungen/Zweigspitzen anzeigen"
 
-#: gitk:11377
+#: gitk:11539
 msgid "Maximum # tags/heads to show"
 msgstr "Maximale Anzahl anzuzeigender Markierungen/Zweigspitzen"
 
-#: gitk:11380
+#: gitk:11542
 msgid "Limit diffs to listed paths"
 msgstr "Vergleich nur für angezeigte Pfade"
 
-#: gitk:11383
+#: gitk:11545
 msgid "Support per-file encodings"
 msgstr "Zeichenkodierung pro Datei ermitteln"
 
-#: gitk:11389 gitk:11536
+#: gitk:11551 gitk:11698
 msgid "External diff tool"
 msgstr "Externes Diff-Programm"
 
-#: gitk:11390
+#: gitk:11552
 msgid "Choose..."
 msgstr "Wählen ..."
 
-#: gitk:11395
+#: gitk:11557
 msgid "General options"
 msgstr "Allgemeine Optionen"
 
-#: gitk:11398
+#: gitk:11560
 msgid "Use themed widgets"
 msgstr "Aussehen der Benutzeroberfläche durch Thema bestimmen"
 
-#: gitk:11400
+#: gitk:11562
 msgid "(change requires restart)"
 msgstr "(Änderungen werden erst nach Neustart wirksam)"
 
-#: gitk:11402
+#: gitk:11564
 msgid "(currently unavailable)"
 msgstr "(Momentan nicht verfügbar)"
 
-#: gitk:11413
+#: gitk:11575
 msgid "Colors: press to choose"
 msgstr "Farben: Klicken zum Wählen"
 
-#: gitk:11416
+#: gitk:11578
 msgid "Interface"
 msgstr "Benutzeroberfläche"
 
-#: gitk:11417
+#: gitk:11579
 msgid "interface"
 msgstr "Benutzeroberfläche"
 
-#: gitk:11420
+#: gitk:11582
 msgid "Background"
 msgstr "Hintergrund"
 
-#: gitk:11421 gitk:11451
+#: gitk:11583 gitk:11613
 msgid "background"
 msgstr "Hintergrund"
 
-#: gitk:11424
+#: gitk:11586
 msgid "Foreground"
 msgstr "Vordergrund"
 
-#: gitk:11425
+#: gitk:11587
 msgid "foreground"
 msgstr "Vordergrund"
 
-#: gitk:11428
+#: gitk:11590
 msgid "Diff: old lines"
 msgstr "Vergleich: Alte Zeilen"
 
-#: gitk:11429
+#: gitk:11591
 msgid "diff old lines"
 msgstr "Vergleich - Alte Zeilen"
 
-#: gitk:11433
+#: gitk:11595
 msgid "Diff: new lines"
 msgstr "Vergleich: Neue Zeilen"
 
-#: gitk:11434
+#: gitk:11596
 msgid "diff new lines"
 msgstr "Vergleich - Neue Zeilen"
 
-#: gitk:11438
+#: gitk:11600
 msgid "Diff: hunk header"
 msgstr "Vergleich: Änderungstitel"
 
-#: gitk:11440
+#: gitk:11602
 msgid "diff hunk header"
 msgstr "Vergleich - Änderungstitel"
 
-#: gitk:11444
+#: gitk:11606
 msgid "Marked line bg"
 msgstr "Hintergrund für markierte Zeile"
 
-#: gitk:11446
+#: gitk:11608
 msgid "marked line background"
 msgstr "Hintergrund für markierte Zeile"
 
-#: gitk:11450
+#: gitk:11612
 msgid "Select bg"
 msgstr "Hintergrundfarbe auswählen"
 
-#: gitk:11459
+#: gitk:11621
 msgid "Fonts: press to choose"
 msgstr "Schriftart: Klicken zum Wählen"
 
-#: gitk:11461
+#: gitk:11623
 msgid "Main font"
 msgstr "Programmschriftart"
 
-#: gitk:11462
+#: gitk:11624
 msgid "Diff display font"
 msgstr "Schriftart für Vergleich"
 
-#: gitk:11463
+#: gitk:11625
 msgid "User interface font"
 msgstr "Beschriftungen"
 
-#: gitk:11485
+#: gitk:11647
 msgid "Gitk preferences"
 msgstr "Gitk-Einstellungen"
 
-#: gitk:11494
+#: gitk:11656
 msgid "General"
 msgstr "Allgemein"
 
-#: gitk:11495
+#: gitk:11657
 msgid "Colors"
 msgstr "Farben"
 
-#: gitk:11496
+#: gitk:11658
 msgid "Fonts"
 msgstr "Schriftarten"
 
-#: gitk:11546
+#: gitk:11708
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: Farbe wählen für %s"
 
-#: gitk:12059
+#: gitk:12224
 msgid ""
 "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
 " Gitk requires at least Tcl/Tk 8.4."
 msgstr ""
-"Entschuldigung, gitk kann nicht mit dieser Tcl/Tk Version ausgeführt werden.\n"
+"Entschuldigung, gitk kann nicht mit dieser Tcl/Tk Version ausgeführt "
+"werden.\n"
 " Gitk erfordert mindestens Tcl/Tk 8.4."
 
-#: gitk:12269
+#: gitk:12442
 msgid "Cannot find a git repository here."
 msgstr "Kein Git-Projektarchiv gefunden."
 
-#: gitk:12316
+#: gitk:12489
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
 msgstr "Mehrdeutige Angabe »%s«: Sowohl Version als auch Dateiname existiert."
 
-#: gitk:12328
+#: gitk:12501
 msgid "Bad arguments to gitk:"
 msgstr "Falsche Kommandozeilen-Parameter für gitk:"
 
-- 
2.9.3


^ permalink raw reply related

* [PATCH 4/4] ignore backup files
From: Florian Schüller @ 2017-03-08 20:52 UTC (permalink / raw)
  To: paulus, git; +Cc: Florian Schüller
In-Reply-To: <20170308205255.18976-1-florian.schueller@gmail.com>

---
 .gitignore | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.gitignore b/.gitignore
index d7ebcaf..a353270 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
 /GIT-TCLTK-VARS
 /gitk-wish
+*~
+po/gitk.pot
-- 
2.9.3


^ permalink raw reply related

* Re: [PATCH 2/2] Fix callsites of real_pathdup() that wanted it to die on error
From: Junio C Hamano @ 2017-03-08 21:16 UTC (permalink / raw)
  To: Brandon Williams
  Cc: René Scharfe, Johannes Schindelin, git, Stefan Beller,
	Jeff King
In-Reply-To: <20170308183840.GA130604@google.com>

Brandon Williams <bmwill@google.com> writes:

>> > diff --git a/abspath.c b/abspath.c
>> > index 2f0c26e0e2c..b02e068aa34 100644
>> > --- a/abspath.c
>> > +++ b/abspath.c
>> > @@ -214,12 +214,12 @@ const char *real_path_if_valid(const char *path)
>> >  	return strbuf_realpath(&realpath, path, 0);
>> >  }
>> >  
>> > -char *real_pathdup(const char *path)
>> > +char *real_pathdup(const char *path, int die_on_error)
>> 
>> Adding a gentle variant (with the current implementation) and making
>> real_pathdup() die on error would be nicer, as it doesn't require
>> callers to pass magic flag values.  Most cases use the dying variant,
>> so such a patch would have to touch less places:
>
> I agree with Junio and Rene that a gentle version would make the api
> slightly nicer (and more consistant with some of the other api's we have
> in git).
>
> This is exactly what I should have done back when I originally made the
> change.  Sorry for missing this!

While I agree that the shape of the code Rene gave us here is what
we would have liked to have in the original, it is a bit too late
for that.

As I already mentioned, as a regression fix patch, I find what Dscho
posted more sensible, because it makes it obvious that all existing
callsites were looked at while constructing the patch and more
importantly, it forces somebody to look at all the new callers of
the function that were added by the topics in flight, by changing
the func-signature and forcing compilation failure.

It may be somewhat unfortunate that that somebody needs to be me,
but that is what maintainers are for, so ... ;-)

Once the dust settles, let's do the clean-up along the lines of
Rene's patch.

Thanks.


^ permalink raw reply

* Re: BUG Report: git branch ignore --no-abbrev flag
From: Junio C Hamano @ 2017-03-08 21:59 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: git, Guillaume Wenzek
In-Reply-To: <xmqqlgsf39fg.fsf@gitster.mtv.corp.google.com>

Junio C Hamano <gitster@pobox.com> writes:

> Guillaume Wenzek <guillaume.wenzek@gmail.com> writes:
>
>> After updating to git 2.12.0 on Monday I noticed that the "git branch"
>> wasn't behaving as usual.
>
> Are you sure you are trying 2.12?  v2.12.0 and before should behave
> the same way and honor --no-abbrev as far as I know.
>
> On the other hand, 'master' has 93e8cd8b ("Merge branch
> 'kn/ref-filter-branch-list'", 2017-02-27), which seems to introduce
> the regression.
>
> Karthik?

I haven't fully checked if filter.abbrev is set correctly, but I
noticed the output format is formulated without taking the value of
filter.abbrev into account at all, so this is an attempt to fix
that omission.

I also notice that filter.abbrev is _ONLY_ used by builtin/branch.c and
the actual ref-filter code does not have to know anything about it.

We probably should eliminate filter.abbrev field from the structure
and use a regular variable in builtin/branch.c and use it to pass
the result of command line parsing from cmd_branch() down to
build_format() as an argument.  

But that is outside the scope of regression fix.


 builtin/branch.c | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/builtin/branch.c b/builtin/branch.c
index cbaa6d03c0..537c47811a 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -335,9 +335,18 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
 		    branch_get_color(BRANCH_COLOR_CURRENT));
 
 	if (filter->verbose) {
+		struct strbuf obname = STRBUF_INIT;
+
+		if (filter->abbrev < 0)
+			strbuf_addf(&obname, "%%(objectname:short)");
+		else if (!filter->abbrev)
+			strbuf_addf(&obname, "%%(objectname)");
+		else
+			strbuf_addf(&obname, " %%(objectname:short=%d) ", filter->abbrev);
+
 		strbuf_addf(&local, "%%(align:%d,left)%%(refname:lstrip=2)%%(end)", maxwidth);
 		strbuf_addf(&local, "%s", branch_get_color(BRANCH_COLOR_RESET));
-		strbuf_addf(&local, " %%(objectname:short=7) ");
+		strbuf_addf(&local, " %s ", obname.buf);
 
 		if (filter->verbose > 1)
 			strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)"
@@ -346,10 +355,12 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
 		else
 			strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)");
 
-		strbuf_addf(&remote, "%s%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s%%(if)%%(symref)%%(then) -> %%(symref:short)"
-			    "%%(else) %%(objectname:short=7) %%(contents:subject)%%(end)",
+		strbuf_addf(&remote, "%s%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s"
+			    "%%(if)%%(symref)%%(then) -> %%(symref:short)"
+			    "%%(else) %s %%(contents:subject)%%(end)",
 			    branch_get_color(BRANCH_COLOR_REMOTE), maxwidth, quote_literal_for_format(remote_prefix),
-			    branch_get_color(BRANCH_COLOR_RESET));
+			    branch_get_color(BRANCH_COLOR_RESET), obname.buf);
+		strbuf_release(&obname);
 	} else {
 		strbuf_addf(&local, "%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
 			    branch_get_color(BRANCH_COLOR_RESET));

^ permalink raw reply related

* Re: diff.ignoreSubmoudles config setting broken?
From: Junio C Hamano @ 2017-03-08 20:54 UTC (permalink / raw)
  To: Stefan Beller
  Cc: Sebastian Schuberth, Jacob Keller, Jeff King, Git Mailing List,
	Jens Lehmann
In-Reply-To: <CAGZ79kbwMhL-ZnL-iYwPH=tWa8cNQbEGOYYQBw6OzFCMhOWE-w@mail.gmail.com>

Stefan Beller <sbeller@google.com> writes:

> On Wed, Mar 8, 2017 at 7:07 AM, Sebastian Schuberth
> <sschuberth@gmail.com> wrote:
>>
>> + Jens
>>
>
> + Jacob Keller, who touched submodule diff display code last.
> (I am thinking of fd47ae6a, diff: teach diff to display submodule
> difference with an inline diff, 2016-08-31), which is first release as
> part of v2.11.0 (that would fit your observance)

Between these two:

	git -c diff.ignoresubmodules=all diff
	git diff --ignore-submodules=all

one difference is that the latter disables reading extra config from
.gitmodules (!) while the former makes the command honor it.

This comes from aee9c7d6 ("Submodules: Add the new "ignore" config
option for diff and status", 2010-08-06), which is ancient and
predates even v2.0, so if you see problems with v2.12 or v2.11 but
not with older ones, that would rule out this theory.

^ permalink raw reply

* Re: [PATCH] t2027: avoid using pipes
From: Prathamesh Chavan @ 2017-03-08 22:13 UTC (permalink / raw)
  To: Jon Loeliger; +Cc: git
In-Reply-To: <CAME+mvWVDPT+-F7Z-O=XR_EN4qeNEoQ5ksLpLVkVBb0O9LKROg@mail.gmail.com>

But when I read the function carefully, it only removes the trash files created
when test_failure is equal to zero. But as far as I know, I can see the files
being removed even when a test_failure is non-zero for some test script.

On Thu, Mar 9, 2017 at 3:08 AM, Prathamesh Chavan <pc44800@gmail.com> wrote:
> Whenever a test suite is executed, after finishing every test, after running
> all tests, the function test_done is called. You may find this function in
> test-lib.sh . This function displays the result of the test and also removes
> the trash created by running the test.
>
> On Wed, Mar 8, 2017 at 9:14 PM, Jon Loeliger <jdl@jdl.com> wrote:
>> So, like, Prathamesh Chavan said:
>>> The exit code of the upstream of a pipe is ignored thus we should avoid
>>> using it. By writing out the output of the git command to a file, we
>>> can test the exit codes of both the commands.
>>>
>>> Signed-off-by: Prathamesh <pc44800@gmail.com>
>>> ---
>>>  t/t2027-worktree-list.sh | 14 +++++++-------
>>>  1 file changed, 7 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/t/t2027-worktree-list.sh b/t/t2027-worktree-list.sh
>>> index 848da5f..daa7a04 100755
>>> --- a/t/t2027-worktree-list.sh
>>> +++ b/t/t2027-worktree-list.sh
>>> @@ -31,7 +31,7 @@ test_expect_success '"list" all worktrees from main' '
>>>       test_when_finished "rm -rf here && git worktree prune" &&
>>>       git worktree add --detach here master &&
>>>       echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short
>>> HEAD) (detached HEAD)" >>expect &&
>>> -     git worktree list | sed "s/  */ /g" >actual &&
>>> +     git worktree list >out && sed "s/  */ /g" <out >actual &&
>>>       test_cmp expect actual
>>>  '
>>
>> I confess I am not familiar with the test set up.
>> However, I'd ask the question do we care about the
>> lingering "out" and "actual" files here?  Or will
>> they silently be cleaned up along the way later?
>>
>> Thanks,
>> jdl

^ permalink raw reply

* Re: Crash on MSYS2 with GIT_WORK_TREE
From: Junio C Hamano @ 2017-03-08 22:19 UTC (permalink / raw)
  To: Brandon Williams; +Cc: Johannes Schindelin, valtron, git
In-Reply-To: <20170308184606.GB130604@google.com>

Brandon Williams <bmwill@google.com> writes:

> Of course, I usually try to clear the parts of the mail I'm not
> responding to...though there are times where I forget or am a bit lazy.
> I'll definitely work on remembering to do that for the future!

This cuts both ways.  Sometimes it is very useful to be able to see
other parts that the responder is not _directly_ responding to when
you come as a third person to the discussion, which forces you to
find messages upthread, so do not overdo it.


^ permalink raw reply

* Re: [PATCH] t2027: avoid using pipes
From: Prathamesh Chavan @ 2017-03-08 21:38 UTC (permalink / raw)
  To: Jon Loeliger; +Cc: git
In-Reply-To: <E1cldl4-0006L6-CU@mylo.jdl.com>

Whenever a test suite is executed, after finishing every test, after running
all tests, the function test_done is called. You may find this function in
test-lib.sh . This function displays the result of the test and also removes
the trash created by running the test.

On Wed, Mar 8, 2017 at 9:14 PM, Jon Loeliger <jdl@jdl.com> wrote:
> So, like, Prathamesh Chavan said:
>> The exit code of the upstream of a pipe is ignored thus we should avoid
>> using it. By writing out the output of the git command to a file, we
>> can test the exit codes of both the commands.
>>
>> Signed-off-by: Prathamesh <pc44800@gmail.com>
>> ---
>>  t/t2027-worktree-list.sh | 14 +++++++-------
>>  1 file changed, 7 insertions(+), 7 deletions(-)
>>
>> diff --git a/t/t2027-worktree-list.sh b/t/t2027-worktree-list.sh
>> index 848da5f..daa7a04 100755
>> --- a/t/t2027-worktree-list.sh
>> +++ b/t/t2027-worktree-list.sh
>> @@ -31,7 +31,7 @@ test_expect_success '"list" all worktrees from main' '
>>       test_when_finished "rm -rf here && git worktree prune" &&
>>       git worktree add --detach here master &&
>>       echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short
>> HEAD) (detached HEAD)" >>expect &&
>> -     git worktree list | sed "s/  */ /g" >actual &&
>> +     git worktree list >out && sed "s/  */ /g" <out >actual &&
>>       test_cmp expect actual
>>  '
>
> I confess I am not familiar with the test set up.
> However, I'd ask the question do we care about the
> lingering "out" and "actual" files here?  Or will
> they silently be cleaned up along the way later?
>
> Thanks,
> jdl

^ permalink raw reply

* Re: diff.ignoreSubmoudles config setting broken?
From: Jacob Keller @ 2017-03-08 22:27 UTC (permalink / raw)
  To: Stefan Beller
  Cc: Sebastian Schuberth, Jeff King, Git Mailing List, Jens Lehmann
In-Reply-To: <CAGZ79kbwMhL-ZnL-iYwPH=tWa8cNQbEGOYYQBw6OzFCMhOWE-w@mail.gmail.com>

On Wed, Mar 8, 2017 at 11:04 AM, Stefan Beller <sbeller@google.com> wrote:
> On Wed, Mar 8, 2017 at 7:07 AM, Sebastian Schuberth
> <sschuberth@gmail.com> wrote:
>>
>> + Jens
>>
>
> + Jacob Keller, who touched submodule diff display code last.
> (I am thinking of fd47ae6a, diff: teach diff to display submodule
> difference with an inline diff, 2016-08-31), which is first release as
> part of v2.11.0 (that would fit your observance)

I don't remember doing anything with ignoreSubmodules, but it's
possible I broke it...

-Jake

^ permalink raw reply

* Re: [PATCHv3] rev-parse: add --show-superproject-working-tree
From: Junio C Hamano @ 2017-03-08 22:28 UTC (permalink / raw)
  To: Stefan Beller; +Cc: szeder.dev, email, git, sandals, ville.skytta
In-Reply-To: <20170308192037.21847-1-sbeller@google.com>

Stefan Beller <sbeller@google.com> writes:

> +--show-superproject-working-tree
> +	Show the absolute path of the top-level directory of
> +	the superproject. A superproject is a repository that records
> +	this repository as a submodule.

The top-level directory of the superproject's working tree?

Describe error conditions, like "you get an error if there is no
such project"?

> diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
> index e08677e559..2549643267 100644
> --- a/builtin/rev-parse.c
> +++ b/builtin/rev-parse.c
> @@ -12,6 +12,7 @@
>  #include "diff.h"
>  #include "revision.h"
>  #include "split-index.h"
> +#include "submodule.h"
>  
>  #define DO_REVS		1
>  #define DO_NOREV	2
> @@ -779,6 +780,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
>  					puts(work_tree);
>  				continue;
>  			}
> +			if (!strcmp(arg, "--show-superproject-working-tree")) {
> +				const char *superproject = get_superproject_working_tree();
> +				if (superproject)
> +					puts(superproject);
> +				continue;

When is superproject NULL?  Shouldn't we die with "there is no
superproject that uses us as a submodule"?

I can accept "it is not an error to ask for the root of the
superproject's working tree when we do not have any---that is one
way to ask if we have a superproject or not".  But if that is the
case, the code can stay the same, but the documentation should say
so, something like...

	Show the absolute path of the root of the superproject's
	working tree (if exists) that uses the current repository as
	its submodule.  Outputs nothing if the current repository is
	not used as a submodule by any project.


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox