* Coming clean, after being stupid and ugly (or: globs and filtering with git-svn)
From: Michael J Gruber @ 2008-06-23 13:17 UTC (permalink / raw)
To: git
Hi there,
I've been a sinner regarding good repo structures: I lumped a lot of
stuff into one SVN repo. I blame SVN's clumsy admin commands and URLs
for that, of course... Along with my transition to git I want to change
this structure.
At the root of my SVN repo I have directories trunk/A, trunk/B and
trunk/C, say; similarly for branches/*/A etc.
I know how to "git-svn clone -s" the tree under A so that it becomes an
independent git repository (using "branches =
branches/*/A:refs/remotes/*" etc., i.e: git-svn init, edit config,
git-svn fetch).
But I want the trees under B and C to end up in the same git repository
(under B and C, of course). I could convert the whole SVN repo and
remove A afterwards using git-filter-branch, but this changes commit IDs
and leaves spurious branches, tags and commits which touch the tree
below A only, and would need to be removed also.
Is there any way to tell git-svn to do the filtering? It does some sort
of filtering already (evaluating the fetch, branches and tags glob
patterns). I would need something like
branches/*/{B,C}:refs/remotes/*
or
branches/*/{B,C}:refs/remotes/*/\1
to mean: treat only the trees below branches/something/B and
branches/something/C and put them into subdirs B resp. C of the same git
repo in branch something, for each something there is.
Alternatively: Where in git-svn should I look if I wanted to implement
something like that?
Cheers,
Michael
P.S.: Actually, the tree below A will end up in a tree of smaller repos,
and in my case I have more than just B and C which should end up in the
same repo. The description above is a minimal case.
^ permalink raw reply
* [PATCH 2/2] git-merge-recursive-{ours,theirs}
From: Miklos Vajna @ 2008-06-23 12:45 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
In-Reply-To: <cover.1214224424.git.vmiklos@frugalware.org>
From: Junio C Hamano <gitster@pobox.com>
This uses the low-level mechanism for "ours" and "theirs" autoresolution
introduced by the previous commit to introduce two additional merge
strategies, merge-recursive-ours and merge-recursive-theirs.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
The patch is the same, but I removed modifications to git-merge.sh and
added them to builtin-merge.c.
Makefile | 3 ++
builtin-merge-recursive.c | 37 ++++++++++++++++++++++++---
builtin-merge.c | 2 +
git.c | 2 +
ll-merge.c | 24 +++++++++++------
ll-merge.h | 4 ++-
t/t6034-merge-ours-theirs.sh | 56 ++++++++++++++++++++++++++++++++++++++++++
7 files changed, 114 insertions(+), 14 deletions(-)
create mode 100755 t/t6034-merge-ours-theirs.sh
diff --git a/Makefile b/Makefile
index 3d8c3d2..4e354fc 100644
--- a/Makefile
+++ b/Makefile
@@ -303,6 +303,8 @@ BUILT_INS += git-format-patch$X
BUILT_INS += git-fsck-objects$X
BUILT_INS += git-get-tar-commit-id$X
BUILT_INS += git-init$X
+BUILT_INS += git-merge-recursive-ours$X
+BUILT_INS += git-merge-recursive-theirs$X
BUILT_INS += git-merge-subtree$X
BUILT_INS += git-peek-remote$X
BUILT_INS += git-repo-config$X
@@ -1381,6 +1383,7 @@ check-docs::
do \
case "$$v" in \
git-merge-octopus | git-merge-ours | git-merge-recursive | \
+ git-merge-recursive-ours | git-merge-recursive-theirs | \
git-merge-resolve | git-merge-stupid | git-merge-subtree | \
git-fsck-objects | git-init-db | \
git-?*--?* ) continue ;; \
diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c
index 98b09fb..c535596 100644
--- a/builtin-merge-recursive.c
+++ b/builtin-merge-recursive.c
@@ -20,7 +20,11 @@
#include "attr.h"
#include "merge-recursive.h"
-static int subtree_merge;
+static enum {
+ MERGE_RECURSIVE_SUBTREE = 1,
+ MERGE_RECURSIVE_OURS,
+ MERGE_RECURSIVE_THEIRS,
+} merge_recursive_variants;
static struct tree *shift_tree_object(struct tree *one, struct tree *two)
{
@@ -634,6 +638,7 @@ static int merge_3way(mmbuffer_t *result_buf,
mmfile_t orig, src1, src2;
char *name1, *name2;
int merge_status;
+ int flag, favor;
name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
@@ -642,9 +647,26 @@ static int merge_3way(mmbuffer_t *result_buf,
fill_mm(a->sha1, &src1);
fill_mm(b->sha1, &src2);
+ if (index_only)
+ favor = 0;
+ else {
+ switch (merge_recursive_variants) {
+ case MERGE_RECURSIVE_OURS:
+ favor = XDL_MERGE_FAVOR_OURS;
+ break;
+ case MERGE_RECURSIVE_THEIRS:
+ favor = XDL_MERGE_FAVOR_THEIRS;
+ break;
+ default:
+ favor = 0;
+ break;
+ }
+ }
+ flag = LL_MERGE_FLAGS(index_only, favor);
+
merge_status = ll_merge(result_buf, a->path, &orig,
&src1, name1, &src2, name2,
- index_only);
+ flag);
free(name1);
free(name2);
@@ -1163,7 +1185,7 @@ int merge_trees(struct tree *head,
{
int code, clean;
- if (subtree_merge) {
+ if (merge_recursive_variants == MERGE_RECURSIVE_SUBTREE) {
merge = shift_tree_object(head, merge);
common = shift_tree_object(head, common);
}
@@ -1371,11 +1393,18 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
int index_fd;
+ merge_recursive_variants = 0;
if (argv[0]) {
int namelen = strlen(argv[0]);
if (8 < namelen &&
!strcmp(argv[0] + namelen - 8, "-subtree"))
- subtree_merge = 1;
+ merge_recursive_variants = MERGE_RECURSIVE_SUBTREE;
+ else if (5 < namelen &&
+ !strcmp(argv[0] + namelen - 5, "-ours"))
+ merge_recursive_variants = MERGE_RECURSIVE_OURS;
+ else if (7 < namelen &&
+ !strcmp(argv[0] + namelen - 7, "-theirs"))
+ merge_recursive_variants = MERGE_RECURSIVE_THEIRS;
}
git_config(merge_config, NULL);
diff --git a/builtin-merge.c b/builtin-merge.c
index dadde80..b7b1b71 100644
--- a/builtin-merge.c
+++ b/builtin-merge.c
@@ -52,6 +52,8 @@ static struct path_list_item strategy_items[] = {
{ "stupid", (void *)0 },
{ "ours", (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },
{ "subtree", (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },
+ { "recursive-ours", (void *)NO_TRIVIAL },
+ { "recursive-theirs", (void *)NO_TRIVIAL },
};
static struct path_list strategies = { strategy_items,
ARRAY_SIZE(strategy_items), 0, 0 };
diff --git a/git.c b/git.c
index 770aadd..f2a8ce5 100644
--- a/git.c
+++ b/git.c
@@ -276,6 +276,8 @@ static void handle_internal_command(int argc, const char **argv)
{ "merge-file", cmd_merge_file },
{ "merge-ours", cmd_merge_ours, RUN_SETUP },
{ "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+ { "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+ { "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
{ "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
{ "name-rev", cmd_name_rev, RUN_SETUP },
diff --git a/ll-merge.c b/ll-merge.c
index 9837c84..c6a05bf 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -19,7 +19,7 @@ typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
mmfile_t *orig,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
- int virtual_ancestor);
+ int flag);
struct ll_merge_driver {
const char *name;
@@ -39,13 +39,15 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
mmfile_t *orig,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
- int virtual_ancestor)
+ int flag)
{
/*
* The tentative merge result is "ours" for the final round,
* or common ancestor for an internal merge. Still return
* "conflicted merge" status.
*/
+ int virtual_ancestor = flag & 01;
+
mmfile_t *stolen = virtual_ancestor ? orig : src1;
result->ptr = stolen->ptr;
@@ -60,9 +62,10 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
mmfile_t *orig,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
- int virtual_ancestor)
+ int flag)
{
xpparam_t xpp;
+ int favor = ((flag)>>1) & 03;
if (buffer_is_binary(orig->ptr, orig->size) ||
buffer_is_binary(src1->ptr, src1->size) ||
@@ -73,14 +76,15 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
path_unused,
orig, src1, name1,
src2, name2,
- virtual_ancestor);
+ flag);
}
memset(&xpp, 0, sizeof(xpp));
return xdl_merge(orig,
src1, name1,
src2, name2,
- &xpp, XDL_MERGE_ZEALOUS,
+ &xpp,
+ XDL_MERGE_FLAGS(XDL_MERGE_ZEALOUS, favor),
result);
}
@@ -90,11 +94,12 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,
mmfile_t *orig,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
- int virtual_ancestor)
+ int flag)
{
char *src, *dst;
long size;
const int marker_size = 7;
+ int virtual_ancestor = flag & 01;
int status = ll_xdl_merge(drv_unused, result, path_unused,
orig, src1, NULL, src2, NULL,
@@ -158,7 +163,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
mmfile_t *orig,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
- int virtual_ancestor)
+ int flag)
{
char temp[3][50];
char cmdbuf[2048];
@@ -362,10 +367,11 @@ int ll_merge(mmbuffer_t *result_buf,
mmfile_t *ancestor,
mmfile_t *ours, const char *our_label,
mmfile_t *theirs, const char *their_label,
- int virtual_ancestor)
+ int flag)
{
const char *ll_driver_name;
const struct ll_merge_driver *driver;
+ int virtual_ancestor = flag & 01;
ll_driver_name = git_path_check_merge(path);
driver = find_ll_merge_driver(ll_driver_name);
@@ -375,5 +381,5 @@ int ll_merge(mmbuffer_t *result_buf,
return driver->fn(driver, result_buf, path,
ancestor,
ours, our_label,
- theirs, their_label, virtual_ancestor);
+ theirs, their_label, flag);
}
diff --git a/ll-merge.h b/ll-merge.h
index 5388422..5daef58 100644
--- a/ll-merge.h
+++ b/ll-merge.h
@@ -5,11 +5,13 @@
#ifndef LL_MERGE_H
#define LL_MERGE_H
+#define LL_MERGE_FLAGS(virtual_ancestor,favor) ((!!(virtual_ancestor)) | ((favor)<<1))
+
int ll_merge(mmbuffer_t *result_buf,
const char *path,
mmfile_t *ancestor,
mmfile_t *ours, const char *our_label,
mmfile_t *theirs, const char *their_label,
- int virtual_ancestor);
+ int flag);
#endif
diff --git a/t/t6034-merge-ours-theirs.sh b/t/t6034-merge-ours-theirs.sh
new file mode 100755
index 0000000..56a9247
--- /dev/null
+++ b/t/t6034-merge-ours-theirs.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+test_description='Merge-recursive ours and theirs variants'
+. ./test-lib.sh
+
+test_expect_success setup '
+ for i in 1 2 3 4 5 6 7 8 9
+ do
+ echo "$i"
+ done >file &&
+ git add file &&
+ cp file elif &&
+ git commit -m initial &&
+
+ sed -e "s/1/one/" -e "s/9/nine/" >file <elif &&
+ git commit -a -m ours &&
+
+ git checkout -b side HEAD^ &&
+
+ sed -e "s/9/nueve/" >file <elif &&
+ git commit -a -m theirs &&
+
+ git checkout master^0
+'
+
+test_expect_success 'plain recursive - should conflict' '
+ git reset --hard master &&
+ test_must_fail git merge -s recursive side &&
+ grep nine file &&
+ grep nueve file &&
+ ! grep 9 file &&
+ grep one file &&
+ ! grep 1 file
+'
+
+test_expect_success 'recursive favouring theirs' '
+ git reset --hard master &&
+ git merge -s recursive-theirs side &&
+ ! grep nine file &&
+ grep nueve file &&
+ ! grep 9 file &&
+ grep one file &&
+ ! grep 1 file
+'
+
+test_expect_success 'recursive favouring ours' '
+ git reset --hard master &&
+ git merge -s recursive-ours side &&
+ grep nine file &&
+ ! grep nueve file &&
+ ! grep 9 file &&
+ grep one file &&
+ ! grep 1 file
+'
+
+test_done
--
1.5.6
^ permalink raw reply related
* [PATCH 0/2] jc/merge-theirs, rebased on top of mv/merge-in-c
From: Miklos Vajna @ 2008-06-23 12:45 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
On Mon, Jun 23, 2008 at 12:15:35AM -0700, Junio C Hamano <gitster@pobox.com> wrote:
> Currently tip of 'pu' is broken and does not pass tests, as j6t/mingw
> has interaction with dr/ceiling and jc/merge-theirs has interaction
> with mv/merge-in-c.
Here is jc/merge-theirs, on top of mv/merge-in-c. Now all tests pass
here if I merge both to master.
To avoid unnecessary traffic, I do not send e0aafb4 ([PATCH 1/2]
git-merge-file --ours, --theirs"), as that is unchanged.
Junio C Hamano (2):
git-merge-file --ours, --theirs
git-merge-recursive-{ours,theirs}
Documentation/git-merge-file.txt | 12 +++++++-
Makefile | 3 ++
builtin-merge-file.c | 10 +++++-
builtin-merge-recursive.c | 37 ++++++++++++++++++++++---
builtin-merge.c | 2 +
git.c | 2 +
ll-merge.c | 24 ++++++++++------
ll-merge.h | 4 ++-
t/t6034-merge-ours-theirs.sh | 56 ++++++++++++++++++++++++++++++++++++++
xdiff/xdiff.h | 8 +++++-
xdiff/xmerge.c | 24 +++++++++++-----
11 files changed, 155 insertions(+), 27 deletions(-)
create mode 100755 t/t6034-merge-ours-theirs.sh
^ permalink raw reply
* Re: [StGit PATCH 03/14] Write to a stack log when stack is modified
From: Karl Hasselström @ 2008-06-23 12:36 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git
In-Reply-To: <b0943d9e0806200214j77aef272sc5cfb98b002cae22@mail.gmail.com>
On 2008-06-20 10:14:29 +0100, Catalin Marinas wrote:
> 2008/6/19 Karl Hasselström <kha@treskal.com>:
>
> > If we ever want to be able to undo "stg repair", we have to be
> > able to represent an inconsistent state where head != top.
>
> I wouldn't bother with this feature. Why would one want to break the
> stack again after repairing? If they merge patches and git commits,
> they either repair the stack or commit all the patches and continue
> with using Git only.
Sometimes, stg repair isn't what you want. Say e.g. that you've done
git merge. stg repair will in this case stop reading your history once
it sees the merge commit, and consider all your patches unapplied. But
what you really want is to undo the git merge. Or that you've done git
rebase -- in that case, stg repair will realize that your patches are
unapplied, which is correct but probably not what you want.
For those of us who know what stg repair does, realizing when not to
run it and what to do instead is easy. But for very little extra
effort in the stack log, we can make the generic "stg undo" able to
fix these mistakes automatically. _And_ give the user assurance that
whatever she does with stg reset and stg undo, it can be undone.
> > I'd actually say the opposite: until we have a good visualizer
> > that doesn't need the simplified log, we need to have the
> > simplified log. If I actually have to look at the diffs in the
> > log, I find gitk indispensible.
>
> And what would the simplified log contain if we decide to go with a
> new scheme? In your proposal, it points to the tree of main log and
> you get the diff of diffs (which also means that the diffs must be
> generated for every modification of a patch). Would this be the
> same? Again, I worry a bit about the overhead to generate the patch
> diff for every push (with refresh I'm OK). It can be optimised as in
> the stable branch where we try git-apply followed by a three-way
> merge (which, BTW, I'd like added before 0.15). If git-apply
> succeeds, there is no need to re-generate the diff.
Yes, I was imagining a simplified log precisely like the one I've
currently implemented (a tree with one blob per patch, which contains
the message, the diff, and some other odds and ends such as the
author). Two optimizations would hopefully make it fast:
1. If the patch's sha1 hasn't changed, we don't have to regenerate
the diff.
2. If the patch's sha1 has changed, but git apply was sufficient
during the merge stage, we can just reuse that patch. We do have
to write it to a blob, but we have already generated the diff and
don't need to do so again. (I've shamelessly stolen your idea
here.)
In most cases, (1) would make sure that only a small handful of
patches would need to be considered. In the cases where a lot of
patches are touched, such as rebase, (2) would provide a good speedup
(except for the cases where we had to call merge-recursive, and those
are slow anyway).
( By the way: Yes, I agree that we want to try git apply before
merge-recursive. It's on my TODO list. )
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
^ permalink raw reply
* Re: Convert 'git blame' to parse_options()
From: Johannes Schindelin @ 2008-06-23 12:28 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Linus Torvalds, Git Mailing List, Pierre Habouzit
In-Reply-To: <7vr6aoirqh.fsf@gitster.siamese.dyndns.org>
Hi,
On Sun, 22 Jun 2008, Junio C Hamano wrote:
> Linus Torvalds <torvalds@linux-foundation.org> writes:
>
> > +static int blame_bottomtop_callback(const struct option *option, const char *arg, int unset)
> > +{
> > + const char **bottomtop = option->value;
> > + if (!arg)
> > + return -1;
> > + if (*bottomtop)
> > + die("More than one '-L n,m' option given");
> > + *bottomtop = arg;
> > + return 0;
> > +}
>
> Hmmmm. I actually wanted to eventually allow more than one -L so that we
> can blame two functions inside a file, for example. Would this make it
> even harder, I have to wonder...
IMHO this would not change anything in the way of making it harder; it is
just a matter of adding the pairs to a container. The more tricky thing
is how to handle a bunch of intervals efficiently, without introducing
too much ugliness.
Ciao,
Dscho
^ permalink raw reply
* Re: [RFC] Re: Convert 'git blame' to parse_options()
From: Johannes Schindelin @ 2008-06-23 12:26 UTC (permalink / raw)
To: Pierre Habouzit; +Cc: Linus Torvalds, Git Mailing List, Junio C Hamano
In-Reply-To: <20080623082223.GA12130@artemis.madism.org>
Hi,
On Mon, 23 Jun 2008, Pierre Habouzit wrote:
> This "PARSE_OPT_IGNORE_UNRECOGNIZED" thing has been discussed many times
> in the past, but it just doesn't fly.
>
> Though to help migrations we can probably introduce a new parse option
> construct that would be a callback that is responsible for dealing with
> "things" the upper level parser doesn't know about, something where the
> callback could be:
>
> enum {
> FLAG_ERROR = -1,
> FLAG_NOT_FOR_ME,
> FLAG_IS_FOR_ME,
> FLAG_AND_VALUE_ARE_FOR_ME,
> }
>
> int (*parse_opt_unknown_cb)(int shortopt, const char *longopt,
> const char *value, void *priv);
I believe that this is what Junio was talking about when he mentioned
callbacks.
However, I think it buys us more trouble than it saves us.
Thinking about the recursive approach again, I came up with this POC:
-- snip --
[PATCH] parse-options: make it possible to have option "subtables"
Some programs share an awful lot of options, such as for the diff
machinery. Allow an option table to recurse into another option
table with the new OPTION_OPTIONS() directive.
The subtable is expected to store the values into a struct whose address
is passed as 2nd parameter to OPTION_OPTIONS().
Subtables can recurse into subtables, too.
Example:
#define null ((struct diff_options *)NULL)
struct option diff__options[] = {
OPT_INTEGER('l', NULL, &null->rename_limit,
"set rename limit"),
[...]
};
struct option diff_files__options[] = {
[...]
OPTION_OPTIONS(diff__options, &revopt.diff_options),
[...]
};
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
parse-options.c | 42 ++++++++++++++++++++++++++++++++++++++++++
parse-options.h | 5 +++++
test-parse-options.c | 16 ++++++++++++++++
3 files changed, 63 insertions(+), 0 deletions(-)
diff --git a/parse-options.c b/parse-options.c
index b8bde2b..c13c503 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -1,5 +1,6 @@
#include "git-compat-util.h"
#include "parse-options.h"
+#include "cache.h"
#define OPT_SHORT 1
#define OPT_UNSET 2
@@ -247,6 +248,41 @@ void check_typos(const char *arg, const struct option *options)
}
}
+struct growable_options {
+ int nr, alloc;
+ struct option *options;
+};
+
+static void *add_base_var(void *value, void *base_var)
+{
+ return (void *)(((char *)base_var) +
+ (((char *)value) - ((char *)NULL)));
+}
+
+static void expand_option_tables(const struct option *options, void *base_var,
+ struct growable_options *result, int add_option_end)
+{
+ for (; options->type != OPTION_END; options++) {
+ if (options->type == OPTION_OPTIONS)
+ expand_option_tables(options->option_table,
+ add_base_var(options->value, base_var),
+ result, 0);
+ else {
+ ALLOC_GROW(result->options,
+ result->nr + 1, result->alloc);
+ result->options[result->nr] = *options;
+ if (base_var)
+ result->options[result->nr].value =
+ add_base_var(options->value, base_var);
+ result->nr++;
+ }
+ }
+ if (add_option_end) {
+ ALLOC_GROW(result->options, result->nr + 1, result->alloc);
+ result->options[result->nr++].type = OPTION_END;
+ }
+}
+
static NORETURN void usage_with_options_internal(const char * const *,
const struct option *, int);
@@ -254,6 +290,10 @@ int parse_options(int argc, const char **argv, const struct option *options,
const char * const usagestr[], int flags)
{
struct optparse_t args = { argv + 1, argv, argc - 1, 0, NULL };
+ struct growable_options growable_options = { 0, 0, NULL };
+
+ expand_option_tables(options, NULL, &growable_options, 1);
+ options = growable_options.options;
for (; args.argc; args.argc--, args.argv++) {
const char *arg = args.argv[0];
@@ -298,6 +338,8 @@ int parse_options(int argc, const char **argv, const struct option *options,
usage_with_options(usagestr, options);
}
+ free(growable_options.options);
+
memmove(args.out + args.cpidx, args.argv, args.argc * sizeof(*args.out));
args.out[args.cpidx + args.argc] = NULL;
return args.cpidx + args.argc;
diff --git a/parse-options.h b/parse-options.h
index 4ee443d..be6e858 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -15,6 +15,8 @@ enum parse_opt_type {
OPTION_STRING,
OPTION_INTEGER,
OPTION_CALLBACK,
+ /* recursing into another options table */
+ OPTION_OPTIONS,
};
enum parse_opt_flags {
@@ -83,6 +85,7 @@ struct option {
int flags;
parse_opt_cb *callback;
intptr_t defval;
+ struct option *option_table;
};
#define OPT_END() { OPTION_END }
@@ -99,6 +102,8 @@ struct option {
parse_opt_approxidate_cb }
#define OPT_CALLBACK(s, l, v, a, h, f) \
{ OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) }
+#define OPT_OPTIONS(table, base_var) \
+ { OPTION_OPTIONS, 0, NULL, base_var, NULL, NULL, 0, NULL, 0, table }
/* parse_options() will filter out the processed options and leave the
* non-option argments in argv[].
diff --git a/test-parse-options.c b/test-parse-options.c
index 2a79e72..5032e71 100644
--- a/test-parse-options.c
+++ b/test-parse-options.c
@@ -18,8 +18,21 @@ int length_callback(const struct option *opt, const char *arg, int unset)
return 0;
}
+struct some_struct {
+ int an_int;
+ char *a_string;
+};
+#define some_struct_null ((struct some_struct *)NULL)
+struct option an_option_table[] = {
+ OPT_GROUP("An option table"),
+ OPT_INTEGER('Y', NULL, &some_struct_null->an_int, "an integer"),
+ OPT_STRING(0, "a-string", &some_struct_null->a_string,
+ "a-string", "get another string"),
+};
+
int main(int argc, const char **argv)
{
+ struct some_struct some = { 0, NULL };
const char *usage[] = {
"test-parse-options <options>",
NULL
@@ -42,6 +55,7 @@ int main(int argc, const char **argv)
OPT_STRING('o', NULL, &string, "str", "get another string"),
OPT_SET_PTR(0, "default-string", &string,
"set string to default", (unsigned long)"default"),
+ OPT_OPTIONS(an_option_table, (void *)&some),
OPT_GROUP("Magic arguments"),
OPT_ARGUMENT("quux", "means --quux"),
OPT_GROUP("Standard options"),
@@ -62,6 +76,8 @@ int main(int argc, const char **argv)
printf("verbose: %d\n", verbose);
printf("quiet: %s\n", quiet ? "yes" : "no");
printf("dry run: %s\n", dry_run ? "yes" : "no");
+ printf("an int: %d\n", some.an_int);
+ printf("a string: %s\n", some.a_string);
for (i = 0; i < argc; i++)
printf("arg %02d: %s\n", i, argv[i]);
--
1.5.6.127.g3fb9f
-- snap --
Maybe this is food for thought.
Ciao,
Dscho
^ permalink raw reply related
* Re: What's cooking in git.git (topics)
From: Miklos Vajna @ 2008-06-23 12:15 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vk5ggipuw.fsf@gitster.siamese.dyndns.org>
[-- Attachment #1: Type: text/plain, Size: 1242 bytes --]
On Mon, Jun 23, 2008 at 12:15:35AM -0700, Junio C Hamano <gitster@pobox.com> wrote:
> * mv/merge-in-c (Sat Jun 21 19:15:35 2008 +0200) 14 commits
> - Add new test case to ensure git-merge reduces octopus parents when
> possible
> - Add new test case to ensure git-merge filters for independent
> parents
> - Build in merge
> - Introduce reduce_heads()
> - Introduce get_merge_bases_many()
> - Add new test to ensure git-merge handles more than 25 refs.
> - Introduce get_octopus_merge_bases() in commit.c
> - git-fmt-merge-msg: make it usable from other builtins
> - Move read_cache_unmerged() to read-cache.c
> - parseopt: add a new PARSE_OPT_ARGV0_IS_AN_OPTION option
> - Add new test to ensure git-merge handles pull.twohead and
> pull.octopus
> - Move parse-options's skip_prefix() to git-compat-util.h
> - Move commit_list_count() to commit.c
> - Move split_cmdline() to alias.c
"Add new test case to ensure git-merge reduces octopus parents when
possible" does exactly the same as "Add new test case to ensure
git-merge filters for independent parents", so I think you should drop
the later. Only the name of the test and the commit message differs, and
I think we want to avoid redundancy in the testsuite. ;-)
[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* Re: MinGW port pull request
From: Johannes Sixt @ 2008-06-23 11:54 UTC (permalink / raw)
To: Junio C Hamano; +Cc: msysGit, Git Mailing List
In-Reply-To: <200806212318.47745.johannes.sixt@telecom.at>
Johannes Sixt schrieb:
> On Samstag, 21. Juni 2008, Junio C Hamano wrote:
>> Johannes Sixt <j.sixt@viscovery.net> writes:
>>> please pull the MinGW (Windows) port patch series from
>>>
>>> git://repo.or.cz/git/mingw/j6t.git for-junio
I've updated the branch (it's still based on v1.5.6). Please pull again.
>> Took a look. A quick impression.
>>
>> * Too many whitespace breakages in borrowed compat/regex.[ch] are very
>> distracting.
>
> Will fixup, no problem.
Done.
>> * Shouldn't my_mktime() if exported out of date.c be named a bit better?
>
> How about tm_to_time_t()?
Done. There's a new commit (for-junio~26) that does only the renaming.
>> * The ifdef block in git.c::main() introduces decl-after-stmt which we
>> tend to avoid, but it is much worse to solve it by adding another ifdef
>> block just to enclose decl of char *bslash at the beginning of the
>> function. Perhaps enclose it in an extra block?
The #ifdef block is gone.
>> * In sanitary_path_copy(), you left "break;" after /* (1) */ but now that
>> "break" is not inside a switch() anymore, so you are breaking out of
>> something else, aren't you? -- Ah, the clean-up phase will be no-op in
>> that case because src points at '\0'. Tricky but looks correct ;-)
>
> I'm pretty certain that it is an omission. I'll remove the 'break' in the next
> round. It's just unnecessarily tricky.
Done.
>> * There seem to be an unrelated general fix in upload-pack.c
>
> Yes, indeed. It's the fflush(pack_pipe) that could make a difference. I wonder
> why this ever worked without it. Notice that traverse_commit_list calls
> show_object() last, but show_object() never flushes pack_pipe. Are fdopen()ed
> pipes line-buffered or unbuffered?
I didn't change anything here because I don't know why the old code works
on *nix, and I only know that the change is *necessary* on Windows.
> To reduce #ifdef in other places I have some proposals. Please tell me which
> you like or dislike:
>
> * The #ifdef STRIP_EXTENSION can be removed with a conditional like this:
>
> static const char ext[] = STRIP_EXTENSION; // "" or ".exe"
> if (sizeof(ext) > 1) {
> ...
> }
Done.
> * The #ifdef in main() of git.c can be removed with a custom loop that checks
> for is_dir_sep():
>
> slash = cmd + strlen(cmd);
> while (slash > cmd && !is_dir_sep(*--slash))
> ;
> if (slash >= cmd) { // was: if (slash) {
> ...
Done in a similar way.
> * We could wrap getenv(), so that the getenv("TEMPDIR") in path.c does not
> need to be followed up with getenv("TMP") and getenv("TEMP"). I'll do that.
Done.
> * The #ifdef in setup.c, prefix_filename() could easily be removed by using
> the MINGW32 arm everywhere. This would penalize non-Windows, however,
> prefix_filename() is not performance critical.
NOT done.
>> * There is an interaction with dr/ceiling topic that is already in 'next'
>> that needs to be resolved before we merge this in 'next'.
Will take care of this next; I'm running out of time now.
The interdiff follows; I created it with diff -b to hide the whitespace
changes. As you can see, there are a few more editorial changes in
compat/mingw.c (in comments and error texts only).
-- Hannes
compat/mingw.c | 33 ++++++++++++++++++++++-----------
compat/mingw.h | 3 +++
date.c | 13 ++++++++-----
git-compat-util.h | 5 +++++
git.c | 23 +++++++++++------------
path.c | 7 -------
setup.c | 1 -
7 files changed, 49 insertions(+), 36 deletions(-)
diff --git a/compat/mingw.c b/compat/mingw.c
index ee26df9..3a05fe7 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -218,7 +218,6 @@ int mkstemp(char *template)
int gettimeofday(struct timeval *tv, void *tz)
{
- extern time_t my_mktime(struct tm *tm);
SYSTEMTIME st;
struct tm tm;
GetSystemTime(&st);
@@ -228,7 +227,7 @@ int gettimeofday(struct timeval *tv, void *tz)
tm.tm_hour = st.wHour;
tm.tm_min = st.wMinute;
tm.tm_sec = st.wSecond;
- tv->tv_sec = my_mktime(&tm);
+ tv->tv_sec = tm_to_time_t(&tm);
if (tv->tv_sec < 0)
return -1;
tv->tv_usec = st.wMilliseconds*1000;
@@ -367,6 +366,19 @@ char *mingw_getcwd(char *pointer, int len)
return ret;
}
+#undef getenv
+char *mingw_getenv(const char *name)
+{
+ char *result = getenv(name);
+ if (!result && !strcmp(name, "TMPDIR")) {
+ /* on Windows it is TMP and TEMP */
+ result = getenv("TMP");
+ if (!result)
+ result = getenv("TEMP");
+ }
+ return result;
+}
+
/*
* See http://msdn2.microsoft.com/en-us/library/17w5ykft(vs.71).aspx
* (Parsing C++ Command-Line Arguments)
@@ -895,13 +907,12 @@ static int one_shot;
static sig_handler_t timer_fn = SIG_DFL;
/* The timer works like this:
- * The thread, ticktack(), is basically a trivial routine that most of the
- * time only waits to receive the signal to terminate. The main thread
- * tells the thread to terminate by setting the timer_event to the signalled
+ * The thread, ticktack(), is a trivial routine that most of the time
+ * only waits to receive the signal to terminate. The main thread tells
+ * the thread to terminate by setting the timer_event to the signalled
* state.
- * But ticktack() does not wait indefinitely; instead, it interrupts the
- * wait state every now and then, namely exactly after timer's interval
- * length. At these opportunities it calls the signal handler.
+ * But ticktack() interrupts the wait state after the timer's interval
+ * length to call the signal handler.
*/
static __stdcall unsigned ticktack(void *dummy)
@@ -927,7 +938,7 @@ static int start_timer_thread(void)
error("cannot start timer thread");
} else
return errno = ENOMEM,
- error("cannot allocate resources timer");
+ error("cannot allocate resources for timer");
return 0;
}
@@ -962,11 +973,11 @@ int setitimer(int type, struct itimerval *in, struct
itimerval *out)
if (out != NULL)
return errno = EINVAL,
- error("setitmer param 3 != NULL not implemented");
+ error("setitimer param 3 != NULL not implemented");
if (!is_timeval_eq(&in->it_interval, &zero) &&
!is_timeval_eq(&in->it_interval, &in->it_value))
return errno = EINVAL,
- error("setitmer: it_interval must be zero or eq it_value");
+ error("setitimer: it_interval must be zero or eq it_value");
if (timer_thread)
stop_timer_thread();
diff --git a/compat/mingw.h b/compat/mingw.h
index 6965e3f..6bc049a 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -145,6 +145,9 @@ int mingw_open (const char *filename, int oflags, ...);
char *mingw_getcwd(char *pointer, int len);
#define getcwd mingw_getcwd
+char *mingw_getenv(const char *name);
+#define getenv mingw_getenv
+
struct hostent *mingw_gethostbyname(const char *host);
#define gethostbyname mingw_gethostbyname
diff --git a/compat/regex.c b/compat/regex.c
index 1d39e08..87b33e4 100644
diff --git a/compat/regex.h b/compat/regex.h
index 408dd21..6eb64f1 100644
diff --git a/date.c b/date.c
index d6f8bf6..35a5257 100644
--- a/date.c
+++ b/date.c
@@ -6,7 +6,10 @@
#include "cache.h"
-time_t my_mktime(struct tm *tm)
+/*
+ * This is like mktime, but without normalization of tm_wday and tm_yday.
+ */
+time_t tm_to_time_t(const struct tm *tm)
{
static const int mdays[] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
@@ -67,7 +70,7 @@ static int local_tzoffset(unsigned long time)
t = time;
localtime_r(&t, &tm);
- t_local = my_mktime(&tm);
+ t_local = tm_to_time_t(&tm);
if (t_local < t) {
eastwest = -1;
@@ -322,7 +325,7 @@ static int is_date(int year, int month, int day,
struct tm *now_tm, time_t now,
if (!now_tm)
return 1;
- specified = my_mktime(r);
+ specified = tm_to_time_t(r);
/* Be it commit time or author time, it does not make
* sense to specify timestamp way into the future. Make
@@ -572,7 +575,7 @@ int parse_date(const char *date, char *result, int maxlen)
}
/* mktime uses local timezone */
- then = my_mktime(&tm);
+ then = tm_to_time_t(&tm);
if (offset == -1)
offset = (then - mktime(&tm)) / 60;
@@ -611,7 +614,7 @@ void datestamp(char *buf, int bufsize)
time(&now);
- offset = my_mktime(localtime(&now)) - now;
+ offset = tm_to_time_t(localtime(&now)) - now;
offset /= 60;
date_string(now, offset, buf, bufsize);
diff --git a/git-compat-util.h b/git-compat-util.h
index 46fc2d3..51823ae 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -114,6 +114,10 @@
#define PATH_SEP ':'
#endif
+#ifndef STRIP_EXTENSION
+#define STRIP_EXTENSION ""
+#endif
+
#ifndef has_dos_drive_prefix
#define has_dos_drive_prefix(path) 0
#endif
@@ -143,6 +147,7 @@ extern void set_error_routine(void (*routine)(const
char *err, va_list params));
extern void set_warn_routine(void (*routine)(const char *warn, va_list
params));
extern int prefixcmp(const char *str, const char *prefix);
+extern time_t tm_to_time_t(const struct tm *tm);
#ifdef NO_MMAP
diff --git a/git.c b/git.c
index a4b0a5e..871b93c 100644
--- a/git.c
+++ b/git.c
@@ -369,15 +369,16 @@ static void handle_internal_command(int argc, const
char **argv)
{ "pack-refs", cmd_pack_refs, RUN_SETUP },
};
int i;
+ static const char ext[] = STRIP_EXTENSION;
-#ifdef STRIP_EXTENSION
- i = strlen(argv[0]) - strlen(STRIP_EXTENSION);
- if (i > 0 && !strcmp(argv[0] + i, STRIP_EXTENSION)) {
+ if (sizeof(ext) > 1) {
+ i = strlen(argv[0]) - strlen(ext);
+ if (i > 0 && !strcmp(argv[0] + i, ext)) {
char *argv0 = strdup(argv[0]);
argv[0] = cmd = argv0;
argv0[i] = '\0';
}
-#endif
+ }
/* Turn "git cmd --help" into "git help cmd" */
if (argc > 1 && !strcmp(argv[1], "--help")) {
@@ -395,8 +396,8 @@ static void handle_internal_command(int argc, const
char **argv)
int main(int argc, const char **argv)
{
- const char *cmd = argv[0] ? argv[0] : "git-help";
- char *slash = strrchr(cmd, '/');
+ const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help";
+ char *slash = (char *)cmd + strlen(cmd);
const char *cmd_path = NULL;
int done_alias = 0;
@@ -405,12 +406,10 @@ int main(int argc, const char **argv)
* name, and the dirname as the default exec_path
* if we don't have anything better.
*/
-#ifdef __MINGW32__
- char *bslash = strrchr(cmd, '\\');
- if (!slash || (bslash && bslash > slash))
- slash = bslash;
-#endif
- if (slash) {
+ do
+ --slash;
+ while (cmd <= slash && !is_dir_sep(*slash));
+ if (cmd <= slash) {
*slash++ = 0;
cmd_path = cmd;
cmd = slash;
diff --git a/path.c b/path.c
index 5da41c7..7a35a26 100644
--- a/path.c
+++ b/path.c
@@ -75,13 +75,6 @@ int git_mkstemp(char *path, size_t len, const char
*template)
size_t n;
tmp = getenv("TMPDIR");
-#ifdef __MINGW32__
- /* on Windows it is TMP and TEMP */
- if (!tmp)
- tmp = getenv("TMP");
- if (!tmp)
- tmp = getenv("TEMP");
-#endif
if (!tmp)
tmp = "/tmp";
n = snprintf(path, len, "%s/%s", tmp, template);
diff --git a/setup.c b/setup.c
index ec33147..8bb7b10 100644
--- a/setup.c
+++ b/setup.c
@@ -35,7 +35,6 @@ static int sanitary_path_copy(char *dst, const char *src)
if (!src[1]) {
/* (1) */
src++;
- break;
} else if (is_dir_sep(src[1])) {
/* (2) */
src += 2;
^ permalink raw reply related
* Re: linux-x86-tip: pilot error?
From: Ingo Molnar @ 2008-06-23 11:22 UTC (permalink / raw)
To: Paul E. McKenney; +Cc: Mikael Magnusson, git, Thomas Gleixner
In-Reply-To: <20080623111144.GP22569@linux.vnet.ibm.com>
* Paul E. McKenney <paulmck@linux.vnet.ibm.com> wrote:
> > That would be expected behavior, yes. You can try a "test-pull" into
> > core/rcu:
> >
> > git-checkout -b test-rcu tip/core/rcu
> > git-merge paulmck-rcu-2008-06-23 # replace with git-pull and an URI
> >
> > ... and then look at how "git-log test-rcu..linus/master" looks like. It
> > should show all the changes of the RCU topic, your two new commits
> > included.
>
> The git-merge seemed to run normally, but the git-log command showed
> no output. Hmmm...
sorry, i accidentally reversed the arguments, that should have been:
git-shortlog linus/master..test-rcu
git-log linus/master..test-rcu
the way such things can be noticed is to replace git-log with git-diff:
git-diff test-rcu..linus/master
and if that does not come empty (and it didnt), it's an ordering
problem.
> > $ git-describe 481c5346d0981940ee63037eb53e4e37b0735c10
> > v2.6.26-rc7-25-g481c534
>
> So this shows the last linus/master commit -not- containing the patch,
> correct? Ah, the most recent -tag-. So I have to be a bit careful
> about creating tags if I want this to work for me. Fair enough!
you most definitely need to be careful about creating tags. Tags have
extra security value (Linus signs them, etc.) and Git will not override
an existing tag by default in any case. So you definitely dont want to
create a tag that collides with any expected future upstream tag. (such
as "v2.6.26" or "2.6.26-rc8")
tags are mostly meant for release management - our use of tags to save
the "merge base" of topic branches in -tip can be considered mild abuse.
( But we did not want to pullute the already crowded branch space with
extra technical branches. -tip has 127 branches at this moment, 89 of
which are topic branches - creating a base branch for each topic would
add another 89 branches and bring it all up to 216 branches. )
Ingo
^ permalink raw reply
* Re: linux-x86-tip: pilot error?
From: Paul E. McKenney @ 2008-06-23 11:11 UTC (permalink / raw)
To: Ingo Molnar; +Cc: Mikael Magnusson, git, Thomas Gleixner
In-Reply-To: <20080623102424.GA28192@elte.hu>
On Mon, Jun 23, 2008 at 12:24:24PM +0200, Ingo Molnar wrote:
>
> * Paul E. McKenney <paulmck@linux.vnet.ibm.com> wrote:
>
> > On Mon, Jun 23, 2008 at 09:14:41AM +0200, Ingo Molnar wrote:
> > >
> > > * Paul E. McKenney <paulmck@linux.vnet.ibm.com> wrote:
> > >
> > > > Trying "git-checkout -b tip-core-rcu
> > > > tip-core-rcu-2008-06-16_09.23_Mon" acts like it is doing something
> > > > useful, but doesn't find the recent updates, which I believe happened
> > > > -before- June 16 2008.
> > >
> > > finding the rcu topic branch in -tip can be done the following way:
> > >
> > > $ git-branch -a | grep rcu
> > > tip/core/rcu
> >
> > Ah!!! Good, that does show me this branch. I created a new branch
> > "paulmck-rcu-2008-06-23" just out of paranoia.
>
> that's OK - having more branches never hurts.
>
> if, while juggling branches, you lose some commit somewhere it makes
> sense to check .git/logs/. [ Up until the point Git does a
> garbage-collection run and zaps any orphaned commits ;-) ]
Thank you -- I have had some things disappear on me in the past. ;-)
> > > if you check out that branch for your own use, you should also do:
> > >
> > > $ git-merge linus/master
> > >
> > > To bring it up to latest upstream.
> >
> > OK, that did pull in a number of changes. The gitk tool then shows my
> > "Merge commit 'linus/master' into paulmck-rcu-2008-06-23" at the head
> > of the display, with parents as follows:
> >
> > Parent: 31a72bce0bd6f3e0114009288bccbc96376eeeca (rcu: make rcutorture more vicious: reinstate boot-time testing)
> > Parent: bec95aab8c056ab490fe7fa54da822938562443d (Merge branch 'release' of git://lm-sensors.org/kernel/mhoffman/hwmon-2.6)
> >
> > This means that the RCU-related changes show up discontinuously in the
> > gitk display, but clicking on the left-most connector and selecting
> > "parent" gets me to the rest of the tip/core/rcu branch, so should be
> > OK, I guess. ;-)
>
> i have just talked to Thomas about it and we'll change our scripting so
> that the tip/core/rcu branch will always be very recent and merged up to
> latest -git.
>
> As one of the goals of the tip/* structure is to distribute topics to
> others (or as Linus has put it, Thomas and me needs to become more
> managerial about maintenance ;), there's real value in having the topics
> appear up-to-date when people try them out.
;-)
> ( it's possible to do this without criss-cross merge commits - it just
> needs some more creative scripting in -tip. )
I am keeping the hints in a README file on my machine -- thank you!!!
> > I then applied my two patches from yesterday (EDT timezone), just for
> > practice.
> >
> > These show up after the merge.
> >
> > But now when I do "git-log tip/core/rcu..linus/master", I get one very
> > large pile of patches. It apparently includes the stuff I merged from
> > linus/master. This is expected behavior, correct?
>
> That would be expected behavior, yes. You can try a "test-pull" into
> core/rcu:
>
> git-checkout -b test-rcu tip/core/rcu
> git-merge paulmck-rcu-2008-06-23 # replace with git-pull and an URI
>
> ... and then look at how "git-log test-rcu..linus/master" looks like. It
> should show all the changes of the RCU topic, your two new commits
> included.
The git-merge seemed to run normally, but the git-log command showed
no output. Hmmm...
> > So, if I want to identify the RCU patches since some specific Linus
> > release (for example, 2.6.26-rc7), I follow the RCU parents down until
> > I find the desired release tag, then generate diffs from the ranges I
> > find, right?
> >
> > Hmmm, actually, no, this bypasses the v2.6.26-rcN tags.
> >
> > One approach is apparently to use gitk to create a view that includes
> > the patches touching the RCU-related files. The git-log command also
> > takes pathname arguments, so that allows me to get an approximation as
> > well.
> >
> > I will have to look more at git-log and gitk -- probably I should be
> > paying more attention to patches adding or deleting the strings "RCU"
> > or "rcu" to the kernel. ;-)
>
> You can use the filenames as a commit filter, for example:
>
> git-shortlog v2.6.25.. kernel/rcu* include/linux/rcu*
OK, this does seem to give a good list.
> Will give you a rather good view about what things changed in RCU land
> in v2.6.26 so far.
>
> To see what is queued up in -tip for v2.6.27 that affect RCU, you can
> do:
>
> git-shortlog linus/master..tip/master kernel/rcu* include/linux/rcu*
This also looks good at first glance.
> This will show tip/core/rcu changes. Not unsurprisingly this will show
> something quite similar to:
>
> git-shortlog linus/master..tip/core/rcu
>
> ... as all RCU patches are supposed to be in that topic branch. [ But it
> does not hurt to double check me on that :-) ]
This looks good at first glance.
> The widest search that doesnt involve the checking of around 100,000
> commits is the tip-log-line utility you can find in the tip/tip branch.
> Via that utility you can filter out all interesting RCU commits:
>
> tip-log-line kernel/rcu* include/linux/rcu*
>
> it will output a tidy list of branches, sha1's and subject lines.
I will dig up the tip-log-line utility.
> (you'll probably first need to run tip-create-local-branches.sh to
> create local branches out of all the tip topics.)
>
> for example, to see RCU affecting changes not queued up in tip/core/rcu,
> you can do:
>
> ~/tip> tip-log-line kernel/rcu* include/linux/rcu* | grep -v ' core/rcu:'
> # core/softirq: 962cf36: Remove argument from open_softirq which is always NULL
> # core/softirq: a60b33c: Merge branch 'linus' into core/softirq
> # cpus4096: 363ab6f: core: use performance variant for_each_cpu_mask_nr
>
> > Is there some way to determine whether a give patch has a tagged patch
> > (e.g., v2.6.26-rc7) as a child? It would be very cool to be able to
> > dump only those patches that are not part of v2.6.26-rc7, as this
> > would allow me to automatically generate the list of RCU-related
> > patches from linux-2.6-tip to test against this RC.
>
> if i understood you correctly, git-describe will do that for you
> normally. If you have an sha1 you can do:
>
> $ git-describe 481c5346d0981940ee63037eb53e4e37b0735c10
> v2.6.26-rc7-25-g481c534
So this shows the last linus/master commit -not- containing the patch,
correct? Ah, the most recent -tag-. So I have to be a bit careful
about creating tags if I want this to work for me. Fair enough!
Thanx, Paul
^ permalink raw reply
* Re: linux-x86-tip: pilot error?
From: Ingo Molnar @ 2008-06-23 10:24 UTC (permalink / raw)
To: Paul E. McKenney; +Cc: Mikael Magnusson, git, Thomas Gleixner
In-Reply-To: <20080623095732.GL22569@linux.vnet.ibm.com>
* Paul E. McKenney <paulmck@linux.vnet.ibm.com> wrote:
> On Mon, Jun 23, 2008 at 09:14:41AM +0200, Ingo Molnar wrote:
> >
> > * Paul E. McKenney <paulmck@linux.vnet.ibm.com> wrote:
> >
> > > Trying "git-checkout -b tip-core-rcu
> > > tip-core-rcu-2008-06-16_09.23_Mon" acts like it is doing something
> > > useful, but doesn't find the recent updates, which I believe happened
> > > -before- June 16 2008.
> >
> > finding the rcu topic branch in -tip can be done the following way:
> >
> > $ git-branch -a | grep rcu
> > tip/core/rcu
>
> Ah!!! Good, that does show me this branch. I created a new branch
> "paulmck-rcu-2008-06-23" just out of paranoia.
that's OK - having more branches never hurts.
if, while juggling branches, you lose some commit somewhere it makes
sense to check .git/logs/. [ Up until the point Git does a
garbage-collection run and zaps any orphaned commits ;-) ]
> > if you check out that branch for your own use, you should also do:
> >
> > $ git-merge linus/master
> >
> > To bring it up to latest upstream.
>
> OK, that did pull in a number of changes. The gitk tool then shows my
> "Merge commit 'linus/master' into paulmck-rcu-2008-06-23" at the head
> of the display, with parents as follows:
>
> Parent: 31a72bce0bd6f3e0114009288bccbc96376eeeca (rcu: make rcutorture more vicious: reinstate boot-time testing)
> Parent: bec95aab8c056ab490fe7fa54da822938562443d (Merge branch 'release' of git://lm-sensors.org/kernel/mhoffman/hwmon-2.6)
>
> This means that the RCU-related changes show up discontinuously in the
> gitk display, but clicking on the left-most connector and selecting
> "parent" gets me to the rest of the tip/core/rcu branch, so should be
> OK, I guess. ;-)
i have just talked to Thomas about it and we'll change our scripting so
that the tip/core/rcu branch will always be very recent and merged up to
latest -git.
As one of the goals of the tip/* structure is to distribute topics to
others (or as Linus has put it, Thomas and me needs to become more
managerial about maintenance ;), there's real value in having the topics
appear up-to-date when people try them out.
( it's possible to do this without criss-cross merge commits - it just
needs some more creative scripting in -tip. )
> I then applied my two patches from yesterday (EDT timezone), just for
> practice.
>
> These show up after the merge.
>
> But now when I do "git-log tip/core/rcu..linus/master", I get one very
> large pile of patches. It apparently includes the stuff I merged from
> linus/master. This is expected behavior, correct?
That would be expected behavior, yes. You can try a "test-pull" into
core/rcu:
git-checkout -b test-rcu tip/core/rcu
git-merge paulmck-rcu-2008-06-23 # replace with git-pull and an URI
... and then look at how "git-log test-rcu..linus/master" looks like. It
should show all the changes of the RCU topic, your two new commits
included.
> So, if I want to identify the RCU patches since some specific Linus
> release (for example, 2.6.26-rc7), I follow the RCU parents down until
> I find the desired release tag, then generate diffs from the ranges I
> find, right?
>
> Hmmm, actually, no, this bypasses the v2.6.26-rcN tags.
>
> One approach is apparently to use gitk to create a view that includes
> the patches touching the RCU-related files. The git-log command also
> takes pathname arguments, so that allows me to get an approximation as
> well.
>
> I will have to look more at git-log and gitk -- probably I should be
> paying more attention to patches adding or deleting the strings "RCU"
> or "rcu" to the kernel. ;-)
You can use the filenames as a commit filter, for example:
git-shortlog v2.6.25.. kernel/rcu* include/linux/rcu*
Will give you a rather good view about what things changed in RCU land
in v2.6.26 so far.
To see what is queued up in -tip for v2.6.27 that affect RCU, you can
do:
git-shortlog linus/master..tip/master kernel/rcu* include/linux/rcu*
This will show tip/core/rcu changes. Not unsurprisingly this will show
something quite similar to:
git-shortlog linus/master..tip/core/rcu
... as all RCU patches are supposed to be in that topic branch. [ But it
does not hurt to double check me on that :-) ]
The widest search that doesnt involve the checking of around 100,000
commits is the tip-log-line utility you can find in the tip/tip branch.
Via that utility you can filter out all interesting RCU commits:
tip-log-line kernel/rcu* include/linux/rcu*
it will output a tidy list of branches, sha1's and subject lines.
(you'll probably first need to run tip-create-local-branches.sh to
create local branches out of all the tip topics.)
for example, to see RCU affecting changes not queued up in tip/core/rcu,
you can do:
~/tip> tip-log-line kernel/rcu* include/linux/rcu* | grep -v ' core/rcu:'
# core/softirq: 962cf36: Remove argument from open_softirq which is always NULL
# core/softirq: a60b33c: Merge branch 'linus' into core/softirq
# cpus4096: 363ab6f: core: use performance variant for_each_cpu_mask_nr
> Is there some way to determine whether a give patch has a tagged patch
> (e.g., v2.6.26-rc7) as a child? It would be very cool to be able to
> dump only those patches that are not part of v2.6.26-rc7, as this
> would allow me to automatically generate the list of RCU-related
> patches from linux-2.6-tip to test against this RC.
if i understood you correctly, git-describe will do that for you
normally. If you have an sha1 you can do:
$ git-describe 481c5346d0981940ee63037eb53e4e37b0735c10
v2.6.26-rc7-25-g481c534
Ingo
^ permalink raw reply
* Re: git blame for a commit
From: Jakub Narebski @ 2008-06-23 10:01 UTC (permalink / raw)
To: Mircea Bardac; +Cc: Ian Hilt, git
In-Reply-To: <485F6710.1080300@mircea.bardac.net>
Mircea Bardac <dev@mircea.bardac.net> writes:
> Ian Hilt wrote:
>> On Sun, 22 Jun 2008 at 11:32pm +0100, Mircea Bardac wrote:
>>>
>>> Is there any straightforward way of doing git blame for all the
>>> files that got changed in a commit. Problems are renames, deletes
>>> and copies.
>>
>> Sounds like you want to track files rather than content. Git tracks
>> the
>> latter.
>
> Hmm... I'm not really sure that my initial intention was to track
> files. I've given this some more thought and I realized that what I
> actually want is a "git diff" with blame info included. I want this
> information in order to facilitate code reviewing.
>
> It is true that this would be a front-end functionality, but I am not
> sure at the moment what the best approach would be for something like
> this. I would see this something like
> $ git diff --blame[="parameters_for_blame"] commit1..commit2
> but this is just a thought.
>
> Has anyone tried blaming a "git diff"?
I think you could script it using "git diff", and "git blame -L m,n",
where line ranges would be calculated from git diff header for
post-image, or both pre-image and post-image (in the case of deletions).
--
Jakub Narebski
Poland
ShadeHawk on #git
^ permalink raw reply
* Re: linux-x86-tip: pilot error?
From: Paul E. McKenney @ 2008-06-23 9:57 UTC (permalink / raw)
To: Ingo Molnar; +Cc: Mikael Magnusson, git, Thomas Gleixner
In-Reply-To: <20080623071441.GA28887@elte.hu>
On Mon, Jun 23, 2008 at 09:14:41AM +0200, Ingo Molnar wrote:
>
> * Paul E. McKenney <paulmck@linux.vnet.ibm.com> wrote:
>
> > Trying "git-checkout -b tip-core-rcu
> > tip-core-rcu-2008-06-16_09.23_Mon" acts like it is doing something
> > useful, but doesn't find the recent updates, which I believe happened
> > -before- June 16 2008.
>
> finding the rcu topic branch in -tip can be done the following way:
>
> $ git-branch -a | grep rcu
> tip/core/rcu
Ah!!! Good, that does show me this branch. I created a new branch
"paulmck-rcu-2008-06-23" just out of paranoia.
> and doing a "git-log tip/core/rcu..linus/master" will show you the
> commits that are in the tip/core/rcu topic branch.
>
> if you check out that branch for your own use, you should also do:
>
> $ git-merge linus/master
>
> To bring it up to latest upstream.
OK, that did pull in a number of changes. The gitk tool then shows my
"Merge commit 'linus/master' into paulmck-rcu-2008-06-23" at the head
of the display, with parents as follows:
Parent: 31a72bce0bd6f3e0114009288bccbc96376eeeca (rcu: make rcutorture more vicious: reinstate boot-time testing)
Parent: bec95aab8c056ab490fe7fa54da822938562443d (Merge branch 'release' of git://lm-sensors.org/kernel/mhoffman/hwmon-2.6)
This means that the RCU-related changes show up discontinuously in
the gitk display, but clicking on the left-most connector and selecting
"parent" gets me to the rest of the tip/core/rcu branch, so should be OK,
I guess. ;-)
I then applied my two patches from yesterday (EDT timezone), just for
practice.
These show up after the merge.
But now when I do "git-log tip/core/rcu..linus/master", I get one very
large pile of patches. It apparently includes the stuff I merged from
linus/master. This is expected behavior, correct?
So, if I want to identify the RCU patches since some specific Linus
release (for example, 2.6.26-rc7), I follow the RCU parents down until
I find the desired release tag, then generate diffs from the ranges I
find, right?
Hmmm, actually, no, this bypasses the v2.6.26-rcN tags.
One approach is apparently to use gitk to create a view that includes
the patches touching the RCU-related files. The git-log command
also takes pathname arguments, so that allows me to get an approximation
as well.
I will have to look more at git-log and gitk -- probably I should be
paying more attention to patches adding or deleting the strings
"RCU" or "rcu" to the kernel. ;-)
Is there some way to determine whether a give patch has a tagged
patch (e.g., v2.6.26-rc7) as a child? It would be very cool to
be able to dump only those patches that are not part of v2.6.26-rc7,
as this would allow me to automatically generate the list of
RCU-related patches from linux-2.6-tip to test against this RC.
> That merge, even if tip/core/rcu
> looks "old" will always be conflict-free, due to scripting we do:
>
> tip-core-rcu-2008-06-16_09.23_Mon is not a snapshot of the rcu topic -
> it is "technical" tag of the upstream Linus -git tree against which the
> rcu topic is based.
>
> We have to track the 'base' of every topic separately because otherwise
> we'd pollute the topic branches with the frequent merges to Linus's
> tree. (occasionally we merge to Linus's tree several times a day, that
> would lead to tons of merge commits that pollute the tree)
>
> So instead we do "on-demand virtual merges": we have scripting which do
> the following: in each iteration step they merge to latest Linus, check
> whether there's any files touched by the merge that are changed by the
> topic branch too - if yes then the merge is made permanent and the "this
> is this topic's latest upstream" tag is updated. If the merge was
> conflict-free, we roll back the merge.
Sounds like also tracking patches/transformations as first-class objects
would be a good research project. ;-)
Thanx, Paul
> Is there a Git way of finding the common ancestor of a topic branch,
> when compared to upstream?
>
> Ingo
^ permalink raw reply
* Re: git-rerere observations and feature suggestions
From: Ingo Molnar @ 2008-06-23 9:49 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Peter Zijlstra, Chris Mason, Thomas Gleixner
In-Reply-To: <7vej6xb4lr.fsf@gitster.siamese.dyndns.org>
[-- Attachment #1: Type: text/plain, Size: 2807 bytes --]
another git-rerere observation: occasionally it happens that i
accidentally commit a merge marker into the source code.
That's obviously stupid, and it normally gets found by testing quickly,
but still it would be a really useful avoid-shoot-self-in-foot feature
if git-commit could warn about such stupidities of mine.
( and if i could configure git-commit to outright reject a commit like
that - i never want to commit lines with <<<<<< or >>>>> markers)
Another merge conflict observation is that Git is much worse at figuring
out the right merge resolution than our previous Quilt based workflow
was. I eventually found it to be mainly due to the following detail:
sometimes it's more useful to first apply the merged branch and then
attempt to merge HEAD, as a patch.
I've got a script for that which also combines it with the "rej" tool,
and in about 70%-80% of the cases where Git is unable to resolve a merge
automatically it figures things out. ('rej' is obviously a more relaxed
merge utility, but it's fairly robust in my experience, with a very low
false positive rate.)
The ad-hoc "tip-mergetool" script we are using is attached below. It's
really just for demonstration purposes - it doesnt work when there's a
rename related conflict, etc.
Peter Zijstra also wrote a git-mergetool extension for the 'rej' tool
btw., he might want to post that patch. I've attached Chris Mason's rej
tool too.
Ingo
[ "$#" = 0 ] && {
SRC=`git-ls-files -u | cut -f2 | head -1`
} || {
SRC=`git-ls-files -u | grep $1 | cut -f2 | head -1`
}
[ "$SRC" = "" -o ! -f "$SRC" ] && { echo "$1 has no conflicts!"; exit -1; }
SRC_SED=`echo $SRC | sed 's/\//\\\\\//g'`
SHA_1=`git-ls-files -u | grep $SRC | grep '^.* .* 1\>' | cut -d' ' -f2`
SHA_2=`git-ls-files -u | grep $SRC | grep '^.* .* 2\>' | cut -d' ' -f2`
SHA_3=`git-ls-files -u | grep $SRC | grep '^.* .* 3\>' | cut -d' ' -f2`
mv -b $SRC $SRC.automerge || { echo error1; exit -1; }
git-diff $SHA_1 $SHA_2 | sed "s/$SHA_1/$SRC_SED/g" |
sed "s/$SHA_2/$SRC_SED/g" > $SRC.diff-v1 || { echo error2; exit -1; }
git-diff $SHA_1 $SHA_3 | sed "s/$SHA_1/$SRC_SED/g" |
sed "s/$SHA_3/$SRC_SED/g" > $SRC.diff-v2 || { echo error2; exit -1; }
git-cat-file -p $SHA_1 > $SRC || { echo error4; exit -1; }
ls -l $SRC.automerge $SRC $SRC.diff-v1 $SRC.diff-v2
patch -p1 < $SRC.diff-v2 || { echo error5; exit -1; }
patch -p1 < $SRC.diff-v1 || {
echo "reject file ..."
ls -l $SRC.rej
echo "trying auto-merge ..."
OK=$(rej -dry-run $SRC.rej 2>&1 | grep ', 0 conflicts remain')
[ "$OK" = "" ] && { echo "$OK"; exit -1; }
rej -a $SRC.rej
}
echo "adding $SRC to the commit"
git-add $SRC
echo 'if happy with the result, do: git-commit -m "Manual merge of conflicts."'
echo "merge successful!"
exit 0
[-- Attachment #2: rej --]
[-- Type: text/plain, Size: 29404 bytes --]
#!/usr/bin/perl
# Reject merging program, released under GPLv2
# Contact Chris Mason <mason@suse.com> with bugs or patches
#
use strict;
use Getopt::Long qw(:config no_ignore_case);
use File::Temp;
use IO::File;
use POSIX ":sys_wait_h";
my @file; # array with the contents of the file
my @merged_file; # temporary copy of the file for merging
my $diff_mode = 0; # running a multi-file diff instead of a reject
my @hunks; # array of hashes for all the hunks
my $hunk_num = 0; # total number of hunks
my $rejfh; # handle for the reject file
my $filefh; # handle for the file
my $mergefh; # handle for the merge stream
my $VERSION = "0.15";
my $context = 0; # prefer context from the reject?
my $merge_prog = "gvimdiff"; # merge program to run
my $source_file; # name of the source file
my $reject_file; # name of the reject file
my $orig_reject_file; # original name of the reject file
my $output_file; # name of merge output (skips merge prog)
my $auto; # auto mode, put results into source file
my $interactive = 0; # go into command mode
my $editor = "gvim"; # selected editor for opening the reject
my $open_reject = 0; # should the reject be opened?
my $report_only = 0; # only try to place the hunks
my $exit_value = 0; # our return value;
my $fully_matched_hunks = 0; # lines that match the -/+ lines in hunk
my $strip_level = 0; # how many path components to strip in diffmode
my $last_diff_line; # for buffering input lines
my $total_hunks; # total hunks found
my $reverse_patch = 0; # should we reverse the input patch?
my $no_forward_search = 0; # only try to match reverse hunks
my $quick = 0; # stop trying after the first conflict
my $skip_merge = 0; # don't run the merge prog at all
my $global_matched_hunks = 0; # total for whole diff
my $global_reject_hunks = 0; #
my $global_conflicts = 0; # number of conflicts for the last rej run
# string equality ignoring whitespace
# the first string has a leading control char from the reject,
# either ' ', '-' or '+'
sub fuzzy_eq($$) {
my ($hunk, $file) = @_;
my $line;
$line = $hunk;
$line =~ s/^.//;
if ($line eq $file) {
return 1;
}
# strip all whitespace and try agin
$line =~ s/\s//g;
$file =~ s/\s//g;
if ($line eq $file) {
return 1;
}
}
# places the aligned hunk into the merge stream.
# in context mode, or if this was a forward match
# all context is taken from the reject file
#
# otherwise, this tries to take as much context as possible from the source
# file.
sub place_hunk($$$) {
my ($hunk, $start, $end) = @_;
my $fhunk = \@{$hunk->{'fwhunk'}};
my $rhunk = $hunk->{'aligned_hunk'};
my $size = scalar(@file);
my $i;
my $k;
my $merged_index = 0;
my $hunk_index = 0;
my $hunk_len = scalar(@$fhunk);
my $line;
my $file_hunk = $hunk->{'file_hunk'};
my @tmp_hunk;
my $file_index;
my $c;
my $fline;
@merged_file = ();
# check for a change that is already applied. print a warning as well
# since this is not a 100% reliable check.
#
if ($hunk->{'method'} eq "forward" &&
$hunk->{'hunk_match'} == count_hunk_changed($hunk, "forward")) {
print STDERR "WARNING: skipping hunk already applied: $hunk->{'desc'}";
return;
}
# try to maximize context from the reject file
# if we matched the forward hunk, it means the file
# already had some form of the change applied. Don't
# try to be smart, just use the context from the reject
if ($hunk->{'options'} =~ m/context/ || $context ||
$hunk->{'method'} eq "forward") {
@merged_file = @file [ 0 .. $start - 1];
foreach $line (@$fhunk) {
$line =~ s/^.//;
push @merged_file, $line;
}
push @merged_file, @file[$end+1 .. scalar(@file)];
@file = @merged_file;
return;
}
# align to the start of the reverse hunk
while($hunk_index < scalar(@$rhunk)) {
$line = $rhunk->[$hunk_index];
$fline = $file_hunk->[0];
$fline =~ s/^.//;
last if (fuzzy_eq($line, $fline));
$hunk_index++;
}
$file_index = 0;
while($file_index < scalar(@$file_hunk)) {
$line = $rhunk->[$hunk_index];
$fline = $file_hunk->[$file_index];
if ($line =~ m/^\|/) {
$fline =~ s/^./\+/;
push @tmp_hunk, $fline;
} elsif ($line =~ m/^ /) {
push @tmp_hunk, $fline;
} elsif ($line =~ m/^-/) {
my $tline = $fline;
$tline =~ s/^.//;
if (!fuzzy_eq($line, $tline)) {
my $cline = $line;
$cline =~ s/^.//;
push @tmp_hunk, "+<<<<<<< delete $cline";
}
}
$file_index++;
$hunk_index++;
if ($hunk_index > scalar(@$rhunk)) {
print STDERR "warning: hunk_index is $hunk_index, limit " .
scalar(@$rhunk) . "\n";
}
}
@$file_hunk = @tmp_hunk;
@tmp_hunk = ();
$size = scalar(@$file_hunk);
$hunk_index = 0;
$file_index = 0;
while($hunk_index < scalar(@$fhunk)) {
$line = $fhunk->[$hunk_index];
$fline = $file_hunk->[0];
if (!($fline =~ m/^\|/)) {
$fline =~ s/^.//;
last if (fuzzy_eq($line, $fline));
}
last if ($line =~ m/^\+/) ;
$hunk_index++;
}
while($file_index < $size || $hunk_index < scalar(@$fhunk)) {
$fline = $file_hunk->[$file_index];
if ($fline =~ m/^\+/ || $hunk_index >= scalar(@$fhunk)) {
$fline =~ s/^.//;
push @tmp_hunk, $fline;
$file_index++;
next;
}
$line = $fhunk->[$hunk_index];
if ($line =~ m/^\+/) {
$line =~ s/^.//;
push @tmp_hunk, $line;
} elsif ($file_index < scalar(@$file_hunk)) {
if ($fline =~ m/^ /) {
$fline =~ s/^.//;
push @tmp_hunk, $fline;
}
$file_index++;
}
$hunk_index++;
}
@merged_file = @file[ 0 .. $start -1];
push @merged_file, @tmp_hunk;
push @merged_file, @file [ $end+1 .. scalar(@file) ];
@file = @merged_file;
}
# try to find the next line in the file or the hunk that
# match. The entire hunk is searched for a line matching the
# file, 20 lines forward are searched in the file.
#
# $aligned_hunk and $file_hunk are pointers to arrays, a line
# containing "|\n" is inserted to indicate missing lines in the
# file or hunk:
#
# FILE HUNK:
# A A
# | B
# C C
# D |
# There is a leading control char on each line in each of the aligned
# arrays
#
sub align_match($$$$$$) {
my ($hunk, $aligned_hunk, $file_hunk, $hindex, $findex, $hunk_changed) = @_;
my $hunk_len = scalar(@$hunk);
my $i;
my $hline;
my $fline;
my $s;
my $limit = 20;
if ($$findex + $limit > scalar(@file)) {
$limit = scalar(@file) - $$findex;
}
# look forward in the hunk for this line from the file
$fline = $file[$$findex];
for ($i = $$hindex; $i < $hunk_len; $i++) {
if ($i != $$hindex && $hunk->[$i] =~ m/^\S/) {
$hunk_changed++;
}
if (fuzzy_eq($hunk->[$i], $fline)) {
for ($s = 0 ; $s < $i - $$hindex ; $s++) {
push @$file_hunk, "|\n";
push @$aligned_hunk, $hunk->[$$hindex + $s];
}
push @$file_hunk, " $fline";
push @$aligned_hunk, $hunk->[$i];
$$hindex = $i;
return 1;
}
}
# look forward in the file for this line from the hunk
$hline = $hunk->[$$hindex];
for ($i = $$findex; $i < $$findex + $limit; $i++) {
if (fuzzy_eq($hline, $file[$i])) {
for ($s = 0 ; $s < $i - $$findex ; $s++) {
push @$aligned_hunk, "|\n";
push @$file_hunk, " $file[$$findex + $s]";
}
push @$file_hunk, " $file[$i]";
push @$aligned_hunk, $hline;
$$findex = $i;
return 1;
}
}
return 0;
}
# once a matching line is found between the hunk and the file,
# test match walks through both trying to find out how many total
# matching lines there are.
sub test_match($$$$) {
my ($hunk, $hunk_line, $file_line, $direction) = @_;
my $line;
my $i;
my $file_len = scalar(@file);
my $hunk_len = scalar(@$hunk);
my @file_hunk = ();
my @aligned_hunk = ();
my $match_len = 0;
my $last_match_line = -1;
my $score = 0;
my $consec = 0;
my $ws_match = 0;
my $hunk_match = 0;
my $hunk_changed = 0;
while($hunk_line < $hunk_len) {
$line = $hunk->[$hunk_line];
if ($line =~ m/^\S/) {
$hunk_changed++;
}
if (fuzzy_eq($line,$file[$file_line])) {
# each line in the file hunk has one char for
# special chars
#
push @file_hunk, " $file[$file_line]";
push @aligned_hunk, $line;
$match_len++;
$last_match_line = $file_line;
# bump the score if we're matching a non-context line
# or if this is a consecutive match
if ($line =~ m/^\S/) {
$hunk_match++;
$score++;
}
if ($consec) {
$score++;
}
$consec = 1;
# count matches that are whitespace alone. These are
# very unreliable.
if ($file[$file_line] =~ m/^\s*$/) {
$ws_match++;
}
} else {
# walk the hunk and the file trying to find the next match
$consec = 0;
if (align_match($hunk, \@aligned_hunk, \@file_hunk,
\$hunk_line, \$file_line, \$hunk_changed)) {
$match_len++;
if ($hunk->[$hunk_line] =~ m/^\S/) {
$score++;
$hunk_match++;
}
} else {
if ($file_line < scalar(@file)) {
push @file_hunk, " $file[$file_line]";
} else {
push @file_hunk, "|\n";
}
push @aligned_hunk, $line;
}
$last_match_line = $file_line;
}
$file_line++;
$hunk_line++;
}
if ($match_len == $ws_match) {
$match_len = 0;
$score = 0;
$last_match_line = -1;
}
return ($match_len, $hunk_match, $score, $last_match_line,
\@aligned_hunk, \@file_hunk)
}
sub count_hunk_changed($$) {
my ($hunk, $method) = @_;
my $fhunk;
my $changed = 0;
if ($method eq "forward") {
$fhunk = \@{$hunk->{'fwhunk'}};
} else {
$fhunk = \@{$hunk->{'revhunk'}};
}
foreach my $l (@$fhunk) {
if ($l =~ m/^\S/) {
$changed++;
}
}
return $changed;
}
# start of the fuzzy matching engine, try to find matching lines
# between the hunk and the file.
sub _find_hunk($$$) {
my ($struct, $hunk, $direction) = @_;
my $hunk_len = scalar(@$hunk);
my $file_len = scalar(@file);
my $i;
my $k;
my $hunk_line;
# for each line in the hunk, try to fuzz it into the file
for ($i = 0 ; $i < $file_len; $i++) {
for ($k = 0; $k < $hunk_len; $k++) {
$hunk_line = $hunk->[$k];
# if the hunk lines remaining are less then the best match
# so far, we're done
#
if (scalar(@$hunk) - $k < $struct->{'match_count'}) {
last;
}
# don't try too far into the hunk, after a while there's
# no chance we'll find any useful match
if ($k > 10) {
last;
}
if (fuzzy_eq($hunk_line, $file[$i])) {
my ($match_len, $hunk_match, $score, $file_end,
$aligned_hunk, $file_hunk) = test_match($hunk, $k, $i,
$direction);
if ($match_len > $struct->{'match_count'} ||
($match_len == $struct->{'match_count'} &&
$score > $struct->{'score'})) {
# don't pick the forward hunk over the reverse hunk
# if the reverse hunk made any matches to non-context lines
# and we don't have a perfect forward match
if ($direction eq "forward" && $struct->{'method'} eq
"reverse") {
if ($hunk_match !=
count_hunk_changed($struct,"forward")) {
my $rch = count_hunk_changed($struct, "reverse");
if ($rch > 0 && $struct->{'hunk_match'} > 0) {
next;
} elsif ($rch == 0 && $struct->{'match_count'} > 2){
next;
} elsif ($rch == $struct->{'hunk_match'}) {
next;
}
}
}
$struct->{'match_count'} = $match_len;
$struct->{'score'} = $score;
$struct->{'start'} = $i;
$struct->{'end'} = $file_end;
$struct->{'method'} = $direction;
$struct->{'aligned_hunk'} = $aligned_hunk;
$struct->{'file_hunk'} = $file_hunk;
$struct->{'hunk_match'} = $hunk_match;
}
# when deciding we've perfectly placed the change
# allow for fuzz of two on either side, unless
# there are no non-context lines in this part of the patch.
# in that case, be fuzz free.
# this code needs a little more work, disabled for now
#my $fuzz = 4;
#if ($struct->{'hunk_match'} == 0) {
# $fuzz = 0;
#}
#if ($struct->{'match_count'} >= (scalar(@$hunk) - $fuzz) &&
# $struct->{'hunk_match'} >=
# count_hunk_changed($struct,$direction)) {
# #return;
#}
last;
}
}
}
}
# figures out where the hunk should go into the file, and
# inserts it into the merge stream. If no suitable location is found
# the hunk is merged at the top.
#
sub find_hunk($) {
my ($hunk) = @_;
my $fhunk = \@{$hunk->{'fwhunk'}};
my $hc;
my $ret = 0;
if (!($hunk->{'options'} =~ m/forward/)) {
_find_hunk($hunk, \@{$hunk->{'revhunk'}}, "reverse");
}
if (!$no_forward_search && !($hunk->{'options'} =~ m/reverse/)) {
_find_hunk($hunk, $fhunk, "forward");
}
$hc = count_hunk_changed($hunk, "reverse");
if ($hunk->{'method'} eq "reverse" &&
$hunk->{'hunk_match'} == count_hunk_changed($hunk, "reverse")) {
# if hunk_mach is zero, then our patch is only adding new lines.
# make sure we've found some reasonable context in the patch
# before calling it a fully matched hunk
if ($hunk->{'hunk_match'} > 0 || $hunk->{'match_count'} > 2) {
$fully_matched_hunks++;
$ret = 1;
}
}
if ($hunk->{'match_count'} >= 2) {
place_hunk($hunk, $hunk->{'start'}, $hunk->{'end'});
} else {
$hunk->{'method'} = "forward";
place_hunk($hunk, 0, 0);
}
return $ret;
}
sub print_interactive_usage {
print "[c]ontext: toggle the context command line parameter off/on\n";
print "[d]one: exit interactive mode\n";
print "[h]elp: this screen\n";
print "[m]erge: run the merge program again\n";
print "[p]rocess: process the hunks again. This will write over the output file\n";
print "[r]eject: open in the reject in \$REJEDITOR or \$EDITOR\n";
print "\t\$REJEDITOR is used first if it exists\n";
print "[t]empreject: copy the reject to a temp file and open for editing\n";
print "\tany later process commands will use the temp file\n";
print "restore: restore backup copy of source file in auto mode\n";
print "\n";
}
sub run_interactive($$$) {
my ($pid, $file, $file2) = @_;
my $editor_pid = 0;
my $tfh;
print ">> rej $VERSION interactive mode (type help for help)\n";
print ">> ";
while(<STDIN>) {
chomp;
if (m/^(h|help)$/) {
print_interactive_usage();
} elsif (m/^restore$/) {
if ($auto) {
`cp $source_file.mergebackup $source_file`;
if ($?) {
print "cp $source_file.mergebackup $source_file \n";
print "exited with " . $? >> 8 . "\n";
}
}
} elsif (m/^(r|reject)$/) {
open_reject();
} elsif (m/^(t|tempreject)$/) {
$rejfh = new IO::File;
$rejfh->open("<$orig_reject_file") ||
die "Unable to open $orig_reject_file";
$tfh = new IO::File;
$tfh->open(">$orig_reject_file.tmp") ||
die "Unable to open $orig_reject_file.tmp";
while(<$rejfh>) {
print $tfh $_;
}
close($rejfh);
close($tfh);
$reject_file = "$orig_reject_file.tmp";
print "Switching reject to $orig_reject_file.tmp";
open_reject();
} elsif (m/^(c|context)$/) {
$context = ($context + 1) % 2;
print "context mode toggled to $context\n";
} elsif (m/^(p|process)$/) {
print "processing reject again\n";
process_reject();
} elsif (m/^(m|mergewindow)$/) {
waitpid(-1, WNOHANG);
$pid = fork();
if (!$pid) {
_run_merge($file, $file2);
exit 0;
}
} elsif (m/^(d|done|quit)$/) {
last;
}
print ">> ";
}
print "waiting for merge windows\n";
wait;
if (!$auto) {
unlink($file2);
}
}
sub open_reject {
if (defined($editor)) {
print "opening reject $reject_file in $editor\n";
system("$editor $reject_file");
} else {
print "please define \$EDITOR or \$REJEDITOR in your env\n";
}
}
sub _run_merge($$) {
my ($file, $file2) = @_;
my $ret;
my $pid;
if ($merge_prog eq "kdiff3") {
$ret = system("kdiff3 -o $file $file $file2");
} elsif ($merge_prog eq "tkdiff") {
$ret = system("tkdiff -o $file $file $file2");
} elsif ($merge_prog =~ m/vimdiff/) {
$ret = system("$merge_prog -f $file $file2");
} else {
$ret = system("$merge_prog $file $file2");
}
if ($ret) {
$ret = $ret >> 8;
print STDERR "warning: $merge_prog exited with $ret\n";
}
}
# run the merge program, with a little customization for each kind
#
sub run_merge($$) {
my ($file, $file2) = @_;
my $ret;
my $pid;
if ($interactive) {
$pid = fork();
if ($pid) {
run_interactive($pid, $file, $file2);
return;
}
}
_run_merge($file, $file2);
if (!$auto && !$interactive) {
unlink $file2;
}
if ($interactive) {
exit 0;
}
}
# look forward in the hunk for three more consecutive context lines
# this is used to split a large hunk into smaller ones
sub three_more_context($$$$) {
my ($rev, $fw, $rindex, $findex) = @_;
my $revctx = 0;
my $fwctx = 0;
while($rindex < scalar(@$rev) && $findex < scalar(@$fw)) {
if ($rev->[$rindex++] =~ m/^ /) {
$revctx++;
} else {
$revctx = 0;
}
if ($fw->[$findex++] =~ m/^ /) {
$fwctx++;
} else {
$fwctx = 0;
}
last if($revctx > 3 && $fwctx > 3);
}
return ($revctx > 3 && $fwctx > 3);
}
# walk a hunk and divide it into smaller pieces. The smaller pieces should
# be easier to place in the file.
#
sub split_and_push_hunk($$) {
my ($hunks, $hunk) = @_;
my $rev;
my $fw;
my $line;
my $i;
my @tmp;
my $tmp_hash;
my $fw_index;
my $context;
my $nonctx;
again:
@tmp = ();
$tmp_hash = {};
$fw_index = 0;
$context = 0;
$i = 0;
$rev = \@{$hunk->{'revhunk'}};
$fw = \@{$hunk->{'fwhunk'}};
$nonctx = 0;
while($i < scalar(@$rev) && $fw_index < scalar(@$fw)) {
$line = $rev->[$i];
if ($line =~ m/^ / && $fw->[$fw_index] =~ m/^ /) {
$context++;
} else {
$nonctx = 1;
# walk both arrays forward until we get to the next next bit
# of context in both
while($fw_index < scalar(@$fw) && !($fw->[$fw_index] =~ m/^ /)) {
$fw_index++;
}
while($i < scalar(@$rev) && !($rev->[$i] =~ m/^ /)) {
$i++;
}
$context = 1;
}
# split the hunk if we've seen a non-context line,
# we've seen three context lines already, and the hunk
# still has three context lines in a row later on.
if ($nonctx && $context >= 3 &&
three_more_context($rev, $fw, $i, $fw_index)) {
my $t;
# for both the rev and forward arrays, copy the
# first part of the hunk to a tmp array and assign it
# back into the old hunk.
#
# Then make a new hunk comprised
# of the remaining parts of both arrays.
# Make sure the context we've found is put into both
# old and new hunks.
for ($t = $i - $context + 1 ; $t < scalar(@$rev); $t++) {
push @tmp, $rev->[$t];
}
$tmp_hash->{'revhunk'} = [@tmp];
@tmp = ();
for ($t = 0; $t <= $i; $t++) {
push @tmp, $rev->[$t];
}
$hunk->{'revhunk'} = [@tmp];
$tmp_hash->{'desc'} = $tmp[0];
@tmp = ();
for ($t = $fw_index - $context + 1 ; $t < scalar(@$fw); $t++) {
push @tmp, $fw->[$t];
}
$tmp_hash->{'fwhunk'} = [@tmp];
@tmp = ();
for ($t = 0; $t <= $fw_index; $t++) {
push @tmp, $fw->[$t];
}
$hunk->{'fwhunk'} = [@tmp];
push @$hunks, $hunk;
$hunk = {%$tmp_hash};
goto again;
}
$fw_index++;
$i++;
}
push @$hunks, $hunk;
}
sub print_usage {
print STDERR "usage: rej [-acdeiFMqrR] [-p num] [-o file] [-m prog] file file.rej\n";
print STDERR "\t-a replace file with merged result. Use with care!\n";
print STDERR "\t\tbackup of original file created as file.mergebackup\n";
print STDERR "\t-c maximize context from the reject. This makes it\n";
print STDERR "\t\teasier to figure out stubborn rejets\n";
print STDERR "\t-dry-run check for conflicts, but do nothing\n";
print STDERR "\t-i interactive mode\n";
print STDERR "\t-F don't check for already applied changes\n";
print STDERR "\t-M don't run the external merge program at all\n";
print STDERR "\t-p num: strip num levels off the paths found in the diff\n";
print STDERR "\t-q in dry-run mode, exit once a conflict is found\n";
print STDERR "\t\totherwise, only run merge prog when there are conflicts\n";
print STDERR "\t-r open the reject file in \$EDITOR\n";
print STDERR "\t-R reverse the diff or reject\n";
print STDERR "\t-o file specify output file for merge\n";
print STDERR "\t\tThe merge program will not be run in this case\n";
print STDERR "\t-m prog specify merge progam. You can use:\n";
print STDERR "\t\t[g]vimdiff (default), kdiff3, tkdiff and meld\n";
print STDERR "\t\tothers called as: mergeprog foo.c foo.c.tmp\n";
print STDERR "\t\tThe REJMERGE environment var specifies the merge program as well\n";
print STDERR "\t-r open the reject with \$REJEDITOR or \$EDITOR\n";
print STDERR "\n";
exit 1;
}
sub strip_path($) {
my ($p) = @_;
#if ($p =~ m/(.*\/){$strip_level}?(.*)/) {
if ($p =~ m/([^\/]+\/){$strip_level}?(.*)/) {
$p = $2;
return $p;
}
return undef;
}
sub reverse_line($) {
my ($line) = @_;
my $orig_line = $line;
if (!$reverse_patch) {
return $line;
}
if ($line =~ s/^-([^-].*)/\+$1/) {
return $line;
} elsif ($line =~ s/^\+([^\+].*)/-$1/) {
return $line;
}
return $line;
}
sub process_reject {
if (!defined($rejfh) || !$diff_mode) {
$rejfh = new IO::File;
$rejfh->open("<$reject_file") || die "Unable to open $reject_file";
}
# in diff mode, find the first file indicator
if ($diff_mode) {
if ($rejfh->eof()) {
return 1;
}
while(defined($last_diff_line) || !$rejfh->eof()) {
if (defined($last_diff_line)) {
$_ = $last_diff_line;
undef $last_diff_line;
} else {
$_ = <$rejfh>;
}
if (m/^--- (\S*)/) {
my $fname = $1;
$fname = strip_path($fname);
if ( -f $fname) {
$source_file = $fname;
last;
} else {
my $t = <$rejfh>;
if ($t =~ m/^\+\+\+ (\S*)/) {
$fname = strip_path($1);
if (-f $fname) {
$source_file = $fname;
last;
}
}
}
}
}
}
$filefh = new IO::File;
$filefh->open("<$source_file") || die "Unable to open $source_file";
# struct hunk {
# @fwhunk;
# @revhunk;
# $hunk_offset; offset in hunk where matching started
# $start;
# $end;
# $score;
# $match_count;
# $desc; @@ line
# $options; # string with the special options for this hunk
# $aligned_hunk; points to array of @revhunk aligned with file
# $file_hunk; points to array of file lines aligned with aligned_hunk
# $method; "forward" or "reverse" defines how the hunk was matched
# }
#build the arrays of hunks
my $hunk;
my @fw;
my @rev;
my $more;
my $last = 0;
my $exclude = 0;
my $hunk_opt = "";
@hunks = ();
$fully_matched_hunks = 0;
$total_hunks = 0;
$hunk_num = 0;
while(<$rejfh>) {
if ($diff_mode && m/^--- /) {
$last_diff_line = $_;
last;
}
$_ = reverse_line($_);
if (m/^(@@|\*\*\* )/) {
again:
last if ($last);
# special options string
$hunk_opt = "";
if (m/###(.*)/) {
$hunk_opt = $1;
if ($hunk_opt =~ m/only/) {
@hunks = ();
$hunk_num = 0;
$last = 1;
}
if ($hunk_opt =~ m/exclude/) {
while(<$rejfh>) {
if ($diff_mode && m/^--- /) {
$last_diff_line = $_;
goto read_file;
}
if (m/^(@@|\*\*\* )/) {
goto again;
}
}
}
if ($hunk_opt =~ m/last/) {
$last = 1;
}
}
if ($hunk_num > 0) {
$hunk->{'fwhunk'} = [@fw];
$hunk->{'revhunk'} = [@rev];
split_and_push_hunk(\@hunks, $hunk);
}
@fw = ();
@rev = ();
$hunk = {};
$hunk_num++;
$hunk->{'desc'} = $_;
$hunk->{'options'} = $hunk_opt;
# not a unified diff?
# process the whole thing right here
if (!m/^@@/) {
if ($diff_mode) {
die "Unable to handle multi file context diffs";
}
$more = 0;
while(<$rejfh>) {
$_ = reverse_line($_);
last if (m/^--- \d+,\d+ ---/);
s/(^.)./$1/;
if ($reverse_patch) {
push @fw, $_;
} else {
push @rev, $_;
}
}
while(<$rejfh>) {
$_ = reverse_line($_);
if (m/^\*\*\* /) {
$more = 1;
last;
}
if (!(m/^\*/)) {
s/(^.)./$1/;
if ($reverse_patch) {
push @rev, $_;
} else {
push @fw, $_;
}
}
}
if ($more) {
goto again;
}
}
} elsif (m/^-[^-]/) {
push @rev, $_;
} elsif (m/^\+[^\+]/) {
push @fw, $_;
} elsif (m/^ /) {
push @fw, $_;
push @rev, $_;
}
}
read_file:
if (!$diff_mode) {
$rejfh->close();
}
# push any leftover hunks from the loop above
if (defined($hunk->{'desc'})) {
$hunk->{'fwhunk'} = [@fw];
$hunk->{'revhunk'} = [@rev];
split_and_push_hunk(\@hunks, $hunk);
}
@file = ();
# build the file array
while(<$filefh>) {
push @file, $_;
}
$filefh->close();
$total_hunks += scalar(@hunks);
# try to place each hunk into the file
my $ret;
foreach my $href (@hunks) {
$ret = find_hunk($href);
if ($report_only && $quick && $ret == 0) {
last;
}
}
my $conflicts = $total_hunks - $fully_matched_hunks;
$global_matched_hunks += $fully_matched_hunks;
$global_reject_hunks += $total_hunks;
$global_conflicts = $conflicts;
if ($conflicts > 0) {
$exit_value = 1;
}
if ($report_only && $quick && $conflicts > 0) {
$conflicts = "some";
}
print STDERR "\t$source_file: $fully_matched_hunks matched, $conflicts conflicts remain\n";
if ($report_only) {
if ($quick && $exit_value) {
return 1;
}
return 0;
}
if (!defined($mergefh)) {
# from here down either copies the merge result to $output_file or
# runs the merge program
if (defined($auto)) {
my $ret;
$ret = rename $source_file, "$source_file.mergebackup";
if (!$ret) {
die "Unable to rename $source_file to $source_file.mergebackup";
}
$output_file = $source_file;
}
if (defined($output_file)) {
$mergefh = new IO::File;
$mergefh->open(">$output_file") ||
die "Unable to open $output_file";
} else {
$mergefh = new File::Temp(TEMPLATE => "$source_file.XXXXX",
UNLINK => 0) ||
die "Unable to create temp file";
}
} else {
# mergefh is only defined when we're reloading.
# Just truncate and seek to 0
if ($output_file) {
$mergefh->open(">$output_file")||die "Unable to open $output_file";
} else {
$mergefh->truncate(0);
seek $mergefh, 0, SEEK_SET;
}
}
foreach my $l (@file) {
print $mergefh $l;
}
$mergefh->flush();
if ($output_file) {
$mergefh->close();
}
return 0;
}
if (defined($ENV{'REJMERGE'})) {
$merge_prog = $ENV{'REJMERGE'};
}
if (defined($ENV{'REJEDITOR'})) {
$editor = $ENV{'REJEDITOR'};
} elsif (defined($ENV{'EDITOR'})) {
$editor = $ENV{'EDITOR'};
}
GetOptions("context" => \$context,
"auto" => \$auto,
"dry-run" => \$report_only,
"out=s" => \$output_file,
"F|no-forward" => \$no_forward_search,
"interactive" => \$interactive,
"reject" => \$open_reject,
"Reverse" => \$reverse_patch,
"p|strip-level=s" => \$strip_level,
"quick" => \$quick,
"M|no-merge" => \$skip_merge,
"merge=s" => \$merge_prog) || print_usage();;
$source_file = $ARGV[0];
$reject_file = $ARGV[1];
if (scalar(@ARGV) < 2) {
if ($ARGV[0] =~ m/\.rej$/) {
$reject_file = $ARGV[0];
$source_file = $reject_file;
$source_file =~ s/\.rej$//;
} elsif (-f $source_file && -f "$source_file.rej") {
$reject_file = "$source_file.rej";
} elsif (-f $source_file) {
$reject_file = $source_file;
undef($source_file);
$diff_mode = 1;
} else {
print_usage();
}
}
$orig_reject_file = $reject_file;
if (!$diff_mode) {
foreach my $f ($source_file, $reject_file) {
if (! -f $f) {
print STDERR "Unable to find $f\n";
exit 1;
}
}
}
while(1) {
if (process_reject()) {
if ($diff_mode) {
print STDERR "$reject_file: total of $global_matched_hunks / $global_reject_hunks matched\n";
}
last;
}
if (!$report_only) {
if ($open_reject) {
open_reject();
}
if (!$skip_merge && (!$quick || $global_conflicts > 0)) {
if (!defined($output_file)) {
run_merge($source_file, $mergefh);
} elsif ($auto) {
run_merge($source_file, "$source_file.mergebackup");
}
}
}
if ($diff_mode) {
undef($mergefh);
} else {
last;
}
}
exit $exit_value;
^ permalink raw reply
* Re: git blame for a commit
From: Mircea Bardac @ 2008-06-23 9:04 UTC (permalink / raw)
To: Ian Hilt; +Cc: git
In-Reply-To: <alpine.LFD.1.10.0806222300410.23258@sys-0.hiltweb.site>
Ian Hilt wrote:
> On Sun, 22 Jun 2008 at 11:32pm +0100, Mircea Bardac wrote:
>> Is there any straightforward way of doing git blame for all the files that got
>> changed in a commit. Problems are renames, deletes and copies.
>
> Sounds like you want to track files rather than content. Git tracks the
> latter.
Hmm... I'm not really sure that my initial intention was to track files.
I've given this some more thought and I realized that what I actually
want is a "git diff" with blame info included. I want this information
in order to facilitate code reviewing.
It is true that this would be a front-end functionality, but I am not
sure at the moment what the best approach would be for something like
this. I would see this something like
$ git diff --blame[="parameters_for_blame"] commit1..commit2
but this is just a thought.
Has anyone tried blaming a "git diff"?
Many thanks.
--
Mircea
http://mircea.bardac.net
^ permalink raw reply
* [RFC] Re: Convert 'git blame' to parse_options()
From: Pierre Habouzit @ 2008-06-23 8:22 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Git Mailing List, Junio C Hamano
In-Reply-To: <alpine.LFD.1.10.0806222207220.2926@woody.linux-foundation.org>
[-- Attachment #1: Type: text/plain, Size: 3077 bytes --]
On Mon, Jun 23, 2008 at 05:15:41AM +0000, Linus Torvalds wrote:
> I think I'll want to add a PARSE_OPT_IGNORE_UNRECOGNIZED flag, and also
> make it not write the resulting array into argv[0..] but back into
> argv[1..] (so that you can use parse_options() as a _filter_ for options
> parsing and make it easier to do partial conversions), but in the meantime
> this mostly works.
You can't, mainly because of option aggregation: if the parser1 knows
about -a and -b, parser2 about -c, then, this kind of things is
problematic: -acb because you need to go to the parser '2' to know about
-c, and you can't filter the arguments and keep -c and give -b to
parser1 again, *BECAUSE* 'b' could also be -c argument.
This "PARSE_OPT_IGNORE_UNRECOGNIZED" thing has been discussed many times
in the past, but it just doesn't fly.
Though to help migrations we can probably introduce a new parse option
construct that would be a callback that is responsible for dealing with
"things" the upper level parser doesn't know about, something where the
callback could be:
enum {
FLAG_ERROR = -1,
FLAG_NOT_FOR_ME,
FLAG_IS_FOR_ME,
FLAG_AND_VALUE_ARE_FOR_ME,
}
int (*parse_opt_unknown_cb)(int shortopt, const char *longopt,
const char *value, void *priv);
Where the callback is supposed to work that way:
we pass to it either a shortopt ('\0' else) or a longopt (NULL else) but
never both, and what the parser could find as a possible value in value
(NULL if no value found). Then the parser does what it has to, and
returns one of the previous enum values. ERROR would be a fatal error
(-1 chosen so that one can return error(...)), NOT_FOR_ME if it didn't
want the flag after all, IS_FOR_ME if it took only the flag, without the
value, the last one being if it consumed both the option flag _and_ the
value.
Of course for things like --long-opt=value if the callback doesn't eat
the value, then parse_options would have to barf loudly.
The major drawback of this method is that parse_options won't generate
the nice usage help for the things that recurse in such a function. But
at least it eases migration to parseoptions, because I believe rev_parse
and diff_opt_parse to be _way_ easier to migrate to such a callback
_and_ with the old API together (I really believe that the old big
consuming loops could use such a callback directly e.g.) so that
commands using diff and revision options can be migrated to parse-opt
*slowly* rather than all at a time (which is a no-go and is such a big
amount of work that I avoided it in despair).
If people believe it's a sane approach I can do it. I absolutely don't
know why no one thought of that before, but I don't see any major
drawback (except for the "help" bits, but like I said, it's a "code
upgrade path" and not meant to be the final state).
--
·O· Pierre Habouzit
··O madcoder@debian.org
OOO http://www.madism.org
[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* [PATCH v2/RFC] git.el: Commands for committing patches
From: Nikolaj Schumacher @ 2008-06-23 7:41 UTC (permalink / raw)
To: git; +Cc: Alexandre Julliard
In-Reply-To: <m2abhcvcil.fsf@nschum.de>
>From 609f6fca6c70919036d41e1e4034b6e4de2e7ea2 Mon Sep 17 00:00:00 2001
From: Nikolaj Schumacher <git@nschum.de>
Date: Mon, 23 Jun 2008 09:34:14 +0200
Subject: [PATCH] git.el: Added command for committing patches.
This adds commands for committing patches from files, buffers and email
buffers.
In order to minimize code duplication, git-start-log-edit and
git-prepare-log-buffer have been extracted from git-commit-file.
Signed-off-by: Nikolaj Schumacher <git@nschum.de>
---
contrib/emacs/git.el | 200 +++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 175 insertions(+), 25 deletions(-)
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index 4fa853f..a6c776d 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -189,6 +189,13 @@ if there is already one that displays the same directory."
process-environment)))
(apply #'call-process "git" nil buffer nil args)))
+(defun git-call-process-env-on-region (buffer env beg end &rest args)
+ "Wrapper for `call-process-region' that sets environment strings."
+ (let ((process-environment (append (git-get-env-strings env)
+ process-environment)))
+ (buffer-string)
+ (apply #'call-process-region beg end "git" nil buffer nil args)))
+
(defun git-call-process-display-error (&rest args)
"Wrapper for call-process that displays error messages."
(let* ((dir default-directory)
@@ -315,6 +322,42 @@ and returns the process output as a string, or nil if the git failed."
"\"")
name))
+(defun git-parse-email-address (email-address)
+ "Split the EMAIL-ADDRESS string into a cons of address and name."
+ (if (string-match "\\`\"?\\([^\n<,\"]+\\)\"?[ \t]<\\([^ @]+@[^ \n\t]+\\)>\\'"
+ email-address)
+ (cons (match-string-no-properties 2 email-address)
+ (match-string-no-properties 1 email-address))
+ (when (string-match
+ "\\`\\([^ \t\n@]+@[^ \t\n,]+\\)\\([ \t](\\([^)]*\\))\\)?"
+ email-address)
+ (cons (match-string-no-properties 1 email-address)
+ (match-string-no-properties 3 email-address)))))
+
+(defun git-find-patch (&optional start)
+ "Find the patch start in the current buffer."
+ (save-excursion
+ (goto-char (or start (point-min)))
+ (when (re-search-forward "^\\(---$\\|diff -\\|Index: \\)" nil t)
+ (match-beginning 0))))
+
+(defun git-find-message ()
+ "Find the start of the commit message in an email buffer."
+ (require 'message)
+ (save-excursion
+ (message-goto-body)
+ (search-forward-regexp "[^:]+:\\([^\n]\\|\n[ \t]\\)+\n\n" nil t)
+ (point)))
+
+(defun git-fetch-header (header limit)
+ (save-excursion
+ (goto-char limit)
+ (let ((case-fold-search t))
+ (when (re-search-backward (concat "^" (regexp-quote header)
+ "[ \t]*:[ \t]*")
+ nil t)
+ (buffer-substring-no-properties (match-end 0) (point-at-eol))))))
+
(defun git-success-message (text files)
"Print a success message after having handled FILES."
(let ((n (length files)))
@@ -891,6 +934,58 @@ Return the list of files that haven't been handled."
(message "No files to commit.")))
(delete-file index-file))))))
+(defun git-apply-patch-to-index (index-file patch &optional beg end)
+ "Run git-apply on a patch."
+ (with-temp-buffer
+ (let ((env (and index-file `(("GIT_INDEX_FILE" . ,index-file))))
+ (temp-buffer (current-buffer))
+ res)
+ (if (stringp patch)
+ (if (file-exists-p patch)
+ (setq res (git-call-process-env temp-buffer env "apply"
+ "--cached"
+ (expand-file-name patch)))
+ (error "Patch file disappeared"))
+ (if (buffer-live-p patch)
+ (setq res (with-current-buffer patch
+ (git-call-process-env-on-region
+ temp-buffer env (or beg (point-min))
+ (or end (point-max)) "apply" "--cached" "-")))
+ (error "Patch buffer disappeared")))
+ (unless (= 0 res)
+ (error "Applying patch failed:\n%s" (buffer-string))))))
+
+(defun git-do-commit-patch (patch &optional beg end)
+ "Actually commit the patch using the current buffer as log message."
+ (interactive)
+ (let ((buffer (current-buffer))
+ (index-file (make-temp-file "gitidx")))
+ (with-current-buffer log-edit-parent-buffer
+ (unwind-protect
+ (let (head parent head-tree)
+ (unless (git-empty-db-p)
+ (setq head (git-rev-parse "HEAD")
+ head-tree (git-rev-parse "HEAD^{tree}")))
+ (message "Running git commit...")
+ (git-read-tree head-tree index-file)
+ ;; Update both the default index and the temporary one.
+ (git-apply-patch-to-index index-file patch beg end)
+ (git-apply-patch-to-index nil patch beg end)
+ (let* ((tree (git-write-tree index-file))
+ (commit (git-commit-tree buffer tree head)))
+ (when commit
+ (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)
+ (condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
+ (with-current-buffer buffer (erase-buffer))
+ (git-call-process-env nil nil "rerere")
+ (git-call-process-env nil nil "gc" "--auto")
+ (git-refresh-status)
+ (git-refresh-ewoc-hf git-status)
+ (message "Committed %s." commit)
+ (git-run-hook "post-commit" nil)))
+ t)
+ (delete-file index-file)
+ nil)))))
;;;; Interactive functions
;;;; ------------------------------------------------------------
@@ -1263,36 +1358,41 @@ Return the list of files that haven't been handled."
(when sign-off (git-append-sign-off committer-name committer-email)))
buffer))
+(defun git-start-log-edit (buffer action)
+ (if (boundp 'log-edit-diff-function)
+ (log-edit action nil '((log-edit-listfun . git-log-edit-files)
+ (log-edit-diff-function . git-log-edit-diff)) buffer)
+ (log-edit action nil 'git-log-edit-files buffer))
+ (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))
+ (setq buffer-file-coding-system (git-get-commits-coding-system))
+ (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))
+
+(defun git-prepare-log-buffer ()
+ (let ((buffer (get-buffer-create "*git-commit*"))
+ author-name author-email subject date)
+ (when (eq 0 (buffer-size buffer))
+ (when (file-readable-p ".dotest/info")
+ (with-temp-buffer
+ (insert-file-contents ".dotest/info")
+ (goto-char (point-min))
+ (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t)
+ (setq author-name (match-string 1))
+ (setq author-email (match-string 2)))
+ (goto-char (point-min))
+ (when (re-search-forward "^Subject: \\(.*\\)$" nil t)
+ (setq subject (match-string 1)))
+ (goto-char (point-min))
+ (when (re-search-forward "^Date: \\(.*\\)$" nil t)
+ (setq date (match-string 1)))))
+ (git-setup-log-buffer buffer author-name author-email subject date))
+ buffer))
+
(defun git-commit-file ()
"Commit the marked file(s), asking for a commit message."
(interactive)
(unless git-status (error "Not in git-status buffer."))
(when (git-run-pre-commit-hook)
- (let ((buffer (get-buffer-create "*git-commit*"))
- (coding-system (git-get-commits-coding-system))
- author-name author-email subject date)
- (when (eq 0 (buffer-size buffer))
- (when (file-readable-p ".dotest/info")
- (with-temp-buffer
- (insert-file-contents ".dotest/info")
- (goto-char (point-min))
- (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t)
- (setq author-name (match-string 1))
- (setq author-email (match-string 2)))
- (goto-char (point-min))
- (when (re-search-forward "^Subject: \\(.*\\)$" nil t)
- (setq subject (match-string 1)))
- (goto-char (point-min))
- (when (re-search-forward "^Date: \\(.*\\)$" nil t)
- (setq date (match-string 1)))))
- (git-setup-log-buffer buffer author-name author-email subject date))
- (if (boundp 'log-edit-diff-function)
- (log-edit 'git-do-commit nil '((log-edit-listfun . git-log-edit-files)
- (log-edit-diff-function . git-log-edit-diff)) buffer)
- (log-edit 'git-do-commit nil 'git-log-edit-files buffer))
- (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))
- (setq buffer-file-coding-system coding-system)
- (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))))
+ (git-start-log-edit (git-prepare-log-buffer) 'git-do-commit)))
(defun git-setup-commit-buffer (commit)
"Setup the commit buffer with the contents of COMMIT."
@@ -1340,6 +1440,56 @@ amended version of it."
(git-setup-commit-buffer commit)
(git-commit-file))))
+(defun git-commit-patch (patch)
+ "Commit the patch in file PATCH, asking for a commit message."
+ (interactive "fPatch file: ")
+ (unless git-status (error "Not in git-status buffer."))
+ (when (git-run-pre-commit-hook)
+ (git-start-log-edit (git-prepare-log-buffer)
+ `(lambda ()
+ (interactive)
+ (git-do-commit-patch ,patch)))))
+
+(defun git-commit-patch-buffer (patch)
+ "Commit the patch in buffer PATCH, asking for a commit message."
+ (interactive "bPatch buffer")
+ (when (stringp patch)
+ (setq patch (get-buffer patch)))
+ (unless git-status (error "Not in git-status buffer."))
+ (when (git-run-pre-commit-hook)
+ (git-start-log-edit (git-prepare-log-buffer)
+ `(lambda ()
+ (interactive)
+ (git-do-commit-patch ,patch)))))
+
+(defun git-commit-email-patch (email)
+ "Commit the patch in the email in buffer EMAIL."
+ (interactive "bEmail buffer")
+ (when (stringp email)
+ (setq email (get-buffer email)))
+ (let ((buffer (get-buffer-create "*git-commit-patch*"))
+ author-name author-email email-subject subject date
+ body-start patch-start)
+ (with-current-buffer email
+ (setq body-start (git-find-message)
+ patch-start (git-find-patch body-start))
+ (let ((address (git-parse-email-address
+ (git-fetch-header "From" patch-start)))
+ (email-subject (git-fetch-header "Subject" patch-start)))
+ (setq author-name (car address)
+ author-email (cdr address)
+ subject (when (string-match "\\`\\[PATCH[^]]*\\]\s *"
+ email-subject)
+ (substring email-subject (match-end 0)))
+ date (git-fetch-header "Date" patch-start)
+ msg (buffer-substring body-start patch-start))))
+ (when (git-run-pre-commit-hook)
+ (git-setup-log-buffer buffer author-name author-email subject date msg)
+ (git-start-log-edit buffer
+ `(lambda ()
+ (interactive)
+ (git-do-commit-patch ,email ,patch-start))))))
+
(defun git-find-file ()
"Visit the current file in its own buffer."
(interactive)
--
1.5.5.3
^ permalink raw reply related
* What's in git.git (stable)
From: Junio C Hamano @ 2008-06-23 7:25 UTC (permalink / raw)
To: git
In-Reply-To: <7vlk0z9k5f.fsf@gitster.siamese.dyndns.org>
There are a few more fixes destined for maint, being tested in next first.
* The 'maint' branch has these fixes since the last announcement.
Michele Ballabio (1):
parse-options.c: fix documentation syntax of optional arguments
Stephan Beyer (3):
api-builtin.txt: update and fix typo
api-parse-options.txt: Introduce documentation for parse options API
Extend parse-options test suite
* The 'master' branch has these since the last announcement
in addition to the above.
Jakub Narebski (2):
gitweb: Separate filling list of projects info
gitweb: Separate generating 'sort by' table header
Jeff King (5):
fix whitespace violations in test scripts
mask necessary whitespace policy violations in test scripts
avoid whitespace on empty line in automatic usage message
avoid trailing whitespace in zero-change diffstat lines
enable whitespace checking of test scripts
Junio C Hamano (1):
diff -c/--cc: do not include uninteresting deletion before leading
context
Karl Hasselström (2):
Clean up builtin-update-ref's option parsing
Make old sha1 optional with git update-ref -d
Linus Torvalds (3):
racy-git: an empty blob has a fixed object name
Make git_dir a path relative to work_tree in setup_work_tree()
Shrink the git binary a bit by avoiding unnecessary inline functions
Marius Storm-Olsen (3):
Add an optional <mode> argument to commit/status -u|--untracked-files
option
Add argument 'no' commit/status option -u|--untracked-files
Add configuration option for default untracked files mode
Nanako Shiraishi (2):
environment.c: remove unused function
config.c: make git_env_bool() static
Pieter de Bie (1):
builtin-fast-export: Add importing and exporting of revision marks
Rafael Garcia-Suarez (1):
gitweb: remove git_blame and rename git_blame2 to git_blame
René Scharfe (1):
Teach new attribute 'export-ignore' to git-archive
^ permalink raw reply
* Re: [PATCH/RFC] git.el: Commands for committing patches
From: Nikolaj Schumacher @ 2008-06-23 7:25 UTC (permalink / raw)
To: Edward Z. Yang; +Cc: git
In-Reply-To: <485F0388.4080907@thewritingpot.com>
"Edward Z. Yang" <edwardzyang@thewritingpot.com> wrote:
> As per Documentation/SubmittingPatches, we'd appreciate it if you
> submitted the patch inline. Thanks!
I'm sorry, I fully intended to. I was mistaken what disposition inline
meant in Gnus. It looked as intended in my tests.
I hope v2 is correct in that regard.
regards,
Nikolaj Schumacher
^ permalink raw reply
* What's cooking in git.git (topics)
From: Junio C Hamano @ 2008-06-23 7:15 UTC (permalink / raw)
To: git
In-Reply-To: <7vwskjazql.fsf@gitster.siamese.dyndns.org>
Here are the topics that have been cooking. Commits prefixed
with '-' are only in 'pu' while commits prefixed with '+' are
in 'next'.
The topics list the commits in reverse chronological order.
It already is beginning to become clear what 1.6.0 will look like. What's
already in 'next' all are well intentioned (I do not guarantee they are
already bug-free --- that is what cooking them in 'next' is for) and are
good set of feature enhancements. But bigger changes will be:
* MinGW will be in.
* /usr/bin/git-cat-file is no more. The bulk of the git commands will
move to /usr/libexec/git-core/ or somesuch.
* git-merge will be rewritten in C.
Currently tip of 'pu' is broken and does not pass tests, as j6t/mingw has
interaction with dr/ceiling and jc/merge-theirs has interaction with
mv/merge-in-c.
----------------------------------------------------------------
[New Topics]
* jc/rerere (Sun Jun 22 02:04:31 2008 -0700) 5 commits
- rerere.autoupdate
- t4200: fix rerere test
- rerere: remove dubious "tail_optimization"
- git-rerere: detect unparsable conflicts
- rerere: rerere_created_at() and has_resolution() abstraction
* sb/rebase (Sun Jun 22 01:55:50 2008 +0200) 2 commits
+ t3404: stricter tests for git-rebase--interactive
+ api-builtin.txt: update and fix typo
* sb/maint-rebase (Sun Jun 22 16:07:02 2008 +0200) 1 commit
+ git-rebase.sh: Add check if rebase is in progress
----------------------------------------------------------------
[Will merge to master soon]
* lw/gitweb (Thu Jun 19 22:03:21 2008 +0200) 1 commit
+ gitweb: standarize HTTP status codes
* lt/config-fsync (Wed Jun 18 15:18:44 2008 -0700) 4 commits
+ Add config option to enable 'fsync()' of object files
+ Split up default "i18n" and "branch" config parsing into helper
routines
+ Split up default "user" config parsing into helper routine
+ Split up default "core" config parsing into helper routine
* nd/dashless (Wed Nov 28 23:21:57 2007 +0700) 1 commit
+ Move all dashed-form commands to libexecdir
Scheduled for 1.6.0.
* sg/merge-options (Sun Apr 6 03:23:47 2008 +0200) 1 commit
+ merge: remove deprecated summary and diffstat options and config
variables
* sr/tests (Sun Jun 8 16:04:35 2008 +0200) 3 commits
+ Hook up the result aggregation in the test makefile.
+ A simple script to parse the results from the testcases
+ Modify test-lib.sh to output stats to t/test-results/*
* jh/clone-packed-refs (Sun Jun 15 16:06:16 2008 +0200) 4 commits
+ Teach "git clone" to pack refs
+ Prepare testsuite for a "git clone" that packs refs
+ Move pack_refs() and friends into libgit
+ Incorporate fetched packs in future object traversal
This is useful when cloning from a repository with insanely large number
of refs.
* lw/perlish (Thu Jun 19 22:32:49 2008 +0200) 2 commits
+ Git.pm: add test suite
+ t/test-lib.sh: add test_external and test_external_without_stderr
Beginning of regression tests for Perl part of the system.
----------------------------------------------------------------
[Actively Cooking]
* mv/merge-in-c (Sat Jun 21 19:15:35 2008 +0200) 14 commits
- Add new test case to ensure git-merge reduces octopus parents when
possible
- Add new test case to ensure git-merge filters for independent
parents
- Build in merge
- Introduce reduce_heads()
- Introduce get_merge_bases_many()
- Add new test to ensure git-merge handles more than 25 refs.
- Introduce get_octopus_merge_bases() in commit.c
- git-fmt-merge-msg: make it usable from other builtins
- Move read_cache_unmerged() to read-cache.c
- parseopt: add a new PARSE_OPT_ARGV0_IS_AN_OPTION option
- Add new test to ensure git-merge handles pull.twohead and
pull.octopus
- Move parse-options's skip_prefix() to git-compat-util.h
- Move commit_list_count() to commit.c
- Move split_cmdline() to alias.c
* jc/dashless (Sat Dec 1 22:09:22 2007 -0800) 2 commits
- Prepare execv_git_cmd() for removal of builtins from the
filesystem
- git-shell: accept "git foo" form
We do not plan to remove git-foo form completely from the filesystem at
this point, but git-shell may need to be updated.
* dr/ceiling (Mon May 19 23:49:34 2008 -0700) 4 commits
+ Eliminate an unnecessary chdir("..")
+ Add support for GIT_CEILING_DIRECTORIES
+ Fold test-absolute-path into test-path-utils
+ Implement normalize_absolute_path
----------------------------------------------------------------
[Graduated to "master"]
* rs/archive-ignore (Sun Jun 8 18:42:33 2008 +0200) 1 commit
+ Teach new attribute 'export-ignore' to git-archive
* lt/racy-empty (Tue Jun 10 10:44:43 2008 -0700) 1 commit
+ racy-git: an empty blob has a fixed object name
* sn/static (Thu Jun 19 08:21:11 2008 +0900) 2 commits
+ config.c: make git_env_bool() static
+ environment.c: remove unused function
* jc/maint-combine-diff-pre-context (Wed Jun 18 23:59:41 2008 -0700) 1 commit
+ diff -c/--cc: do not include uninteresting deletion before leading
context
* lt/maint-gitdir-relative (Thu Jun 19 12:34:06 2008 -0700) 1 commit
+ Make git_dir a path relative to work_tree in setup_work_tree()
* jn/web (Tue Jun 10 19:21:44 2008 +0200) 2 commits
+ gitweb: Separate generating 'sort by' table header
+ gitweb: Separate filling list of projects info
* rg/gitweb (Fri Jun 6 09:53:32 2008 +0200) 1 commit
+ gitweb: remove git_blame and rename git_blame2 to git_blame
* kh/update-ref (Tue Jun 3 01:34:53 2008 +0200) 2 commits
+ Make old sha1 optional with git update-ref -d
+ Clean up builtin-update-ref's option parsing
* mo/status-untracked (Thu Jun 5 14:47:50 2008 +0200) 3 commits
+ Add configuration option for default untracked files mode
+ Add argument 'no' commit/status option -u|--untracked-files
+ Add an optional <mode> argument to commit/status -u|--untracked-
files option
* jk/test (Sat Jun 14 03:28:07 2008 -0400) 5 commits
+ enable whitespace checking of test scripts
+ avoid trailing whitespace in zero-change diffstat lines
+ avoid whitespace on empty line in automatic usage message
+ mask necessary whitespace policy violations in test scripts
+ fix whitespace violations in test scripts
Tightens whitespace rules for t/*.sh scripts.
* pb/fast-export (Wed Jun 11 13:17:04 2008 +0200) 1 commit
+ builtin-fast-export: Add importing and exporting of revision marks
----------------------------------------------------------------
[On Hold]
* ph/mergetool (Mon Jun 16 17:33:41 2008 -0600) 1 commit
+ Remove the use of '--' in merge program invocation
Waiting for success reports from people who use various backends.
* j6t/mingw (Sat Nov 17 20:48:14 2007 +0100) 38 commits
- compat/pread.c: Add a forward declaration to fix a warning
- Windows: Fix ntohl() related warnings about printf formatting
- Windows: TMP and TEMP environment variables specify a temporary
directory.
- Windows: Make 'git help -a' work.
- Windows: Work around an oddity when a pipe with no reader is
written to.
- Windows: Make the pager work.
- When installing, be prepared that template_dir may be relative.
- Windows: Use a relative default template_dir and ETC_GITCONFIG
- Windows: Compute the fallback for exec_path from the program
invocation.
- Turn builtin_exec_path into a function.
- Windows: Use a customized struct stat that also has the st_blocks
member.
- Windows: Add a custom implementation for utime().
- Windows: Add a new lstat and fstat implementation based on Win32
API.
- Windows: Implement a custom spawnve().
- Windows: Implement wrappers for gethostbyname(), socket(), and
connect().
- Windows: Work around incompatible sort and find.
- Windows: Implement asynchronous functions as threads.
- Windows: Disambiguate DOS style paths from SSH URLs.
- Windows: A rudimentary poll() emulation.
- Windows: Change the name of hook scripts to make them not
executable.
- Windows: Implement start_command().
- Windows: A pipe() replacement whose ends are not inherited to
children.
- Windows: Wrap execve so that shell scripts can be invoked.
- Windows: Implement setitimer() and sigaction().
- Windows: Fix PRIuMAX definition.
- Windows: Implement gettimeofday().
- Windows: Handle absolute paths in
safe_create_leading_directories().
- Windows: Treat Windows style path names.
- setup.c: Prepare for Windows directory separators.
- Windows: Work around misbehaved rename().
- Windows: always chmod(, 0666) before unlink().
- Windows: A minimal implemention of getpwuid().
- Windows: Implement a wrapper of the open() function.
- Windows: Strip ".exe" from the program name.
- Windows: Use the Windows style PATH separator ';'.
- Add target architecture MinGW.
- Compile some programs only conditionally.
- Add compat/regex.[ch] and compat/fnmatch.[ch].
No explanation is necessary ;-). The series is probably 'next' worthy
as-is.
* jk/renamelimit (Sat May 3 13:58:42 2008 -0700) 1 commit
- diff: enable "too large a rename" warning when -M/-C is explicitly
asked for
This would be the right thing to do for command line use, but gitk will be
hit due to tcl/tk's limitation, so I am holding this back for now.
----------------------------------------------------------------
[Stalled/Needs more work]
* jc/reflog-expire (Sun Jun 15 23:48:46 2008 -0700) 1 commit
- Per-ref reflog expiry configuration
Perhaps a good foundation for optionally unexpirable stash. As 1.6.0 will
be a good time to make backward incompatible changes, we might make expiry
period of stash 'never' in new repositories. Needs a concensus.
* jc/merge-theirs (Fri Jun 20 00:17:59 2008 -0700) 2 commits
- git-merge-recursive-{ours,theirs}
- git-merge-file --ours, --theirs
Punting a merge by discarding your own work in conflicting parts but still
salvaging the parts that are cleanly automerged. It is likely that this
will result in nonsense mishmash, but somehow often people want this, so
here they are. The interface to the backends may need to change, though.
* jc/blame (Wed Jun 4 22:58:40 2008 -0700) 7 commits
- blame: show "previous" information in --porcelain/--incremental
format
- git-blame: refactor code to emit "porcelain format" output
+ git-blame --reverse
+ builtin-blame.c: allow more than 16 parents
+ builtin-blame.c: move prepare_final() into a separate function.
+ rev-list --children
+ revision traversal: --children option
The blame that finds where each line in the original lines moved to. This
may help a GSoC project that wants to gather statistical overview of the
history. The final presentation may need tweaking (see the log message of
the commit ""git-blame --reverse" on the series).
The tip two commits are for peeling to see what's behind the blamed
commit, which we should be able to separate out into an independent topic
from the rest.
----------------------------------------------------------------
[Dropped for now]
* sj/merge (Sat May 3 16:55:47 2008 -0700) 6 commits
. Introduce fast forward option only
. Head reduction before selecting merge strategy
. Restructure git-merge.sh
. Introduce -ff=<fast forward option>
. New merge tests
. Documentation for joining more than two histories
This will interfere with Miklos's rewrite of merge to C.
* js/rebase-i-sequencer (Sun Apr 27 02:55:50 2008 -0400) 17 commits
. Use perl instead of tac
. Fix t3404 assumption that `wc -l` does not use whitespace.
. rebase -i: Use : in expr command instead of match.
. rebase -i: update the implementation of 'mark' command
. Add option --preserve-tags
. Teach rebase interactive the tag command
. Add option --first-parent
. Do rebase with preserve merges with advanced TODO list
. Select all lines with fake-editor
. Unify the length of $SHORT* and the commits in the TODO list
. Teach rebase interactive the merge command
. Move redo merge code in a function
. Teach rebase interactive the reset command
. Teach rebase interactive the mark command
. Move cleanup code into it's own function
. Don't append default merge message to -m message
. fake-editor: output TODO list if unchanged
* jc/cherry-pick (Wed Feb 20 23:17:06 2008 -0800) 3 commits
. WIP: rethink replay merge
. Start using replay-tree merge in cherry-pick
. revert/cherry-pick: start refactoring call to merge_recursive
This is meant to improve cherry-pick's behaviour when renames are
involved, by not using merge-recursive (whose d/f conflict resolution is
quite broken), but unfortunately has stalled for some time now.
* jc/stripspace (Sun Mar 9 00:30:35 2008 -0800) 6 commits
. git-am --forge: add Signed-off-by: line for the author
. git-am: clean-up Signed-off-by: lines
. stripspace: add --log-clean option to clean up signed-off-by:
lines
. stripspace: use parse_options()
. Add "git am -s" test
. git-am: refactor code to add signed-off-by line for the committer
Just my toy at this moment.
* jc/send-pack-tell-me-more (Thu Mar 20 00:44:11 2008 -0700) 1 commit
. "git push": tellme-more protocol extension
^ permalink raw reply
* Re: linux-x86-tip: pilot error?
From: Ingo Molnar @ 2008-06-23 7:14 UTC (permalink / raw)
To: Paul E. McKenney; +Cc: Mikael Magnusson, git, Thomas Gleixner
In-Reply-To: <20080622132105.GD22569@linux.vnet.ibm.com>
* Paul E. McKenney <paulmck@linux.vnet.ibm.com> wrote:
> Trying "git-checkout -b tip-core-rcu
> tip-core-rcu-2008-06-16_09.23_Mon" acts like it is doing something
> useful, but doesn't find the recent updates, which I believe happened
> -before- June 16 2008.
finding the rcu topic branch in -tip can be done the following way:
$ git-branch -a | grep rcu
tip/core/rcu
and doing a "git-log tip/core/rcu..linus/master" will show you the
commits that are in the tip/core/rcu topic branch.
if you check out that branch for your own use, you should also do:
$ git-merge linus/master
To bring it up to latest upstream. That merge, even if tip/core/rcu
looks "old" will always be conflict-free, due to scripting we do:
tip-core-rcu-2008-06-16_09.23_Mon is not a snapshot of the rcu topic -
it is "technical" tag of the upstream Linus -git tree against which the
rcu topic is based.
We have to track the 'base' of every topic separately because otherwise
we'd pollute the topic branches with the frequent merges to Linus's
tree. (occasionally we merge to Linus's tree several times a day, that
would lead to tons of merge commits that pollute the tree)
So instead we do "on-demand virtual merges": we have scripting which do
the following: in each iteration step they merge to latest Linus, check
whether there's any files touched by the merge that are changed by the
topic branch too - if yes then the merge is made permanent and the "this
is this topic's latest upstream" tag is updated. If the merge was
conflict-free, we roll back the merge.
Is there a Git way of finding the common ancestor of a topic branch,
when compared to upstream?
Ingo
^ permalink raw reply
* Re: [PATCH v6] gitweb: add test suite with Test::WWW::Mechanize::CGI
From: Lea Wiemann @ 2008-06-23 7:00 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Jakub Narebski
In-Reply-To: <7vd4m8khmb.fsf@gitster.siamese.dyndns.org>
Junio C Hamano wrote:
> My Perl must be getting rusty. I had to look up this tricky syntax in
> perlfunc.pod,
I looked in up in #perl. ;-)
>> +# Copyright (c) 2008 Jakub Narebski
>> +# Copyright (c) 2008 Lea Wiemann
>
> If you mean by this that originally Jakub started and then Lea continued
> extending the work, probably the order of Sign-off should match that order
> to express the patch flow trail better.
Sure; I'll change it if I post another version, or feel free to change
it when you apply the patch.
-- Lea
^ permalink raw reply
* Re: Importing non-version controlled bits and pieces to Git
From: Peter Karlsson @ 2008-06-23 6:35 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Miklos Vajna, Git Mailing List
In-Reply-To: <alpine.DEB.1.00.0806201359320.6439@racer>
Johannes Schindelin:
> Why not import everything with a CR/LF, and then use filter-branch
> with a really simple tree-filter? It is slow, alright, but it is
> safe, too.
That might work. My problem is the non-linearity of the data I want to
import. But I might be able to massage the import-tars output before I
feed it to git-fast-import to describe the history I need it to.
--
\\// Peter - http://www.softwolves.pp.se/
^ permalink raw reply
* Re: Convert 'git blame' to parse_options()
From: Junio C Hamano @ 2008-06-23 6:35 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Git Mailing List, Pierre Habouzit
In-Reply-To: <alpine.LFD.1.10.0806222207220.2926@woody.linux-foundation.org>
Linus Torvalds <torvalds@linux-foundation.org> writes:
> That builtin-blame option parsing is really ugly,...
Yeah, but wasn't it because it needed to be compatible with both annotate
syntax and rev-list style "range" notation at the same time?
> +static int blame_bottomtop_callback(const struct option *option, const char *arg, int unset)
> +{
> + const char **bottomtop = option->value;
> + if (!arg)
> + return -1;
> + if (*bottomtop)
> + die("More than one '-L n,m' option given");
> + *bottomtop = arg;
> + return 0;
> +}
Hmmmm. I actually wanted to eventually allow more than one -L so that we
can blame two functions inside a file, for example. Would this make it
even harder, I have to wonder...
^ permalink raw reply
* Re: [JGIT RFC PATCH] Add a stdio prompt for SSH connection information.
From: Shawn O. Pearce @ 2008-06-23 6:34 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: Marek Zawirski, git
In-Reply-To: <200806230823.20534.robin.rosenberg.lists@dewire.com>
Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:
>
> > > I'm also a little unsure about how to invoke the promptKeyboardInteractive method.
> >
> > I think you implemented this method correctly. Its a confusing API,
> > but it does seem to make sense.
>
> No idea on how to make JSch inoke it?
Might require a server that has a token cards for authentication.
Server can ask for a user password and also give you a code to
enter into the card, which gives you the response to enter back in.
I have never worked with such a configuration so I have never seen
that sort of multi-input prompt come up in any SSH client before.
--
Shawn.
^ 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