* [PATCH 2/3] receive-pack: don't mention successful updates
From: Jeff King @ 2007-11-05 5:11 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Nicolas Pitre, Johannes Schindelin
In-Reply-To: <20071105050517.GA6244@sigill.intra.peff.net>
The proposed updates are already shown to the user by
send-pack, so there's no point. We continue to show errors,
since they are unexpected.
Signed-off-by: Jeff King <peff@peff.net>
---
receive-pack.c | 4 ----
1 files changed, 0 insertions(+), 4 deletions(-)
diff --git a/receive-pack.c b/receive-pack.c
index 38e35c0..ed44b89 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -204,8 +204,6 @@ static const char *update(struct command *cmd)
error("failed to delete %s", name);
return "failed to delete";
}
- fprintf(stderr, "%s: %s -> deleted\n", name,
- sha1_to_hex(old_sha1));
return NULL; /* good */
}
else {
@@ -217,8 +215,6 @@ static const char *update(struct command *cmd)
if (write_ref_sha1(lock, new_sha1, "push")) {
return "failed to write"; /* error() already called */
}
- fprintf(stderr, "%s: %s -> %s\n", name,
- sha1_to_hex(old_sha1), sha1_to_hex(new_sha1));
return NULL; /* good */
}
}
--
1.5.3.5.1530.g7353
^ permalink raw reply related
* [PATCH 1/3] more terse push output
From: Jeff King @ 2007-11-05 5:11 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Nicolas Pitre, Johannes Schindelin
In-Reply-To: <20071105050517.GA6244@sigill.intra.peff.net>
This changes the output of send-pack to match the new,
more terse fetch output. It looks like this:
To git://host.tld/path/to/repo
+ f3325dc...3b91d1c hasforce -> mirror/hasforce (forced update)
f3325dc..bb022dc master -> mirror/master
! [rejected] needsforce -> mirror/needsforce (non-fast forward)
* [new branch] newbranch -> mirror/newbranch
* [new tag] v1.0 -> v1.0
instead of:
updating 'refs/heads/mirror/hasforce' using 'refs/heads/hasforce'
from f3325dca9c4a34d74012c0e159254f454930cec7
to 3b91d1c310ca9d7b547b85466dd876e143498304
updating 'refs/heads/mirror/master' using 'refs/heads/master'
from f3325dca9c4a34d74012c0e159254f454930cec7
to bb022dc363d5c2aa9aa3026beb9706d44fbe1328
error: remote 'refs/heads/mirror/needsforce' is not an ancestor of
local 'refs/heads/needsforce'.
Maybe you are not up-to-date and need to pull first?
updating 'refs/heads/mirror/newbranch' using 'refs/heads/newbranch'
from 0000000000000000000000000000000000000000
to 3b91d1c310ca9d7b547b85466dd876e143498304
updating 'refs/tags/v1.0'
from 0000000000000000000000000000000000000000
to bb022dc363d5c2aa9aa3026beb9706d44fbe1328
Signed-off-by: Jeff King <peff@peff.net>
---
builtin-send-pack.c | 81 ++++++++++++++++++++++++++++++++++++++++-----------
1 files changed, 64 insertions(+), 17 deletions(-)
diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index 947c42b..d74cc3c 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -206,7 +206,18 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref)
}
}
-static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec, const char **refspec)
+static const char *prettify_ref(const char *name)
+{
+ return name + (
+ !prefixcmp(name, "refs/heads/") ? 11 :
+ !prefixcmp(name, "refs/tags/") ? 10 :
+ !prefixcmp(name, "refs/remotes/") ? 13 :
+ 0);
+}
+
+#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
+
+static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec)
{
struct ref *ref;
int new_refs;
@@ -214,6 +225,7 @@ static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec,
int ask_for_status_report = 0;
int allow_deleting_refs = 0;
int expect_status_report = 0;
+ int shown_dest = 0;
/* No funny business with the matcher */
remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL);
@@ -245,21 +257,33 @@ static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec,
for (ref = remote_refs; ref; ref = ref->next) {
char old_hex[60], *new_hex;
int will_delete_ref;
+ const char *pretty_ref;
+ const char *pretty_peer;
if (!ref->peer_ref)
continue;
+ if (!shown_dest) {
+ fprintf(stderr, "To %s\n", dest);
+ shown_dest = 1;
+ }
+
+ pretty_ref = prettify_ref(ref->name);
+ pretty_peer = prettify_ref(ref->peer_ref->name);
will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
if (will_delete_ref && !allow_deleting_refs) {
- error("remote does not support deleting refs");
+ fprintf(stderr, " ! %-*s %s (remote does not support deleting refs)\n",
+ SUMMARY_WIDTH, "[rejected]", pretty_ref);
ret = -2;
continue;
}
if (!will_delete_ref &&
!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
if (args.verbose)
- fprintf(stderr, "'%s': up-to-date\n", ref->name);
+ fprintf(stderr, " = %-*s %s -> %s\n",
+ SUMMARY_WIDTH, "[up to date]",
+ pretty_peer, pretty_ref);
continue;
}
@@ -296,12 +320,9 @@ static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec,
* commits at the remote end and likely
* we were not up to date to begin with.
*/
- error("remote '%s' is not an ancestor of\n"
- " local '%s'.\n"
- " Maybe you are not up-to-date and "
- "need to pull first?",
- ref->name,
- ref->peer_ref->name);
+ fprintf(stderr, " ! %-*s %s -> %s (non-fast forward)\n",
+ SUMMARY_WIDTH, "[rejected]",
+ pretty_peer, pretty_ref);
ret = -2;
continue;
}
@@ -325,14 +346,40 @@ static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec,
old_hex, new_hex, ref->name);
}
if (will_delete_ref)
- fprintf(stderr, "deleting '%s'\n", ref->name);
+ fprintf(stderr, " - %-*s %s\n",
+ SUMMARY_WIDTH, "[deleting]",
+ pretty_ref);
+ else if (is_null_sha1(ref->old_sha1)) {
+ const char *msg;
+
+ if (!prefixcmp(ref->name, "refs/tags/"))
+ msg = "[new tag]";
+ else
+ msg = "[new branch]";
+ fprintf(stderr, " * %-*s %s -> %s\n",
+ SUMMARY_WIDTH, msg,
+ pretty_peer, pretty_ref);
+ }
else {
- fprintf(stderr, "updating '%s'", ref->name);
- if (strcmp(ref->name, ref->peer_ref->name))
- fprintf(stderr, " using '%s'",
- ref->peer_ref->name);
- fprintf(stderr, "\n from %s\n to %s\n",
- old_hex, new_hex);
+ char quickref[83];
+ char type = ' ';
+ const char *msg = "";
+
+ strcpy(quickref, find_unique_abbrev(ref->old_sha1, DEFAULT_ABBREV));
+ if (ref_newer(ref->peer_ref->new_sha1, ref->old_sha1))
+ strcat(quickref, "..");
+ else {
+ strcat(quickref, "...");
+ type = '+';
+ msg = " (forced update)";
+ }
+ strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
+
+ fprintf(stderr, " %c %-*s %s -> %s%s\n",
+ type,
+ SUMMARY_WIDTH, quickref,
+ pretty_peer, pretty_ref,
+ msg);
}
}
@@ -460,7 +507,7 @@ int send_pack(struct send_pack_args *my_args,
verify_remote_names(nr_heads, heads);
conn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0);
- ret = do_send_pack(fd[0], fd[1], remote, nr_heads, heads);
+ ret = do_send_pack(fd[0], fd[1], remote, dest, nr_heads, heads);
close(fd[0]);
close(fd[1]);
ret |= finish_connect(conn);
--
1.5.3.5.1530.g7353
^ permalink raw reply related
* [PATCH 0/3] more terse push output
From: Jeff King @ 2007-11-05 5:05 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Nicolas Pitre, Johannes Schindelin
Here are some patches to match push output to the new fetch output. The
entire output of "git-push" for my test repo now looks like this:
To file:///tmp/parent
+ f3325dc...3b91d1c hasforce -> mirror/hasforce (forced update)
f3325dc..bb022dc master -> mirror/master
! [rejected] needsforce -> mirror/needsforce (non-fast forward)
* [new branch] newbranch -> mirror/newbranch
* [new tag] v1.0 -> v1.0
Counting objects: 5, done.
Writing objects: 100% (3/3), done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
(where I'm mirroring the heads in refs/heads/mirror/* to show off the name
mapping).
The interesting part is in the first patch; the other two are just
removing clutter on stderr. The patches are based on 'next' to follow
Daniel's send-pack work. I'm happy to hold on to them and rebase once
things settle down in this area.
A few caveats:
- the output is generated by send-pack, so it's actually "here's what
I'm about to do" rather than "here's what has happened or is
happening." receive-pack could generate more accurate results
(including marking rejections based on hooks), but it has no
knowledge of the name mapping (so it can't say "foo -> other/foo").
Right now receive-pack generates 'error: blah blah blah' as it
always has.
- the name mapping is perhaps superfluous here, since most of the time
people are just pushing using the same branch name. But it matches
the new fetch output, and if you do use a refspec, it's a lot more
clear.
- this only covers send-pack, since that is where we do the ref
matching. Presumably it would be possible to cover pushing over http
and rsync, but it would have to be totally separate code.
- no code is shared with the git-fetch implementation (they just
happen to use the same format). The code is short enough that I
think it would just end up more confusing to try to factor out the
commonality.
- the 'ref is not up-to-date, maybe you need to push' message has gone
away in favor of the terse '[rejected] ... (non-fast forward)'. I
know there was some discussion recently of enhancing that message.
Is this perhaps too terse?
-Peff
^ permalink raw reply
* [PATCH] Add more tests for git-clean
From: Shawn Bohrer @ 2007-11-05 4:28 UTC (permalink / raw)
To: gitster; +Cc: madcoder, git, Shawn Bohrer
Signed-off-by: Shawn Bohrer <shawn.bohrer@gmail.com>
---
t/t7300-clean.sh | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 105 insertions(+), 0 deletions(-)
On Sun, Nov 04, 2007 at 04:17:47PM -0800, Junio C Hamano wrote:
> ...
> ( cd src && git-clean ) &&
> ...
>
> would be the best way to write this.
Agreed here is an updated patch that does this.
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index 8697213..25d3102 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -39,6 +39,93 @@ test_expect_success 'git-clean' '
'
+test_expect_success 'git-clean src/' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ git-clean src/ &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f src/part3.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git-clean src/ src/' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ git-clean src/ src/ &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f src/part3.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git-clean with prefix' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ (cd src/ && git-clean) &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f src/part3.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+test_expect_success 'git-clean -d with prefix and path' '
+
+ mkdir -p build docs src/feature &&
+ touch a.out src/part3.c src/feature/file.c docs/manual.txt obj.o build/lib.so &&
+ (cd src/ && git-clean -d feature/) &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f src/part3.c &&
+ test ! -f src/feature/file.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git-clean symbolic link' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ ln -s docs/manual.txt src/part4.c
+ git-clean &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test ! -f src/part3.c &&
+ test ! -f src/part4.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
test_expect_success 'git-clean -n' '
mkdir -p build docs &&
@@ -73,6 +160,24 @@ test_expect_success 'git-clean -d' '
'
+test_expect_success 'git-clean -d src/ examples/' '
+
+ mkdir -p build docs examples &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so examples/1.c &&
+ git-clean -d src/ examples/ &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f src/part3.c &&
+ test ! -f examples/1.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
test_expect_success 'git-clean -x' '
mkdir -p build docs &&
--
1.5.3.GIT
^ permalink raw reply related
* [PATCH] remove dead code from the csum-file interface
From: Nicolas Pitre @ 2007-11-05 3:54 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
The provided name argument is always constant and valid in every
caller's context, so no need to have an array of PATH_MAX chars to copy
it into when a simple pointer will do. Unfortunately that means getting
rid of wascally wabbits too.
The 'error' field is also unused.
Signed-off-by: Nicolas Pitre <nico@cam.org>
---
diff --git a/csum-file.c b/csum-file.c
index b445e6a..9728a99 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -88,22 +88,12 @@ struct sha1file *sha1fd(int fd, const char *name)
struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp)
{
- struct sha1file *f;
- unsigned len;
-
- f = xmalloc(sizeof(*f));
-
- len = strlen(name);
- if (len >= PATH_MAX)
- die("you wascally wabbit, you");
- f->namelen = len;
- memcpy(f->name, name, len+1);
-
+ struct sha1file *f = xmalloc(sizeof(*f));
f->fd = fd;
- f->error = 0;
f->offset = 0;
f->total = 0;
f->tp = tp;
+ f->name = name;
f->do_crc = 0;
SHA1_Init(&f->ctx);
return f;
diff --git a/csum-file.h b/csum-file.h
index a38cc3a..1af7656 100644
--- a/csum-file.h
+++ b/csum-file.h
@@ -5,12 +5,12 @@ struct progress;
/* A SHA1-protected file */
struct sha1file {
- int fd, error;
- unsigned int offset, namelen;
+ int fd;
+ unsigned int offset;
SHA_CTX ctx;
off_t total;
struct progress *tp;
- char name[PATH_MAX];
+ const char *name;
int do_crc;
uint32_t crc32;
unsigned char buffer[8192];
^ permalink raw reply related
* [PATCH] Use parseopts in builtin-push
From: Daniel Barkalow @ 2007-11-05 3:35 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
---
builtin-push.c | 88 +++++++++++++++++++++++---------------------------------
1 files changed, 36 insertions(+), 52 deletions(-)
diff --git a/builtin-push.c b/builtin-push.c
index 4b39ef3..2c56195 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -7,8 +7,12 @@
#include "builtin.h"
#include "remote.h"
#include "transport.h"
+#include "parse-options.h"
-static const char push_usage[] = "git-push [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]";
+static const char * const push_usage[] = {
+ "git-push [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]",
+ NULL,
+};
static int thin, verbose;
static const char *receivepack;
@@ -85,63 +89,43 @@ static int do_push(const char *repo, int flags)
int cmd_push(int argc, const char **argv, const char *prefix)
{
- int i;
int flags = 0;
+ int all = 0;
+ int dry_run = 0;
+ int force = 0;
+ int tags = 0;
const char *repo = NULL; /* default repository */
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ struct option options[] = {
+ OPT__VERBOSE(&verbose),
+ OPT_STRING( 0 , "repo", &repo, "repository", "repository"),
+ OPT_BOOLEAN( 0 , "all", &all, "push all refs"),
+ OPT_BOOLEAN( 0 , "tags", &tags, "push tags"),
+ OPT_BOOLEAN( 0 , "dry-run", &dry_run, "dry run"),
+ OPT_BOOLEAN('f', "force", &force, "force updates"),
+ OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"),
+ OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", "receive pack program"),
+ OPT_STRING( 0 , "exec", &receivepack, "receive-pack", "receive pack program"),
+ OPT_END()
+ };
- if (arg[0] != '-') {
- repo = arg;
- i++;
- break;
- }
- if (!strcmp(arg, "-v")) {
- verbose=1;
- continue;
- }
- if (!prefixcmp(arg, "--repo=")) {
- repo = arg+7;
- continue;
- }
- if (!strcmp(arg, "--all")) {
- flags |= TRANSPORT_PUSH_ALL;
- continue;
- }
- if (!strcmp(arg, "--dry-run")) {
- flags |= TRANSPORT_PUSH_DRY_RUN;
- continue;
- }
- if (!strcmp(arg, "--tags")) {
- add_refspec("refs/tags/*");
- continue;
- }
- if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
- flags |= TRANSPORT_PUSH_FORCE;
- continue;
- }
- if (!strcmp(arg, "--thin")) {
- thin = 1;
- continue;
- }
- if (!strcmp(arg, "--no-thin")) {
- thin = 0;
- continue;
- }
- if (!prefixcmp(arg, "--receive-pack=")) {
- receivepack = arg + 15;
- continue;
- }
- if (!prefixcmp(arg, "--exec=")) {
- receivepack = arg + 7;
- continue;
- }
- usage(push_usage);
+ argc = parse_options(argc, argv, options, push_usage, 0);
+
+ if (force)
+ flags |= TRANSPORT_PUSH_FORCE;
+ if (dry_run)
+ flags |= TRANSPORT_PUSH_DRY_RUN;
+ if (tags)
+ add_refspec("refs/tags/*");
+ if (all)
+ flags |= TRANSPORT_PUSH_ALL;
+
+ if (argc > 0) {
+ repo = argv[0];
+ set_refspecs(argv + 1, argc - 1);
}
- set_refspecs(argv + i, argc - i);
if ((flags & TRANSPORT_PUSH_ALL) && refspec)
- usage(push_usage);
+ usage_with_options(push_usage, options);
return do_push(repo, flags);
}
--
1.5.3.5.1528.gb6568-dirty
^ permalink raw reply related
* [PATCH] Use parseopts in builtin-fetch
From: Daniel Barkalow @ 2007-11-05 3:35 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
---
I mostly did this and the next one for practice with the API. I'm
impressed that "git fetch -vv" is even handled correctly without anything
special. Now that I've done it, assuming I did it right, it might as well
get added to the series.
builtin-fetch.c | 95 +++++++++++++++---------------------------------------
1 files changed, 27 insertions(+), 68 deletions(-)
diff --git a/builtin-fetch.c b/builtin-fetch.c
index 6b1750d..a079cb0 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -8,8 +8,12 @@
#include "path-list.h"
#include "remote.h"
#include "transport.h"
+#include "parse-options.h"
-static const char fetch_usage[] = "git-fetch [-a | --append] [--upload-pack <upload-pack>] [-f | --force] [--no-tags] [-t | --tags] [-k | --keep] [-u | --update-head-ok] [--depth <depth>] [-v | --verbose] [<repository> <refspec>...]";
+static const char * const fetch_usage[] = {
+ "git-fetch [option] [<repository> <refspec>...]",
+ NULL
+};
static int append, force, tags, no_tags, update_head_ok, verbose, quiet;
static char *default_rla = NULL;
@@ -470,71 +474,21 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
int cmd_len = 0;
const char *depth = NULL, *upload_pack = NULL;
int keep = 0;
-
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
- cmd_len += strlen(arg);
-
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--append") || !strcmp(arg, "-a")) {
- append = 1;
- continue;
- }
- if (!prefixcmp(arg, "--upload-pack=")) {
- upload_pack = arg + 14;
- continue;
- }
- if (!strcmp(arg, "--upload-pack")) {
- i++;
- if (i == argc)
- usage(fetch_usage);
- upload_pack = argv[i];
- continue;
- }
- if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
- force = 1;
- continue;
- }
- if (!strcmp(arg, "--no-tags")) {
- no_tags = 1;
- continue;
- }
- if (!strcmp(arg, "--tags") || !strcmp(arg, "-t")) {
- tags = 1;
- continue;
- }
- if (!strcmp(arg, "--keep") || !strcmp(arg, "-k")) {
- keep = 1;
- continue;
- }
- if (!strcmp(arg, "--update-head-ok") || !strcmp(arg, "-u")) {
- update_head_ok = 1;
- continue;
- }
- if (!prefixcmp(arg, "--depth=")) {
- depth = arg + 8;
- continue;
- }
- if (!strcmp(arg, "--depth")) {
- i++;
- if (i == argc)
- usage(fetch_usage);
- depth = argv[i];
- continue;
- }
- if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) {
- quiet = 1;
- continue;
- }
- if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) {
- verbose++;
- continue;
- }
- usage(fetch_usage);
- }
-
- for (j = i; j < argc; j++)
+ struct option options[] = {
+ OPT__VERBOSE(&verbose),
+ OPT_BOOLEAN('a', "append", &append, "append fetched data to exisitng FETCH_HEAD"),
+ OPT_BOOLEAN('f', "force", &force, "force"),
+ OPT_BOOLEAN( 0 , "no-tags", &no_tags, "don't fetch tags"),
+ OPT_BOOLEAN('t', "tags", &tags, "fetch all tags"),
+ OPT_BOOLEAN('k', "keep", &keep, "keep pack file"),
+ OPT_STRING( 0, "depth", &depth, "depth", "deepen the repository by <depth> commits"),
+ OPT_BOOLEAN('u', "update-head-ok", &update_head_ok, "allow updating head"),
+ OPT_BOOLEAN('q', "quiet", &quiet, "fetch silently"),
+ OPT_STRING( 0 , "upload-pack", &upload_pack, "upload-pack", "remote executable for git-upload-pack"),
+ OPT_END(),
+ };
+
+ for (j = 1; j < argc; j++)
cmd_len += strlen(argv[j]);
default_rla = xmalloc(cmd_len + 5 + argc + 1);
@@ -545,12 +499,16 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
rla_offset += strlen(argv[j]) + 1;
}
- if (i == argc)
+ argc = parse_options(argc, argv, options, fetch_usage, 0);
+
+ if (argc == 0)
remote = remote_get(NULL);
else
- remote = remote_get(argv[i++]);
+ remote = remote_get(argv[0]);
transport = transport_get(remote, remote->url[0]);
+ if (!transport)
+ die("couldn't get transport");
if (verbose >= 2)
transport->verbose = 1;
if (quiet)
@@ -565,6 +523,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (!transport->url)
die("Where do you want to fetch from today?");
+ i = 1;
if (i < argc) {
int j = 0;
refs = xcalloc(argc - i + 1, sizeof(const char *));
--
1.5.3.5.1528.gb6568-dirty
^ permalink raw reply related
* [PATCH] make display of total transferred more accurate
From: Nicolas Pitre @ 2007-11-05 3:15 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
The throughput display needs a delay period before accounting and
displaying anything. Yet it might be called after some amount of data
has already been transferred. The display of total data is therefore
accounted late and therefore smaller than the reality.
Let's call display_throughput() with an absolute amount of transferred
data instead of a relative number, and let the throughput code find the
relative amount of data by itself as needed. This way the displayed
total is always exact.
Signed-off-by: Nicolas Pitre <nico@cam.org>
---
diff --git a/csum-file.c b/csum-file.c
index 3729e73..b445e6a 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -18,7 +18,8 @@ static void sha1flush(struct sha1file *f, unsigned int count)
for (;;) {
int ret = xwrite(f->fd, buf, count);
if (ret > 0) {
- display_throughput(f->tp, ret);
+ f->total += ret;
+ display_throughput(f->tp, f->total);
buf = (char *) buf + ret;
count -= ret;
if (count)
@@ -101,6 +102,7 @@ struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp
f->fd = fd;
f->error = 0;
f->offset = 0;
+ f->total = 0;
f->tp = tp;
f->do_crc = 0;
SHA1_Init(&f->ctx);
diff --git a/csum-file.h b/csum-file.h
index 4d1b231..a38cc3a 100644
--- a/csum-file.h
+++ b/csum-file.h
@@ -8,6 +8,7 @@ struct sha1file {
int fd, error;
unsigned int offset, namelen;
SHA_CTX ctx;
+ off_t total;
struct progress *tp;
char name[PATH_MAX];
int do_crc;
diff --git a/index-pack.c b/index-pack.c
index 715a5bb..581a7f5 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -87,9 +87,9 @@ static void *fill(int min)
die("early EOF");
die("read error on input: %s", strerror(errno));
}
- if (from_stdin)
- display_throughput(progress, ret);
input_len += ret;
+ if (from_stdin)
+ display_throughput(progress, consumed_bytes + input_len);
} while (input_len < min);
return input_buffer;
}
diff --git a/progress.c b/progress.c
index 3f6a602..a963bd8 100644
--- a/progress.c
+++ b/progress.c
@@ -14,11 +14,10 @@
#define TP_IDX_MAX 8
struct throughput {
+ off_t prev_total;
struct timeval prev_tv;
- off_t total;
- unsigned long count;
- unsigned long avg_bytes;
- unsigned long last_bytes[TP_IDX_MAX];
+ unsigned int avg_bytes;
+ unsigned int last_bytes[TP_IDX_MAX];
unsigned int avg_misecs;
unsigned int last_misecs[TP_IDX_MAX];
unsigned int idx;
@@ -110,7 +109,7 @@ static int display(struct progress *progress, unsigned n, int done)
return 0;
}
-void display_throughput(struct progress *progress, unsigned long n)
+void display_throughput(struct progress *progress, off_t total)
{
struct throughput *tp;
struct timeval tv;
@@ -124,14 +123,13 @@ void display_throughput(struct progress *progress, unsigned long n)
if (!tp) {
progress->throughput = tp = calloc(1, sizeof(*tp));
- if (tp)
+ if (tp) {
+ tp->prev_total = total;
tp->prev_tv = tv;
+ }
return;
}
- tp->total += n;
- tp->count += n;
-
/*
* We have x = bytes and y = microsecs. We want z = KiB/s:
*
@@ -152,37 +150,37 @@ void display_throughput(struct progress *progress, unsigned long n)
if (misecs > 512) {
int l = sizeof(tp->display);
+ unsigned int count = total - tp->prev_total;
+ tp->prev_total = total;
tp->prev_tv = tv;
- tp->avg_bytes += tp->count;
+ tp->avg_bytes += count;
tp->avg_misecs += misecs;
- if (tp->total > 1 << 30) {
+ if (total > 1 << 30) {
l -= snprintf(tp->display, l, ", %u.%2.2u GiB",
- (int)(tp->total >> 30),
- (int)(tp->total & ((1 << 30) - 1)) / 10737419);
- } else if (tp->total > 1 << 20) {
+ (int)(total >> 30),
+ (int)(total & ((1 << 30) - 1)) / 10737419);
+ } else if (total > 1 << 20) {
l -= snprintf(tp->display, l, ", %u.%2.2u MiB",
- (int)(tp->total >> 20),
- ((int)(tp->total & ((1 << 20) - 1))
+ (int)(total >> 20),
+ ((int)(total & ((1 << 20) - 1))
* 100) >> 20);
- } else if (tp->total > 1 << 10) {
+ } else if (total > 1 << 10) {
l -= snprintf(tp->display, l, ", %u.%2.2u KiB",
- (int)(tp->total >> 10),
- ((int)(tp->total & ((1 << 10) - 1))
+ (int)(total >> 10),
+ ((int)(total & ((1 << 10) - 1))
* 100) >> 10);
} else {
- l -= snprintf(tp->display, l, ", %u bytes",
- (int)tp->total);
+ l -= snprintf(tp->display, l, ", %u bytes", (int)total);
}
snprintf(tp->display + sizeof(tp->display) - l, l,
- " | %lu KiB/s", tp->avg_bytes / tp->avg_misecs);
+ " | %u KiB/s", tp->avg_bytes / tp->avg_misecs);
tp->avg_bytes -= tp->last_bytes[tp->idx];
tp->avg_misecs -= tp->last_misecs[tp->idx];
- tp->last_bytes[tp->idx] = tp->count;
+ tp->last_bytes[tp->idx] = count;
tp->last_misecs[tp->idx] = misecs;
tp->idx = (tp->idx + 1) % TP_IDX_MAX;
- tp->count = 0;
if (progress->last_value != -1 && progress_update)
display(progress, progress->last_value, 0);
diff --git a/progress.h b/progress.h
index 61cb68d..3912969 100644
--- a/progress.h
+++ b/progress.h
@@ -3,7 +3,7 @@
struct progress;
-void display_throughput(struct progress *progress, unsigned long n);
+void display_throughput(struct progress *progress, off_t total);
int display_progress(struct progress *progress, unsigned n);
struct progress *start_progress(const char *title, unsigned total);
struct progress *start_progress_delay(const char *title, unsigned total,
^ permalink raw reply related
* [StGit PATCH 5/5] Add "stg coalesce"
From: Karl Hasselström @ 2007-11-05 3:15 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git
In-Reply-To: <20071105030847.6108.44653.stgit@yoghurt>
It coalesces two or more consecutive applied patches, with no need to
touch index/worktree, and no possibiliy of conflicts.
Future improvements could relax the "consecutive" and "applied"
restrictions, by building a new chain of commits just like "stg push"
will do once it's been converted to the new infrastructure.
Signed-off-by: Karl Hasselström <kha@treskal.com>
---
stgit/commands/coalesce.py | 87 ++++++++++++++++++++++++++++++++++++++++++++
stgit/main.py | 2 +
stgit/utils.py | 11 ++++++
t/t2600-coalesce.sh | 31 ++++++++++++++++
4 files changed, 131 insertions(+), 0 deletions(-)
create mode 100644 stgit/commands/coalesce.py
create mode 100755 t/t2600-coalesce.sh
diff --git a/stgit/commands/coalesce.py b/stgit/commands/coalesce.py
new file mode 100644
index 0000000..11b7b52
--- /dev/null
+++ b/stgit/commands/coalesce.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+
+__copyright__ = """
+Copyright (C) 2007, Karl Hasselström <kha@treskal.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+from optparse import make_option
+from stgit.out import *
+from stgit import utils
+from stgit.commands import common
+from stgit.lib import git, transaction
+
+help = 'coalesce two or more patches into one'
+usage = """%prog [options] <patches>
+
+Coalesce two or more patches, creating one big patch that contains all
+their changes. The patches must all be applied, and must be
+consecutive."""
+
+directory = common.DirectoryHasRepositoryLib()
+options = [make_option('-n', '--name', help = 'name of coalesced patch'),
+ make_option('-m', '--message',
+ help = 'commit message of coalesced patch')]
+
+def _coalesce(stack, name, msg, patches):
+ applied = stack.patchorder.applied
+
+ # Make sure the patches are consecutive.
+ applied_ix = dict((applied[i], i) for i in xrange(len(applied)))
+ ixes = list(sorted(applied_ix[p] for p in patches))
+ i0, i1 = ixes[0], ixes[-1]
+ if i1 - i0 + 1 != len(patches):
+ raise common.CmdException('The patches must be consecutive')
+
+ # Make a commit for the coalesced patch.
+ def bad_name(pn):
+ return pn not in patches and stack.patches.exists(pn)
+ if name and bad_name(name):
+ raise common.CmdException('Patch name "%s" already taken')
+ ps = [stack.patches.get(pn) for pn in applied[i0:i1+1]]
+ if msg == None:
+ msg = '\n\n'.join('%s\n\n%s' % (p.name.ljust(70, '-'),
+ p.commit.data.message)
+ for p in ps)
+ msg = utils.edit_string(msg, '.stgit-coalesce.txt').strip()
+ if not name:
+ name = utils.make_patch_name(msg, bad_name)
+ cd = git.Commitdata(tree = ps[-1].commit.data.tree,
+ parents = ps[0].commit.data.parents, message = msg)
+
+ # Rewrite refs.
+ trans = transaction.StackTransaction(stack, 'coalesce')
+ parent = trans.patches[name] = stack.repository.commit(cd)
+ trans.applied = applied[:i0]
+ trans.applied.append(name)
+ for pn in applied[i0:i1+1]:
+ trans.patches[pn] = None
+ for pn in applied[i1+1:]:
+ p = stack.patches.get(pn)
+ parent = trans.patches[pn] = stack.repository.commit(
+ p.commit.data.set_parent(parent))
+ trans.applied.append(pn)
+ trans.run()
+
+def func(parser, options, args):
+ stack = directory.repository.current_stack
+ applied = set(stack.patchorder.applied)
+ for pn in args:
+ if not pn in applied:
+ raise common.CmdException('%s is not applied' % pn)
+ patches = set(args)
+ if len(patches) < 2:
+ raise common.CmdException('Need at least two patches')
+ _coalesce(stack, options.name, options.message, args)
diff --git a/stgit/main.py b/stgit/main.py
index 9ef6d44..ac46cde 100644
--- a/stgit/main.py
+++ b/stgit/main.py
@@ -65,6 +65,7 @@ commands = Commands({
'diff': 'diff',
'clean': 'clean',
'clone': 'clone',
+ 'coalesce': 'coalesce',
'commit': 'commit',
'edit': 'edit',
'export': 'export',
@@ -109,6 +110,7 @@ stackcommands = (
'assimilate',
'branch',
'clean',
+ 'coalesce',
'commit',
'float',
'goto',
diff --git a/stgit/utils.py b/stgit/utils.py
index b3f6232..688276c 100644
--- a/stgit/utils.py
+++ b/stgit/utils.py
@@ -189,6 +189,17 @@ def call_editor(filename):
raise EditorException, 'editor failed, exit code: %d' % err
out.done()
+def edit_string(s, filename):
+ f = file(filename, 'w')
+ f.write(s)
+ f.close()
+ call_editor(filename)
+ f = file(filename)
+ s = f.read()
+ f.close()
+ os.remove(filename)
+ return s
+
def patch_name_from_msg(msg):
"""Return a string to be used as a patch name. This is generated
from the top line of the string passed as argument."""
diff --git a/t/t2600-coalesce.sh b/t/t2600-coalesce.sh
new file mode 100755
index 0000000..f13a309
--- /dev/null
+++ b/t/t2600-coalesce.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='Run "stg coalesce"'
+
+. ./test-lib.sh
+
+test_expect_success 'Initialize StGit stack' '
+ stg init &&
+ for i in 0 1 2 3; do
+ stg new p$i -m "foo $i" &&
+ echo "foo $i" >> foo.txt &&
+ git add foo.txt &&
+ stg refresh
+ done
+'
+
+test_expect_success 'Coalesce some patches' '
+ [ "$(echo $(stg applied))" = "p0 p1 p2 p3" ] &&
+ [ "$(echo $(stg unapplied))" = "" ] &&
+ stg coalesce --name=q0 --message="wee woo" p1 p2 &&
+ [ "$(echo $(stg applied))" = "p0 q0 p3" ] &&
+ [ "$(echo $(stg unapplied))" = "" ]
+'
+
+test_expect_success 'Coalesce at stack top' '
+ stg coalesce --name=q1 --message="wee woo wham" q0 p3 &&
+ [ "$(echo $(stg applied))" = "p0 q1" ] &&
+ [ "$(echo $(stg unapplied))" = "" ]
+'
+
+test_done
^ permalink raw reply related
* [StGit PATCH 3/5] Simple test for "stg clean"
From: Karl Hasselström @ 2007-11-05 3:14 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git
In-Reply-To: <20071105030847.6108.44653.stgit@yoghurt>
Signed-off-by: Karl Hasselström <kha@treskal.com>
---
t/t2500-clean.sh | 27 +++++++++++++++++++++++++++
1 files changed, 27 insertions(+), 0 deletions(-)
create mode 100755 t/t2500-clean.sh
diff --git a/t/t2500-clean.sh b/t/t2500-clean.sh
new file mode 100755
index 0000000..3364c18
--- /dev/null
+++ b/t/t2500-clean.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+test_description='Run "stg clean"'
+
+. ./test-lib.sh
+
+test_expect_success 'Initialize StGit stack' '
+ stg init &&
+ stg new e0 -m e0 &&
+ stg new p0 -m p0 &&
+ echo foo > foo.txt &&
+ git add foo.txt &&
+ stg refresh &&
+ stg new e1 -m e1 &&
+ stg new e2 -m e2 &&
+ stg pop
+'
+
+test_expect_success 'Clean empty patches' '
+ [ "$(echo $(stg applied))" = "e0 p0 e1" ] &&
+ [ "$(echo $(stg unapplied))" = "e2" ] &&
+ stg clean &&
+ [ "$(echo $(stg applied))" = "p0" ] &&
+ [ "$(echo $(stg unapplied))" = "" ]
+'
+
+test_done
^ permalink raw reply related
* [StGit PATCH 4/5] Let "stg clean" use the new infrastructure
From: Karl Hasselström @ 2007-11-05 3:14 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git
In-Reply-To: <20071105030847.6108.44653.stgit@yoghurt>
TODO:
* detect uninitialized stack
* upgrade stack
Signed-off-by: Karl Hasselström <kha@treskal.com>
---
stgit/commands/clean.py | 68 ++++++++++++++++++++++++----------------------
stgit/commands/common.py | 10 ++++++-
2 files changed, 44 insertions(+), 34 deletions(-)
diff --git a/stgit/commands/clean.py b/stgit/commands/clean.py
index 4484ecd..ad0b920 100644
--- a/stgit/commands/clean.py
+++ b/stgit/commands/clean.py
@@ -15,14 +15,10 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
-import sys, os
-from optparse import OptionParser, make_option
-
-from stgit.commands.common import *
-from stgit.utils import *
+from optparse import make_option
from stgit.out import *
-from stgit import stack, git
-
+from stgit.commands import common
+from stgit.lib import transaction
help = 'delete the empty patches in the series'
usage = """%prog [options]
@@ -31,7 +27,7 @@ Delete the empty patches in the whole series or only those applied or
unapplied. A patch is considered empty if the two commit objects
representing its boundaries refer to the same tree object."""
-directory = DirectoryHasRepository()
+directory = common.DirectoryHasRepositoryLib()
options = [make_option('-a', '--applied',
help = 'delete the empty applied patches',
action = 'store_true'),
@@ -40,18 +36,35 @@ options = [make_option('-a', '--applied',
action = 'store_true')]
-def __delete_empty(patches, applied):
- """Delete the empty patches
- """
- for p in patches:
- if crt_series.empty_patch(p):
- out.start('Deleting patch "%s"' % p)
- if applied and crt_series.patch_applied(p):
- crt_series.pop_patch(p)
- crt_series.delete_patch(p)
- out.done()
- elif applied and crt_series.patch_unapplied(p):
- crt_series.push_patch(p)
+def _clean(stack, clean_applied, clean_unapplied):
+ def deleting(pn):
+ out.info('Deleting empty patch %s' % pn)
+ trans = transaction.StackTransaction(stack, 'clean')
+ if clean_unapplied:
+ trans.unapplied = []
+ for pn in stack.patchorder.unapplied:
+ p = stack.patches.get(pn)
+ if p.is_empty():
+ trans.patches[pn] = None
+ deleting(pn)
+ else:
+ trans.unapplied.append[pn]
+ if clean_applied:
+ trans.applied = []
+ parent = stack.base
+ for pn in stack.patchorder.applied:
+ p = stack.patches.get(pn)
+ if p.is_empty():
+ trans.patches[pn] = None
+ deleting(pn)
+ else:
+ if parent != p.commit.data.parent:
+ parent = trans.patches[pn] = stack.repository.commit(
+ p.commit.data.set_parent(parent))
+ else:
+ parent = p.commit
+ trans.applied.append(pn)
+ trans.run()
def func(parser, options, args):
"""Delete the empty patches in the series
@@ -59,19 +72,8 @@ def func(parser, options, args):
if len(args) != 0:
parser.error('incorrect number of arguments')
- check_local_changes()
- check_conflicts()
- check_head_top_equal(crt_series)
-
if not (options.applied or options.unapplied):
options.applied = options.unapplied = True
- if options.applied:
- applied = crt_series.get_applied()
- __delete_empty(applied, True)
-
- if options.unapplied:
- unapplied = crt_series.get_unapplied()
- __delete_empty(unapplied, False)
-
- print_crt_patch(crt_series)
+ _clean(directory.repository.current_stack,
+ options.applied, options.unapplied)
diff --git a/stgit/commands/common.py b/stgit/commands/common.py
index ceeebbc..0a017a8 100644
--- a/stgit/commands/common.py
+++ b/stgit/commands/common.py
@@ -27,7 +27,7 @@ from stgit.out import *
from stgit.run import *
from stgit import stack, git, basedir
from stgit.config import config, file_extensions
-
+from stgit.lib import stack as libstack
# Command exception class
class CmdException(StgException):
@@ -537,3 +537,11 @@ class DirectoryGotoToplevel(DirectoryInWorktree):
def setup(self):
DirectoryInWorktree.setup(self)
self.cd_to_topdir()
+
+class DirectoryHasRepositoryLib(_Directory):
+ """For commands that use the new infrastructure in stgit.lib.*."""
+ def __init__(self):
+ self.needs_current_series = False
+ def setup(self):
+ # This will throw an exception if we don't have a repository.
+ self.repository = libstack.Repository.default()
^ permalink raw reply related
* [StGit PATCH 2/5] Write metadata files used by the old infrastructure
From: Karl Hasselström @ 2007-11-05 3:14 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git
In-Reply-To: <20071105030847.6108.44653.stgit@yoghurt>
The new infrastructure doesn't use them, but they're needed to support
the old infrastructure during the transition when both of them are in
use.
Signed-off-by: Karl Hasselström <kha@treskal.com>
---
stgit/lib/stack.py | 35 +++++++++++++++++++++++++++++++++++
1 files changed, 35 insertions(+), 0 deletions(-)
diff --git a/stgit/lib/stack.py b/stgit/lib/stack.py
index b4d99ba..ce514d6 100644
--- a/stgit/lib/stack.py
+++ b/stgit/lib/stack.py
@@ -9,10 +9,45 @@ class Patch(object):
name = property(lambda self: self.__name)
def __ref(self):
return 'refs/patches/%s/%s' % (self.__stack.name, self.__name)
+ def __log_ref(self):
+ return self.__ref() + '.log'
@property
def commit(self):
return self.__stack.repository.refs.get(self.__ref())
+ def __write_compat_files(self, new_commit, msg):
+ """Write files used by the old infrastructure."""
+ fdir = os.path.join(self.__stack.directory, 'patches', self.__name)
+ def write(name, val, multiline = False):
+ fn = os.path.join(fdir, name)
+ if val:
+ utils.write_string(fn, val, multiline)
+ elif os.path.isfile(fn):
+ os.remove(fn)
+ def write_patchlog():
+ try:
+ old_log = [self.__stack.repository.refs.get(self.__log_ref())]
+ except KeyError:
+ old_log = []
+ cd = git.Commitdata(tree = new_commit.data.tree, parents = old_log,
+ message = '%s\t%s' % (msg, new_commit.sha1))
+ c = self.__stack.repository.commit(cd)
+ self.__stack.repository.refs.set(self.__log_ref(), c, msg)
+ return c
+ d = new_commit.data
+ write('authname', d.author.name)
+ write('authemail', d.author.email)
+ write('authdate', d.author.date)
+ write('commname', d.committer.name)
+ write('commemail', d.committer.email)
+ write('description', d.message)
+ write('log', write_patchlog().sha1)
+ try:
+ old_commit_sha1 = self.commit
+ except KeyError:
+ old_commit_sha1 = None
+ write('top.old', old_commit_sha1)
def set_commit(self, commit, msg):
+ self.__write_compat_files(commit, msg)
self.__stack.repository.refs.set(self.__ref(), commit, msg)
def delete(self):
self.__stack.repository.refs.delete(self.__ref())
^ permalink raw reply related
* [StGit PATCH 1/5] New StGit core infrastructure: repository operations
From: Karl Hasselström @ 2007-11-05 3:14 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git
In-Reply-To: <20071105030847.6108.44653.stgit@yoghurt>
This is the first part of the New and Improved StGit core
infrastructure. It has functions for manipulating the git repository
(commits, refs, and so on), but doesn't yet touch the index or
worktree.
Currently not used by anything.
Signed-off-by: Karl Hasselström <kha@treskal.com>
---
stgit/lib/__init__.py | 18 +++
stgit/lib/git.py | 245 ++++++++++++++++++++++++++++++++++++++++++++++
stgit/lib/stack.py | 121 +++++++++++++++++++++++
stgit/lib/transaction.py | 79 +++++++++++++++
stgit/utils.py | 13 ++
5 files changed, 476 insertions(+), 0 deletions(-)
create mode 100644 stgit/lib/__init__.py
create mode 100644 stgit/lib/git.py
create mode 100644 stgit/lib/stack.py
create mode 100644 stgit/lib/transaction.py
diff --git a/stgit/lib/__init__.py b/stgit/lib/__init__.py
new file mode 100644
index 0000000..45eb307
--- /dev/null
+++ b/stgit/lib/__init__.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+
+__copyright__ = """
+Copyright (C) 2007, Karl Hasselström <kha@treskal.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
diff --git a/stgit/lib/git.py b/stgit/lib/git.py
new file mode 100644
index 0000000..1b873e3
--- /dev/null
+++ b/stgit/lib/git.py
@@ -0,0 +1,245 @@
+import os, os.path, re
+from stgit import exception, run, utils
+
+class RepositoryException(exception.StgException):
+ pass
+
+class DetachedHeadException(RepositoryException):
+ pass
+
+class Repr(object):
+ def __repr__(self):
+ return str(self)
+
+class NoValue(object):
+ pass
+
+def make_defaults(defaults):
+ def d(val, attr):
+ if val != NoValue:
+ return val
+ elif defaults != NoValue:
+ return getattr(defaults, attr)
+ else:
+ return None
+ return d
+
+class Person(Repr):
+ """Immutable."""
+ def __init__(self, name = NoValue, email = NoValue,
+ date = NoValue, defaults = NoValue):
+ d = make_defaults(defaults)
+ self.__name = d(name, 'name')
+ self.__email = d(email, 'email')
+ self.__date = d(date, 'date')
+ name = property(lambda self: self.__name)
+ email = property(lambda self: self.__email)
+ date = property(lambda self: self.__date)
+ def set_name(self, name):
+ return type(self)(name = name, defaults = self)
+ def set_email(self, email):
+ return type(self)(email = email, defaults = self)
+ def set_date(self, date):
+ return type(self)(date = date, defaults = self)
+ def __str__(self):
+ return '%s <%s> %s' % (self.name, self.email, self.date)
+ @classmethod
+ def parse(cls, s):
+ m = re.match(r'^([^<]*)<([^>]*)>\s+(\d+\s+[+-]\d{4})$', s)
+ assert m
+ name = m.group(1).strip()
+ email = m.group(2)
+ date = m.group(3)
+ return cls(name, email, date)
+
+class Tree(Repr):
+ """Immutable."""
+ def __init__(self, sha1):
+ self.__sha1 = sha1
+ sha1 = property(lambda self: self.__sha1)
+ def __str__(self):
+ return 'Tree<%s>' % self.sha1
+
+class Commitdata(Repr):
+ """Immutable."""
+ def __init__(self, tree = NoValue, parents = NoValue, author = NoValue,
+ committer = NoValue, message = NoValue, defaults = NoValue):
+ d = make_defaults(defaults)
+ self.__tree = d(tree, 'tree')
+ self.__parents = d(parents, 'parents')
+ self.__author = d(author, 'author')
+ self.__committer = d(committer, 'committer')
+ self.__message = d(message, 'message')
+ tree = property(lambda self: self.__tree)
+ parents = property(lambda self: self.__parents)
+ @property
+ def parent(self):
+ assert len(self.__parents) == 1
+ return self.__parents[0]
+ author = property(lambda self: self.__author)
+ committer = property(lambda self: self.__committer)
+ message = property(lambda self: self.__message)
+ def set_tree(self, tree):
+ return type(self)(tree = tree, defaults = self)
+ def set_parents(self, parents):
+ return type(self)(parents = parents, defaults = self)
+ def add_parent(self, parent):
+ return type(self)(parents = list(self.parents or []) + [parent],
+ defaults = self)
+ def set_parent(self, parent):
+ return self.set_parents([parent])
+ def set_author(self, author):
+ return type(self)(author = author, defaults = self)
+ def set_committer(self, committer):
+ return type(self)(committer = committer, defaults = self)
+ def set_message(self, message):
+ return type(self)(message = message, defaults = self)
+ def __str__(self):
+ if self.tree == None:
+ tree = None
+ else:
+ tree = self.tree.sha1
+ if self.parents == None:
+ parents = None
+ else:
+ parents = [p.sha1 for p in self.parents]
+ return ('Commitdata<tree: %s, parents: %s, author: %s,'
+ ' committer: %s, message: "%s">'
+ ) % (tree, parents, self.author, self.committer, self.message)
+ @classmethod
+ def parse(cls, repository, s):
+ cd = cls()
+ lines = list(s.splitlines(True))
+ for i in xrange(len(lines)):
+ line = lines[i].strip()
+ if not line:
+ return cd.set_message(''.join(lines[i+1:]))
+ key, value = line.split(None, 1)
+ if key == 'tree':
+ cd = cd.set_tree(repository.get_tree(value))
+ elif key == 'parent':
+ cd = cd.add_parent(repository.get_commit(value))
+ elif key == 'author':
+ cd = cd.set_author(Person.parse(value))
+ elif key == 'committer':
+ cd = cd.set_committer(Person.parse(value))
+ else:
+ assert False
+ assert False
+
+class Commit(Repr):
+ """Immutable."""
+ def __init__(self, repository, sha1):
+ self.__sha1 = sha1
+ self.__repository = repository
+ self.__data = None
+ sha1 = property(lambda self: self.__sha1)
+ @property
+ def data(self):
+ if self.__data == None:
+ self.__data = Commitdata.parse(
+ self.__repository,
+ self.__repository.cat_object(self.sha1))
+ return self.__data
+ def __str__(self):
+ return 'Commit<sha1: %s, data: %s>' % (self.sha1, self.__data)
+
+class Refs(object):
+ def __init__(self, repository):
+ self.__repository = repository
+ self.__refs = None
+ def __cache_refs(self):
+ self.__refs = {}
+ for line in self.__repository.run(['git', 'show-ref']).output_lines():
+ m = re.match(r'^([0-9a-f]{40})\s+(\S+)$', line)
+ sha1, ref = m.groups()
+ self.__refs[ref] = sha1
+ def get(self, ref):
+ """Throws KeyError if ref doesn't exist."""
+ if self.__refs == None:
+ self.__cache_refs()
+ return self.__repository.get_commit(self.__refs[ref])
+ def set(self, ref, commit, msg):
+ if self.__refs == None:
+ self.__cache_refs()
+ old_sha1 = self.__refs.get(ref, '0'*40)
+ new_sha1 = commit.sha1
+ if old_sha1 != new_sha1:
+ self.__repository.run(['git', 'update-ref', '-m', msg,
+ ref, new_sha1, old_sha1]).no_output()
+ self.__refs[ref] = new_sha1
+ def delete(self, ref):
+ if self.__refs == None:
+ self.__cache_refs()
+ self.__repository.run(['git', 'update-ref',
+ '-d', ref, self.__refs[ref]]).no_output()
+ del self.__refs[ref]
+
+class ObjectCache(object):
+ """Cache for Python objects, for making sure that we create only one
+ Python object per git object."""
+ def __init__(self, create):
+ self.__objects = {}
+ self.__create = create
+ def __getitem__(self, name):
+ if not name in self.__objects:
+ self.__objects[name] = self.__create(name)
+ return self.__objects[name]
+ def __contains__(self, name):
+ return name in self.__objects
+ def __setitem__(self, name, val):
+ assert not name in self.__objects
+ self.__objects[name] = val
+
+class RunWithEnv(object):
+ def run(self, args, env = {}):
+ return run.Run(*args).env(utils.add_dict(self.env, env))
+
+class Repository(RunWithEnv):
+ def __init__(self, directory):
+ self.__git_dir = directory
+ self.__refs = Refs(self)
+ self.__trees = ObjectCache(lambda sha1: Tree(sha1))
+ self.__commits = ObjectCache(lambda sha1: Commit(self, sha1))
+ env = property(lambda self: { 'GIT_DIR': self.__git_dir })
+ @classmethod
+ def default(cls):
+ """Return the default repository."""
+ try:
+ return cls(run.Run('git', 'rev-parse', '--git-dir'
+ ).output_one_line())
+ except run.RunException:
+ raise RepositoryException('Cannot find git repository')
+ directory = property(lambda self: self.__git_dir)
+ refs = property(lambda self: self.__refs)
+ def cat_object(self, sha1):
+ return self.run(['git', 'cat-file', '-p', sha1]).raw_output()
+ def get_tree(self, sha1):
+ return self.__trees[sha1]
+ def get_commit(self, sha1):
+ return self.__commits[sha1]
+ def commit(self, commitdata):
+ c = ['git', 'commit-tree', commitdata.tree.sha1]
+ for p in commitdata.parents:
+ c.append('-p')
+ c.append(p.sha1)
+ env = {}
+ for p, v1 in ((commitdata.author, 'AUTHOR'),
+ (commitdata.committer, 'COMMITTER')):
+ if p != None:
+ for attr, v2 in (('name', 'NAME'), ('email', 'EMAIL'),
+ ('date', 'DATE')):
+ if getattr(p, attr) != None:
+ env['GIT_%s_%s' % (v1, v2)] = getattr(p, attr)
+ sha1 = self.run(c, env = env).raw_input(commitdata.message
+ ).output_one_line()
+ return self.get_commit(sha1)
+ @property
+ def head(self):
+ try:
+ return self.run(['git', 'symbolic-ref', '-q', 'HEAD']
+ ).output_one_line()
+ except run.RunException:
+ raise DetachedHeadException()
+ def set_head(self, ref, msg):
+ self.run(['git', 'symbolic-ref', '-m', msg, 'HEAD', ref]).no_output()
diff --git a/stgit/lib/stack.py b/stgit/lib/stack.py
new file mode 100644
index 0000000..b4d99ba
--- /dev/null
+++ b/stgit/lib/stack.py
@@ -0,0 +1,121 @@
+import os.path
+from stgit import utils
+from stgit.lib import git
+
+class Patch(object):
+ def __init__(self, stack, name):
+ self.__stack = stack
+ self.__name = name
+ name = property(lambda self: self.__name)
+ def __ref(self):
+ return 'refs/patches/%s/%s' % (self.__stack.name, self.__name)
+ @property
+ def commit(self):
+ return self.__stack.repository.refs.get(self.__ref())
+ def set_commit(self, commit, msg):
+ self.__stack.repository.refs.set(self.__ref(), commit, msg)
+ def delete(self):
+ self.__stack.repository.refs.delete(self.__ref())
+ def is_applied(self):
+ return self.name in self.__stack.patchorder.applied
+ def is_empty(self):
+ c = self.commit
+ return c.data.tree == c.data.parent.data.tree
+
+class PatchOrder(object):
+ """Keeps track of patch order, and which patches are applied.
+ Works with patch names, not actual patches."""
+ __list_order = [ 'applied', 'unapplied' ]
+ def __init__(self, stack):
+ self.__stack = stack
+ self.__lists = {}
+ def __read_file(self, fn):
+ return tuple(utils.read_strings(
+ os.path.join(self.__stack.directory, fn)))
+ def __write_file(self, fn, val):
+ utils.write_strings(os.path.join(self.__stack.directory, fn), val)
+ def __get_list(self, name):
+ if not name in self.__lists:
+ self.__lists[name] = self.__read_file(name)
+ return self.__lists[name]
+ def __set_list(self, name, val):
+ val = tuple(val)
+ if val != self.__lists.get(name, None):
+ self.__lists[name] = val
+ self.__write_file(name, val)
+ applied = property(lambda self: self.__get_list('applied'),
+ lambda self, val: self.__set_list('applied', val))
+ unapplied = property(lambda self: self.__get_list('unapplied'),
+ lambda self, val: self.__set_list('unapplied', val))
+
+class Patches(object):
+ """Creates Patch objects."""
+ def __init__(self, stack):
+ self.__stack = stack
+ def create_patch(name):
+ p = Patch(self.__stack, name)
+ p.commit # raise exception if the patch doesn't exist
+ return p
+ self.__patches = git.ObjectCache(create_patch) # name -> Patch
+ def exists(self, name):
+ try:
+ self.get(name)
+ return True
+ except KeyError:
+ return False
+ def get(self, name):
+ return self.__patches[name]
+ def new(self, name, commit, msg):
+ assert not name in self.__patches
+ p = Patch(self.__stack, name)
+ p.set_commit(commit, msg)
+ self.__patches[name] = p
+ return p
+
+class Stack(object):
+ def __init__(self, repository, name):
+ self.__repository = repository
+ self.__name = name
+ self.__patchorder = PatchOrder(self)
+ self.__patches = Patches(self)
+ name = property(lambda self: self.__name)
+ repository = property(lambda self: self.__repository)
+ patchorder = property(lambda self: self.__patchorder)
+ patches = property(lambda self: self.__patches)
+ @property
+ def directory(self):
+ return os.path.join(self.__repository.directory, 'patches', self.__name)
+ def __ref(self):
+ return 'refs/heads/%s' % self.__name
+ @property
+ def head(self):
+ return self.__repository.refs.get(self.__ref())
+ def set_head(self, commit, msg):
+ self.__repository.refs.set(self.__ref(), commit, msg)
+ @property
+ def base(self):
+ if self.patchorder.applied:
+ return self.patches.get(self.patchorder.applied[0]
+ ).commit.data.parent
+ else:
+ return self.head
+
+class Repository(git.Repository):
+ def __init__(self, *args, **kwargs):
+ git.Repository.__init__(self, *args, **kwargs)
+ self.__stacks = {} # name -> Stack
+ @property
+ def current_branch(self):
+ return utils.strip_leading('refs/heads/', self.head)
+ @property
+ def current_stack(self):
+ return self.get_stack(self.current_branch)
+ def get_stack(self, name):
+ if not name in self.__stacks:
+ if name == None:
+ s = None # detached HEAD
+ else:
+ # TODO: verify that the branch exists
+ s = Stack(self, name)
+ self.__stacks[name] = s
+ return self.__stacks[name]
diff --git a/stgit/lib/transaction.py b/stgit/lib/transaction.py
new file mode 100644
index 0000000..991e64e
--- /dev/null
+++ b/stgit/lib/transaction.py
@@ -0,0 +1,79 @@
+from stgit import exception
+from stgit.out import *
+
+class TransactionException(exception.StgException):
+ pass
+
+def print_current_patch(old_applied, new_applied):
+ def now_at(pn):
+ out.info('Now at patch "%s"' % pn)
+ if not old_applied and not new_applied:
+ pass
+ elif not old_applied:
+ now_at(new_applied[-1])
+ elif not new_applied:
+ out.info('No patch applied')
+ elif old_applied[-1] == new_applied[-1]:
+ pass
+ else:
+ now_at(new_applied[-1])
+
+class StackTransaction(object):
+ def __init__(self, stack, msg):
+ self.__stack = stack
+ self.__msg = msg
+ self.__patches = {}
+ self.__applied = list(self.__stack.patchorder.applied)
+ self.__unapplied = list(self.__stack.patchorder.unapplied)
+ def __set_patches(self, val):
+ self.__patches = dict(val)
+ patches = property(lambda self: self.__patches, __set_patches)
+ def __set_applied(self, val):
+ self.__applied = list(val)
+ applied = property(lambda self: self.__applied, __set_applied)
+ def __set_unapplied(self, val):
+ self.__unapplied = list(val)
+ unapplied = property(lambda self: self.__unapplied, __set_unapplied)
+ def __check_consistency(self):
+ remaining = set(self.__applied + self.__unapplied)
+ for pn, commit in self.__patches.iteritems():
+ if commit == None:
+ assert self.__stack.patches.exists(pn)
+ else:
+ assert pn in remaining
+ def run(self):
+ self.__check_consistency()
+
+ # Get new head commit.
+ if self.__applied:
+ top_patch = self.__applied[-1]
+ try:
+ new_head = self.__patches[top_patch]
+ except KeyError:
+ new_head = self.__stack.patches.get(top_patch).commit
+ else:
+ new_head = self.__stack.base
+
+ # Set branch head.
+ if new_head == self.__stack.head:
+ pass # same commit: OK
+ elif new_head.data.tree == self.__stack.head.data.tree:
+ pass # same tree: OK
+ else:
+ # We can't handle this case yet.
+ raise TransactionException('Error: HEAD tree changed')
+ self.__stack.set_head(new_head, self.__msg)
+
+ # Write patches.
+ for pn, commit in self.__patches.iteritems():
+ if self.__stack.patches.exists(pn):
+ p = self.__stack.patches.get(pn)
+ if commit == None:
+ p.delete()
+ else:
+ p.set_commit(commit, self.__msg)
+ else:
+ self.__stack.patches.new(pn, commit, self.__msg)
+ print_current_patch(self.__stack.patchorder.applied, self.__applied)
+ self.__stack.patchorder.applied = self.__applied
+ self.__stack.patchorder.unapplied = self.__unapplied
diff --git a/stgit/utils.py b/stgit/utils.py
index 3a480c0..b3f6232 100644
--- a/stgit/utils.py
+++ b/stgit/utils.py
@@ -256,3 +256,16 @@ def add_sign_line(desc, sign_str, name, email):
if not any(s in desc for s in ['\nSigned-off-by:', '\nAcked-by:']):
desc = desc + '\n'
return '%s\n%s\n' % (desc, sign_str)
+
+def strip_leading(prefix, s):
+ """Strip leading prefix from a string. Blow up if the prefix isn't
+ there."""
+ assert s.startswith(prefix)
+ return s[len(prefix):]
+
+def add_dict(d1, d2):
+ """Return a new dict with the contents of both d1 and d2. In case of
+ conflicting mappings, d2 takes precedence."""
+ d = dict(d1)
+ d.update(d2)
+ return d
^ permalink raw reply related
* [StGit PATCH 0/5] Start the refactoring
From: Karl Hasselström @ 2007-11-05 3:14 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git
This series introduces parts of the proposed new infrastructure, and
uses it to implement "stg clean" and the new "stg coalesce".
There are undoubtedly more bugs to be found, but I'm pushing it to
git://repo.or.cz/stgit/kha.git experimental
to make it easy for people to try it out. It does pass the test suite
after all.
---
Karl Hasselström (5):
Add "stg coalesce"
Let "stg clean" use the new infrastructure
Simple test for "stg clean"
Write metadata files used by the old infrastructure
New StGit core infrastructure: repository operations
stgit/commands/clean.py | 68 ++++++------
stgit/commands/coalesce.py | 87 ++++++++++++++++
stgit/commands/common.py | 10 ++
stgit/lib/__init__.py | 18 +++
stgit/lib/git.py | 245 ++++++++++++++++++++++++++++++++++++++++++++
stgit/lib/stack.py | 156 ++++++++++++++++++++++++++++
stgit/lib/transaction.py | 79 ++++++++++++++
stgit/main.py | 2
stgit/utils.py | 24 ++++
t/t2500-clean.sh | 27 +++++
t/t2600-coalesce.sh | 31 ++++++
11 files changed, 713 insertions(+), 34 deletions(-)
create mode 100644 stgit/commands/coalesce.py
create mode 100644 stgit/lib/__init__.py
create mode 100644 stgit/lib/git.py
create mode 100644 stgit/lib/stack.py
create mode 100644 stgit/lib/transaction.py
create mode 100755 t/t2500-clean.sh
create mode 100755 t/t2600-coalesce.sh
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
^ permalink raw reply
* [StGit PATCH] Cogito is deprecated, so don't point to it
From: Karl Hasselström @ 2007-11-05 3:07 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git
Signed-off-by: Karl Hasselström <kha@treskal.com>
---
Also available from
git://repo.or.cz/stgit/kha.git safe
README | 4 +---
1 files changed, 1 insertions(+), 3 deletions(-)
diff --git a/README b/README
index 0e648f5..4f20154 100644
--- a/README
+++ b/README
@@ -6,9 +6,7 @@ other repositories using standard GIT functionality.
Note that StGIT is not an SCM interface on top of GIT and it expects a
previously initialised GIT repository (unless it is cloned using StGIT
-directly). For standard SCM operations, either use plain GIT commands
-or the Cogito tool but it is not recommended to mix them with the
-StGIT commands.
+directly). For standard SCM operations, use plain GIT commands.
For the latest version see http://www.procode.org/stgit/
For a tutorial see http://wiki.procode.org/cgi-bin/wiki.cgi/StGIT_Tutorial
^ permalink raw reply related
* [PATCH] t7501-commit.sh: Add test case for fixing author in amend commit.
From: Kristian Høgsberg @ 2007-11-05 3:42 UTC (permalink / raw)
To: gitster; +Cc: git, Kristian Høgsberg
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
---
t/t7501-commit.sh | 16 ++++++++++++++++
1 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index b151b51..7a37c13 100644
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -151,6 +151,7 @@ test_expect_success 'partial commit that involves removal (2)' '
diff expected current
'
+test_tick
test_expect_success 'partial commit that involves removal (3)' '
@@ -163,4 +164,19 @@ test_expect_success 'partial commit that involves removal (3)' '
'
+oldtick=$GIT_AUTHOR_DATE
+test_tick
+
+author="The Real Author <someguy@his.email.org>"
+committer="$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE"
+
+test_expect_success 'amend commit to fix author' '
+
+ git reset --hard
+ git cat-file -p HEAD | sed -e "s/author.*/author $author $oldtick/" -e "s/committer.*/committer $committer/" > expected &&
+ git commit --amend --author="$author" &&
+ git cat-file -p HEAD > current &&
+ diff expected current
+
+'
test_done
--
1.5.3.5.1527.g6161-dirty
^ permalink raw reply related
* Re: [PATCH] upload-pack: Use finish_{command,async}() instead of waitpid().
From: Michael J. Cohen @ 2007-11-05 1:42 UTC (permalink / raw)
To: Git Mailing List
In-Reply-To: <200711042046.48257.johannes.sixt@telecom.at>
On Nov 4, 2007, at 2:46 PM, Johannes Sixt wrote:
> The change is smaller than it looks because most of it only reduces
> the
> indentation of a large part of the inner loop.
mac-pro:git mjc$ git diff -u -b HEAD | diffstat
t/t5530-upload-pack-error.sh | 49 ++++++++++++++++++++++++++++
upload-pack.c | 74 +++++++++
+---------------------------------
2 files changed, 67 insertions(+), 56 deletions(-)
agreed. :P
-mjc
^ permalink raw reply
* Re: [PATCH 5/5] pretty describe: add %ds, %dn, %dd placeholders
From: René Scharfe @ 2007-11-05 1:20 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Junio C Hamano, Git Mailing List
In-Reply-To: <472E010B.5070602@lsrfire.ath.cx>
René Scharfe schrieb:
> In any case, interpolate.c needs some attention, with or without my
> patch. I agree that a native strbuf version would be nice. How about
> an interface like that:
>
> typedef const char *(*expand_fn_t)
> (const char *placeholder, void *context);
> void strbuf_addexpand(struct strbuf *sb, const char *format,
> const char **placeholders,
> expand_fn_t fn, void *context);
>
> strbuf_addexpand() would call fn() when it recognizes a placeholder,
> avoiding unneeded setup code. It could cache the result, so that fn()
> gets called at most a single time for each given placeholder. context
> would be passed through to fn(), e.g. a struct commit in case of
> format_commit_message(). Makes sense?
OK, for discussion, I've experimented a bit, and here's what I could
come up with. This version doesn't do any caching. Is it even needed?
Anyway, strbuf_expand() is really simple, and the callback is expected
to add it's output directly to the strbuf. That means that the number
of strdups and memdupz is reduced significantly, speeding up the whole
thing. Converting code to use strbuf_expand is easy, but not trivial
(i.e. not doable by a script).
René
commit.c | 275 ++++++++++++++++++++++++++++++++++---------------------------
strbuf.c | 22 +++++
strbuf.h | 3 +
3 files changed, 178 insertions(+), 122 deletions(-)
diff --git a/commit.c b/commit.c
index 8262f6a..4a47b0e 100644
--- a/commit.c
+++ b/commit.c
@@ -698,7 +698,8 @@ static char *logmsg_reencode(const struct commit *commit,
return out;
}
-static void fill_person(struct interp *table, const char *msg, int len)
+static void format_person_part(struct strbuf *sb, char part,
+ const char *msg, int len)
{
int start, end, tz = 0;
unsigned long date;
@@ -710,7 +711,10 @@ static void fill_person(struct interp *table, const char *msg, int len)
start = end + 1;
while (end > 0 && isspace(msg[end - 1]))
end--;
- table[0].value = xmemdupz(msg, end);
+ if (part == 'n') { /* name */
+ strbuf_add(sb, msg, end);
+ return;
+ }
if (start >= len)
return;
@@ -722,7 +726,10 @@ static void fill_person(struct interp *table, const char *msg, int len)
if (end >= len)
return;
- table[1].value = xmemdupz(msg + start, end - start);
+ if (part == 'e') { /* email */
+ strbuf_add(sb, msg + start, end - start);
+ return;
+ }
/* parse date */
for (start = end + 1; start < len && isspace(msg[start]); start++)
@@ -733,7 +740,10 @@ static void fill_person(struct interp *table, const char *msg, int len)
if (msg + start == ep)
return;
- table[5].value = xmemdupz(msg + start, ep - (msg + start));
+ if (part == 't') { /* date, UNIX timestamp */
+ strbuf_add(sb, msg + start, ep - (msg + start));
+ return;
+ }
/* parse tz */
for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
@@ -744,123 +754,107 @@ static void fill_person(struct interp *table, const char *msg, int len)
tz = -tz;
}
- interp_set_entry(table, 2, show_date(date, tz, DATE_NORMAL));
- interp_set_entry(table, 3, show_date(date, tz, DATE_RFC2822));
- interp_set_entry(table, 4, show_date(date, tz, DATE_RELATIVE));
- interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601));
+ switch (part) {
+ case 'd': /* date */
+ strbuf_addstr(sb, show_date(date, tz, DATE_NORMAL));
+ return;
+ case 'D': /* date, RFC2822 style */
+ strbuf_addstr(sb, show_date(date, tz, DATE_RFC2822));
+ return;
+ case 'r': /* date, relative */
+ strbuf_addstr(sb, show_date(date, tz, DATE_RELATIVE));
+ return;
+ case 'i': /* date, ISO 8601 */
+ strbuf_addstr(sb, show_date(date, tz, DATE_ISO8601));
+ return;
+ }
}
-void format_commit_message(const struct commit *commit,
- const void *format, struct strbuf *sb)
+static void format_commit_item(struct strbuf *sb, const char *placeholder,
+ void *context)
{
- struct interp table[] = {
- { "%H" }, /* commit hash */
- { "%h" }, /* abbreviated commit hash */
- { "%T" }, /* tree hash */
- { "%t" }, /* abbreviated tree hash */
- { "%P" }, /* parent hashes */
- { "%p" }, /* abbreviated parent hashes */
- { "%an" }, /* author name */
- { "%ae" }, /* author email */
- { "%ad" }, /* author date */
- { "%aD" }, /* author date, RFC2822 style */
- { "%ar" }, /* author date, relative */
- { "%at" }, /* author date, UNIX timestamp */
- { "%ai" }, /* author date, ISO 8601 */
- { "%cn" }, /* committer name */
- { "%ce" }, /* committer email */
- { "%cd" }, /* committer date */
- { "%cD" }, /* committer date, RFC2822 style */
- { "%cr" }, /* committer date, relative */
- { "%ct" }, /* committer date, UNIX timestamp */
- { "%ci" }, /* committer date, ISO 8601 */
- { "%e" }, /* encoding */
- { "%s" }, /* subject */
- { "%b" }, /* body */
- { "%Cred" }, /* red */
- { "%Cgreen" }, /* green */
- { "%Cblue" }, /* blue */
- { "%Creset" }, /* reset color */
- { "%n" }, /* newline */
- { "%m" }, /* left/right/bottom */
- };
- enum interp_index {
- IHASH = 0, IHASH_ABBREV,
- ITREE, ITREE_ABBREV,
- IPARENTS, IPARENTS_ABBREV,
- IAUTHOR_NAME, IAUTHOR_EMAIL,
- IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE,
- IAUTHOR_TIMESTAMP, IAUTHOR_ISO8601,
- ICOMMITTER_NAME, ICOMMITTER_EMAIL,
- ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822,
- ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP,
- ICOMMITTER_ISO8601,
- IENCODING,
- ISUBJECT,
- IBODY,
- IRED, IGREEN, IBLUE, IRESET_COLOR,
- INEWLINE,
- ILEFT_RIGHT,
- };
+ struct commit *commit = context;
struct commit_list *p;
- char parents[1024];
- unsigned long len;
int i;
enum { HEADER, SUBJECT, BODY } state;
const char *msg = commit->buffer;
- if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))
- die("invalid interp table!");
-
/* these are independent of the commit */
- interp_set_entry(table, IRED, "\033[31m");
- interp_set_entry(table, IGREEN, "\033[32m");
- interp_set_entry(table, IBLUE, "\033[34m");
- interp_set_entry(table, IRESET_COLOR, "\033[m");
- interp_set_entry(table, INEWLINE, "\n");
+ switch (placeholder[0]) {
+ case 'C':
+ switch (placeholder[3]) {
+ case 'd': /* red */
+ strbuf_addstr(sb, "\033[31m");
+ return;
+ case 'e': /* green */
+ strbuf_addstr(sb, "\033[32m");
+ return;
+ case 'u': /* blue */
+ strbuf_addstr(sb, "\033[34m");
+ return;
+ case 's': /* reset color */
+ strbuf_addstr(sb, "\033[m");
+ return;
+ }
+ case 'n': /* newline */
+ strbuf_addch(sb, '\n');
+ return;
+ }
/* these depend on the commit */
if (!commit->object.parsed)
parse_object(commit->object.sha1);
- interp_set_entry(table, IHASH, sha1_to_hex(commit->object.sha1));
- interp_set_entry(table, IHASH_ABBREV,
- find_unique_abbrev(commit->object.sha1,
- DEFAULT_ABBREV));
- interp_set_entry(table, ITREE, sha1_to_hex(commit->tree->object.sha1));
- interp_set_entry(table, ITREE_ABBREV,
- find_unique_abbrev(commit->tree->object.sha1,
- DEFAULT_ABBREV));
- interp_set_entry(table, ILEFT_RIGHT,
- (commit->object.flags & BOUNDARY)
- ? "-"
- : (commit->object.flags & SYMMETRIC_LEFT)
- ? "<"
- : ">");
-
- parents[1] = 0;
- for (i = 0, p = commit->parents;
- p && i < sizeof(parents) - 1;
- p = p->next)
- i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
- sha1_to_hex(p->item->object.sha1));
- interp_set_entry(table, IPARENTS, parents + 1);
-
- parents[1] = 0;
- for (i = 0, p = commit->parents;
- p && i < sizeof(parents) - 1;
- p = p->next)
- i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
- find_unique_abbrev(p->item->object.sha1,
- DEFAULT_ABBREV));
- interp_set_entry(table, IPARENTS_ABBREV, parents + 1);
+ switch (placeholder[0]) {
+ case 'H': /* commit hash */
+ strbuf_addstr(sb, sha1_to_hex(commit->object.sha1));
+ return;
+ case 'h': /* abbreviated commit hash */
+ strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,
+ DEFAULT_ABBREV));
+ return;
+ case 'T': /* tree hash */
+ strbuf_addstr(sb, sha1_to_hex(commit->tree->object.sha1));
+ return;
+ case 't': /* abbreviated tree hash */
+ strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1,
+ DEFAULT_ABBREV));
+ return;
+ case 'P': /* parent hashes */
+ for (p = commit->parents; p; p = p->next) {
+ if (p != commit->parents)
+ strbuf_addch(sb, ' ');
+ strbuf_addstr(sb, sha1_to_hex(p->item->object.sha1));
+ }
+ return;
+ case 'p': /* abbreviated parent hashes */
+ for (p = commit->parents; p; p = p->next) {
+ if (p != commit->parents)
+ strbuf_addch(sb, ' ');
+ strbuf_addstr(sb, find_unique_abbrev(
+ p->item->object.sha1, DEFAULT_ABBREV));
+ }
+ return;
+ case 'm': /* left/right/bottom */
+ strbuf_addch(sb, (commit->object.flags & BOUNDARY)
+ ? '-'
+ : (commit->object.flags & SYMMETRIC_LEFT)
+ ? '<'
+ : '>');
+ return;
+ }
+
+ /* For the rest we have to parse the commit header. */
for (i = 0, state = HEADER; msg[i] && state < BODY; i++) {
int eol;
for (eol = i; msg[eol] && msg[eol] != '\n'; eol++)
; /* do nothing */
if (state == SUBJECT) {
- table[ISUBJECT].value = xmemdupz(msg + i, eol - i);
+ if (placeholder[0] == 's') {
+ strbuf_add(sb, msg + i, eol - i);
+ return;
+ }
i = eol;
}
if (i == eol) {
@@ -868,29 +862,66 @@ void format_commit_message(const struct commit *commit,
/* strip empty lines */
while (msg[eol + 1] == '\n')
eol++;
- } else if (!prefixcmp(msg + i, "author "))
- fill_person(table + IAUTHOR_NAME,
- msg + i + 7, eol - i - 7);
- else if (!prefixcmp(msg + i, "committer "))
- fill_person(table + ICOMMITTER_NAME,
- msg + i + 10, eol - i - 10);
- else if (!prefixcmp(msg + i, "encoding "))
- table[IENCODING].value =
- xmemdupz(msg + i + 9, eol - i - 9);
+ } else if (!prefixcmp(msg + i, "author ")) {
+ if (placeholder[0] == 'a') {
+ format_person_part(sb, placeholder[1],
+ msg + i + 7, eol - i - 7);
+ return;
+ }
+ } else if (!prefixcmp(msg + i, "committer ")) {
+ if (placeholder[0] == 'c') {
+ format_person_part(sb, placeholder[1],
+ msg + i + 10, eol - i - 10);
+ return;
+ }
+ } else if (!prefixcmp(msg + i, "encoding ")) {
+ if (placeholder[0] == 'e') {
+ strbuf_add(sb, msg + i + 9, eol - i - 9);
+ return;
+ }
+ }
i = eol;
}
- if (msg[i])
- table[IBODY].value = xstrdup(msg + i);
-
- len = interpolate(sb->buf + sb->len, strbuf_avail(sb),
- format, table, ARRAY_SIZE(table));
- if (len > strbuf_avail(sb)) {
- strbuf_grow(sb, len);
- interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1,
- format, table, ARRAY_SIZE(table));
- }
- strbuf_setlen(sb, sb->len + len);
- interp_clear_table(table, ARRAY_SIZE(table));
+ if (msg[i] && placeholder[0] == 'b') /* body */
+ strbuf_addstr(sb, msg + i);
+}
+
+void format_commit_message(const struct commit *commit,
+ const void *format, struct strbuf *sb)
+{
+ const char *placeholders[] = {
+ "H", /* commit hash */
+ "h", /* abbreviated commit hash */
+ "T", /* tree hash */
+ "t", /* abbreviated tree hash */
+ "P", /* parent hashes */
+ "p", /* abbreviated parent hashes */
+ "an", /* author name */
+ "ae", /* author email */
+ "ad", /* author date */
+ "aD", /* author date, RFC2822 style */
+ "ar", /* author date, relative */
+ "at", /* author date, UNIX timestamp */
+ "ai", /* author date, ISO 8601 */
+ "cn", /* committer name */
+ "ce", /* committer email */
+ "cd", /* committer date */
+ "cD", /* committer date, RFC2822 style */
+ "cr", /* committer date, relative */
+ "ct", /* committer date, UNIX timestamp */
+ "ci", /* committer date, ISO 8601 */
+ "e", /* encoding */
+ "s", /* subject */
+ "b", /* body */
+ "Cred", /* red */
+ "Cgreen", /* green */
+ "Cblue", /* blue */
+ "Creset", /* reset color */
+ "n", /* newline */
+ "m", /* left/right/bottom */
+ NULL
+ };
+ strbuf_expand(sb, format, placeholders, format_commit_item, (void *)commit);
}
static void pp_header(enum cmit_fmt fmt,
diff --git a/strbuf.c b/strbuf.c
index f4201e1..b71da99 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -129,6 +129,28 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
strbuf_setlen(sb, sb->len + len);
}
+void strbuf_expand(struct strbuf *sb, const char *fmt,
+ const char **placeholders, expand_fn_t fn, void *context)
+{
+ char c;
+ const char **p;
+
+ while ((c = *fmt++)) {
+ if (c != '%') {
+ strbuf_addch(sb, c);
+ continue;
+ }
+
+ for (p = placeholders; *p; p++) {
+ if (!prefixcmp(fmt, *p)) {
+ fn(sb, *p, context);
+ fmt += strlen(*p);
+ break;
+ }
+ }
+ }
+}
+
size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
{
size_t res;
diff --git a/strbuf.h b/strbuf.h
index 9b9e861..b9c3e79 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -102,6 +102,9 @@ static inline void strbuf_addbuf(struct strbuf *sb, struct strbuf *sb2) {
strbuf_add(sb, sb2->buf, sb2->len);
}
+typedef void (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
+extern void strbuf_expand(struct strbuf *sb, const char *fmt, const char **placeholders, expand_fn_t fn, void *context);
+
__attribute__((format(printf,2,3)))
extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
^ permalink raw reply related
* Re: gitk graph routing problem
From: Alex Riesen @ 2007-11-05 1:19 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Paul Mackerras, git
In-Reply-To: <20071105011700.GB5355@steel.home>
Alex Riesen, Mon, Nov 05, 2007 02:17:00 +0100:
> Alex Riesen, Mon, Nov 05, 2007 01:53:49 +0100:
> > Junio C Hamano, Mon, Nov 05, 2007 00:14:29 +0100:
> > > Paul Mackerras <paulus@samba.org> writes:
> > >
> > > > Alex Riesen writes:
> > > >
> > > >> To reproduce, try running in git repo:
> > > >>
> > > >> gitk 02f630448e5d48e..06ea6ba9cf46ef5
> > > >
> > > > I can't reproduce it here, as my clone of the git repo doesn't have
> > > > 02f630448e5d48e in it, even after updating...
> > >
> > > Heh, me neither ;-).
> >
> > Stupid me. Will follow up with a link to the archived repo in private mail.
> >
and of course, I send it to everyone... Ach, well. Will keep it for a
week, or until it is reproduced somewhere else. Whichever comes first.
^ permalink raw reply
* Re: gitk graph routing problem
From: Alex Riesen @ 2007-11-05 1:17 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Paul Mackerras, git
In-Reply-To: <20071105005349.GA5355@steel.home>
Alex Riesen, Mon, Nov 05, 2007 01:53:49 +0100:
> Junio C Hamano, Mon, Nov 05, 2007 00:14:29 +0100:
> > Paul Mackerras <paulus@samba.org> writes:
> >
> > > Alex Riesen writes:
> > >
> > >> To reproduce, try running in git repo:
> > >>
> > >> gitk 02f630448e5d48e..06ea6ba9cf46ef5
> > >
> > > I can't reproduce it here, as my clone of the git repo doesn't have
> > > 02f630448e5d48e in it, even after updating...
> >
> > Heh, me neither ;-).
>
> Stupid me. Will follow up with a link to the archived repo in private mail.
>
http://ftp-tmp:ftp-tmp@h877881.stratoserver.net/~ftp-tmp/break-gitk.git.tar.bz2
Its around 60mb.
^ permalink raw reply
* Re: [PATCH] fix display overlap between remote and local progress
From: Nicolas Pitre @ 2007-11-05 1:07 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Johannes Schindelin, git
In-Reply-To: <7v4pg1sj1r.fsf@gitster.siamese.dyndns.org>
On Sun, 4 Nov 2007, Junio C Hamano wrote:
> The compiler at k.org complains of "\e" being non ISO-C, though.
Bummer.
...
Signed-off-by: Nicolas Pitre <nico@cam.org>
---
diff --git a/sideband.c b/sideband.c
index 58edea6..756bbc2 100644
--- a/sideband.c
+++ b/sideband.c
@@ -13,7 +13,7 @@
*/
#define PREFIX "remote:"
-#define SUFFIX "\e[K" /* change to " " if ANSI sequences don't work */
+#define SUFFIX "\033[K" /* change to " " if ANSI sequences don't work */
int recv_sideband(const char *me, int in_stream, int out, int err)
{
^ permalink raw reply related
* Re: gitk graph routing problem
From: Alex Riesen @ 2007-11-05 0:53 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Paul Mackerras, git
In-Reply-To: <7vzlxtr4bu.fsf@gitster.siamese.dyndns.org>
Junio C Hamano, Mon, Nov 05, 2007 00:14:29 +0100:
> Paul Mackerras <paulus@samba.org> writes:
>
> > Alex Riesen writes:
> >
> >> To reproduce, try running in git repo:
> >>
> >> gitk 02f630448e5d48e..06ea6ba9cf46ef5
> >
> > I can't reproduce it here, as my clone of the git repo doesn't have
> > 02f630448e5d48e in it, even after updating...
>
> Heh, me neither ;-).
Stupid me. Will follow up with a link to the archived repo in private mail.
^ permalink raw reply
* Re: [ANNOUNCE] cgit v0.7
From: Jakub Narebski @ 2007-11-05 0:29 UTC (permalink / raw)
To: Lars Hjemli; +Cc: git list, Jakub Narebski
In-Reply-To: <8c5c35580711030408n658eb11fk19d554f0fa3b17@mail.gmail.com>
Lars Hjemli wrote:
> cgit v0.7 (a fast webinterface for git) is now available at
>
> git://hjemli.net/pub/git/cgit
>
> This release includes better search capabilities, better diff,
> filtered and sorted branch/tag lists on the summary page, a simple way
> to switch between branches and finally a much needed restructuring of
> the user interface. There is even a brand new logo, and a number of
> bugfixes.
>
> The latest version can be seen in action on http://hjemli.net/git/
> (disclaimer: I'm not a web/user interface designer, and should not be
> held responsible for any eyeball-related damages).
A few comments about new cgit:
* Very nice cgit logo, but no favicon. Perhaps pacman head and G,
or pacman head (like in logo) and +/-...
* Providing reference with full sha1 of referenced object for tags
list is not IMVHO a good design: what is interesting is type of
tag, if it is signed it's first line, and if it is lightweight
pointing to tag then perhaps commit subject.
* Nice diffstat in commit view; the diff view is better, although I
wouldn't lump from-file / to-file diff header together with git
diff header and extended git diff header.
* I like the sidebar very much, although I'm not sure how it would
work for larger projects (more branches, much more tags). Also the
search textbox is not very visible; I'd rather it have "groove"
view.
* I like separate 'mirrors' section, although I think it rather
clashes badly with notion of forks (alternates).
* I'm not sure if it wouln't be beter to provide -n/+m lines changed
instead of nn likes changed column.
* Nice submodule support!
By the way, Freedesktop provides besides standard gitweb interface
also cgit interface at
http://cgit.freedesktop.org/
Take a look at how such site looks like with large number of projects
(perhaps sidebar is noot such a good idea then?), and with large
projects.
--
Jakub Narebski
Poland
^ permalink raw reply
* Re: [PATCH] Add more tests for git-clean
From: Junio C Hamano @ 2007-11-05 0:17 UTC (permalink / raw)
To: Pierre Habouzit; +Cc: Shawn Bohrer, git
In-Reply-To: <20071104234617.GG4207@artemis.corp>
Pierre Habouzit <madcoder@debian.org> writes:
> On Sun, Nov 04, 2007 at 11:35:42PM +0000, Junio C Hamano wrote:
>> Shawn Bohrer <shawn.bohrer@gmail.com> writes:
>>
>> > +test_expect_success 'git-clean with prefix' '
>> > +
>> > + mkdir -p build docs &&
>> > + touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
>> > + cd src/ &&
>> > + git-clean &&
>> > + cd - &&
>>
>> This is wrong for two reasons.
>>
>> - Is "cd -" portable?
>
> this is POSIX:
That actually doesn't matter. What the real world shells do
matters more.
In addition, "cd -" is a nice shorthand for interactive use but
it is a bad discipline to use it in a script anyway.
...
( cd src && git-clean ) &&
...
would be the best way to write this.
^ permalink raw reply
* Re: [PATCH] Add more tests for git-clean
From: Johannes Schindelin @ 2007-11-04 23:49 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Shawn Bohrer, git
In-Reply-To: <7vve8hr3ch.fsf@gitster.siamese.dyndns.org>
Hi,
On Sun, 4 Nov 2007, Junio C Hamano wrote:
> Shawn Bohrer <shawn.bohrer@gmail.com> writes:
>
> > +test_expect_success 'git-clean with prefix' '
> > +
> > + mkdir -p build docs &&
> > + touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
> > + cd src/ &&
> > + git-clean &&
> > + cd - &&
>
> This is wrong for two reasons.
>
> - Is "cd -" portable?
>
> - What happens when git-clean fails? This test fails, and then
> it goes on to the next test without cd'ing back.
So it should be
(cd src/ && git clean) &&
right? (Note that I also removed the dash, since it will be a builtin
after the next commit.)
Ciao,
Dscho
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox