Git development
 help / color / mirror / Atom feed
* [PATCH 0/14] resumable network bundles
From: Jeff King @ 2011-11-10  7:43 UTC (permalink / raw)
  To: git

One possible option for resumable clones that has been discussed is
letting the server point the client by http to a static bundle
containing most of history, followed by a fetch from the actual git repo
(which should be much cheaper now that we have all of the bundled
history).  This series implements "step 0" of this plan: just letting
bundles be fetched across the network in the first place.

Shawn raised some issues about using bundles for this (as opposed to
accessing the packfiles themselves); specifically, this raises the I/O
footprint of a repository that has to serve both the bundled version of
the pack and the regular packfile.

So it may be that we don't follow this plan all the way through.
However, even if we don't, fetching bundles over http is still a useful
thing to be able to do. Which makes this first step worth doing either
way.

  [01/14]: t/lib-httpd: check for NO_CURL
  [02/14]: http: turn off curl signals
  [03/14]: http: refactor http_request function
  [04/14]: http: add a public function for arbitrary-callback request
  [05/14]: remote-curl: use http callback for requesting refs
  [06/14]: transport: factor out bundle to ref list conversion
  [07/14]: bundle: add is_bundle_buf helper
  [08/14]: remote-curl: free "discovery" object
  [09/14]: remote-curl: auto-detect bundles when fetching refs
  [10/14]: remote-curl: try base $URL after $URL/info/refs
  [11/14]: progress: allow pure-throughput progress meters
  [12/14]: remote-curl: show progress for bundle downloads
  [13/14]: remote-curl: resume interrupted bundle transfers
  [14/14]: clone: give advice on how to resume a failed clone

-Peff

^ permalink raw reply

* [PATCH 6/6] Build in git-repack
From: Nguyễn Thái Ngọc Duy @ 2011-11-10  7:12 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <1320909155-4575-1-git-send-email-pclouds@gmail.com>

pack-objects learns a few more options to take over what's been done
by git-repack.sh. cmd_repack() becomes a wrapper around
cmd_pack_objects().

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Makefile                       |    2 +-
 builtin.h                      |    1 +
 builtin/pack-objects.c         |  297 +++++++++++++++++++++++++++++++++++++++-
 contrib/examples/git-repack.sh |  186 +++++++++++++++++++++++++
 git-repack.sh                  |  186 -------------------------
 git.c                          |    1 +
 6 files changed, 483 insertions(+), 190 deletions(-)
 create mode 100755 contrib/examples/git-repack.sh
 delete mode 100755 git-repack.sh

diff --git a/Makefile b/Makefile
index 17404c4..164052d 100644
--- a/Makefile
+++ b/Makefile
@@ -374,7 +374,6 @@ SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-pull.sh
 SCRIPT_SH += git-quiltimport.sh
 SCRIPT_SH += git-rebase.sh
-SCRIPT_SH += git-repack.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-stash.sh
 SCRIPT_SH += git-submodule.sh
@@ -460,6 +459,7 @@ BUILT_INS += git-init$X
 BUILT_INS += git-merge-subtree$X
 BUILT_INS += git-peek-remote$X
 BUILT_INS += git-repo-config$X
+BUILT_INS += git-repack$X
 BUILT_INS += git-show$X
 BUILT_INS += git-stage$X
 BUILT_INS += git-status$X
diff --git a/builtin.h b/builtin.h
index 0a5b511..2a890ce 100644
--- a/builtin.h
+++ b/builtin.h
@@ -115,6 +115,7 @@ extern int cmd_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_remote_ext(int argc, const char **argv, const char *prefix);
 extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);
+extern int cmd_repack(int argc, const char **argv, const char *prefix);
 extern int cmd_repo_config(int argc, const char **argv, const char *prefix);
 extern int cmd_rerere(int argc, const char **argv, const char *prefix);
 extern int cmd_reset(int argc, const char **argv, const char *prefix);
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index c1ca748..7acec4c 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -17,10 +17,17 @@
 #include "progress.h"
 #include "refs.h"
 #include "thread-utils.h"
+#include "sigchain.h"
 
 static const char *pack_usage[] = {
 	"git pack-objects --stdout [options...] [< ref-list | < object-list]",
 	"git pack-objects [options...] base-name [< ref-list | < object-list]",
+	"git pack-objects --repack [options...]",
+	NULL
+};
+
+static char const * const repack_usage[] = {
+	"git repack [options]",
 	NULL
 };
 
@@ -101,6 +108,15 @@ static struct object_entry *locate_object_entry(const unsigned char *sha1);
 static uint32_t written, written_delta;
 static uint32_t reused, reused_delta;
 
+#define REPACK_IN_PROGRESS		(1 << 0)
+#define REPACK_UPDATE_INFO		(1 << 1)
+#define REPACK_ALL_INTO_ONE		(1 << 2)
+#define REPACK_REMOVE_REDUNDANT		(1 << 3)
+
+static int repack_flags, nr_written_packs;
+static int repack_usedeltabaseoffset;
+static struct string_list written_packs;
+static struct string_list backup_files;
 
 static void *get_delta(struct object_entry *entry)
 {
@@ -561,6 +577,19 @@ static struct object_entry **compute_write_order(void)
 	return wo;
 }
 
+static void backup_file(const char *path)
+{
+	struct stat st;
+	if (repack_flags & REPACK_IN_PROGRESS &&
+	    !stat(path, &st)) {
+		struct strbuf old = STRBUF_INIT;
+		strbuf_addf(&old, "%s.old", path);
+		if (rename(path, old.buf))
+			die_errno("unable to rename pack %s", path);
+		string_list_append(&backup_files, strbuf_detach(&old, NULL));
+	}
+}
+
 static void write_pack_file(void)
 {
 	uint32_t i = 0, j;
@@ -632,6 +661,7 @@ static void write_pack_file(void)
 			free_pack_by_name(tmpname);
 			if (adjust_shared_perm(pack_tmp_name))
 				die_errno("unable to make temporary pack file readable");
+			backup_file(tmpname);
 			if (rename(pack_tmp_name, tmpname))
 				die_errno("unable to rename temporary pack file");
 
@@ -660,12 +690,22 @@ static void write_pack_file(void)
 				 base_name, sha1_to_hex(sha1));
 			if (adjust_shared_perm(idx_tmp_name))
 				die_errno("unable to make temporary index file readable");
+			backup_file(tmpname);
 			if (rename(idx_tmp_name, tmpname))
 				die_errno("unable to rename temporary index file");
 
 			free((void *) idx_tmp_name);
 			free(pack_tmp_name);
-			puts(sha1_to_hex(sha1));
+			if (repack_flags & REPACK_IN_PROGRESS) {
+				int len = strlen(tmpname);
+				char *s = xmalloc(len + 2);
+				memcpy(s, tmpname, len - 4);
+				memcpy(s + len - 4, ".pack", 6);
+				string_list_append(&written_packs, s);
+				nr_written_packs++;
+			}
+			else
+				puts(sha1_to_hex(sha1));
 		}
 
 		/* mark written objects as written to previous pack */
@@ -2222,7 +2262,8 @@ static void get_object_list(int ac, const char **av)
 	save_commit_buffer = 0;
 	setup_revisions(ac, av, &revs, NULL);
 
-	while (fgets(line, sizeof(line), stdin) != NULL) {
+	while (!(repack_flags & REPACK_IN_PROGRESS) &&
+	       fgets(line, sizeof(line), stdin) != NULL) {
 		int len = strlen(line);
 		if (len && line[len - 1] == '\n')
 			line[--len] = 0;
@@ -2250,6 +2291,31 @@ static void get_object_list(int ac, const char **av)
 		loosen_unused_packed_objects(&revs);
 }
 
+static void rollback_repack(void)
+{
+	struct strbuf dst = STRBUF_INIT;
+	int i, ret;
+	for (i = 0; i < backup_files.nr; i++) {
+		const char *src = backup_files.items[i].string;
+		strbuf_addstr(&dst, src);
+		strbuf_setlen(&dst, dst.len - 4); /* remove .old */
+		ret = rename(src, dst.buf);
+		if (ret)
+			warning("failed to restore %s: %s", src, strerror(errno));
+		strbuf_setlen(&dst, 0);
+	}
+	strbuf_release(&dst);
+	string_list_clear(&backup_files, 0);
+}
+
+static void rollback_repack_on_signal(int signo)
+{
+	rollback_repack();
+	sigchain_pop(signo);
+	raise(signo);
+}
+
+
 static int option_parse_index_version(const struct option *opt,
 				      const char *arg, int unset)
 {
@@ -2267,12 +2333,14 @@ static int option_parse_index_version(const struct option *opt,
 
 int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 {
+	struct strbuf repack_base_name = STRBUF_INIT;
 	int use_internal_rev_list = 0;
 	int thin = 0;
 	int all_progress_implied = 0;
 	const char **rp_av;
 	int rp_ac_alloc = 64;
 	int rp_ac;
+	int i;
 	struct option pack_objects_options[] = {
 		OPT_SET_INT('q', NULL, &progress,
 			    "do not show progress meter", 0),
@@ -2328,6 +2396,16 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 			    "pack compression level"),
 		OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents,
 			    "do not hide commits by grafts", 0),
+
+		OPT_BIT(0, "repack", &repack_flags,
+			"repack mode", REPACK_IN_PROGRESS),
+		OPT_BIT(0, "repack-all", &repack_flags,
+			"repack everything into one pack", REPACK_ALL_INTO_ONE),
+		OPT_BIT(0, "remove-redundant", &repack_flags,
+			"remove redundant objects after repack", REPACK_REMOVE_REDUNDANT),
+		OPT_BIT(0, "update-info", &repack_flags,
+			"run git-update-server-info after repack", REPACK_UPDATE_INFO),
+
 		OPT_END(),
 	};
 
@@ -2374,6 +2452,39 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 	if (delta_search_threads != 1)
 		warning("no threads support, ignoring %s", arg);
 #endif
+	if ((repack_flags & REPACK_IN_PROGRESS) == 0 &&
+	    (repack_flags & ~REPACK_IN_PROGRESS))
+		die("--repack must be given for any repack related options");
+	if (repack_flags & REPACK_IN_PROGRESS) {
+		if (pack_to_stdout)
+			die("--stdout cannot be used with --repack");
+		if (argc)
+			die("base name cannot be used with --repack");
+
+		if (rp_ac + 2 >= rp_ac_alloc) {
+			rp_ac_alloc = alloc_nr(rp_ac_alloc);
+			rp_av = xrealloc(rp_av, rp_ac_alloc * sizeof(*rp_av));
+		}
+		rp_av[rp_ac++] = "--all";
+		rp_av[rp_ac++] = "--reflog";
+		use_internal_rev_list = 1;
+
+		grafts_replace_parents = 0; /* --keep-true-parents */
+		ignore_packed_keep = 1;	    /* --honor-pack-keep */
+		non_empty = 1;		    /* --non-empty */
+
+		if (!(repack_flags & REPACK_ALL_INTO_ONE)) {
+			incremental = 1; /* --incremental */
+			rp_av[rp_ac++] = "--unpacked";
+		}
+
+		strbuf_addf(&repack_base_name,
+			    "%s/pack/pack", get_object_directory());
+		base_name = repack_base_name.buf;
+
+		sigchain_push_common(rollback_repack_on_signal);
+		atexit(rollback_repack);
+	}
 
 	/* Traditionally "pack-objects [options] base extra" failed;
 	 * we would however want to take refs parameter that would
@@ -2388,7 +2499,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 	 * walker.
 	 */
 
-	if (!pack_to_stdout) {
+	else if (!pack_to_stdout) {
 		if (!argc)
 			die("base name required if --stdout is not given");
 		base_name = argv[0];
@@ -2439,5 +2550,185 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 		fprintf(stderr, "Total %"PRIu32" (delta %"PRIu32"),"
 			" reused %"PRIu32" (delta %"PRIu32")\n",
 			written, written_delta, reused, reused_delta);
+
+	if (!(repack_flags & REPACK_IN_PROGRESS))
+		return 0;
+
+	if (!nr_written_packs) {
+		printf(_("Nothing new to pack.\n"));
+		return 0;
+	}
+
+	/* At this point all new packs should be in place. We can
+	   safely remove old ones */
+	for (i = 0; i < backup_files.nr; i++) {
+		const char *s = backup_files.items[i].string;
+		int ret = unlink(s);
+		if (ret)
+			warning("failed to remove %s: %s", s, strerror(errno));
+	}
+	string_list_clear(&backup_files, 0);
+
+	if (repack_flags & REPACK_REMOVE_REDUNDANT) {
+		struct packed_git *p;
+		struct string_list to_be_removed = STRING_LIST_INIT_DUP;
+
+		/* free_pack_by_name() may have freed a few packs in
+		   write_pack_file() */
+		reprepare_packed_git();
+		for (p = packed_git; p; p = p->next) {
+			if (!p->pack_local || p->pack_keep)
+				continue;
+
+			for (i = 0; i < written_packs.nr; i++) {
+				char *s = written_packs.items[i].string;
+				if (!strcmp(s, p->pack_name))
+					break;
+			}
+			if (i < written_packs.nr)
+				continue;
+
+			string_list_append(&to_be_removed, p->pack_name);
+		}
+		written_packs.strdup_strings = 1;
+		string_list_clear(&written_packs, 0);
+
+		for (i = 0; i < to_be_removed.nr; i++) {
+			char *path = to_be_removed.items[i].string;
+
+			/* Windows limitation on unlink().
+			   See c74faea19e39ca933492f697596310397175c329 */
+			free_pack_by_name(path);
+
+			if (unlink(path))
+				warning("failed to remove %s: %s", path, strerror(errno));
+			strcpy(path + strlen(path)-5, ".idx");
+			if (unlink(path))
+				warning("failed to remove %s: %s", path, strerror(errno));
+		}
+		string_list_clear(&to_be_removed, 0);
+
+		reprepare_packed_git();
+		prune_packed_objects(0);
+	}
+
+	if (repack_flags & REPACK_UPDATE_INFO)
+		update_server_info(0);
+
 	return 0;
 }
+
+static int repack_config(const char *k, const char *v, void *cb)
+{
+	if (!strcasecmp(k, "repack.usedeltabaseoffset")) {
+		repack_usedeltabaseoffset = git_config_bool(k, v);
+		return 0;
+	}
+	return git_default_config(k, v, cb);
+}
+
+int cmd_repack(int argc, const char **argv, const char *prefix)
+{
+	int all_in_one = 0;
+	int all_in_one_and_unreachable = 0;
+	int unpack_unreachable = 0;
+	int remove_redundant = 0;
+	int no_reuse_delta = 0;
+	int no_reuse_object = 0;
+	int no_update = 0;
+	int quiet = 0;
+	int local = 0;
+
+	struct option opts[] = {
+		OPT_BOOL('a', NULL, &all_in_one,
+			 "pack everything in a single pack"),
+		OPT_BOOL('A', NULL, &all_in_one_and_unreachable,
+			 "same as -a, and turn unreachable objects loose"),
+		OPT_BOOL('d', NULL, &remove_redundant,
+			 "remove redundant packs, and run git-prune-packed"),
+		OPT_BOOL('f', NULL, &no_reuse_delta,
+			 "pass --no-reuse-delta to git-pack-objects"),
+		OPT_BOOL('F', NULL, &no_reuse_object,
+			 "pass --no-reuse-object to git-pack-objects"),
+		OPT_BOOL('n', NULL, &no_update,
+			 "do not run git-update-server-info"),
+		OPT_BOOL('q', NULL, &quiet, "be quiet"),
+		OPT_BOOL('l', NULL, &local,
+			 "pass --local to git-pack-objects"),
+		{ OPTION_ARGUMENT, 0, "window", NULL, "n",
+		 "size of the window used for delta compression", 0 },
+		{ OPTION_ARGUMENT, 0, "window-memory", NULL, "n",
+		  "same as the above, but limit memory size instead of entries count", 0 },
+		{ OPTION_ARGUMENT, 0, "depth", NULL, "n",
+		  "limits the maximum delta depth", 0 },
+		{ OPTION_ARGUMENT, 0, "max-pack-size", NULL, "n",
+		  "maximum size of each packfile", 0},
+		OPT_END(),
+	};
+
+	const char *av[] = { "pack-objects", "--repack",
+			     NULL, /* --[no-]update-info */
+			     NULL, /* --delta-base-offset */
+			     NULL, /* --repack-all */
+			     NULL, /* --remove-redundant */
+			     NULL, /* --no-reuse-delta */
+			     NULL, /* --no-reuse-object */
+			     NULL, /* --local */
+			     NULL, /* -q */
+			     NULL, /* --unpack-unreachable */
+			     NULL, /* --window */
+			     NULL, /* --window-memory */
+			     NULL, /* --depth */
+			     NULL, /* --max-pack-size */
+			     NULL
+	};
+	int ac = 2;
+
+	git_config(repack_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, opts, repack_usage, 0);
+
+	if (no_update)
+		av[ac++] = "--no-update-info";
+	else
+		av[ac++] = "--update-info";
+	if (repack_usedeltabaseoffset)
+		av[ac++] = "--delta-base-offset";
+	if (all_in_one_and_unreachable) {
+		av[ac++] = "--repack-all";
+		all_in_one = 1;
+		unpack_unreachable = 1;
+	}
+	if (all_in_one)
+		av[ac++] = "--repack-all";
+	if (remove_redundant)
+		av[ac++] = "--remove-redundant";
+	if (no_reuse_delta)
+		av[ac++] = "--no-reuse-delta";
+	if (no_reuse_object)
+		av[ac++] = "--no-reuse-object";
+	if (local)
+		av[ac++] = "--local";
+	if (quiet)
+		av[ac++] = "-q";
+	if ((ac + argc) * sizeof(*av) > sizeof(av))
+		die("Too many options");
+	memcpy(av + ac, argv, argc * sizeof(*argv));
+	ac += argc;
+
+	if (all_in_one && remove_redundant) {
+		struct packed_git *p;
+
+		prepare_packed_git();
+		for (p = packed_git; p; p = p->next) {
+			if (!p->pack_keep &&
+			    unpack_unreachable && remove_redundant) {
+				av[ac++] = "--unpack-unreachable";
+				break;
+			}
+		}
+	}
+
+	trace_argv_printf(av, "trace: built-in: git");
+	return cmd_pack_objects(ac, av, prefix);
+}
diff --git a/contrib/examples/git-repack.sh b/contrib/examples/git-repack.sh
new file mode 100755
index 0000000..624feec
--- /dev/null
+++ b/contrib/examples/git-repack.sh
@@ -0,0 +1,186 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Linus Torvalds
+#
+
+OPTIONS_KEEPDASHDASH=
+OPTIONS_SPEC="\
+git repack [options]
+--
+a               pack everything in a single pack
+A               same as -a, and turn unreachable objects loose
+d               remove redundant packs, and run git-prune-packed
+f               pass --no-reuse-delta to git-pack-objects
+F               pass --no-reuse-object to git-pack-objects
+n               do not run git-update-server-info
+q,quiet         be quiet
+l               pass --local to git-pack-objects
+ Packing constraints
+window=         size of the window used for delta compression
+window-memory=  same as the above, but limit memory size instead of entries count
+depth=          limits the maximum delta depth
+max-pack-size=  maximum size of each packfile
+"
+SUBDIRECTORY_OK='Yes'
+. git-sh-setup
+
+no_update_info= all_into_one= remove_redundant= unpack_unreachable=
+local= no_reuse= extra=
+while test $# != 0
+do
+	case "$1" in
+	-n)	no_update_info=t ;;
+	-a)	all_into_one=t ;;
+	-A)	all_into_one=t
+		unpack_unreachable=--unpack-unreachable ;;
+	-d)	remove_redundant=t ;;
+	-q)	GIT_QUIET=t ;;
+	-f)	no_reuse=--no-reuse-delta ;;
+	-F)	no_reuse=--no-reuse-object ;;
+	-l)	local=--local ;;
+	--max-pack-size|--window|--window-memory|--depth)
+		extra="$extra $1=$2"; shift ;;
+	--) shift; break;;
+	*)	usage ;;
+	esac
+	shift
+done
+
+case "`git config --bool repack.usedeltabaseoffset || echo true`" in
+true)
+	extra="$extra --delta-base-offset" ;;
+esac
+
+PACKDIR="$GIT_OBJECT_DIRECTORY/pack"
+PACKTMP="$PACKDIR/.tmp-$$-pack"
+rm -f "$PACKTMP"-*
+trap 'rm -f "$PACKTMP"-*' 0 1 2 3 15
+
+# There will be more repacking strategies to come...
+case ",$all_into_one," in
+,,)
+	args='--unpacked --incremental'
+	;;
+,t,)
+	args= existing=
+	if [ -d "$PACKDIR" ]; then
+		for e in `cd "$PACKDIR" && find . -type f -name '*.pack' \
+			| sed -e 's/^\.\///' -e 's/\.pack$//'`
+		do
+			if [ -e "$PACKDIR/$e.keep" ]; then
+				: keep
+			else
+				existing="$existing $e"
+			fi
+		done
+		if test -n "$existing" -a -n "$unpack_unreachable" -a \
+			-n "$remove_redundant"
+		then
+			args="$args $unpack_unreachable"
+		fi
+	fi
+	;;
+esac
+
+mkdir -p "$PACKDIR" || exit
+
+args="$args $local ${GIT_QUIET:+-q} $no_reuse$extra"
+names=$(git pack-objects --keep-true-parents --honor-pack-keep --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
+	exit 1
+if [ -z "$names" ]; then
+	say Nothing new to pack.
+fi
+
+# Ok we have prepared all new packfiles.
+
+# First see if there are packs of the same name and if so
+# if we can move them out of the way (this can happen if we
+# repacked immediately after packing fully.
+rollback=
+failed=
+for name in $names
+do
+	for sfx in pack idx
+	do
+		file=pack-$name.$sfx
+		test -f "$PACKDIR/$file" || continue
+		rm -f "$PACKDIR/old-$file" &&
+		mv "$PACKDIR/$file" "$PACKDIR/old-$file" || {
+			failed=t
+			break
+		}
+		rollback="$rollback $file"
+	done
+	test -z "$failed" || break
+done
+
+# If renaming failed for any of them, roll the ones we have
+# already renamed back to their original names.
+if test -n "$failed"
+then
+	rollback_failure=
+	for file in $rollback
+	do
+		mv "$PACKDIR/old-$file" "$PACKDIR/$file" ||
+		rollback_failure="$rollback_failure $file"
+	done
+	if test -n "$rollback_failure"
+	then
+		echo >&2 "WARNING: Some packs in use have been renamed by"
+		echo >&2 "WARNING: prefixing old- to their name, in order to"
+		echo >&2 "WARNING: replace them with the new version of the"
+		echo >&2 "WARNING: file.  But the operation failed, and"
+		echo >&2 "WARNING: attempt to rename them back to their"
+		echo >&2 "WARNING: original names also failed."
+		echo >&2 "WARNING: Please rename them in $PACKDIR manually:"
+		for file in $rollback_failure
+		do
+			echo >&2 "WARNING:   old-$file -> $file"
+		done
+	fi
+	exit 1
+fi
+
+# Now the ones with the same name are out of the way...
+fullbases=
+for name in $names
+do
+	fullbases="$fullbases pack-$name"
+	chmod a-w "$PACKTMP-$name.pack"
+	chmod a-w "$PACKTMP-$name.idx"
+	mv -f "$PACKTMP-$name.pack" "$PACKDIR/pack-$name.pack" &&
+	mv -f "$PACKTMP-$name.idx"  "$PACKDIR/pack-$name.idx" ||
+	exit
+done
+
+# Remove the "old-" files
+for name in $names
+do
+	rm -f "$PACKDIR/old-pack-$name.idx"
+	rm -f "$PACKDIR/old-pack-$name.pack"
+done
+
+# End of pack replacement.
+
+if test "$remove_redundant" = t
+then
+	# We know $existing are all redundant.
+	if [ -n "$existing" ]
+	then
+		( cd "$PACKDIR" &&
+		  for e in $existing
+		  do
+			case " $fullbases " in
+			*" $e "*) ;;
+			*)	rm -f "$e.pack" "$e.idx" "$e.keep" ;;
+			esac
+		  done
+		)
+	fi
+	git prune-packed ${GIT_QUIET:+-q}
+fi
+
+case "$no_update_info" in
+t) : ;;
+*) git update-server-info ;;
+esac
diff --git a/git-repack.sh b/git-repack.sh
deleted file mode 100755
index 624feec..0000000
--- a/git-repack.sh
+++ /dev/null
@@ -1,186 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Linus Torvalds
-#
-
-OPTIONS_KEEPDASHDASH=
-OPTIONS_SPEC="\
-git repack [options]
---
-a               pack everything in a single pack
-A               same as -a, and turn unreachable objects loose
-d               remove redundant packs, and run git-prune-packed
-f               pass --no-reuse-delta to git-pack-objects
-F               pass --no-reuse-object to git-pack-objects
-n               do not run git-update-server-info
-q,quiet         be quiet
-l               pass --local to git-pack-objects
- Packing constraints
-window=         size of the window used for delta compression
-window-memory=  same as the above, but limit memory size instead of entries count
-depth=          limits the maximum delta depth
-max-pack-size=  maximum size of each packfile
-"
-SUBDIRECTORY_OK='Yes'
-. git-sh-setup
-
-no_update_info= all_into_one= remove_redundant= unpack_unreachable=
-local= no_reuse= extra=
-while test $# != 0
-do
-	case "$1" in
-	-n)	no_update_info=t ;;
-	-a)	all_into_one=t ;;
-	-A)	all_into_one=t
-		unpack_unreachable=--unpack-unreachable ;;
-	-d)	remove_redundant=t ;;
-	-q)	GIT_QUIET=t ;;
-	-f)	no_reuse=--no-reuse-delta ;;
-	-F)	no_reuse=--no-reuse-object ;;
-	-l)	local=--local ;;
-	--max-pack-size|--window|--window-memory|--depth)
-		extra="$extra $1=$2"; shift ;;
-	--) shift; break;;
-	*)	usage ;;
-	esac
-	shift
-done
-
-case "`git config --bool repack.usedeltabaseoffset || echo true`" in
-true)
-	extra="$extra --delta-base-offset" ;;
-esac
-
-PACKDIR="$GIT_OBJECT_DIRECTORY/pack"
-PACKTMP="$PACKDIR/.tmp-$$-pack"
-rm -f "$PACKTMP"-*
-trap 'rm -f "$PACKTMP"-*' 0 1 2 3 15
-
-# There will be more repacking strategies to come...
-case ",$all_into_one," in
-,,)
-	args='--unpacked --incremental'
-	;;
-,t,)
-	args= existing=
-	if [ -d "$PACKDIR" ]; then
-		for e in `cd "$PACKDIR" && find . -type f -name '*.pack' \
-			| sed -e 's/^\.\///' -e 's/\.pack$//'`
-		do
-			if [ -e "$PACKDIR/$e.keep" ]; then
-				: keep
-			else
-				existing="$existing $e"
-			fi
-		done
-		if test -n "$existing" -a -n "$unpack_unreachable" -a \
-			-n "$remove_redundant"
-		then
-			args="$args $unpack_unreachable"
-		fi
-	fi
-	;;
-esac
-
-mkdir -p "$PACKDIR" || exit
-
-args="$args $local ${GIT_QUIET:+-q} $no_reuse$extra"
-names=$(git pack-objects --keep-true-parents --honor-pack-keep --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
-	exit 1
-if [ -z "$names" ]; then
-	say Nothing new to pack.
-fi
-
-# Ok we have prepared all new packfiles.
-
-# First see if there are packs of the same name and if so
-# if we can move them out of the way (this can happen if we
-# repacked immediately after packing fully.
-rollback=
-failed=
-for name in $names
-do
-	for sfx in pack idx
-	do
-		file=pack-$name.$sfx
-		test -f "$PACKDIR/$file" || continue
-		rm -f "$PACKDIR/old-$file" &&
-		mv "$PACKDIR/$file" "$PACKDIR/old-$file" || {
-			failed=t
-			break
-		}
-		rollback="$rollback $file"
-	done
-	test -z "$failed" || break
-done
-
-# If renaming failed for any of them, roll the ones we have
-# already renamed back to their original names.
-if test -n "$failed"
-then
-	rollback_failure=
-	for file in $rollback
-	do
-		mv "$PACKDIR/old-$file" "$PACKDIR/$file" ||
-		rollback_failure="$rollback_failure $file"
-	done
-	if test -n "$rollback_failure"
-	then
-		echo >&2 "WARNING: Some packs in use have been renamed by"
-		echo >&2 "WARNING: prefixing old- to their name, in order to"
-		echo >&2 "WARNING: replace them with the new version of the"
-		echo >&2 "WARNING: file.  But the operation failed, and"
-		echo >&2 "WARNING: attempt to rename them back to their"
-		echo >&2 "WARNING: original names also failed."
-		echo >&2 "WARNING: Please rename them in $PACKDIR manually:"
-		for file in $rollback_failure
-		do
-			echo >&2 "WARNING:   old-$file -> $file"
-		done
-	fi
-	exit 1
-fi
-
-# Now the ones with the same name are out of the way...
-fullbases=
-for name in $names
-do
-	fullbases="$fullbases pack-$name"
-	chmod a-w "$PACKTMP-$name.pack"
-	chmod a-w "$PACKTMP-$name.idx"
-	mv -f "$PACKTMP-$name.pack" "$PACKDIR/pack-$name.pack" &&
-	mv -f "$PACKTMP-$name.idx"  "$PACKDIR/pack-$name.idx" ||
-	exit
-done
-
-# Remove the "old-" files
-for name in $names
-do
-	rm -f "$PACKDIR/old-pack-$name.idx"
-	rm -f "$PACKDIR/old-pack-$name.pack"
-done
-
-# End of pack replacement.
-
-if test "$remove_redundant" = t
-then
-	# We know $existing are all redundant.
-	if [ -n "$existing" ]
-	then
-		( cd "$PACKDIR" &&
-		  for e in $existing
-		  do
-			case " $fullbases " in
-			*" $e "*) ;;
-			*)	rm -f "$e.pack" "$e.idx" "$e.keep" ;;
-			esac
-		  done
-		)
-	fi
-	git prune-packed ${GIT_QUIET:+-q}
-fi
-
-case "$no_update_info" in
-t) : ;;
-*) git update-server-info ;;
-esac
diff --git a/git.c b/git.c
index 8e34903..05235ca 100644
--- a/git.c
+++ b/git.c
@@ -410,6 +410,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "remote-ext", cmd_remote_ext },
 		{ "remote-fd", cmd_remote_fd },
 		{ "replace", cmd_replace, RUN_SETUP },
+		{ "repack", cmd_repack, RUN_SETUP },
 		{ "repo-config", cmd_repo_config, RUN_SETUP_GENTLY },
 		{ "rerere", cmd_rerere, RUN_SETUP },
 		{ "reset", cmd_reset, RUN_SETUP },
-- 
1.7.4.74.g639db

^ permalink raw reply related

* [PATCH 5/6] parse-options: allow OPTION_ARGUMENT to take argument
From: Nguyễn Thái Ngọc Duy @ 2011-11-10  7:12 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <1320909155-4575-1-git-send-email-pclouds@gmail.com>


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 parse-options.c |   18 +++++++++++++++---
 1 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/parse-options.c b/parse-options.c
index 58bb83d..3a1575a 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -218,11 +218,23 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
 		if (options->type == OPTION_ARGUMENT) {
 			if (!rest)
 				continue;
-			if (*rest == '=')
-				return opterror(options, "takes no value", flags);
+			if (*rest == '=') {
+				if (options->flags & PARSE_OPT_NOARG)
+					return opterror(options, "takes no value", flags);
+				p->out[p->cpidx++] = *p->argv;
+				return 0;
+			}
+
 			if (*rest)
 				continue;
-			p->out[p->cpidx++] = arg - 2;
+			p->out[p->cpidx++] = *p->argv;
+			if (!(options->flags & PARSE_OPT_NOARG)) {
+				p->argv++;
+				p->argc--;
+				if (!*p->argv)
+					return opterror(options, "takes no value", flags);
+				p->out[p->cpidx++] = *p->argv;
+			}
 			return 0;
 		}
 		if (!rest) {
-- 
1.7.4.74.g639db

^ permalink raw reply related

* [PATCH 4/6] pack-objects: use parse_options()
From: Nguyễn Thái Ngọc Duy @ 2011-11-10  7:12 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <1320909155-4575-1-git-send-email-pclouds@gmail.com>


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 There is a side effect of this conversion: --window and --depth now
 can take units because git_parse_ulong() is always used by
 OPTION_ULONG

 builtin/pack-objects.c |  276 +++++++++++++++++++-----------------------------
 1 files changed, 110 insertions(+), 166 deletions(-)

diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 2b18de5..c1ca748 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -18,16 +18,11 @@
 #include "refs.h"
 #include "thread-utils.h"
 
-static const char pack_usage[] =
-  "git pack-objects [ -q | --progress | --all-progress ]\n"
-  "        [--all-progress-implied]\n"
-  "        [--max-pack-size=<n>] [--local] [--incremental]\n"
-  "        [--window=<n>] [--window-memory=<n>] [--depth=<n>]\n"
-  "        [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset]\n"
-  "        [--threads=<n>] [--non-empty] [--revs [--unpacked | --all]]\n"
-  "        [--reflog] [--stdout | base-name] [--include-tag]\n"
-  "        [--keep-unreachable | --unpack-unreachable]\n"
-  "        [< ref-list | < object-list]";
+static const char *pack_usage[] = {
+	"git pack-objects --stdout [options...] [< ref-list | < object-list]",
+	"git pack-objects [options...] base-name [< ref-list | < object-list]",
+	NULL
+};
 
 struct object_entry {
 	struct pack_idx_entry idx;
@@ -2255,15 +2250,86 @@ static void get_object_list(int ac, const char **av)
 		loosen_unused_packed_objects(&revs);
 }
 
+static int option_parse_index_version(const struct option *opt,
+				      const char *arg, int unset)
+{
+	char *c;
+	const char *val = arg;
+	pack_idx_opts.version = strtoul(val, &c, 10);
+	if (pack_idx_opts.version > 2)
+		die("bad %s", val);
+	if (*c == ',')
+		pack_idx_opts.off32_limit = strtoul(c+1, &c, 0);
+	if (*c || pack_idx_opts.off32_limit & 0x80000000)
+		die("bad %s", val);
+	return 0;
+}
+
 int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 {
 	int use_internal_rev_list = 0;
 	int thin = 0;
 	int all_progress_implied = 0;
-	uint32_t i;
 	const char **rp_av;
 	int rp_ac_alloc = 64;
 	int rp_ac;
+	struct option pack_objects_options[] = {
+		OPT_SET_INT('q', NULL, &progress,
+			    "do not show progress meter", 0),
+		OPT_SET_INT(0, "progress", &progress,
+			    "show progress meter", 1),
+		OPT_SET_INT(0, "all-progress", &progress,
+			    "show progress meter during object writing phase", 2),
+		OPT_BOOL(0, "all-progress-implied",
+			 &all_progress_implied,
+			 "similar to --all-progress when progress meter is shown"),
+		{ OPTION_CALLBACK, 0, "index-version", NULL, "version",
+		  "force generating pack index at a particular version",
+		  0, option_parse_index_version },
+		OPT_ULONG(0, "max-pack-size", &pack_size_limit,
+			  "maximum size of each output pack file"),
+		OPT_BOOL(0, "local", &local,
+			 "ignore borrowed objects from alternate object store"),
+		OPT_BOOL(0, "incremental", &incremental,
+			 "ignore packed objects"),
+		OPT_ULONG(0, "window", &window, "limit pack window by objects"),
+		OPT_ULONG(0, "window-memory", &window_memory_limit,
+			  "limit pack window by memory"),
+		OPT_INTEGER(0, "depth", &depth,
+			    "limit pack window by maximum delta depth"),
+		OPT_BOOL(0, "reuse-delta", &reuse_delta,
+			 "reusing existing deltas"),
+		OPT_BOOL(0, "reuse-object", &reuse_object,
+			 "reusing existing objects"),
+		OPT_BOOL(0, "delta-base-offset", &allow_ofs_delta,
+			 "use OFS_DELTA objects"),
+		OPT_INTEGER(0, "threads", &delta_search_threads,
+			    "use threads when searching for best delta matches"),
+		OPT_BOOL(0, "non-empty", &non_empty,
+			 "only create if it would contain at least one object"),
+		OPT_BOOL(0, "revs", &use_internal_rev_list,
+			 "read revision arguments from standard output"),
+		OPT_ARGUMENT("unpacked", "limit the objects to those that are not already packed"),
+		OPT_ARGUMENT("all", "include all refs under $GIT_DIR/refs directory"),
+		OPT_ARGUMENT("reflog", "include objects referred by reflog entries"),
+		OPT_BOOL(0, "stdout", &pack_to_stdout,
+			 "output pack to stdout"),
+		OPT_BOOL(0, "include-tag", &include_tag,
+			 "include unasked-for annotated tags"),
+		OPT_BOOL(0, "keep-unreachable", &keep_unreachable,
+			 "keep unreachable objects"),
+		OPT_BOOL(0, "unpack-unreachable", &unpack_unreachable,
+			 "unpack unreachable objects"),
+		OPT_BOOL(0, "thin", &thin,
+			 "create thin packs"),
+		OPT_BOOL(0, "honor-pack-keep", &ignore_packed_keep,
+			 "ignore packs that have companion .keep file"),
+		OPT_INTEGER(0, "compression", &pack_compression_level,
+			    "pack compression level"),
+		OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents,
+			    "do not hide commits by grafts", 0),
+		OPT_END(),
+	};
 
 	read_replace_refs = 0;
 
@@ -2279,160 +2345,35 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 		pack_compression_level = core_compression_level;
 
 	progress = isatty(2);
-	for (i = 1; i < argc; i++) {
-		const char *arg = argv[i];
-
-		if (*arg != '-')
-			break;
+	argc = parse_options(argc, argv, prefix, pack_objects_options,
+			     pack_usage, 0);
 
-		if (!strcmp("--non-empty", arg)) {
-			non_empty = 1;
-			continue;
-		}
-		if (!strcmp("--local", arg)) {
-			local = 1;
-			continue;
-		}
-		if (!strcmp("--incremental", arg)) {
-			incremental = 1;
-			continue;
-		}
-		if (!strcmp("--honor-pack-keep", arg)) {
-			ignore_packed_keep = 1;
-			continue;
-		}
-		if (!prefixcmp(arg, "--compression=")) {
-			char *end;
-			int level = strtoul(arg+14, &end, 0);
-			if (!arg[14] || *end)
-				usage(pack_usage);
-			if (level == -1)
-				level = Z_DEFAULT_COMPRESSION;
-			else if (level < 0 || level > Z_BEST_COMPRESSION)
-				die("bad pack compression level %d", level);
-			pack_compression_level = level;
-			continue;
+	while (argc && argv[0][0] == '-') {
+		use_internal_rev_list = 1;
+		if (rp_ac >= rp_ac_alloc - 1) {
+			rp_ac_alloc = alloc_nr(rp_ac_alloc);
+			rp_av = xrealloc(rp_av,
+					 rp_ac_alloc * sizeof(*rp_av));
 		}
-		if (!prefixcmp(arg, "--max-pack-size=")) {
-			pack_size_limit_cfg = 0;
-			if (!git_parse_ulong(arg+16, &pack_size_limit))
-				usage(pack_usage);
-			continue;
-		}
-		if (!prefixcmp(arg, "--window=")) {
-			char *end;
-			window = strtoul(arg+9, &end, 0);
-			if (!arg[9] || *end)
-				usage(pack_usage);
-			continue;
-		}
-		if (!prefixcmp(arg, "--window-memory=")) {
-			if (!git_parse_ulong(arg+16, &window_memory_limit))
-				usage(pack_usage);
-			continue;
-		}
-		if (!prefixcmp(arg, "--threads=")) {
-			char *end;
-			delta_search_threads = strtoul(arg+10, &end, 0);
-			if (!arg[10] || *end || delta_search_threads < 0)
-				usage(pack_usage);
+		rp_av[rp_ac++] = *argv;
+		argv++;
+		argc--;
+	}
+
+	if (!reuse_object)
+		reuse_delta = 0;
+	if (thin) {
+		use_internal_rev_list = 1;
+		rp_av[1] = "--objects-edge";
+	}
+	if (pack_compression_level == -1)
+		pack_compression_level = Z_DEFAULT_COMPRESSION;
+	else if (pack_compression_level < 0 || pack_compression_level > Z_BEST_COMPRESSION)
+		die("bad pack compression level %d", pack_compression_level);
 #ifdef NO_PTHREADS
-			if (delta_search_threads != 1)
-				warning("no threads support, "
-					"ignoring %s", arg);
+	if (delta_search_threads != 1)
+		warning("no threads support, ignoring %s", arg);
 #endif
-			continue;
-		}
-		if (!prefixcmp(arg, "--depth=")) {
-			char *end;
-			depth = strtoul(arg+8, &end, 0);
-			if (!arg[8] || *end)
-				usage(pack_usage);
-			continue;
-		}
-		if (!strcmp("--progress", arg)) {
-			progress = 1;
-			continue;
-		}
-		if (!strcmp("--all-progress", arg)) {
-			progress = 2;
-			continue;
-		}
-		if (!strcmp("--all-progress-implied", arg)) {
-			all_progress_implied = 1;
-			continue;
-		}
-		if (!strcmp("-q", arg)) {
-			progress = 0;
-			continue;
-		}
-		if (!strcmp("--no-reuse-delta", arg)) {
-			reuse_delta = 0;
-			continue;
-		}
-		if (!strcmp("--no-reuse-object", arg)) {
-			reuse_object = reuse_delta = 0;
-			continue;
-		}
-		if (!strcmp("--delta-base-offset", arg)) {
-			allow_ofs_delta = 1;
-			continue;
-		}
-		if (!strcmp("--stdout", arg)) {
-			pack_to_stdout = 1;
-			continue;
-		}
-		if (!strcmp("--revs", arg)) {
-			use_internal_rev_list = 1;
-			continue;
-		}
-		if (!strcmp("--keep-unreachable", arg)) {
-			keep_unreachable = 1;
-			continue;
-		}
-		if (!strcmp("--unpack-unreachable", arg)) {
-			unpack_unreachable = 1;
-			continue;
-		}
-		if (!strcmp("--include-tag", arg)) {
-			include_tag = 1;
-			continue;
-		}
-		if (!strcmp("--unpacked", arg) ||
-		    !strcmp("--reflog", arg) ||
-		    !strcmp("--all", arg)) {
-			use_internal_rev_list = 1;
-			if (rp_ac >= rp_ac_alloc - 1) {
-				rp_ac_alloc = alloc_nr(rp_ac_alloc);
-				rp_av = xrealloc(rp_av,
-						 rp_ac_alloc * sizeof(*rp_av));
-			}
-			rp_av[rp_ac++] = arg;
-			continue;
-		}
-		if (!strcmp("--thin", arg)) {
-			use_internal_rev_list = 1;
-			thin = 1;
-			rp_av[1] = "--objects-edge";
-			continue;
-		}
-		if (!prefixcmp(arg, "--index-version=")) {
-			char *c;
-			pack_idx_opts.version = strtoul(arg + 16, &c, 10);
-			if (pack_idx_opts.version > 2)
-				die("bad %s", arg);
-			if (*c == ',')
-				pack_idx_opts.off32_limit = strtoul(c+1, &c, 0);
-			if (*c || pack_idx_opts.off32_limit & 0x80000000)
-				die("bad %s", arg);
-			continue;
-		}
-		if (!strcmp(arg, "--keep-true-parents")) {
-			grafts_replace_parents = 0;
-			continue;
-		}
-		usage(pack_usage);
-	}
 
 	/* Traditionally "pack-objects [options] base extra" failed;
 	 * we would however want to take refs parameter that would
@@ -2447,11 +2388,14 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 	 * walker.
 	 */
 
-	if (!pack_to_stdout)
-		base_name = argv[i++];
-
-	if (pack_to_stdout != !base_name)
-		usage(pack_usage);
+	if (!pack_to_stdout) {
+		if (!argc)
+			die("base name required if --stdout is not given");
+		base_name = argv[0];
+		argc--;
+	}
+	if (argc)
+		die("base name or --stdout are mutually exclusive");
 
 	if (!pack_to_stdout && !pack_size_limit)
 		pack_size_limit = pack_size_limit_cfg;
-- 
1.7.4.74.g639db

^ permalink raw reply related

* [PATCH 3/6] parse-options: add OPT_ULONG
From: Nguyễn Thái Ngọc Duy @ 2011-11-10  7:12 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <1320909155-4575-1-git-send-email-pclouds@gmail.com>


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 parse-options.c |   15 +++++++++++++++
 parse-options.h |    2 ++
 2 files changed, 17 insertions(+), 0 deletions(-)

diff --git a/parse-options.c b/parse-options.c
index f0098eb..58bb83d 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -144,6 +144,21 @@ static int get_value(struct parse_opt_ctx_t *p,
 			return opterror(opt, "expects a numerical value", flags);
 		return 0;
 
+	case OPTION_ULONG:
+		if (unset) {
+			*(unsigned long *)opt->value = 0;
+			return 0;
+		}
+		if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+			*(unsigned long *)opt->value = opt->defval;
+			return 0;
+		}
+		if (get_arg(p, opt, flags, &arg))
+			return -1;
+		if (!git_parse_ulong(arg, (unsigned long *)opt->value))
+			return opterror(opt, "expects a numerical value", flags);
+		return 0;
+
 	default:
 		die("should not happen, someone must be hit on the forehead");
 	}
diff --git a/parse-options.h b/parse-options.h
index 2e811dc..12294d7 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -16,6 +16,7 @@ enum parse_opt_type {
 	/* options with arguments (usually) */
 	OPTION_STRING,
 	OPTION_INTEGER,
+	OPTION_ULONG,
 	OPTION_CALLBACK,
 	OPTION_LOWLEVEL_CALLBACK,
 	OPTION_FILENAME
@@ -133,6 +134,7 @@ struct option {
 #define OPT_SET_PTR(s, l, v, h, p)  { OPTION_SET_PTR, (s), (l), (v), NULL, \
 				      (h), PARSE_OPT_NOARG, NULL, (p) }
 #define OPT_INTEGER(s, l, v, h)     { OPTION_INTEGER, (s), (l), (v), "n", (h) }
+#define OPT_ULONG(s, l, v, h)       { OPTION_ULONG, (s), (l), (v), "n", (h) }
 #define OPT_STRING(s, l, v, a, h)   { OPTION_STRING,  (s), (l), (v), (a), (h) }
 #define OPT_STRING_LIST(s, l, v, a, h) \
 				    { OPTION_CALLBACK, (s), (l), (v), (a), \
-- 
1.7.4.74.g639db

^ permalink raw reply related

* [PATCH 2/6] find_pack_entry(): do not keep packed_git pointer locally
From: Nguyễn Thái Ngọc Duy @ 2011-11-10  7:12 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <1320909155-4575-1-git-send-email-pclouds@gmail.com>

Commit f7c22cc (always start looking up objects in the last used pack
first - 2007-05-30) introduce a static packed_git* pointer as an
optimization.

However keeping the pointer may become invalid if free_pack_by_name()
happens to free that particular pack.

Move the pointer out and reset the pointer in free_pack_by_name() if we
free it.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 sha1_file.c |   13 ++++++++-----
 1 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/sha1_file.c b/sha1_file.c
index 3401301..155c808 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -53,6 +53,8 @@ static struct cached_object empty_tree = {
 	0
 };
 
+static struct packed_git *find_pack_entry_last_found = (void *)1;
+
 static struct cached_object *find_cached_object(const unsigned char *sha1)
 {
 	int i;
@@ -719,6 +721,8 @@ void free_pack_by_name(const char *pack_name)
 			close_pack_index(p);
 			free(p->bad_object_sha1);
 			*pp = p->next;
+			if (find_pack_entry_last_found == p)
+				find_pack_entry_last_found = (void*)1;
 			free(p);
 			return;
 		}
@@ -2010,14 +2014,13 @@ static int is_pack_valid(struct packed_git *p)
 
 static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
 {
-	static struct packed_git *last_found = (void *)1;
 	struct packed_git *p;
 	off_t offset;
 
 	prepare_packed_git();
 	if (!packed_git)
 		return 0;
-	p = (last_found == (void *)1) ? packed_git : last_found;
+	p = (find_pack_entry_last_found == (void *)1) ? packed_git : find_pack_entry_last_found;
 
 	do {
 		if (p->num_bad_objects) {
@@ -2044,16 +2047,16 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
 			e->offset = offset;
 			e->p = p;
 			hashcpy(e->sha1, sha1);
-			last_found = p;
+			find_pack_entry_last_found = p;
 			return 1;
 		}
 
 		next:
-		if (p == last_found)
+		if (p == find_pack_entry_last_found)
 			p = packed_git;
 		else
 			p = p->next;
-		if (p == last_found)
+		if (p == find_pack_entry_last_found)
 			p = p->next;
 	} while (p);
 	return 0;
-- 
1.7.4.74.g639db

^ permalink raw reply related

* [PATCH 1/6] Print trace in rev-list machinery
From: Nguyễn Thái Ngọc Duy @ 2011-11-10  7:12 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 revision.c |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/revision.c b/revision.c
index 8764dde..0aa3638 100644
--- a/revision.c
+++ b/revision.c
@@ -1690,6 +1690,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 	if (opt)
 		submodule = opt->submodule;
 
+	if (argc)
+		trace_argv_printf(argv + 1, "trace: rev-list");
+	else
+		trace_printf("trace: rev-list\n");
+
 	/* First, search for "--" */
 	seen_dashdash = 0;
 	for (i = 1; i < argc; i++) {
-- 
1.7.4.74.g639db

^ permalink raw reply related

* Re: Bash tab completion for _git_fetch alias is broken on Git 1.7.7.1
From: Johannes Sixt @ 2011-11-10  7:09 UTC (permalink / raw)
  To: nathan.f77; +Cc: Junio C Hamano, git
In-Reply-To: <7vehxgu0fy.fsf@alter.siamese.dyndns.org>

Am 11/10/2011 4:21, schrieb Junio C Hamano:
> Nathan Broadbent <nathan.f77@gmail.com> writes:
> 
>> Dear git mailing list,
>>
>> I'm assigning the `_git_fetch` bash tab completion to the alias `gf`,
>> with the following command:
>>
>>     complete -o default -o nospace -F _git_fetch gf
>>
>> The tab completion then works fine in git 1.7.0.4, but breaks on git
>> 1.7.7.1, with the following error:
> 
> We have been cooking for 1.7.8 and have the first release candidate
> 1.7.8-rc1; could you try it and report what you find out?

It looks like _git_fetch is not meant to be called directly. All git
completions must go through _git.

See also this post:

http://thread.gmane.org/gmane.comp.version-control.msysgit/13310/focus=13335

-- Hannes

^ permalink raw reply

* Re: git-svn: t9155 fails against subversion 1.7.0
From: Frans Klaver @ 2011-11-10  6:02 UTC (permalink / raw)
  To: git@vger.kernel.org
In-Reply-To: <op.v4neh4q20aolir@keputer>

I missed $gmane/184644 in my search for this issue.


On Tue, 08 Nov 2011 23:09:30 +0100, Frans Klaver <fransklaver@gmail.com>  
wrote:

> For kicks I decided to run the tests and noticed that on master  
> t9155-git-svn-fetch-deleted-tag fails against svn 1.7.0. We hit an  
> assertion in subversion's dirent_uri.c, stating that we don't provide a  
> canonical url. I haven't tested against other subversion versions. I  
> dare assume that this issue doesn't arise on earlier versions. It  
> probably won't affect a lot of users right now, but it will in the  
> future.
>
> Here's some verbose test output:
> expecting success:
> 	git svn init --stdlayout "$svnrepo" git_project &&
> 	cd git_project &&
> 	git svn fetch &&
>
> 	git diff --exit-code mybranch:trunk/subdir/file tags/mytag:file &&
> 	git diff --exit-code master:subdir/file tags/mytag^:file
>
> Initialized empty Git repository in /home/frans/devsw/git/t/trash  
> directory.t9155-git-svn-fetch-deleted-tag/git_project/.git/
> svn: E235000: In file 'subversion/libsvn_subr/dirent_uri.c' line 2291:  
> assertion failed (svn_uri_is_canonical(url, pool))
> error: git-svn died of signal 6
> not ok - 2 fetch deleted tags from same revision with checksum error
>
> I've been trying to debug and got down to:
> Git::SVN::Ra::new(/home/frans/devsw/git/git-svn:5496):
> 5496:		my $self = SVN::Ra->new(url => escape_url($url), auth => $baton,
> 5497:		                      config => $config,
> 5498:				      pool => SVN::Pool->new,
> 5499:		                      auth_provider_callbacks => $callbacks);
> ...
> SVN::Ra::new(/usr/lib/perl5/vendor_perl/5.12.4/i686-linux/SVN/Ra.pm:529):
> 529:	    $self->{session} = SVN::_Ra::svn_ra_open($self->{url},  
> $callback, $self->{config} || {}, $pool);
>    DB<3> p $self->{url}
> file:///home/frans/devsw/git/t/trash  
> directory.t9155-git-svn-fetch-deleted-tag/svnrepo
>
> The url looks like that throughout the stack (as far as I've seen), so  
> if it is wrong, it is probably wrong at top-level. Hope someone with a  
> bit more experience knows how to deal with this.
>
> Thanks,
> Frans


-- 
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/

^ permalink raw reply

* uncommitted changes in a renamed directory are clobbered on merge
From: Paul Grayson @ 2011-11-10  5:28 UTC (permalink / raw)
  To: git

Hello,

While merging with uncommitted changes is strongly discouraged, Git
generally tries to not overwrite your work without warning.  However,
there are some situations where Git silently erases uncommitted
changes.  Specifically, it seems that if you rename a directory on one
branch, make uncommitted changes to files within that branch, then
merge in another branch, files that would conflict get replaced by the
version on the other branch.  We have observed this on Git versions
1.7.5.4 and 1.7.7.3.

I have placed a script demonstrating this problem at
https://gist.github.com/1354160

Here is a shell transcript showing what it looks like:

gitbug$ git init single
Initialized empty Git repository in /home/paul/gitprojects/gitbug/single/.git/
gitbug$ cd single/
gitbug/single$ mkdir test
gitbug/single$ echo "hi" > test/test.txt
gitbug/single$ git add .
gitbug/single$ git commit -am "initial revision"
[master (root-commit) 8be9a1e] initial revision
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 test/test.txt
gitbug/single$ git checkout -b branch1
Switched to a new branch 'branch1'
gitbug/single$ git mv test/ test2
gitbug/single$ git commit -am "renamed test to test2"
[branch1 80d497c] renamed test to test2
 1 files changed, 0 insertions(+), 0 deletions(-)
 rename {test => test2}/test.txt (100%)
gitbug/single$ git checkout master
Switched to branch 'master'
gitbug/single$ echo "change on master" >> test/test.txt
gitbug/single$ git commit -am "made a change on master"
[master 51d3a75] made a change on master
 1 files changed, 1 insertions(+), 0 deletions(-)
gitbug/single$ git checkout branch1
Switched to branch 'branch1'
gitbug/single$ echo "change on branch1" >> test2/test.txt
gitbug/single$ git merge master
Auto-merging test2/test.txt
Merge made by recursive.
 test2/test.txt |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
gitbug/single$ cat test2/test.txt
hi
change on master
gitbug/single$

I expected the merge to fail as it usually does when there is a
conflict with uncommitted changes.  Instead, it silently deleted the
changes in test.txt.

One of our developers just lost a few hours of work to this bug.  Please fix!

Sincerely,
Paul Grayson

^ permalink raw reply

* Re: Bash tab completion for _git_fetch alias is broken on Git 1.7.7.1
From: Junio C Hamano @ 2011-11-10  3:21 UTC (permalink / raw)
  To: nathan.f77; +Cc: git
In-Reply-To: <CAPXHQbND61TyU21ckHwRyMYH=P=H7+GZR5KNY8m+qaMEEhEZKQ@mail.gmail.com>

Nathan Broadbent <nathan.f77@gmail.com> writes:

> Dear git mailing list,
>
> I'm assigning the `_git_fetch` bash tab completion to the alias `gf`,
> with the following command:
>
>     complete -o default -o nospace -F _git_fetch gf
>
> The tab completion then works fine in git 1.7.0.4, but breaks on git
> 1.7.7.1, with the following error:

We have been cooking for 1.7.8 and have the first release candidate
1.7.8-rc1; could you try it and report what you find out?

^ permalink raw reply

* Bash tab completion for _git_fetch alias is broken on Git 1.7.7.1
From: Nathan Broadbent @ 2011-11-10  2:46 UTC (permalink / raw)
  To: git
In-Reply-To: <CAPXHQbPgepSFHX63F+Nt8TJ+znAaVqzzmSZmJqxj2mekhStO-g@mail.gmail.com>

Dear git mailing list,

I'm assigning the `_git_fetch` bash tab completion to the alias `gf`,
with the following command:

    complete -o default -o nospace -F _git_fetch gf

The tab completion then works fine in git 1.7.0.4, but breaks on git
1.7.7.1, with the following error:

    -bash: [: 2: unary operator expected


Here is the related issue on github (the project is a set of git shortcuts):
https://github.com/ndbroadbent/scm_breeze/issues/11


Thanks for your time, I'd really appreciate some help!


Regards,
Nathan B

^ permalink raw reply

* Re: pretty placeholders for reflog entries
From: Jack Nagel @ 2011-11-10  1:56 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git
In-Reply-To: <20111108054745.GD29643@sigill.intra.peff.net>

On Mon, Nov 7, 2011 at 11:47 PM, Jeff King <peff@peff.net> wrote:
> Sure. I'll re-send the patch once the release is out. That will also
> give Jack and any other interested parties time to comment and test.

Thanks. I used this day, and combined with "--date=", it gets me pretty
close to where I wanted to be. But your comments about reorganizing the
date placeholders make sense, so this is perfectly fine for the time
being.

Thanks again.

Jack

^ permalink raw reply

* RE: Updating Files
From: Ben Walton @ 2011-11-10  1:08 UTC (permalink / raw)
  To: greg.brand; +Cc: git
In-Reply-To: <338A71EC3E356A47BF5279971E4EF72A04D860D7F6@WESMSG51.nordsoncorp.local>

Excerpts from Brand, Greg's message of Wed Nov 09 19:54:34 -0500 2011:

Hi Greg,

>                .<filename>.revision    ~ Does GIT have the same, or
> similar options???  I understand with the distributed nature of GIT,
> there may be several ways to accomplish this. It is nice, though, to
> be able to get a clean version without losing changes you may (or
> may not) want to keep.

You've got at least two options to store changes you want to keep but
have them kept out of the way.

1. Commit the change on a branch and leave that branch sitting there.
2. Stash the change with a descriptive note.

If you're on branch master and want to save this change but not have
it 'live' you could do:

git checkout -b save_my_change
git add modified_file
git commit -m 'saving this for later, just in case'
git checkout master

The master branch will not have the change but the save_my_change
branch will.  You can see the commit with a command like:

git show save_my_change

That looks up the sha1 for the ref that save_my_change refers to and
then shows the commit object.  To re-apply this later, you could merge
the branch to master or cherry pick the sha1 to master (or any other
branch).

The second option, stashing, is likely nicer for this type of thing
but ymmv.  In your working tree, with the modified file(s), do:

git stash save -m 'something potentially useful, but maybe not'

Your working tree is now clean and you can see stashes with:

git stash list

You can later get at a stash with git stash apply.


The real gurus may point out something nicer that I didn't think of
too...

Hope this helps.

Thanks
-Ben
--
Ben Walton
Systems Programmer - CHASS
University of Toronto
C:416.407.5610 | W:416.978.4302

^ permalink raw reply

* Re: Updating Files
From: Andrew Ardill @ 2011-11-10  1:05 UTC (permalink / raw)
  To: Brand, Greg; +Cc: git@vger.kernel.org
In-Reply-To: <338A71EC3E356A47BF5279971E4EF72A04D860D7F6@WESMSG51.nordsoncorp.local>

An oft heard recommendation when taking up git is to disregard any SCM
you might already know, to clear your mind from an pre-existing
expectations and to learn the tool with a blank slate, so to speak.
This is probably a good idea in your case.

A solid understanding of the core concepts git uses will really help
you - for example, it is very quick and easy to create branches in
git. A branch is just a reference to a snapshot of your files. So, you
can of course have a 'clean' copy and 'dirty' copy, but they would be
contained in different branches, not different files. Once you have
two branches you can easily compare them, merge them, steal from them
- whatever you want.

Other key ideas you may want to understand (in the blank slate sense)
include the index, the working directory and staging commits. I'm sure
others have their own ideas about what they key ideas are too.

If you haven't already, check out the pro-git book[1], it is very good
at explaining these things.

Regards,

Andrew Ardill

[1] http://progit.org/book/



On 10 November 2011 11:54, Brand, Greg <greg.brand@nordsonasymtek.com> wrote:
> Good Day,
>
> Let me begin by describing my SCM experience.
> I am an old school UNIX guy, and grew up with RCS.
> I have written many BASH and CSH scripts for RCS.
>
> Moving on, the next logical step was CVS.
> I've worked with (and administered) CVS for the last 12 years.
> I have also worked with SourceSafe (ugh), Perforce and ClearCase.
>
> I am an avid CVS proponent.
>
> I have Recently changed jobs, to a SourceSafe shop.
> We HAVE to move to something different.
>
> I've played with both Subversion and GIT.
> I REALLY like GIT, but have some questions.
>
>
> For my questions, I will use a CVS comparison sense this is what I'm most familiar with.
>
> - Updating a File:
>    ~ CVS's default behavior, is to try to merge changes from the repository into my local copy. Sometimes, this isn't desirable.
>    ~ With CVS I can also choose to get a "clean" copy. If the file I'm updating has been modified, CVS will create a backup of the original file before updating to the clean version. The backup file is saved as a "hidden" file, with the format:
>                .<filename>.revision
>    ~ Does GIT have the same, or similar options???  I understand with the distributed nature of GIT, there may be several ways to accomplish this. It is nice, though, to be able to get a clean version without losing changes you may (or may not) want to keep.
>
>
> Thank you for your help.
> I am sure I will have more questions.
>
> Best Regards,
> Greg Brand
>
>
> _____________________________________________________________________________
> Scanned by IBM Email Security Management Services powered by MessageLabs. For more information please visit http://www.ers.ibm.com
>
> This email is intended only for the use of the party to which it is addressed and may contain information that is privileged, confidential, or protected by law.  If you are not the intended recipient you are hereby notified that any dissemination, copying or distribution of the email or its contents is strictly prohibited.  If you have received this message in error, please notify us immediately, by replying to the message and deleting it from your computer.
>
> WARNING: Internet communications are not assured to be secure or clear of inaccuracies as information could be intercepted, corrupted, lost, destroyed, arrive late or incomplete, or contain viruses.  Therefore, we do not accept responsibility for any errors or omissions that are present in this email, or any attachment, that have arisen as a result of e-mail transmission.
> _____________________________________________________________________________
> --
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply

* RE: Updating Files
From: Brand, Greg @ 2011-11-10  0:54 UTC (permalink / raw)
  To: git@vger.kernel.org

Good Day,

Let me begin by describing my SCM experience.
I am an old school UNIX guy, and grew up with RCS.
I have written many BASH and CSH scripts for RCS.

Moving on, the next logical step was CVS.
I've worked with (and administered) CVS for the last 12 years.
I have also worked with SourceSafe (ugh), Perforce and ClearCase.

I am an avid CVS proponent.

I have Recently changed jobs, to a SourceSafe shop.
We HAVE to move to something different.

I've played with both Subversion and GIT.
I REALLY like GIT, but have some questions.


For my questions, I will use a CVS comparison sense this is what I'm most familiar with.

- Updating a File: 
   ~ CVS's default behavior, is to try to merge changes from the repository into my local copy. Sometimes, this isn't desirable. 
   ~ With CVS I can also choose to get a "clean" copy. If the file I'm updating has been modified, CVS will create a backup of the original file before updating to the clean version. The backup file is saved as a "hidden" file, with the format:
               .<filename>.revision
   ~ Does GIT have the same, or similar options???  I understand with the distributed nature of GIT, there may be several ways to accomplish this. It is nice, though, to be able to get a clean version without losing changes you may (or may not) want to keep.


Thank you for your help.
I am sure I will have more questions.

Best Regards,
Greg Brand


_____________________________________________________________________________
Scanned by IBM Email Security Management Services powered by MessageLabs. For more information please visit http://www.ers.ibm.com

This email is intended only for the use of the party to which it is addressed and may contain information that is privileged, confidential, or protected by law.  If you are not the intended recipient you are hereby notified that any dissemination, copying or distribution of the email or its contents is strictly prohibited.  If you have received this message in error, please notify us immediately, by replying to the message and deleting it from your computer.

WARNING: Internet communications are not assured to be secure or clear of inaccuracies as information could be intercepted, corrupted, lost, destroyed, arrive late or incomplete, or contain viruses.  Therefore, we do not accept responsibility for any errors or omissions that are present in this email, or any attachment, that have arisen as a result of e-mail transmission.
_____________________________________________________________________________

^ permalink raw reply

* Re: hook for rebase --continue
From: Neal Kreitzinger @ 2011-11-10  0:50 UTC (permalink / raw)
  Cc: Matt Graham, git
In-Reply-To: <4EBB1F17.4060907@gmail.com>

On 11/9/2011 6:47 PM, Neal Kreitzinger wrote:
> On 11/7/2011 1:42 PM, Matt Graham wrote:
>> I did some testing and it appears that during a rebase, if I resolve
>> a conflict and call git rebase --continue, the pre-commit hook
>> doesn't run. This means that if I don't resolve the conflict
>> correctly, our check for invalid syntax doesn't get run and creates
>> the risk that someone could push code with invalid syntax, not
>> realizing that the check didn't run.
>>
>> Does anyone else share my expectation that the pre-commit hook
>> should run during a rebase? Or at least for the first commit
>> following a rebase conflict?
>>
>> If not, is there another hook that is triggered by a rebase that I
>> should be using instead?
>>
> You could try creating an alias (e.g. "git rebase-continue") in your
> gitconfig that runs the desired validation logic and then executes git
> rebase --continue. Then you would run "git rebase-continue" instead of "git rebase
> --continue".
>
correction: added the --continue option in the alias example above.

--neal

^ permalink raw reply

* Re: hook for rebase --continue
From: Neal Kreitzinger @ 2011-11-10  0:47 UTC (permalink / raw)
  To: Matt Graham; +Cc: git
In-Reply-To: <CALts4TQ545L1d1J0EiUjd7x=WBJpjCCv6UsXZOoGQAC29RqC5g@mail.gmail.com>

On 11/7/2011 1:42 PM, Matt Graham wrote:
> I did some testing and it appears that during a rebase, if I resolve
> a conflict and call git rebase --continue, the pre-commit hook
> doesn't run.  This means that if I don't resolve the conflict
> correctly, our check for invalid syntax doesn't get run and creates
> the risk that someone could push code with invalid syntax, not
> realizing that the check didn't run.
>
> Does anyone else share my expectation that the pre-commit hook
> should run during a rebase? Or at least for the first commit
> following a rebase conflict?
>
> If not, is there another hook that is triggered by a rebase that I
> should be using instead?
>
You could try creating an alias (e.g. "git rebase-continue") in your 
gitconfig that runs the desired validation logic and then executes git 
rebase.  Then you would run "git rebase-continue" instead of "git rebase 
--continue".

v/r,
neal

^ permalink raw reply

* Re: [PATCH 1/1] apply.c: reject patch without --(ex,in)clude and path outside.
From: Junio C Hamano @ 2011-11-09 23:07 UTC (permalink / raw)
  To: Bruce E. Robertson; +Cc: git
In-Reply-To: <1320878942-9811-1-git-send-email-bruce.e.robertson@intel.com>

"Bruce E. Robertson" <bruce.e.robertson@intel.com> writes:

> From: "Bruce E. Robertson" <bruce.e.robertson@intel.com>
>
> Patches are silently ignored when applied with neither --include nor
> --exclude options when the current working dir is not on patch's
> path. This contravenes the principle of least surprise.

I do not necessarily agree but if you think so perhaps the user should be
told about which paths are rejected. In other words, I think it is wrong
to change the exit code to 1 when you are applying a patch that touches
outside your area, but I think it is not wrong if the program warned about
it.

Does your patch behave like that?

> diff --git a/builtin/apply.c b/builtin/apply.c
> index 84a8a0b..162e2aa 100644
> --- a/builtin/apply.c
> +++ b/builtin/apply.c
> @@ -3619,6 +3619,7 @@ static struct lock_file lock_file;
>  
>  static struct string_list limit_by_name;
>  static int has_include;
> +static int has_exclude;
>  static void add_name_limit(const char *name, int exclude)
>  {
>  	struct string_list_item *it;
> @@ -3717,9 +3718,13 @@ static int apply_patch(int fd, const char *filename, int options)
>  			listp = &patch->next;
>  		}
>  		else {
> -			/* perhaps free it a bit better? */
> -			free(patch);
> -			skipped_patch++;
> +			if ( !has_exclude && !has_include ) {

Style; extra SP inside ().

I am not convinced that the logic here is correct, either.  If you have
exclude but not include, and the patch records a path outside your area,
the path will be rejected even if it does not match any of the exclude
patterns. Shouldn't you be treating that case exactly the same as the case
without any exclude patterns?

> +				patch->rejected = 1;

Doesn't it trigger "errs" to be set in write_out_results()?  This patch is
not free()'ed, but it is not on the "list" either. Where does it go, and
how is that unfreed patch used later in the program?

> +			} else {
> +				/* perhaps free it a bit better? */
> +				free(patch);
> +				skipped_patch++;
> +			}
>  		}
>  		offset += nr;
>  	}
> @@ -3773,6 +3778,7 @@ static int option_parse_exclude(const struct option *opt,
>  				const char *arg, int unset)
>  {
>  	add_name_limit(arg, 1);
> +	has_exclude = 1;
>  	return 0;
>  }

^ permalink raw reply

* [PATCH 1/1] apply.c: reject patch without --(ex,in)clude and path outside.
From: Bruce E. Robertson @ 2011-11-09 22:49 UTC (permalink / raw)
  To: git; +Cc: Bruce E. Robertson

From: "Bruce E. Robertson" <bruce.e.robertson@intel.com>

Patches are silently ignored when applied with neither --include nor
--exclude options when the current working dir is not on patch's
path. This contravenes the principle of least surprise.

"make test" results for this change:
fixed   0
success 8032
failed  0
broken  58
total   8126

Signed-off-by: Bruce E. Robertson <bruce.e.robertson@intel.com>
---
 builtin/apply.c |   12 +++++++++---
 1 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/builtin/apply.c b/builtin/apply.c
index 84a8a0b..162e2aa 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -3619,6 +3619,7 @@ static struct lock_file lock_file;
 
 static struct string_list limit_by_name;
 static int has_include;
+static int has_exclude;
 static void add_name_limit(const char *name, int exclude)
 {
 	struct string_list_item *it;
@@ -3717,9 +3718,13 @@ static int apply_patch(int fd, const char *filename, int options)
 			listp = &patch->next;
 		}
 		else {
-			/* perhaps free it a bit better? */
-			free(patch);
-			skipped_patch++;
+			if ( !has_exclude && !has_include ) {
+				patch->rejected = 1;
+			} else {
+				/* perhaps free it a bit better? */
+				free(patch);
+				skipped_patch++;
+			}
 		}
 		offset += nr;
 	}
@@ -3773,6 +3778,7 @@ static int option_parse_exclude(const struct option *opt,
 				const char *arg, int unset)
 {
 	add_name_limit(arg, 1);
+	has_exclude = 1;
 	return 0;
 }
 
-- 
1.7.7.1.432.gca458.dirty

^ permalink raw reply related

* Re: Problem with git-svn with limited SVN access
From: Thomas Rast @ 2011-11-09 22:38 UTC (permalink / raw)
  To: Antoine Bonavita; +Cc: git
In-Reply-To: <4EBA63CA.7000201@stickyadstv.com>

Antoine Bonavita wrote:
> ### If I try to add one of the branches manually:
> branches = branches/XXX:refs/remotes/branches/XXX
>  > git svn fetch
> One '*' is needed in glob: 'branches/XXX'

I think having several fetch specs should do the trick, although I
cannot easily test with actual permissions.

You can start configuring the repo with

  git init
  git svn init svn://server/ -T trunk

to get an initial layout.  The .git/config will look like

  [svn-remote "svn"]
          url = svn://server/
          fetch = trunk:refs/remotes/trunk

The clue is that the config says 'fetch', not 'trunk'.  Much like with
git remotes, you can add more fetch specs along the lines of

          fetch = branches/XXX:refs/remotes/svn/XXX

or whatever layout you prefer.

Please tell us whether that works even in the face of restrictions on
branches/ itself :-)

-- 
Thomas Rast
trast@{inf,student}.ethz.ch

^ permalink raw reply

* Re: RFH: unexpected reflog behavior with --since=
From: Jeff King @ 2011-11-09 22:26 UTC (permalink / raw)
  To: Eric Raible; +Cc: git@vger.kernel.org
In-Reply-To: <20111109222032.GB31535@sigill.intra.peff.net>

On Wed, Nov 09, 2011 at 05:20:32PM -0500, Jeff King wrote:

>   git log -g --format='%ct %H' |
>   awk '{ print $2 if $1 < SOME_TIMESTAMP }'

Hmm, that is obviously not valid awk syntax. My brain has been too fried
by perl. And the comparison goes the wrong way. A (closer to) working
example would be:

  git log -g --format='%ct %H' |
  perl -alne 'print $F[1] if $F[0] > SOME_TIMESTAMP'

But hopefully you get the point.

-Peff

^ permalink raw reply

* Re: RFH: unexpected reflog behavior with --since=
From: Jeff King @ 2011-11-09 22:20 UTC (permalink / raw)
  To: Eric Raible; +Cc: git@vger.kernel.org
In-Reply-To: <20111109220128.GA31535@sigill.intra.peff.net>

On Wed, Nov 09, 2011 at 05:01:28PM -0500, Jeff King wrote:

> In the latter case, we would either need a new specifier (like
> "--reflog-since"), or to rewrite the commit timestamp when we rewrite
> the parent pointers.
> 
> The latter has a certain elegance to it (we are making a pretend linear
> history graph out of the reflog, so faking the timestamps to be sensible
> and in order is a logical thing to do) but I worry about lying too much
> in the output. Something like "git log -g --format=%cd" would now have
> the fake timestamp in the output. But then, we already show the fake
> parents in the output, so I don't know that this is any worse.

This patch (which is below) turns out to be absurdly simple. And it
actually still prints the original commit timestamp, because we end up
reparsing it out of the commit object during the pretty-print phase.

So I think the only decision is whether "--since" should respect the
commit timestamps (and be used as a sort of "grep" filter for
timestamps), or whether it should be respecting the fake history we
create when doing a reflog walk.

I think I am leaning towards the latter. It seems to me to be the more
likely guess for what the user would want. And there is real benefit to
doing it in git, since we can stop the traversal early. In the
"grep-like" case, doing it inside git is not really any more efficient
than filtering in a pipeline, like:

  git log -g --format='%ct %H' |
  awk '{ print $2 if $1 < SOME_TIMESTAMP }'

Of course we could still offer both (with a "--reflog-since" type of
option). We'd also need to turn off the optimization for "--since", and
then check whether "--until" has a similar bug (and offer
"--reflog-until").

diff --git a/reflog-walk.c b/reflog-walk.c
index 5d81d39..2e5b270 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -231,6 +231,7 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
 	reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
 	info->last_commit_reflog = commit_reflog;
 	commit_reflog->recno--;
+	commit->date = reflog->timestamp;
 	commit_info->commit = (struct commit *)parse_object(reflog->osha1);
 	if (!commit_info->commit) {
 		commit->parents = NULL;

^ permalink raw reply related

* Re: [PATCH 3/3] commit-tree: teach -x <extra>
From: Brad King @ 2011-11-09 22:19 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <1320872495-7545-4-git-send-email-gitster@pobox.com>

On 11/9/2011 4:01 PM, Junio C Hamano wrote:
> By feeding the header part of the original commit with this parameter,
> e.g. -x "$(git cat-file commit $commit | sed -n -e /^$/q -e p)", extra
> headers of another commit can be transplanted to the resulting commit.

One of the interface features typical in git plumbing that I've always liked
is that any place that can pass long strings tends to be implemented using
pipes or files, at least optionally.  This is especially helpful for operating
systems that have severe limitations on total command line size.

I suggest a flag (-X ?) that tells commit-tree to treat the commit message
(from wherever it is read) in "cat-file commit" format.  Read the extra
header lines from the part before the blank line, and the rest of the message
normally after the blank line.  For example:

  (git cat-file commit $commit | sed -n -e /^$/q -e p
   echo ''
   echo 'New commit message') |
  git commit-tree -X -p $parent $tree

If we want the original extended headers and the original message:

  git cat-file commit $commit | git commit-tree -X -p $parent $tree

-Brad

^ permalink raw reply

* Re: RFH: unexpected reflog behavior with --since=
From: Jeff King @ 2011-11-09 22:01 UTC (permalink / raw)
  To: Eric Raible; +Cc: git@vger.kernel.org
In-Reply-To: <4EB9C7D1.30201@nextest.com>

On Tue, Nov 08, 2011 at 04:22:41PM -0800, Eric Raible wrote:

>     # It's reported correctly here:
>     git log -g --oneline --since=$add_b
> 
>     # But after a reset no history isn't shown.
>     git reset --hard HEAD^
>     git log -g --oneline --since=$add_b
> 
> Is this a bug?  Of course everything is reported when --since isn't used,
> but not so when limited with --since.

It's sort of a bug. And sort of a missing feature.

In the normal revision walking case, git walks the history graph
backwards, hitting the parent of each commit (and when there are
multiple lines of history, we traverse them in commit timestamp order).

So "--since" works not just by omitting non-matching commits from the
output, but also by stopping the traversal when we go too far back in
time. In a sense, this is purely an optimization, as it shouldn't change
the output. But it's an important one, because it makes looking back in
time O(how far back) instead of O(size of all history).

This optimization breaks down badly, of course, in the face of clock
skew (i.e., a commit whose timestamp is further back than its parent).
There are a few tricks we do to avoid small runs of moderate skew, and
in practice it works well.

Now let's look at reflog walking. It's kind of bolted on to the side
of the revision traversal machinery. We walk through the reflog
backwards and pretend that entry N's parent is entry N-1 (you can see
this if you do "git log -g -p", for example; you see the patch versus
the last reflog entry, not the patch against the commit's true parent).

In the case of rewound history (like the reset you showed above), this
means that the history graph will appear to have bad clock skew. The
timestamp of HEAD@{0} is going to be much earlier than its pretend
parent, HEAD@{1}. And the "--since" optimization is going to cut off
traversal, even though there are more interesting commits to be shown.

So in that sense, I think it's a bug, and we should probably disable the
exit-early-from-traversal optimization when we're walking reflogs.

But it may also be a misfeature, because it's not clear what you're
actually trying to limit by. We have commit timestamps, of course, but
when we are walking reflogs, we also have reflog timestamps. Did you
actually want to say "show me all commits in the reflog, in reverse
reflog order, omitting commits that happened before time t"? Or did you
really mean "show me the reflog entries that happened before time t,
regardless of their commit timestamp"?

In the latter case, we would either need a new specifier (like
"--reflog-since"), or to rewrite the commit timestamp when we rewrite
the parent pointers.

The latter has a certain elegance to it (we are making a pretend linear
history graph out of the reflog, so faking the timestamps to be sensible
and in order is a logical thing to do) but I worry about lying too much
in the output. Something like "git log -g --format=%cd" would now have
the fake timestamp in the output. But then, we already show the fake
parents in the output, so I don't know that this is any worse.

-Peff

^ 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