From 3c5e8468a93cdb427b4c4a129339e80e1813e5c0 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 26 Nov 2005 09:38:20 -0800 Subject: ls-tree: major rewrite to do pathspec git-ls-tree should be rewritten to use a pathspec the same way everybody else does. Right now it's the odd man out: if you do git-ls-tree HEAD divers/char drivers/ it will show the same files _twice_, which is not how pathspecs in general work. How about this patch? It breaks some of the git-ls-tree tests, but it makes git-ls-tree work a lot more like other git pathspec commands, and it removes more than 150 lines by re-using the recursive tree traversal (but the "-d" flag is gone for good, so I'm not pushing this too hard). Linus diff --git a/ls-tree.c b/ls-tree.c index d7c7e75..598b729 100644 --- a/ls-tree.c +++ b/ls-tree.c @@ -13,217 +13,32 @@ static int line_termination = '\n'; #define LS_TREE_ONLY 2 static int ls_options = 0; -static struct tree_entry_list root_entry; - -static void prepare_root(unsigned char *sha1) -{ - unsigned char rsha[20]; - unsigned long size; - void *buf; - struct tree *root_tree; - - buf = read_object_with_reference(sha1, "tree", &size, rsha); - free(buf); - if (!buf) - die("Could not read %s", sha1_to_hex(sha1)); - - root_tree = lookup_tree(rsha); - if (!root_tree) - die("Could not read %s", sha1_to_hex(sha1)); - - /* Prepare a fake entry */ - root_entry.directory = 1; - root_entry.executable = root_entry.symlink = 0; - root_entry.mode = S_IFDIR; - root_entry.name = ""; - root_entry.item.tree = root_tree; - root_entry.parent = NULL; -} - -static int prepare_children(struct tree_entry_list *elem) -{ - if (!elem->directory) - return -1; - if (!elem->item.tree->object.parsed) { - struct tree_entry_list *e; - if (parse_tree(elem->item.tree)) - return -1; - /* Set up the parent link */ - for (e = elem->item.tree->entries; e; e = e->next) - e->parent = elem; - } - return 0; -} - -static struct tree_entry_list *find_entry(const char *path, char *pathbuf) -{ - const char *next, *slash; - int len; - struct tree_entry_list *elem = &root_entry, *oldelem = NULL; - - *(pathbuf) = '\0'; - - /* Find tree element, descending from root, that - * corresponds to the named path, lazily expanding - * the tree if possible. - */ - - while (path) { - /* The fact we still have path means that the caller - * wants us to make sure that elem at this point is a - * directory, and possibly descend into it. Even what - * is left is just trailing slashes, we loop back to - * here, and this call to prepare_children() will - * catch elem not being a tree. Nice. - */ - if (prepare_children(elem)) - return NULL; - - slash = strchr(path, '/'); - if (!slash) { - len = strlen(path); - next = NULL; - } - else { - next = slash + 1; - len = slash - path; - } - if (len) { - if (oldelem) { - pathbuf += sprintf(pathbuf, "%s/", oldelem->name); - } - - /* (len == 0) if the original path was "drivers/char/" - * and we have run already two rounds, having elem - * pointing at the drivers/char directory. - */ - elem = elem->item.tree->entries; - while (elem) { - if ((strlen(elem->name) == len) && - !strncmp(elem->name, path, len)) { - /* found */ - break; - } - elem = elem->next; - } - if (!elem) - return NULL; - - oldelem = elem; - } - path = next; - } - - return elem; -} - -static const char *entry_type(struct tree_entry_list *e) -{ - return (e->directory ? "tree" : "blob"); -} - -static const char *entry_hex(struct tree_entry_list *e) -{ - return sha1_to_hex(e->directory - ? e->item.tree->object.sha1 - : e->item.blob->object.sha1); -} - -/* forward declaration for mutually recursive routines */ -static int show_entry(struct tree_entry_list *, int, char *pathbuf); +static const char ls_tree_usage[] = + "git-ls-tree [-d] [-r] [-z] [path...]"; -static int show_children(struct tree_entry_list *e, int level, char *pathbuf) +static int show_tree(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage) { - int oldlen = strlen(pathbuf); + const char *type = "blob"; + int retval = 0; - if (e != &root_entry) - sprintf(pathbuf + oldlen, "%s/", e->name); - - if (prepare_children(e)) - die("internal error: ls-tree show_children called with non tree"); - e = e->item.tree->entries; - while (e) { - show_entry(e, level, pathbuf); - e = e->next; + if (S_ISDIR(mode)) { + type = "tree"; + if (ls_options & LS_RECURSIVE) + retval = READ_TREE_RECURSIVE; } - pathbuf[oldlen] = '\0'; - - return 0; + printf("%06o %s %s\t%.*s%s%c", mode, type, sha1_to_hex(sha1), baselen, base, pathname, line_termination); + return retval; } -static int show_entry(struct tree_entry_list *e, int level, char *pathbuf) +int main(int argc, const char **argv) { - int err = 0; - - if (e != &root_entry) { - int pathlen = strlen(pathbuf); - printf("%06o %s %s ", - e->mode, entry_type(e), entry_hex(e)); - write_name_quoted(pathbuf, pathlen, e->name, - line_termination, stdout); - putchar(line_termination); - } - - if (e->directory) { - /* If this is a directory, we have the following cases: - * (1) This is the top-level request (explicit path from the - * command line, or "root" if there is no command line). - * a. Without any flag. We show direct children. We do not - * recurse into them. - * b. With -r. We do recurse into children. - * c. With -d. We do not recurse into children. - * (2) We came here because our caller is either (1-a) or - * (1-b). - * a. Without any flag. We do not show our children (which - * are grandchildren for the original request). - * b. With -r. We continue to recurse into our children. - * c. With -d. We should not have come here to begin with. - */ - if (level == 0 && !(ls_options & LS_TREE_ONLY)) - /* case (1)-a and (1)-b */ - err = err | show_children(e, level+1, pathbuf); - else if (level && ls_options & LS_RECURSIVE) - /* case (2)-b */ - err = err | show_children(e, level+1, pathbuf); - } - return err; -} - -static int list_one(const char *path) -{ - int err = 0; - char pathbuf[MAXPATHLEN + 1]; - struct tree_entry_list *e = find_entry(path, pathbuf); - if (!e) { - /* traditionally ls-tree does not complain about - * missing path. We may change this later to match - * what "/bin/ls -a" does, which is to complain. - */ - return err; - } - err = err | show_entry(e, 0, pathbuf); - return err; -} - -static int list(char **path) -{ - int i; - int err = 0; - for (i = 0; path[i]; i++) - err = err | list_one(path[i]); - return err; -} - -static const char ls_tree_usage[] = - "git-ls-tree [-d] [-r] [-z] [path...]"; - -int main(int argc, char **argv) -{ - static char *path0[] = { "", NULL }; - char **path; + const char **path, *prefix; unsigned char sha1[20]; + char *buf; + unsigned long size; + prefix = setup_git_directory(); while (1 < argc && argv[1][0] == '-') { switch (argv[1][1]) { case 'z': @@ -246,9 +61,11 @@ int main(int argc, char **argv) if (get_sha1(argv[1], sha1) < 0) usage(ls_tree_usage); - path = (argc == 2) ? path0 : (argv + 2); - prepare_root(sha1); - if (list(path) < 0) - die("list failed"); + path = get_pathspec(prefix, argv + 2); + buf = read_object_with_reference(sha1, "tree", &size, NULL); + if (!buf) + die("not a tree object"); + read_tree_recursive(buf, size, "", 0, 0, path, show_tree); + return 0; } diff --git a/tree.c b/tree.c index 8b42a07..043f032 100644 --- a/tree.c +++ b/tree.c @@ -9,9 +9,16 @@ const char *tree_type = "tree"; static int read_one_entry(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage) { - int len = strlen(pathname); - unsigned int size = cache_entry_size(baselen + len); - struct cache_entry *ce = xmalloc(size); + int len; + unsigned int size; + struct cache_entry *ce; + + if (S_ISDIR(mode)) + return READ_TREE_RECURSIVE; + + len = strlen(pathname); + size = cache_entry_size(baselen + len); + ce = xmalloc(size); memset(ce, 0, size); @@ -67,9 +74,10 @@ static int match_tree_entry(const char *base, int baselen, const char *path, uns return 0; } -static int read_tree_recursive(void *buffer, unsigned long size, - const char *base, int baselen, - int stage, const char **match) +int read_tree_recursive(void *buffer, unsigned long size, + const char *base, int baselen, + int stage, const char **match, + read_tree_fn_t fn) { while (size) { int len = strlen(buffer)+1; @@ -86,6 +94,14 @@ static int read_tree_recursive(void *buffer, unsigned long size, if (!match_tree_entry(base, baselen, path, mode, match)) continue; + switch (fn(sha1, base, baselen, path, mode, stage)) { + case 0: + continue; + case READ_TREE_RECURSIVE: + break;; + default: + return -1; + } if (S_ISDIR(mode)) { int retval; int pathlen = strlen(path); @@ -106,22 +122,20 @@ static int read_tree_recursive(void *buffer, unsigned long size, retval = read_tree_recursive(eltbuf, eltsize, newbase, baselen + pathlen + 1, - stage, match); + stage, match, fn); free(eltbuf); free(newbase); if (retval) return -1; continue; } - if (read_one_entry(sha1, base, baselen, path, mode, stage) < 0) - return -1; } return 0; } int read_tree(void *buffer, unsigned long size, int stage, const char **match) { - return read_tree_recursive(buffer, size, "", 0, stage, match); + return read_tree_recursive(buffer, size, "", 0, stage, match, read_one_entry); } struct tree *lookup_tree(const unsigned char *sha1) diff --git a/tree.h b/tree.h index 9975e88..768e5e9 100644 --- a/tree.h +++ b/tree.h @@ -35,4 +35,13 @@ int parse_tree(struct tree *tree); /* Parses and returns the tree in the given ent, chasing tags and commits. */ struct tree *parse_tree_indirect(const unsigned char *sha1); +#define READ_TREE_RECURSIVE 1 +typedef int (*read_tree_fn_t)(unsigned char *, const char *, int, const char *, unsigned int, int); + +extern int read_tree_recursive(void *buffer, unsigned long size, + const char *base, int baselen, + int stage, const char **match, + read_tree_fn_t fn); + + #endif /* TREE_H */ -- cgit v0.10.2-6-g49f6 From b45c569b6f257d8905acd8313224dc086266f602 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 27 Nov 2005 11:00:09 -0800 Subject: ls-tree: further cleanup to parallel ls-files. To get more a "git-ls-files" approach, this trivial patch (on top of my previous one) enables recursion, and doesn't show partial trees. [jc: after further discussion, this version enables recursion by default, and you can disable it with "-d" flag. git-ls-tree -d HEAD Documentation/no/such/directory shows Documentation tree (without -d it shows nothing). git-ls-tree HEAD shows everything from the tree. Only to get the single level from the top git-ls-tree -d HEAD is needed. But there is no way to get the single level with pathspec. You need to extract the object name of Documentation tree from the parent tree and run git-ls-tree -d $tree_id_of_Documentation_tree to get something similar to what you can get from the current git-ls-tree HEAD Documentation ] diff --git a/ls-tree.c b/ls-tree.c index 598b729..cf0dbbc 100644 --- a/ls-tree.c +++ b/ls-tree.c @@ -11,7 +11,7 @@ static int line_termination = '\n'; #define LS_RECURSIVE 1 #define LS_TREE_ONLY 2 -static int ls_options = 0; +static int ls_options = LS_RECURSIVE; static const char ls_tree_usage[] = "git-ls-tree [-d] [-r] [-z] [path...]"; @@ -19,16 +19,15 @@ static const char ls_tree_usage[] = static int show_tree(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage) { const char *type = "blob"; - int retval = 0; if (S_ISDIR(mode)) { - type = "tree"; if (ls_options & LS_RECURSIVE) - retval = READ_TREE_RECURSIVE; + return READ_TREE_RECURSIVE; + type = "tree"; } printf("%06o %s %s\t%.*s%s%c", mode, type, sha1_to_hex(sha1), baselen, base, pathname, line_termination); - return retval; + return 0; } int main(int argc, const char **argv) -- cgit v0.10.2-6-g49f6 From e2466376ec9a8508b4c8ba2b0c29945c5bfc6cc2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 27 Nov 2005 22:48:08 -0800 Subject: ls-tree: further tweaks of the rewrite It modifies the selection a bit, so that a pathspec that is a superset of a particular tree path will always cause it to recurse into that tree. As an example, let's say that we do git-ls-tree HEAD drivers/char _without_ the "-r". What will happen is that it will start out doing all the base tree, and for "drivers" it will notice that it's a proper subset of "drivers/char", so it will always recurse into _that_ tree (but not into other trees). Then, it will not match anything else than "char" in that subdirectory, and because that's not a proper superset (it's an exact match), it will _not_ recurse into it, so you get: [torvalds@g5 linux]$ ~/git/git-ls-tree HEAD drivers/char 040000 tree 9568cda453aae205bb58983747fa73b9696d9d51 drivers/char which is what you got with the old git-ls-tree too. But interestingly, if you add the slash, it will become a proper superset and it will recurse into _that_ subdirectory (but no deeper: so if you want all subdirectories _below_ drivers/char/, you still need to give "-r"): [torvalds@g5 linux]$ ~/git/git-ls-tree HEAD drivers/char/ 100644 blob 2b6b1d772ed776fff87927fc34adc2e40500218e drivers/char/.gitignore 100644 blob 56b8a2e76ab10a5c21787cb7068a846075cbaffd drivers/char/ChangeLog 100644 blob 970f70d498f4c814e1cf3362e33d7e23ac53c299 drivers/char/Kconfig ... See? This is on top of the previous two diffs, holler if you want a whole new "everything combined" version.. It hasn't gotten lots of testing, but it should work. Linus diff --git a/ls-tree.c b/ls-tree.c index cf0dbbc..4df5830 100644 --- a/ls-tree.c +++ b/ls-tree.c @@ -11,7 +11,8 @@ static int line_termination = '\n'; #define LS_RECURSIVE 1 #define LS_TREE_ONLY 2 -static int ls_options = LS_RECURSIVE; +static int ls_options = 0; +const char **pathspec; static const char ls_tree_usage[] = "git-ls-tree [-d] [-r] [-z] [path...]"; @@ -21,8 +22,29 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen, const c const char *type = "blob"; if (S_ISDIR(mode)) { + const char **s; if (ls_options & LS_RECURSIVE) return READ_TREE_RECURSIVE; + s = pathspec; + if (s) { + for (;;) { + const char *spec = *s++; + int len, speclen; + + if (!spec) + break; + if (strncmp(base, spec, baselen)) + continue; + len = strlen(pathname); + spec += baselen; + speclen = strlen(spec); + if (speclen <= len) + continue; + if (memcmp(pathname, spec, len)) + continue; + return READ_TREE_RECURSIVE; + } + } type = "tree"; } @@ -32,7 +54,7 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen, const c int main(int argc, const char **argv) { - const char **path, *prefix; + const char *prefix; unsigned char sha1[20]; char *buf; unsigned long size; @@ -60,11 +82,11 @@ int main(int argc, const char **argv) if (get_sha1(argv[1], sha1) < 0) usage(ls_tree_usage); - path = get_pathspec(prefix, argv + 2); + pathspec = get_pathspec(prefix, argv + 2); buf = read_object_with_reference(sha1, "tree", &size, NULL); if (!buf) die("not a tree object"); - read_tree_recursive(buf, size, "", 0, 0, path, show_tree); + read_tree_recursive(buf, size, "", 0, 0, pathspec, show_tree); return 0; } -- cgit v0.10.2-6-g49f6 From 32b5904b5f94829aa7ae11b585ca9c639d57cc74 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Nov 2005 02:30:04 -0800 Subject: ls-tree: Resurrect funny name quoting lost during rewrite. The rewrite to match ls-files/diff-tree behaviour accidentally lost the name quoting. I am not proud about this code, but this would get the test going. Signed-off-by: Junio C Hamano diff --git a/ls-tree.c b/ls-tree.c index 4df5830..d4b6219 100644 --- a/ls-tree.c +++ b/ls-tree.c @@ -48,7 +48,9 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen, const c type = "tree"; } - printf("%06o %s %s\t%.*s%s%c", mode, type, sha1_to_hex(sha1), baselen, base, pathname, line_termination); + printf("%06o %s %s\t", mode, type, sha1_to_hex(sha1)); + write_name_quoted(base, baselen, pathname, line_termination, stdout); + putchar(line_termination); return 0; } -- cgit v0.10.2-6-g49f6 From 246cc52f388cae8ca99e5a12b8458c9bfa467765 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Nov 2005 02:32:42 -0800 Subject: ls-tree: match the test to the new semantics. The diff for this commit is a good illustration of what changed in ls-tree behaviour. - With -r, tree nodes themselves are not shown anymore, but blobs in subtrees are shown. - The order of paths parameters do not matter, since they are not like arguments to /bin/ls, but are filter patterns. - When filter patterns overlap, unintuitive things happen. Signed-off-by: Junio C Hamano diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index dff7d69..22bdaca 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -126,19 +126,18 @@ test_expect_success \ 'git-ls-tree output for a known tree.' \ 'diff current expected' +# This changed in ls-tree pathspec change -- recursive does +# not show tree nodes anymore. test_expect_success \ 'showing tree with git-ls-tree -r' \ 'git-ls-tree -r $tree >current' cat >expected <<\EOF 100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0 120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym -040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe path2 100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 path2/file2 120000 blob d8ce161addc5173867a3c3c730924388daedbc38 path2/file2sym -040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3 path3 100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376 path3/file3 120000 blob 8599103969b43aff7e430efea79ca4636466794f path3/file3sym -040000 tree 3c5e5399f3a333eddecce7a9b9465b63f65f51e2 path3/subp3 100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f path3/subp3/file3 120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c path3/subp3/file3sym EOF diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh index c6ce56c..ae08675 100755 --- a/t/t3100-ls-tree-restrict.sh +++ b/t/t3100-ls-tree-restrict.sh @@ -54,8 +54,6 @@ test_expect_success \ cat >expected <<\EOF && 100644 blob X path0 120000 blob X path1 -040000 tree X path2 -040000 tree X path2/baz 100644 blob X path2/baz/b 120000 blob X path2/bazbo 100644 blob X path2/foo @@ -70,12 +68,14 @@ EOF test_output' +# it used to be path1 and then path0, but with pathspec semantics +# they are shown in canonical order. test_expect_success \ 'ls-tree filtered with path1 path0' \ 'git-ls-tree $tree path1 path0 >current && cat >expected <<\EOF && -120000 blob X path1 100644 blob X path0 +120000 blob X path1 EOF test_output' @@ -86,45 +86,34 @@ test_expect_success \ EOF test_output' +# It used to show path2 and its immediate children but +# with pathspec semantics it shows only path2 test_expect_success \ 'ls-tree filtered with path2' \ 'git-ls-tree $tree path2 >current && cat >expected <<\EOF && 040000 tree X path2 -040000 tree X path2/baz -120000 blob X path2/bazbo -100644 blob X path2/foo -EOF - test_output' - -test_expect_success \ - 'ls-tree filtered with path2/baz' \ - 'git-ls-tree $tree path2/baz >current && - cat >expected <<\EOF && -040000 tree X path2/baz -100644 blob X path2/baz/b EOF test_output' +# ... and path2/ shows the children. test_expect_success \ - 'ls-tree filtered with path2' \ - 'git-ls-tree $tree path2 >current && + 'ls-tree filtered with path2/' \ + 'git-ls-tree $tree path2/ >current && cat >expected <<\EOF && -040000 tree X path2 040000 tree X path2/baz 120000 blob X path2/bazbo 100644 blob X path2/foo EOF test_output' +# The same change -- exact match does not show children of +# path2/baz test_expect_success \ - 'ls-tree filtered with path2/' \ - 'git-ls-tree $tree path2/ >current && + 'ls-tree filtered with path2/baz' \ + 'git-ls-tree $tree path2/baz >current && cat >expected <<\EOF && -040000 tree X path2 040000 tree X path2/baz -120000 blob X path2/bazbo -100644 blob X path2/foo EOF test_output' diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh index 5410368..d78deb1 100644 --- a/t/t3101-ls-tree-dirname.sh +++ b/t/t3101-ls-tree-dirname.sh @@ -59,24 +59,16 @@ test_expect_success \ EOF test_output' +# Recursive does not show tree nodes anymore... test_expect_success \ 'ls-tree recursive' \ 'git-ls-tree -r $tree >current && cat >expected <<\EOF && 100644 blob X 1.txt 100644 blob X 2.txt -040000 tree X path0 -040000 tree X path0/a -040000 tree X path0/a/b -040000 tree X path0/a/b/c 100644 blob X path0/a/b/c/1.txt -040000 tree X path1 -040000 tree X path1/b -040000 tree X path1/b/c 100644 blob X path1/b/c/1.txt -040000 tree X path2 100644 blob X path2/1.txt -040000 tree X path3 100644 blob X path3/1.txt 100644 blob X path3/2.txt EOF @@ -110,41 +102,27 @@ test_expect_success \ EOF test_output' +# I am not so sure about this one after ls-tree doing pathspec match. +# Having both path0/a and path0/a/b/c makes path0/a redundant, and +# it behaves as if path0/a/b/c, path1/b/c, path2 and path3 are specified. test_expect_success \ 'ls-tree filter directories' \ 'git-ls-tree $tree path3 path2 path0/a/b/c path1/b/c path0/a >current && cat >expected <<\EOF && -040000 tree X path3 -100644 blob X path3/1.txt -100644 blob X path3/2.txt -040000 tree X path2 -100644 blob X path2/1.txt 040000 tree X path0/a/b/c -100644 blob X path0/a/b/c/1.txt 040000 tree X path1/b/c -100644 blob X path1/b/c/1.txt -040000 tree X path0/a -040000 tree X path0/a/b +040000 tree X path2 +040000 tree X path3 EOF test_output' +# Again, duplicates are filtered away so this is equivalent to +# having 1.txt and path3 test_expect_success \ 'ls-tree filter odd names' \ 'git-ls-tree $tree 1.txt /1.txt //1.txt path3/1.txt /path3/1.txt //path3//1.txt path3 /path3/ path3// >current && cat >expected <<\EOF && 100644 blob X 1.txt -100644 blob X 1.txt -100644 blob X 1.txt -100644 blob X path3/1.txt -100644 blob X path3/1.txt -100644 blob X path3/1.txt -040000 tree X path3 -100644 blob X path3/1.txt -100644 blob X path3/2.txt -040000 tree X path3 -100644 blob X path3/1.txt -100644 blob X path3/2.txt -040000 tree X path3 100644 blob X path3/1.txt 100644 blob X path3/2.txt EOF -- cgit v0.10.2-6-g49f6 From b3cfd939c330211c9812f814094aa1dbe9531bdc Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 27 Nov 2005 11:32:03 -0800 Subject: bisect: limit the searchspace by pathspecs It was surprisingly easy to do. git bisect start followed by all the normal "git bisect good/bad" stuff. Almost totally untested, and I guarantee that if your pathnames have spaces in them (or your GIT_DIR has spaces in it) this won't work. I don't know how to fix that, my shell programming isn't good enough. This involves small changes to make "git-rev-list --bisect" work in the presense of a pathspec limiter, and then truly trivial (and that's the broken part) changes to make "git bisect" save away and use the pathspec. I tried one bisection, and a "git bisect visualize", and it all looked correct. But hey, don't be surprised if it has problems. Linus Signed-off-by: Junio C Hamano diff --git a/git-bisect.sh b/git-bisect.sh index d92993b..2455f00 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -33,7 +33,6 @@ bisect_autostart() { } bisect_start() { - case "$#" in 0) ;; *) usage ;; esac # # Verify HEAD. If we were bisecting before this, reset to the # top-of-line master first! @@ -57,7 +56,8 @@ bisect_start() { rm -f "$GIT_DIR/refs/heads/bisect" rm -rf "$GIT_DIR/refs/bisect/" mkdir "$GIT_DIR/refs/bisect" - echo "git-bisect start" >"$GIT_DIR/BISECT_LOG" + echo "git-bisect start $@" >"$GIT_DIR/BISECT_LOG" + echo "$@" > "$GIT_DIR/BISECT_NAMES" } bisect_bad() { @@ -121,7 +121,7 @@ bisect_next() { bad=$(git-rev-parse --verify refs/bisect/bad) && good=$(git-rev-parse --sq --revs-only --not \ $(cd "$GIT_DIR" && ls refs/bisect/good-*)) && - rev=$(eval "git-rev-list --bisect $good $bad") || exit + rev=$(eval "git-rev-list --bisect $good $bad -- $(cat $GIT_DIR/BISECT_NAMES)") || exit if [ -z "$rev" ]; then echo "$bad was both good and bad" exit 1 @@ -131,7 +131,7 @@ bisect_next() { git-diff-tree --pretty $rev exit 0 fi - nr=$(eval "git-rev-list $rev $good" | wc -l) || exit + nr=$(eval "git-rev-list $rev $good -- $(cat $GIT_DIR/BISECT_NAMES)" | wc -l) || exit echo "Bisecting: $nr revisions left to test after this" echo "$rev" > "$GIT_DIR/refs/heads/new-bisect" git checkout new-bisect || exit @@ -142,7 +142,7 @@ bisect_next() { bisect_visualize() { bisect_next_check fail - gitk bisect/bad --not `cd "$GIT_DIR/refs" && echo bisect/good-*` + gitk bisect/bad --not `cd "$GIT_DIR/refs" && echo bisect/good-*` -- $(cat $GIT_DIR/BISECT_NAMES) } bisect_reset() { diff --git a/rev-list.c b/rev-list.c index e17f928..8020d97 100644 --- a/rev-list.c +++ b/rev-list.c @@ -350,7 +350,8 @@ static int count_distance(struct commit_list *entry) if (commit->object.flags & (UNINTERESTING | COUNTED)) break; - nr++; + if (!paths || (commit->object.flags & TREECHANGE)) + nr++; commit->object.flags |= COUNTED; p = commit->parents; entry = p; @@ -362,6 +363,7 @@ static int count_distance(struct commit_list *entry) } } } + return nr; } @@ -382,15 +384,20 @@ static struct commit_list *find_bisection(struct commit_list *list) nr = 0; p = list; while (p) { - nr++; + if (!paths || (p->item->object.flags & TREECHANGE)) + nr++; p = p->next; } closest = 0; best = list; - p = list; - while (p) { - int distance = count_distance(p); + for (p = list; p; p = p->next) { + int distance; + + if (paths && !(p->item->object.flags & TREECHANGE)) + continue; + + distance = count_distance(p); clear_distance(list); if (nr - distance < distance) distance = nr - distance; @@ -398,7 +405,6 @@ static struct commit_list *find_bisection(struct commit_list *list) best = p; closest = distance; } - p = p->next; } if (best) best->next = NULL; -- cgit v0.10.2-6-g49f6 From e9a45d75b5eccfc288c4533b5e34af1f77dd32c9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 27 Nov 2005 17:42:05 -0800 Subject: bisect: quote pathnames for eval safety. ... and make sure they are on the same line. Signed-off-by: Junio C Hamano diff --git a/git-bisect.sh b/git-bisect.sh index 2455f00..68838f3 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -1,9 +1,19 @@ #!/bin/sh . git-sh-setup +sq() { + perl -e ' + for (@ARGV) { + s/'\''/'\'\\\\\'\''/g; + print " '\''$_'\''"; + } + print "\n"; + ' "$@" +} + usage() { echo >&2 'usage: git bisect [start|bad|good|next|reset|visualize] -git bisect start reset bisect state and start bisection. +git bisect start [] reset bisect state and start bisection. git bisect bad [] mark a known-bad revision. git bisect good [...] mark ... known-good revisions. git bisect next find next bisection to test and check it out. @@ -56,8 +66,11 @@ bisect_start() { rm -f "$GIT_DIR/refs/heads/bisect" rm -rf "$GIT_DIR/refs/bisect/" mkdir "$GIT_DIR/refs/bisect" - echo "git-bisect start $@" >"$GIT_DIR/BISECT_LOG" - echo "$@" > "$GIT_DIR/BISECT_NAMES" + { + echo -n "git-bisect start" + sq "$@" + } >"$GIT_DIR/BISECT_LOG" + sq "$@" >"$GIT_DIR/BISECT_NAMES" } bisect_bad() { @@ -142,7 +155,8 @@ bisect_next() { bisect_visualize() { bisect_next_check fail - gitk bisect/bad --not `cd "$GIT_DIR/refs" && echo bisect/good-*` -- $(cat $GIT_DIR/BISECT_NAMES) + not=`cd "$GIT_DIR/refs" && echo bisect/good-*` + eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES") } bisect_reset() { @@ -173,7 +187,8 @@ bisect_replay () { test "$bisect" = "git-bisect" || continue case "$command" in start) - bisect_start + cmd="bisect_start $rev" + eval "$cmd" ;; good) echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev" -- cgit v0.10.2-6-g49f6 From e1355547fd30b21fbdc3fe4e576759683777c4ce Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 29 Nov 2005 14:14:42 -0800 Subject: applymbox: typofix to enable -m option. The -m option to fall back on 3-way merge was not honoured at all because of a typo. Signed-off-by: Junio C Hamano diff --git a/git-applymbox.sh b/git-applymbox.sh index 24d4a8c..c686cc8 100755 --- a/git-applymbox.sh +++ b/git-applymbox.sh @@ -33,7 +33,7 @@ do -k) keep_subject=-k ;; -q) query_apply=t ;; -c) continue="$2"; resume=f; shift ;; - -m) fallback_3way=t ;; + -m) fall_back_3way=t ;; -*) usage ;; *) break ;; esac -- cgit v0.10.2-6-g49f6 From bf3e274873e56d7df25d60800c8d59a309e0d8c6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 29 Nov 2005 13:53:30 -0800 Subject: applypatch: use "index" lines not "applies-to". This matches the 3-way fallback used by applypatch to use per-blob "index" lines, not "applies-to" tree object name, to match what git-am does. Signed-off-by: Junio C Hamano diff --git a/git-applypatch.sh b/git-applypatch.sh index f0549960..4c577eb 100755 --- a/git-applypatch.sh +++ b/git-applypatch.sh @@ -120,26 +120,36 @@ git-apply --index "$PATCHFILE" || { O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd` rm -fr .patch-merge-* + if git-apply -z --index-info "$PATCHFILE" \ + >.patch-merge-index-info 2>/dev/null && + GIT_INDEX_FILE=.patch-merge-tmp-index \ + git-update-index -z --index-info <.patch-merge-index-info && + GIT_INDEX_FILE=.patch-merge-tmp-index \ + git-write-tree >.patch-merge-tmp-base && + ( + mkdir .patch-merge-tmp-dir && + cd .patch-merge-tmp-dir && + GIT_INDEX_FILE="../.patch-merge-tmp-index" \ + GIT_OBJECT_DIRECTORY="$O_OBJECT" \ + git-apply $binary --index + ) <"$PATCHFILE" + then + echo Using index info to reconstruct a base tree... + mv .patch-merge-tmp-base .patch-merge-base + mv .patch-merge-tmp-index .patch-merge-index + else ( N=10 - # if the patch records the base tree... - sed -ne ' - /^diff /q - /^applies-to: \([0-9a-f]*\)$/{ - s//\1/p - q - } - ' "$PATCHFILE" - - # or hoping the patch is against our recent commits... + # Otherwise, try nearby trees that can be used to apply the + # patch. git-rev-list --max-count=$N HEAD # or hoping the patch is against known tags... git-ls-remote --tags . ) | - while read base junk - do + while read base junk + do # Try it if we have it as a tree. git-cat-file tree "$base" >/dev/null 2>&1 || continue @@ -155,7 +165,8 @@ git-apply --index "$PATCHFILE" || { mv ../.patch-merge-tmp-index ../.patch-merge-index && echo "$base" >../.patch-merge-base ) <"$PATCHFILE" 2>/dev/null && break - done + done + fi test -f .patch-merge-index && his_tree=$(GIT_INDEX_FILE=.patch-merge-index git-write-tree) && -- cgit v0.10.2-6-g49f6 From 99e368b996d6fcc2383a4b385130a0cb70c164e2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 29 Nov 2005 13:51:27 -0800 Subject: format-patch: remove applies-to. The attempt to help 3-way fallback by recording the tree object id for the entire pre-image was unnecessary, and we already have an better alternative in the form of per-blob "index" lines. Signed-off-by: Junio C Hamano diff --git a/git-format-patch.sh b/git-format-patch.sh index 9b40880..a26d46d 100755 --- a/git-format-patch.sh +++ b/git-format-patch.sh @@ -238,7 +238,6 @@ Date: '"$ad" echo git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary echo - git-cat-file commit "$commit^" | sed -e 's/^tree /applies-to: /' -e q git-diff-tree -p $diff_opts "$commit" echo "---" echo "@@GIT_VERSION@@" -- cgit v0.10.2-6-g49f6 From a004d3f70f1c074f2d9bd55e7a925ff5916ebbeb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 29 Nov 2005 13:51:27 -0800 Subject: format-patch: do not abuse 3-dash marker line. Before GIT version at the end of output we used a 3-dash marker; but 3-dash marker is special and should not be overused. Instead, use "-- " which is a standard practice in e-mails to signal the beginning of trailing garbage. Signed-off-by: Junio C Hamano diff --git a/git-format-patch.sh b/git-format-patch.sh index a26d46d..4cd38f3 100755 --- a/git-format-patch.sh +++ b/git-format-patch.sh @@ -239,7 +239,7 @@ Date: '"$ad" git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary echo git-diff-tree -p $diff_opts "$commit" - echo "---" + echo "-- " echo "@@GIT_VERSION@@" case "$mbox" in -- cgit v0.10.2-6-g49f6 From 830273d10cf1cfe646db39b87d3ccb49bf88b73a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 29 Nov 2005 13:51:27 -0800 Subject: format-patch: run diff in C locale Otherwise it would show incomplete line and binary markers in a locale dependent way. Signed-off-by: Junio C Hamano diff --git a/git-format-patch.sh b/git-format-patch.sh index 4cd38f3..1eebe85 100755 --- a/git-format-patch.sh +++ b/git-format-patch.sh @@ -5,6 +5,10 @@ . git-sh-setup +# Force diff to run in C locale. +LANG=C LC_ALL=C +export LANG LC_ALL + usage () { echo >&2 "usage: $0"' [-n] [-o dir | --stdout] [--keep-subject] [--mbox] [--check] [--signoff] [-...] @@ -202,7 +206,7 @@ process_one () { ;; esac - eval "$(LANG=C LC_ALL=C sed -ne "$whosepatchScript" $commsg)" + eval "$(sed -ne "$whosepatchScript" $commsg)" test "$author,$au" = ",$me" || { mailScript="$mailScript"' a\ -- cgit v0.10.2-6-g49f6 From 10637b84d91cf8870d1db8609a10dc5e58722378 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 29 Nov 2005 21:06:10 -0800 Subject: diff-files: -1/-2/-3 to diff against unmerged stage. While resolving conflicted merge, it was not easy to compare the working tree file with unmerged index entries. This commit introduces new options -1/-2/-3 (with synonyms --base, --ours, and --theirs) to compare working tree files with specified stages. When none of these options are given, the command defaults to -2 if the index file is unmerged, otherwise it acts as before. [jc: majorly butchered from the version Linus originally posted.] Signed-off-by: Junio C Hamano diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt index 3b04bfe..b45d1e6 100644 --- a/Documentation/git-diff-files.txt +++ b/Documentation/git-diff-files.txt @@ -21,6 +21,16 @@ OPTIONS ------- include::diff-options.txt[] +-1 -2 -3 or --base --ours --theirs, and -0:: + Diff against the "base" version, "our branch" or "their + branch" respectively. With these options, diffs for + merged entries are not shown. ++ +The default is to diff against our branch (-2) if there +is an unmerged path, and show diff for unmerged entries +otherwise. The option -0 can be given to force diff for +unmerged entries even when the index is unmerged. + -q:: Remain silent even on nonexisting files diff --git a/diff-files.c b/diff-files.c index 38599b5..bbeeea7 100644 --- a/diff-files.c +++ b/diff-files.c @@ -7,12 +7,12 @@ #include "diff.h" static const char diff_files_usage[] = -"git-diff-files [-q] " -"[] [...]" +"git-diff-files [-q] [-0/-1/2/3] [] [...]" COMMON_DIFF_OPTIONS_HELP; static struct diff_options diff_options; static int silent = 0; +static int diff_unmerged_stage = -1; static void show_unmerge(const char *path) { @@ -46,7 +46,21 @@ int main(int argc, const char **argv) argc--; break; } - if (!strcmp(argv[1], "-q")) + if (!strcmp(argv[1], "-0")) + diff_unmerged_stage = 0; + else if (!strcmp(argv[1], "-1")) + diff_unmerged_stage = 1; + else if (!strcmp(argv[1], "-2")) + diff_unmerged_stage = 2; + else if (!strcmp(argv[1], "-3")) + diff_unmerged_stage = 3; + else if (!strcmp(argv[1], "--base")) + diff_unmerged_stage = 1; + else if (!strcmp(argv[1], "--ours")) + diff_unmerged_stage = 2; + else if (!strcmp(argv[1], "--theirs")) + diff_unmerged_stage = 3; + else if (!strcmp(argv[1], "-q")) silent = 1; else if (!strcmp(argv[1], "-r")) ; /* no-op */ @@ -73,6 +87,20 @@ int main(int argc, const char **argv) pathspec = get_pathspec(prefix, argv + 1); entries = read_cache(); + if (diff_unmerged_stage < 0) { + /* default to "ours" if unmerged index, otherwise 0 */ + for (i = 0; i < entries; i++) { + struct cache_entry *ce = active_cache[i]; + if (ce_stage(ce)) { + diff_unmerged_stage = 2; + break; + } + } + if (diff_unmerged_stage < 0) + diff_unmerged_stage = 0; + } + + if (diff_setup_done(&diff_options) < 0) usage(diff_files_usage); @@ -94,13 +122,31 @@ int main(int argc, const char **argv) continue; if (ce_stage(ce)) { - show_unmerge(ce->name); - while (i < entries && - !strcmp(ce->name, active_cache[i]->name)) + if (!diff_unmerged_stage) + show_unmerge(ce->name); + while (i < entries) { + struct cache_entry *nce = active_cache[i]; + + if (strcmp(ce->name, nce->name)) + break; + /* diff against the proper unmerged stage */ + if (ce_stage(nce) == diff_unmerged_stage) + ce = nce; i++; - i--; /* compensate for loop control increments */ - continue; + } + /* + * Compensate for loop update + */ + i--; + /* + * Show the diff for the 'ce' if we found the one + * from the desired stage. + */ + if (ce_stage(ce) != diff_unmerged_stage) + continue; } + else if (diff_unmerged_stage) + continue; if (lstat(ce->name, &st) < 0) { if (errno != ENOENT && errno != ENOTDIR) { -- cgit v0.10.2-6-g49f6 From 354b9b59b0a02ce7e32a7b0c10caaa1e3532f22b Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 29 Nov 2005 21:06:10 -0800 Subject: merge-one-file: leave unmerged index entries upon automerge failure. When automerge fails, we used to collapse the path to stage0 from "our" branch, to help "diff-files" users to view the half-merged state against the current HEAD. Now diff-files has been taught how to compare with unmerged stage2,leaving them unmerged is a better thing to do, especially this prevents the unresolved conflicts to be committed by mistake. Signed-off-by: Junio C Hamano diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh index c3eca8b..739a072 100755 --- a/git-merge-one-file.sh +++ b/git-merge-one-file.sh @@ -79,11 +79,7 @@ case "${1:-.}${2:-.}${3:-.}" in ;; esac - # We reset the index to the first branch, making - # git-diff-file useful - git-update-index --add --cacheinfo "$6" "$2" "$4" - git-checkout-index -u -f -- "$4" && - merge "$4" "$orig" "$src2" + merge "$4" "$orig" "$src2" ret=$? rm -f -- "$orig" "$src2" -- cgit v0.10.2-6-g49f6 From 15bf57a18aaf10c038e2026dfe54281edbb6080a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 30 Nov 2005 02:16:36 -0800 Subject: diff-files: show diffs with stage0 and unmerged stage at the same time. After thinking about it more, I realized that much of the change I did on top of Linus' version does not make much sense. This commit reverts it so that it by default shows diffs with stage0 paths or stage2 paths with working tree; the unmerged stage to use can be overridden with -1/-2/-3 option (-2 is the default so essentially is a no-op). When the index file is unmerged, we are by definition in the middle of a conflicting merge, and we should show the diff with stage 2 by default. More importantly, paths without conflicts are updated in the working tree and collapsed to stage0 in the index, so showing diff with stage0 at the same time does not hurt. In normal cases, stage0 entries should be in sync with the working tree files and does not clutter the output. It even helps the user to realize that the working tree has local changes unrelated to the merge and remember to be careful not to do a "git-commit -a" after resolving the conflicts. When there is no unmerged entries, giving diff_unmerged_stage a default value of 2 does not cause any harm, because it would not be used anyway. So in all, always showing diff between stage0 paths and unmerged entries from a stage (defaulting to 2) is the right thing to do, as Linus originally did. Signed-off-by: Junio C Hamano diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt index b45d1e6..67f5126 100644 --- a/Documentation/git-diff-files.txt +++ b/Documentation/git-diff-files.txt @@ -26,10 +26,9 @@ include::diff-options.txt[] branch" respectively. With these options, diffs for merged entries are not shown. + -The default is to diff against our branch (-2) if there -is an unmerged path, and show diff for unmerged entries -otherwise. The option -0 can be given to force diff for -unmerged entries even when the index is unmerged. +The default is to diff against our branch (-2) and the +cleanly resolved paths. The option -0 can be given to +omit diff output for unmerged entries and just show "Unmerged". -q:: Remain silent even on nonexisting files diff --git a/diff-files.c b/diff-files.c index bbeeea7..6c0696c 100644 --- a/diff-files.c +++ b/diff-files.c @@ -12,7 +12,7 @@ COMMON_DIFF_OPTIONS_HELP; static struct diff_options diff_options; static int silent = 0; -static int diff_unmerged_stage = -1; +static int diff_unmerged_stage = 2; static void show_unmerge(const char *path) { @@ -87,20 +87,6 @@ int main(int argc, const char **argv) pathspec = get_pathspec(prefix, argv + 1); entries = read_cache(); - if (diff_unmerged_stage < 0) { - /* default to "ours" if unmerged index, otherwise 0 */ - for (i = 0; i < entries; i++) { - struct cache_entry *ce = active_cache[i]; - if (ce_stage(ce)) { - diff_unmerged_stage = 2; - break; - } - } - if (diff_unmerged_stage < 0) - diff_unmerged_stage = 0; - } - - if (diff_setup_done(&diff_options) < 0) usage(diff_files_usage); @@ -122,8 +108,7 @@ int main(int argc, const char **argv) continue; if (ce_stage(ce)) { - if (!diff_unmerged_stage) - show_unmerge(ce->name); + show_unmerge(ce->name); while (i < entries) { struct cache_entry *nce = active_cache[i]; @@ -145,8 +130,6 @@ int main(int argc, const char **argv) if (ce_stage(ce) != diff_unmerged_stage) continue; } - else if (diff_unmerged_stage) - continue; if (lstat(ce->name, &st) < 0) { if (errno != ENOENT && errno != ENOTDIR) { -- cgit v0.10.2-6-g49f6 From 58cce8a85da745e20c9527c569a0e0935ff3ab30 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 30 Nov 2005 02:37:06 -0800 Subject: merge-recursive: match the unmerged index entry behaviour with merge-resolve This minimally changes merge-recursive to match what happens when O->A, O->B, A!=B 3-way filelevel merge leaves conflicts to the new merge-resolve behaviour. Signed-off-by: Junio C Hamano diff --git a/git-merge-recursive.py b/git-merge-recursive.py index 0129233..e599b11 100755 --- a/git-merge-recursive.py +++ b/git-merge-recursive.py @@ -828,8 +828,6 @@ def processEntry(entry, branch1Name, branch2Name): if cacheOnly: updateFile(False, sha, mode, path) else: - updateFileExt(aSha, aMode, path, - updateCache=True, updateWd=False) updateFileExt(sha, mode, path, updateCache=False, updateWd=True) else: die("ERROR: Fatal merge failure, shouldn't happen.") -- cgit v0.10.2-6-g49f6 From 0501c2409d7661d2b13f094136d246b9b5b8acf9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 30 Nov 2005 02:38:24 -0800 Subject: Tutorial: adjust merge example to recursive strategy. Current default, merge-recursive, gives slightly different message while working from merge-resolve which was used to prepare the illustration in the tutorial. Signed-off-by: Junio C Hamano diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt index e2dfb00..cf7ba76 100644 --- a/Documentation/tutorial.txt +++ b/Documentation/tutorial.txt @@ -898,9 +898,8 @@ file, which had no differences in the `mybranch` branch), and say: fatal: Merge requires file-level merging Nope. ... - merge: warning: conflicts during merge - ERROR: Merge conflict in hello. - fatal: merge program failed + Auto-merging hello + CONFLICT (content): Merge conflict in hello Automatic merge failed/prevented; fix up by hand ---------------- @@ -942,10 +941,10 @@ environment, is `git show-branch`. ------------------------------------------------ $ git show-branch master mybranch -* [master] Merged "mybranch" changes. +* [master] Merge work in mybranch ! [mybranch] Some work. -- -+ [master] Merged "mybranch" changes. ++ [master] Merge work in mybranch ++ [mybranch] Some work. ------------------------------------------------ @@ -998,10 +997,10 @@ looks like, or run `show-branch`, which tells you this. ------------------------------------------------ $ git show-branch master mybranch -! [master] Merged "mybranch" changes. - * [mybranch] Merged "mybranch" changes. +! [master] Merge work in mybranch + * [mybranch] Merge work in mybranch -- -++ [master] Merged "mybranch" changes. +++ [master] Merge work in mybranch ------------------------------------------------ -- cgit v0.10.2-6-g49f6