* [PATCH] Add support for author-oriented git-rev-list switches [rev 6]
From: Jon Seymour @ 2005-06-09 9:01 UTC (permalink / raw)
To: git; +Cc: jon.seymour
This patch is a complete replacement for the previous --wrt
and --wrt-author patches; it adds support for --wrt-author,
--prune-at-author and --author switches to git-rev-list.
These switches perform various sorting and filtering operations that
help any git user reconstruct a linear view of merge history from
the point of any given change author.
When --wrt-author is specified, --merge-order is implied but
the linearisation of non-linear epochs is adjusted so that branches
contributed to by the author sort after branches contributed to by
others. This has the effect of presenting a linear history of changes as seen
by the author's own workspace.
For example, given this commit history:
a4 ---
| \ \
| b4 |
|/ | |
a3 | |
| | |
a2 | |
| | c3
| | |
| | c2
| b3 |
| | /|
| b2 |
| | c1
| | /
| b1 <-- commit authored by author@domain
a1 |
| |
a0 |
| /
root
--merge-order alone would sort it as follows:
= a4
| c3
| c2
| c1
^ b4
| b3
| b2
| b1
^ a3
| a2
| a1
| a0
= root
however, with --wrt-author --author=author@domain, git-rev-list sorts as follows:
= a4
| c3
| c2
| c1
^ b4
| a3
| a2
| a1
| a0
^ b3
| b2
| b1
= root
This has the effect of showing the first 3 of author@domain's changes
occuring before any of the changes in other branches. This is the
order in which author@domain perceived change occurring - changes
a0->a3 weren't visible in author@domain's workspace until the merge at b4.
If --prune-at-author is specified, then git-rev-list behaves as if a
^argument had been added for each commit authored by the author.
The --prune-at-author option allows an author to see a brief summary of
changes since they last made a contribution to the commit graph. For example:
git-rev-list --prune-at-author --pretty HEAD
To configure which author git-rev-list uses when processing the --wrt-author
and --prune-at-author switches, use the --author=author@domain option. If this
option is not specified, git-rev-list uses GIT_AUTHOR_EMAIL or a value
derived from the local user, host and domain names
(per git-commit-tree's algorithm)
To support this change, some additional functions were added to commit.c to
allow the extraction of author from the commit header.
Also, --show-breaks, like --wrt-author now implies --merge-order rather than
requires it.
This change also updates Documentation/git-rev-list.txt to include
documentation for --wrt-author and other git-rev-list options not currently
documented.
A test case has been included in t/t6001-rev-list-merge-order.sh to
verify this change works as expected.
Use of --wrt-author implies a small overhead (linear with respect to nodes
printed) compared with --merge-order.
This change extracts real email id formatting functions from commit-tree.c
into user.c and user.h.
Signed-off-by: Jon Seymour <jon.seymour@gmail.com>
---
NOTE TO REVIEWERS: please pay special attention to get_real_identity()
which was extracted from commit-tree.c and placed into user.c. It was
essentially a cut and paste, but since this has the potential to affect the
operation of git-commit-tree, it would be worth being very careful about
checking this change.
Another thing to review is my use of /dev/urandom and dd in my
t/t6001-rev-list-merge-order.sh. I am using it so that two
closely timed commits look diffrent to git. If /dev/urandom doesn't
exist I fall back to a slower, but safer technique using only
date.
[rev 1] * original patch
[rev 2-4] * various cleanups
[rev 5] * --merge-order bug fixes already merged with Linus.
[rev 6] * remerged with Linus' head (98a96b00b88ee35866cd0b1e94697db76bd5ddf9)
* changed name of --exclude-author switch to --prune-at-author
* reformatted to be more consistent with preferred coding style, namely:
* aggressive elimination of intra-block blank lines
except immediately after declarations
* declarations at top of block
* one statement blocks without braces
* everything except first line of function declaration
wrapped at <= 80 columns
* modified test cases to use /dev/urandom to get some entropy for
commit text so that closely spaced commits aren't identical
* added struct epoch_methods to gather 3 function pointers now used
by merge order logic
* added a comment to commit.c to indicate that epoch.c is assuming
that parse_commit stacks parents read from the commit header
rather than queues them
Could someone else review this patch please and if it, does, please your ok
to the list? I am more than happy to make any coding style adjustments required.
Diverged from 98a96b00b88ee35866cd0b1e94697db76bd5ddf9 by Linus Torvalds <torvalds@ppc970.osdl.org>
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -9,65 +9,113 @@ git-rev-list - Lists commit objects in r
SYNOPSIS
--------
-'git-rev-list' [ *--max-count*=number ] [ *--max-age*=timestamp ] [ *--min-age*=timestamp ] [ *--merge-order* [ *--show-breaks* ] ] <commit>
+'git-rev-list' [ *--max-count*=number ] [ *--max-age*=timestamp ]
+[ *--min-age*=timestamp ] [ *--pretty* ] [ *--parents* ] [ *--header* ]
+[ *--merge-order* ] [ *--show-breaks* ] [ *--wrt-author* ]
+[ *--prune-at-author* ] [ *--author=author@domain* ]
+<head-to-include> <head-to-include> ...
+^<base-to-prune>
+^<base-to-prune> ...
DESCRIPTION
-----------
-Lists commit objects in reverse chronological order starting at the
-given commit, taking ancestry relationship into account. This is
-useful to produce human-readable log output.
-
-If *--merge-order* is specified, the commit history is decomposed into a
-unique sequence of minimal, non-linear epochs and maximal, linear epochs.
-Non-linear epochs are then linearised by sorting them into merge order, which
+
+Lists commit objects in reverse chronological order starting at the commits
+specified by head-to-include values, taking ancestry relationship into account.
+git-rev-list will not print any commits that are reachable by any of the
+base-to-prune values.
+
+If *--max-count* is specified, the number of commits output is
+limited to the number specified.
+
+If *--max-age* is specified, commits older than the date specified
+(in seconds since Jan 1, 1970) are excluded from the output.
+
+If *--min-age* is specified, commits younger than the date specified
+(in seconds since Jan 1, 1970) are excluded from the output.
+
+If *--pretty* is specified, git-rev-list prints a short summary of each
+commit on stdout. This can be usefully parsed by git-shortlog to produce a
+short log of changes.
+
+If *--parents* is specified, git-rev-list prints a space separated list
+of parents on the same line as the commit identifier.
+
+If *--header* is specified, git-rev-list prints to stdout, on lines after
+the identifier of each commit, the contents of the commit's header followed by
+a trailing NUL.
+
+If *--merge-order* is specified, the commit history is decomposed into a
+unique sequence of minimal, non-linear epochs and maximal, linear epochs.
+Non-linear epochs are then linearised by sorting them into merge order, which
is described below.
-Maximal, linear epochs correspond to periods of sequential development.
-Minimal, non-linear epochs correspond to periods of divergent development
-followed by a converging merge. The theory of epochs is described in more
-detail at
+Maximal, linear epochs correspond to periods of sequential development.
+Minimal, non-linear epochs correspond to periods of divergent development
+followed by a converging merge. The theory of epochs is described in more
+detail at
link:http://blackcubes.dyndns.org/epoch/[http://blackcubes.dyndns.org/epoch/].
-The merge order for a non-linear epoch is defined as a linearisation for which
-the following invariants are true:
-
- 1. if a commit P is reachable from commit N, commit P sorts after commit N
- in the linearised list.
- 2. if Pi and Pj are any two parents of a merge M (with i < j), then any
- commit N, such that N is reachable from Pj but not reachable from Pi,
- sorts before all commits reachable from Pi.
-
-Invariant 1 states that later commits appear before earlier commits they are
-derived from.
+The merge order for a non-linear epoch is defined as a linearisation for
+which the following invariants are true:
-Invariant 2 states that commits unique to "later" parents in a merge, appear
-before all commits from "earlier" parents of a merge.
+ 1. if a commit P is reachable from commit N, commit P sorts after
+ commit N in the linearised list.
+ 2. if Pi and Pj are any two parents of a merge M (with i < j), then
+ any commit N, such that N is reachable from Pj but not reachable from
+ Pi, sorts before all commits reachable from Pi.
+
+Invariant 1 states that later commits appear before earlier commits
+they are derived from.
+
+Invariant 2 states that commits unique to "later" parents in a merge,
+appear before all commits from "earlier" parents of a merge.
+
+If *--show-breaks* is specified, each item of the list is output with a
+2-character prefix consisting of one of: (|), (^), (=) followed by a space.
+
+Commits marked with (=) represent the boundaries of minimal, non-linear
+epochs and correspond either to the start of a period of divergent development
+or to the end of such a period.
-If *--show-breaks* is specified, each item of the list is output with a
-2-character prefix consisting of one of: (|), (^), (=) followed by a space.
-
-Commits marked with (=) represent the boundaries of minimal, non-linear epochs
-and correspond either to the start of a period of divergent development or to
-the end of such a period.
-
-Commits marked with (|) are direct parents of commits immediately preceding
+Commits marked with (|) are direct parents of commits immediately preceding
the marked commit in the list.
-Commits marked with (^) are not parents of the immediately preceding commit.
-These "breaks" represent necessary discontinuities implied by trying to
+Commits marked with (^) are not parents of the immediately preceding commit.
+These "breaks" represent necessary discontinuities implied by trying to
represent an arbtirary DAG in a linear form.
-*--show-breaks* is only valid if *--merge-order* is also specified.
+If *--wrt-author* is specified, then git-rev-list sorts commits so that
+contemporaneously developed commits appear to occur after (that is, sort before)
+any commits authored by the author specified by *--author*. This reflects the
+order in which that author sees contemporaneously developed commits appear
+in her own workspace.
+
+If *--prune-at-author* is specified, then any commit authored by the
+author is marked as uninteresting as if it had been specified with ^ on
+the command line. This option restricts git-rev-list's output to just
+the changes following any changes contributed by the author.
+
+The *--author* option is used to specify the author used by the
+*--wrt-author* and *--prune-at-author* switches. If an *--author* option is
+not specified, the author used is derived from GIT_AUTHOR_EMAIL or, ultimately,
+from the local user, host and domain names as per the logic used by
+git-commit-tree.
+
+Use of *--show-breaks*, *--prune-at-author* or *--wrt-author*
+implies *--merge-order*.
Author
------
Written by Linus Torvalds <torvalds@osdl.org>
-Original *--merge-order* logic by Jon Seymour <jon.seymour@gmail.com>
+Original *--merge-order*, *--wrt-author* and *--prune-at-author* logic
+by Jon Seymour <jon.seymour@gmail.com>
Documentation
--------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+Documentation by David Greaves, Junio C Hamano, Jon Seymour and
+the git-list <git@vger.kernel.org>.
GIT
---
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -42,9 +42,9 @@ install: $(PROG) $(SCRIPTS)
LIB_OBJS=read-cache.o sha1_file.o usage.o object.o commit.o tree.o blob.o \
tag.o delta.o date.o index.o diff-delta.o patch-delta.o entry.o \
- epoch.o refs.o
+ epoch.o refs.o user.o
LIB_FILE=libgit.a
-LIB_H=cache.h object.h blob.h tree.h commit.h tag.h delta.h epoch.h
+LIB_H=cache.h object.h blob.h tree.h commit.h tag.h delta.h epoch.h user.h
LIB_H += strbuf.h
LIB_OBJS += strbuf.o
@@ -140,6 +140,7 @@ diffcore-pickaxe.o : $(LIB_H) diffcore.h
diffcore-break.o : $(LIB_H) diffcore.h
diffcore-order.o : $(LIB_H) diffcore.h
epoch.o: $(LIB_H)
+user.o: $(LIB_H)
test: all
$(MAKE) -C t/ all
diff --git a/commit-tree.c b/commit-tree.c
--- a/commit-tree.c
+++ b/commit-tree.c
@@ -4,8 +4,8 @@
* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
+#include "user.h"
-#include <pwd.h>
#include <time.h>
#include <ctype.h>
@@ -100,17 +100,16 @@ static char *commit_tree_usage = "git-co
int main(int argc, char **argv)
{
- int i, len;
+ int i;
int parents = 0;
unsigned char tree_sha1[20];
unsigned char parent_sha1[MAXPARENT][20];
unsigned char commit_sha1[20];
char *gecos, *realgecos, *commitgecos;
- char *email, *commitemail, realemail[1000];
+ char *email, *commitemail, *realemail;
char date[50], realdate[50];
char *audate, *cmdate;
char comment[1000];
- struct passwd *pw;
char *buffer;
unsigned int size;
@@ -128,19 +127,7 @@ int main(int argc, char **argv)
}
if (!parents)
fprintf(stderr, "Committing initial tree %s\n", argv[1]);
- pw = getpwuid(getuid());
- if (!pw)
- die("You don't exist. Go away!");
- realgecos = pw->pw_gecos;
- len = strlen(pw->pw_name);
- memcpy(realemail, pw->pw_name, len);
- realemail[len] = '@';
- gethostname(realemail+len+1, sizeof(realemail)-len-1);
- if (!strchr(realemail+len+1, '.')) {
- strcat(realemail, ".");
- getdomainname(realemail+strlen(realemail), sizeof(realemail)-strlen(realemail)-1);
- }
-
+ get_real_identity(&realemail, &realgecos);
datestamp(realdate, sizeof(realdate));
strcpy(date, realdate);
diff --git a/commit.c b/commit.c
--- a/commit.c
+++ b/commit.c
@@ -76,6 +76,15 @@ int parse_commit_buffer(struct commit *i
!get_sha1_hex(bufptr + 7, parent)) {
struct commit *new_parent = lookup_commit(parent);
if (new_parent) {
+ /**
+ * The current stacking (as opposed to queueing)
+ * behaviour implied by use of commit_list_insert
+ * is assumed in several places elsewhere in git
+ * - particularly epoch.c. If this ever changes
+ * it will be necessary to carefully re-evaluate
+ * order-sensitive commit list processing loops to
+ * ensure they are still doing the right thing.
+ */
commit_list_insert(new_parent, &item->parents);
add_ref(&item->object, &new_parent->object);
}
@@ -302,3 +311,56 @@ int count_parents(struct commit * commit
return count;
}
+int copy_commit_header(struct commit * commit, char * header, int index, char * buffer, int len)
+{
+ char * p = commit->buffer;
+ while (*p != '\n') {
+ char * q = header;
+ int matched;
+
+ for (matched = 1; *p != ' ' && *p != '\n'; p++, q++) {
+ matched = matched && (*q==*p);
+ }
+
+ if (matched && index) {
+ /*
+ * if we matched but we haven't seen the
+ * index'th element yet, just decrement
+ * the index then pretend we didn't match
+ */
+ index--;
+ matched = 0;
+ }
+
+ if (!matched) {
+ /* skip to start of next header line */
+ for (;*p!='\n';p++)
+ ;
+ p++;
+ } else {
+ int count = 0; /* number of characters in value */
+ if (*p == ' ') {
+ p++;
+ count = 0;
+ while(*p != '\n') {
+ if (len > 0)
+ *buffer++=*p;
+ p++;
+ count++;
+ len--;
+ }
+ }
+
+ if (len > 0)
+ *buffer = 0;
+
+ return (len > 0) ? count+1 : -(count+1);
+ }
+ }
+ return 0;
+}
+
+int copy_author(struct commit * commit, char * buffer, int len)
+{
+ return copy_commit_header(commit, "author", 0, buffer, len);
+}
diff --git a/commit.h b/commit.h
--- a/commit.h
+++ b/commit.h
@@ -53,4 +53,23 @@ struct commit *pop_most_recent_commit(st
struct commit *pop_commit(struct commit_list **stack);
int count_parents(struct commit * commit);
+
+/*
+ * Copies the value of the (index+1)'th commit header matching the name
+ * specified into the buffer supplied and append a trailing NUL.
+ *
+ * Returns n<0 if the buffer was too short, where -n is the required length.
+ *
+ * Returns 0 if the header doesn't exist.
+ *
+ * Returns n>0 where n is the length of the copied zero-terminated value,
+ * including the terminating zero.
+ */
+int copy_commit_header(struct commit * commit, char * header, int index, char * buffer, int len);
+
+/*
+ * Copies the commit's author value into the buffer supplied.
+ * Return values as per copy_commit_header.
+ */
+int copy_author(struct commit * commit, char * buffer, int len);
#endif /* COMMIT_H */
diff --git a/epoch.c b/epoch.c
--- a/epoch.c
+++ b/epoch.c
@@ -8,7 +8,6 @@
*
*/
#include <stdlib.h>
-
/* Provides arbitrary precision integers required to accurately represent
* fractional mass: */
#include <openssl/bn.h>
@@ -23,7 +22,19 @@ struct fraction {
};
#define HAS_EXACTLY_ONE_PARENT(n) ((n)->parents && !(n)->parents->next)
+#define ASSERT(x,m,c) if (!(x)) { assertion_failed(__LINE__, __FUNCTION__, m, c); }
+static void assertion_failed(int line, char * function, char * message, struct commit * item)
+{
+ die( "%s:%d:%s: assertion_failed: %s: commit %s, flags %x",
+ __FILE__,
+ line,
+ function,
+ message,
+ item ? sha1_to_hex(item->object.sha1) : "[]",
+ item ? item->object.flags : 0xFFFFFFFF);
+}
+
static BN_CTX *context = NULL;
static struct fraction *one = NULL;
static struct fraction *zero = NULL;
@@ -58,10 +69,10 @@ static struct fraction *divide(struct fr
BN_init(&bn_divisor);
BN_set_word(&bn_divisor, divisor);
-
BN_copy(&result->numerator, &fraction->numerator);
- BN_mul(&result->denominator, &fraction->denominator, &bn_divisor, get_BN_CTX());
-
+ BN_mul( &result->denominator,
+ &fraction->denominator,
+ &bn_divisor, get_BN_CTX());
BN_clear(&bn_divisor);
return result;
}
@@ -99,28 +110,44 @@ static struct fraction *copy(struct frac
return to;
}
-static struct fraction *add(struct fraction *result, struct fraction *left, struct fraction *right)
+static struct fraction *inc(struct fraction *left, struct fraction *right)
{
BIGNUM a, b, gcd;
BN_init(&a);
BN_init(&b);
BN_init(&gcd);
-
- BN_mul(&a, &left->numerator, &right->denominator, get_BN_CTX());
- BN_mul(&b, &left->denominator, &right->numerator, get_BN_CTX());
- BN_mul(&result->denominator, &left->denominator, &right->denominator, get_BN_CTX());
- BN_add(&result->numerator, &a, &b);
-
- BN_gcd(&gcd, &result->denominator, &result->numerator, get_BN_CTX());
- BN_div(&result->denominator, NULL, &result->denominator, &gcd, get_BN_CTX());
- BN_div(&result->numerator, NULL, &result->numerator, &gcd, get_BN_CTX());
-
+ BN_mul( &a,
+ &left->numerator,
+ &right->denominator,
+ get_BN_CTX());
+ BN_mul( &b,
+ &left->denominator,
+ &right->numerator,
+ get_BN_CTX());
+ BN_mul( &left->denominator,
+ &left->denominator,
+ &right->denominator,
+ get_BN_CTX());
+ BN_add( &left->numerator,
+ &a,
+ &b);
+ BN_gcd( &gcd,
+ &left->denominator,
+ &left->numerator,
+ get_BN_CTX());
+ BN_div( &left->denominator,
+ NULL, &left->denominator,
+ &gcd,
+ get_BN_CTX());
+ BN_div( &left->numerator,
+ NULL, &left->numerator,
+ &gcd,
+ get_BN_CTX());
BN_clear(&a);
BN_clear(&b);
BN_clear(&gcd);
-
- return result;
+ return left;
}
static int compare(struct fraction *left, struct fraction *right)
@@ -130,15 +157,11 @@ static int compare(struct fraction *left
BN_init(&a);
BN_init(&b);
-
BN_mul(&a, &left->numerator, &right->denominator, get_BN_CTX());
BN_mul(&b, &left->denominator, &right->numerator, get_BN_CTX());
-
result = BN_cmp(&a, &b);
-
BN_clear(&a);
BN_clear(&b);
-
return result;
}
@@ -150,21 +173,15 @@ struct mass_counter {
static struct mass_counter *new_mass_counter(struct commit *commit, struct fraction *pending)
{
struct mass_counter *mass_counter = xmalloc(sizeof(*mass_counter));
- memset(mass_counter, 0, sizeof(*mass_counter));
+ ASSERT(!commit->object.util, "not already initialized", commit);
+
+ memset(mass_counter, 0, sizeof(*mass_counter));
init_fraction(&mass_counter->seen);
init_fraction(&mass_counter->pending);
-
copy(&mass_counter->pending, pending);
copy(&mass_counter->seen, get_zero());
-
- if (commit->object.util) {
- die("multiple attempts to initialize mass counter for %s",
- sha1_to_hex(commit->object.sha1));
- }
-
commit->object.util = mass_counter;
-
return mass_counter;
}
@@ -218,73 +235,63 @@ static int find_base_for_list(struct com
struct commit_list *cleaner = NULL;
struct commit_list *pending = NULL;
struct fraction injected;
+
init_fraction(&injected);
*boundary = NULL;
-
- for (; list; list = list->next) {
+ for (; list && !ret; list = list->next) {
struct commit *item = list->item;
- if (item->object.util) {
- die("%s:%d:%s: logic error: this should not have happened - commit %s",
- __FILE__, __LINE__, __FUNCTION__,
- sha1_to_hex(item->object.sha1));
- }
-
+ ASSERT(!item->object.util, "no duplicates in list", item);
new_mass_counter(list->item, get_one());
- add(&injected, &injected, get_one());
-
+ inc(&injected, get_one());
commit_list_insert(list->item, &cleaner);
commit_list_insert(list->item, &pending);
}
-
while (!*boundary && pending && !ret) {
struct commit *latest = pop_commit(&pending);
struct mass_counter *latest_node = (struct mass_counter *) latest->object.util;
int num_parents;
- if ((ret = parse_commit(latest)))
+ ret = parse_commit(latest);
+ if (ret)
continue;
- add(&latest_node->seen, &latest_node->seen, &latest_node->pending);
-
+ inc(&latest_node->seen, &latest_node->pending);
num_parents = count_parents(latest);
if (num_parents) {
- struct fraction distribution;
- struct commit_list *parents;
+ struct fraction div;
+ struct commit_list *parents = latest->parents;
- divide(init_fraction(&distribution), &latest_node->pending, num_parents);
-
- for (parents = latest->parents; parents; parents = parents->next) {
- struct commit *parent = parents->item;
- struct mass_counter *parent_node = (struct mass_counter *) parent->object.util;
-
- if (!parent_node) {
- parent_node = new_mass_counter(parent, &distribution);
- insert_by_date(&pending, parent);
- commit_list_insert(parent, &cleaner);
- } else {
- if (!compare(&parent_node->pending, get_zero()))
- insert_by_date(&pending, parent);
- add(&parent_node->pending, &parent_node->pending, &distribution);
- }
+ divide( init_fraction(&div),
+ &latest_node->pending,
+ num_parents);
+ while (parents) {
+ struct mass_counter * pmc;
+ struct commit *pc = parents->item;
+
+ pmc = (struct mass_counter *) pc->object.util;
+ if (!pmc) {
+ pmc = new_mass_counter(pc, get_zero());
+ commit_list_insert(pc, &cleaner);
+ }
+ if (!compare(&pmc->pending, get_zero()))
+ insert_by_date(&pending, pc);
+ inc(&pmc->pending, &div);
+ parents = parents->next;
}
-
- clear_fraction(&distribution);
+ clear_fraction(&div);
}
-
- if (!compare(&latest_node->seen, &injected))
+ if (!compare(&latest_node->seen, &injected)) {
*boundary = latest;
+ }
copy(&latest_node->pending, get_zero());
}
-
while (cleaner) {
struct commit *next = pop_commit(&cleaner);
free_mass_counter((struct mass_counter *) next->object.util);
next->object.util = NULL;
}
-
if (pending)
free_commit_list(pending);
-
clear_fraction(&injected);
return ret;
}
@@ -305,7 +312,6 @@ static int find_base(struct commit *head
}
ret = find_base_for_list(pending, boundary);
free_commit_list(pending);
-
return ret;
}
@@ -319,13 +325,12 @@ static int find_base(struct commit *head
*/
static int find_next_epoch_boundary(struct commit *head_of_epoch, struct commit **boundary)
{
- int ret;
struct commit *item = head_of_epoch;
+ int ret;
ret = parse_commit(item);
if (ret)
return ret;
-
if (HAS_EXACTLY_ONE_PARENT(item)) {
/*
* We are at the start of a maximimal linear epoch.
@@ -336,7 +341,6 @@ static int find_next_epoch_boundary(stru
ret = parse_commit(item);
}
*boundary = item;
-
} else {
/*
* Otherwise, we are at the start of a minimal, non-linear
@@ -344,7 +348,6 @@ static int find_next_epoch_boundary(stru
*/
ret = find_base(item, boundary);
}
-
return ret;
}
@@ -354,8 +357,10 @@ static int find_next_epoch_boundary(stru
static int is_parent_of(struct commit *parent, struct commit *child)
{
struct commit_list *parents;
+
for (parents = child->parents; parents; parents = parents->next) {
- if (!memcmp(parent->object.sha1, parents->item->object.sha1,
+ if (!memcmp(parent->object.sha1,
+ parents->item->object.sha1,
sizeof(parents->item->object.sha1)))
return 1;
}
@@ -369,11 +374,12 @@ static int is_parent_of(struct commit *p
*/
static void push_onto_merge_order_stack(struct commit_list **stack, struct commit *item)
{
- struct commit_list *top = *stack;
- if (top && (top->item->object.flags & DISCONTINUITY)) {
- if (is_parent_of(top->item, item)) {
- top->item->object.flags &= ~DISCONTINUITY;
- }
+ struct commit * top_item = *stack ? (*stack)->item : NULL;
+
+ if ( top_item
+ && (top_item->object.flags & DISCONTINUITY)
+ && is_parent_of(top_item, item)) {
+ top_item->object.flags &= ~DISCONTINUITY;
}
commit_list_insert(item, stack);
}
@@ -381,7 +387,7 @@ static void push_onto_merge_order_stack(
/*
* Marks all interesting, visited commits reachable from this commit
* as uninteresting. We stop recursing when we reach the epoch boundary,
- * an unvisited node or a node that has already been marking uninteresting.
+ * an unvisited node or a node that has already been marked uninteresting.
*
* This doesn't actually mark all ancestors between the start node and the
* epoch boundary uninteresting, but does ensure that they will eventually
@@ -397,7 +403,6 @@ static void mark_ancestors_uninteresting
struct commit_list *next;
commit->object.flags |= UNINTERESTING;
-
/*
* We only need to recurse if
* we are not on the boundary and
@@ -416,65 +421,77 @@ static void mark_ancestors_uninteresting
if (uninteresting || boundary || !visited)
return;
-
for (next = commit->parents; next; next = next->next)
mark_ancestors_uninteresting(next->item);
}
/*
- * Sorts the nodes of the first epoch of the epoch sequence of the epoch headed at head
- * into merge order.
+ * Sorts the nodes of the first epoch of the epoch sequence of the epoch
+ * headed at head into merge order.
*/
-static void sort_first_epoch(struct commit *head, struct commit_list **stack)
+static void sort_first_epoch(struct commit *head, struct commit_list **stack, struct epoch_methods * methods)
{
- struct commit_list *parents;
- struct commit_list *reversed_parents = NULL;
+ struct commit_list *reversed = NULL;
+ if (methods->marker) {
+ (*(methods->marker)) (head);
+ }
head->object.flags |= VISITED;
-
/*
- * parse_commit() builds the parent list in reverse order with respect
- * to the order of the git-commit-tree arguments. So we need to reverse
- * this list to output the oldest (or most "local") commits last.
+ * reverse them so that "local" parents are pushed first
+ * and hence print last. Note: this is relying
+ * on parse_commit always stacking rather than queueing
+ * parents it reads from the commit header. If this
+ * assumption ever changes, then this code and
+ * also that in sort_list_merge_order.
*/
- for (parents = head->parents; parents; parents = parents->next)
- commit_list_insert(parents->item, &reversed_parents);
+ if (!head->object.util) {
+ struct commit_list *parents;
- /*
- * TODO: By sorting the parents in a different order, we can alter the
- * merge order to show contemporaneous changes in parallel branches
- * occurring after "local" changes. This is useful for a developer
- * when a developer wants to see all changes that were incorporated
- * into the same merge as her own changes occur after her own
- * changes.
- */
+ /*
+ * make a copy of the parents, but don't destroy them
+ */
+ parents = head->parents;
+ while (parents) {
+ commit_list_insert(parents->item, &reversed);
+ parents = parents->next;
+ }
+ } else {
+ struct commit_list **p;
- while (reversed_parents) {
- struct commit *parent = pop_commit(&reversed_parents);
+ /*
+ * reverse the temporary list, destroying it
+ * in the process list.
+ */
+ p = (struct commit_list **)&head->object.util;
+ while (*p)
+ commit_list_insert(pop_commit(p), &reversed);
+ ASSERT(!head->object.util, "temporary list cleared", head);
+ }
+ while (reversed) {
+ struct commit *parent = pop_commit(&reversed);
if (head->object.flags & UNINTERESTING) {
/*
* Propagates the uninteresting bit to all parents.
- * if we have already visited this parent, then
+ * If we have already visited this parent, then
* the uninteresting bit will be propagated to each
* reachable commit that is still not marked
- * uninteresting and won't otherwise be reached.
+ * uninteresting and won't otherwise be reached by
+ * this traversal.
*/
mark_ancestors_uninteresting(parent);
}
-
if (!(parent->object.flags & VISITED)) {
if (parent->object.flags & BOUNDARY) {
- if (*stack) {
- die("something else is on the stack - %s",
- sha1_to_hex((*stack)->item->object.sha1));
- }
+ ASSERT(!(*stack),
+ "empty stack at boundary",
+ (*stack)->item);
push_onto_merge_order_stack(stack, parent);
parent->object.flags |= VISITED;
-
} else {
- sort_first_epoch(parent, stack);
- if (reversed_parents) {
+ sort_first_epoch(parent, stack, methods);
+ if (reversed) {
/*
* This indicates a possible
* discontinuity it may not be be
@@ -485,23 +502,21 @@ static void sort_first_epoch(struct comm
* The next push onto the stack will
* resolve the question.
*/
- (*stack)->item->object.flags |= DISCONTINUITY;
+ (*stack)->item
+ ->object.flags |= DISCONTINUITY;
}
}
}
}
-
push_onto_merge_order_stack(stack, head);
}
/*
* Emit the contents of the stack.
- *
* The stack is freed and replaced by NULL.
- *
* Sets the return value to STOP if no further output should be generated.
*/
-static int emit_stack(struct commit_list **stack, emitter_func emitter)
+static int emit_stack(struct commit_list **stack, struct epoch_methods * methods)
{
unsigned int seen = 0;
int action = CONTINUE;
@@ -510,15 +525,93 @@ static int emit_stack(struct commit_list
struct commit *next = pop_commit(stack);
seen |= next->object.flags;
if (*stack)
- action = (*emitter) (next);
+ action = (*(methods->emitter)) (next);
}
-
if (*stack) {
free_commit_list(*stack);
*stack = NULL;
- }
+ }
+ if (seen & UNINTERESTING)
+ /**
+ * We stop at the base of the stack, rather than
+ * when we encounter the first UNINTERESTING flag.
+ *
+ * The reason is that there may still be interesting
+ * stuff on the stack but once we reach the base
+ * there can be no more interesting stuff by definition
+ * of what the base of an epoch is - everything reachable
+ * from the base is also reachable from the UNINTERESTING
+ * node and hence is uninteresting.
+ */
+ action = STOP;
+ return action;
+}
- return (action == STOP || (seen & UNINTERESTING)) ? STOP : CONTINUE;
+/*
+ * Sort a list of commits in local last order. A commit is "local"
+ * if any of its ancestors (except the base) causes (*local_test)() to
+ * return a non-zero value.
+ *
+ * The sorted list is returned in *sorted. A side effect of this function
+ * is to set each object.util pointer in each ancestor up until the base
+ * to a list of parents sorted in local_last order.
+ *
+ * Does nothing except set *sorted to NULL if either list or local_test are
+ * NULL.
+ *
+ * The return value contains LOCAL if any of the list is local or had a
+ * local ancestor.
+ */
+static unsigned int sort_list_in_local_last_order(struct commit_list *list, struct commit_list **sorted, struct epoch_methods * methods)
+{
+ struct commit_list *next;
+ struct commit_list *non_local = NULL;
+ struct commit_list *local = NULL;
+ struct commit_list **p = &local;
+ struct commit_list **q = &non_local;
+
+ if (!list || !methods->local_test) {
+ *sorted = NULL;
+ return 0;
+ }
+ for (next = list; next; next = next->next) {
+ struct commit *item = next->item;
+
+ if (item->object.flags & BOUNDARY || item->object.util)
+ /*
+ * we have already visited this item or we have
+ * reached the boundary.
+ */
+ continue;
+ if ((*(methods->local_test)) (item))
+ item->object.flags |= LOCAL;
+ item->object.flags |=
+ sort_list_in_local_last_order(
+ item->parents,
+ (struct commit_list **) &item->object.util,
+ methods);
+ }
+ /*
+ * Create a re-ordered list by copying the locally marked
+ * items to the end of the new list, but otherwise preserving the
+ * order.
+ */
+ for (next = list; next; next = next->next) {
+ if (next->item->object.flags & LOCAL)
+ p = &commit_list_insert(next->item, p)->next;
+ else
+ q = &commit_list_insert(next->item, q)->next;
+ }
+ /*
+ * if parse_commit in commit.c ever changes stack behaviour
+ * to queueing behaviour this needs to change to:
+ *
+ * *p = non_local;
+ * *sorted = local;
+ */
+ *q = local;
+ *sorted = non_local;
+ return (local ? LOCAL : 0);
}
/*
@@ -528,14 +621,13 @@ static int emit_stack(struct commit_list
* Note: this algorithm currently leaves traces of its execution in the
* object flags of nodes it discovers. This should probably be fixed.
*/
-static int sort_in_merge_order(struct commit *head_of_epoch, emitter_func emitter)
+static int sort_in_merge_order(struct commit *head_of_epoch, struct epoch_methods * methods)
{
struct commit *next = head_of_epoch;
int ret = 0;
int action = CONTINUE;
ret = parse_commit(head_of_epoch);
-
while (next && next->parents && !ret && (action != STOP)) {
struct commit *base = NULL;
@@ -545,34 +637,36 @@ static int sort_in_merge_order(struct co
next->object.flags |= BOUNDARY;
if (base)
base->object.flags |= BOUNDARY;
-
if (HAS_EXACTLY_ONE_PARENT(next)) {
while (HAS_EXACTLY_ONE_PARENT(next)
&& (action != STOP)
&& !ret) {
- if (next->object.flags & UNINTERESTING) {
+ if (methods->marker)
+ (*(methods->marker)) (next);
+ if (next->object.flags & UNINTERESTING)
action = STOP;
- } else {
- action = (*emitter) (next);
- }
+ else
+ action = (*(methods->emitter)) (next);
if (action != STOP) {
next = next->parents->item;
ret = parse_commit(next);
}
}
-
} else {
struct commit_list *stack = NULL;
- sort_first_epoch(next, &stack);
- action = emit_stack(&stack, emitter);
+
+ sort_list_in_local_last_order(
+ next->parents,
+ (struct commit_list **) &next->object.util,
+ methods);
+ sort_first_epoch(next, &stack, methods);
+ action = emit_stack(&stack, methods);
next = base;
}
}
-
if (next && (action != STOP) && !ret) {
- (*emitter) (next);
+ (*(methods->emitter))(next);
}
-
return ret;
}
@@ -582,52 +676,65 @@ static int sort_in_merge_order(struct co
* in this subgraph using the sort_first_epoch algorithm. Once we have
* reached the base we can continue sorting using sort_in_merge_order.
*/
-int sort_list_in_merge_order(struct commit_list *list, emitter_func emitter)
+int sort_list_in_merge_order(struct commit_list *list, struct epoch_methods * methods)
{
struct commit_list *stack = NULL;
+ struct commit_list *filtered = NULL;
+ struct commit_list **p = &filtered;
struct commit *base;
int ret = 0;
int action = CONTINUE;
- struct commit_list *reversed = NULL;
+ if (!methods)
+ die("methods argument must not be null");
+ if (!methods->emitter)
+ die("an emitter methods must be supplied");
for (; list; list = list->next) {
struct commit *next = list->item;
if (!(next->object.flags & UNINTERESTING)) {
- if (next->object.flags & DUPCHECK) {
- fprintf(stderr, "%s: duplicate commit %s ignored\n",
- __FUNCTION__, sha1_to_hex(next->object.sha1));
- } else {
+ if (!(next->object.flags & DUPCHECK)) {
+ /**
+ * Duplicates will cause find_base_for_list
+ * to die, so we silently remove them
+ * here - it isn't actually an error.
+ */
next->object.flags |= DUPCHECK;
- commit_list_insert(list->item, &reversed);
+ p = &commit_list_insert(list->item, p)->next;
}
}
}
-
- if (!reversed->next) {
+ if (!filtered->next) {
/*
* If there is only one element in the list, we can sort it
* using sort_in_merge_order.
*/
- base = reversed->item;
+ base = filtered->item;
} else {
/*
* Otherwise, we search for the base of the list.
- */
- ret = find_base_for_list(reversed, &base);
+ */
+ struct commit_list *sorted;
+ struct commit_list *reversed = NULL;
+
+ ret = find_base_for_list(filtered, &base);
if (ret)
return ret;
if (base)
base->object.flags |= BOUNDARY;
-
+ sort_list_in_local_last_order(filtered, &sorted, methods);
+ if (!sorted)
+ sorted = filtered;
+ while (sorted)
+ commit_list_insert(pop_commit(&sorted), &reversed);
while (reversed) {
- sort_first_epoch(pop_commit(&reversed), &stack);
+ sort_first_epoch(pop_commit(&reversed), &stack, methods);
if (reversed) {
/*
* If we have more commits to push, then the
- * first push for the next parent may (or may
+ * first push for the next commit may (or may
* not) represent a discontinuity with respect
- * to the parent currently on the top of
+ * to the commit currently on the top of
* the stack.
*
* Mark it for checking here, and check it
@@ -637,13 +744,15 @@ int sort_list_in_merge_order(struct comm
stack->item->object.flags |= DISCONTINUITY;
}
}
-
- action = emit_stack(&stack, emitter);
+ action = emit_stack(&stack, methods);
}
-
if (base && (action != STOP)) {
- ret = sort_in_merge_order(base, emitter);
+ ret = sort_in_merge_order(base, methods);
}
-
return ret;
}
+
+void init_epoch_methods(struct epoch_methods * methods)
+{
+ memset(methods, 0, sizeof(*methods));
+}
diff --git a/epoch.h b/epoch.h
--- a/epoch.h
+++ b/epoch.h
@@ -1,20 +1,55 @@
#ifndef EPOCH_H
#define EPOCH_H
-
-// return codes for emitter_func
-#define STOP 0
-#define CONTINUE 1
-#define DO 2
-typedef int (*emitter_func) (struct commit *);
-
-int sort_list_in_merge_order(struct commit_list *list, emitter_func emitter);
-
+/**
+ * Flags used by merge order logic and also by rev-list.c
+ */
#define UNINTERESTING (1u<<2)
#define BOUNDARY (1u<<3)
#define VISITED (1u<<4)
#define DISCONTINUITY (1u<<5)
#define DUPCHECK (1u<<6)
+#define LOCAL (1u<<7)
+/**
+ * Return codes for emitter method. Also used by rev-list.c
+ */
+#define STOP 0
+#define CONTINUE 1
+#define DO 2
-#endif /* EPOCH_H */
+struct epoch_methods {
+ /*
+ * Returns 0 if traversal should stop, non-zero if it should continue.
+ */
+ int (*emitter)(struct commit *);
+ /*
+ * Returns non-zero if the commit is regarded "local", 0 otherwise.
+ */
+ int (*local_test)(struct commit *);
+ /*
+ * Implementers may use this method to mark commits uninteresting
+ * according to some locally determined criteria. The tree
+ * will be pruned at any commit so marked.
+ */
+ void (*marker)(struct commit *);
+};
+
+/**
+ * Initializes an epoch_methods structure which
+ * may be customized by the caller by overriding any of the method pointers.
+ */
+extern void init_epoch_methods(struct epoch_methods *);
+
+/**
+ * Sorts the list of commits in merge order, using the methods specified
+ * to customize the tactics of the search.
+ *
+ * The prune points should be marked with the UNINTERESTING flags.
+ *
+ * Note: this algorithm is dirty in the sense that it leaves traces
+ * of its execution in the object.flags word of some or all of the commits
+ * visited.
+ */
+extern int sort_list_in_merge_order(struct commit_list *list, struct epoch_methods * methods);
+#endif /* EPOCH_H */
diff --git a/rev-list.c b/rev-list.c
--- a/rev-list.c
+++ b/rev-list.c
@@ -1,18 +1,24 @@
#include "cache.h"
#include "commit.h"
#include "epoch.h"
+#include "user.h"
#define SEEN (1u << 0)
#define INTERESTING (1u << 1)
static const char rev_list_usage[] =
"usage: git-rev-list [OPTION] commit-id <commit-id>\n"
- " --max-count=nr\n"
- " --max-age=epoch\n"
- " --min-age=epoch\n"
- " --header\n"
- " --pretty\n"
- " --merge-order [ --show-breaks ]";
+ " --max-count=nr\n"
+ " --max-age=epoch\n"
+ " --min-age=epoch\n"
+ " --header\n"
+ " --parents\n"
+ " --pretty\n"
+ " --merge-order\n"
+ " --wrt-author\n"
+ " --prune-at-author\n"
+ " --author=author@domain\n"
+ " --show-breaks ]";
static int verbose_header = 0;
static int show_parents = 0;
@@ -24,6 +30,9 @@ static int max_count = -1;
static enum cmit_fmt commit_format = CMIT_FMT_RAW;
static int merge_order = 0;
static int show_breaks = 0;
+static char * local_author = NULL;
+static int prune_at_author = 0;
+static int wrt_author = 0;
static void show_commit(struct commit *commit)
{
@@ -147,6 +156,23 @@ static enum cmit_fmt get_commit_format(c
usage(rev_list_usage);
}
+static int is_local_author(struct commit * commit)
+{
+ static char author[16384];
+ if (copy_author(commit, author, sizeof(author)) > 0) {
+ if (strstr(author, local_author)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void mark_authors_own_uninteresting(struct commit * commit)
+{
+ if (is_local_author(commit)) {
+ commit->object.flags |= UNINTERESTING;
+ }
+}
int main(int argc, char **argv)
{
@@ -186,14 +212,26 @@ int main(int argc, char **argv)
show_parents = 1;
continue;
}
- if (!strncmp(arg, "--merge-order", 13)) {
+ if (!strcmp(arg, "--merge-order")) {
merge_order = 1;
continue;
}
- if (!strncmp(arg, "--show-breaks", 13)) {
+ if (!strcmp(arg, "--show-breaks")) {
show_breaks = 1;
continue;
}
+ if (!strncmp(arg, "--author=", 9)) {
+ local_author=strdup(&arg[9]);
+ continue;
+ }
+ if (!strcmp(arg, "--wrt-author")) {
+ wrt_author = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--prune-at-author")) {
+ prune_at_author = 1;
+ continue;
+ }
flags = 0;
if (*arg == '^') {
@@ -201,7 +239,7 @@ int main(int argc, char **argv)
arg++;
limited = 1;
}
- if (get_sha1(arg, sha1) || (show_breaks && !merge_order))
+ if (get_sha1(arg, sha1))
usage(rev_list_usage);
commit = lookup_commit_reference(sha1);
if (!commit || parse_commit(commit) < 0)
@@ -213,14 +251,38 @@ int main(int argc, char **argv)
if (!list)
usage(rev_list_usage);
+ if (!merge_order)
+ merge_order = wrt_author || prune_at_author || show_breaks;
+
if (!merge_order) {
if (limited)
list = limit_list(list);
show_commit_list(list);
- } else {
- if (sort_list_in_merge_order(list, &process_commit)) {
- die("merge order sort failed\n");
- }
+ } else {
+ struct epoch_methods methods;
+
+ init_epoch_methods(&methods);
+ if ((prune_at_author|wrt_author) && !local_author) {
+ local_author = gitenv("GIT_AUTHOR_EMAIL") ? : NULL;
+ if (!local_author)
+ get_real_identity(&local_author, NULL);
+ else
+ local_author = strdup(local_author);
+ }
+ if (local_author) {
+ /* add delimiters to improve accuracy of match */
+ char * tmp=xmalloc(strlen(local_author)+3);
+ sprintf(tmp, "<%s>", local_author);
+ free(local_author);
+ local_author = tmp;
+ }
+ methods.emitter = &process_commit;
+ if (wrt_author)
+ methods.local_test = &is_local_author;
+ if (prune_at_author)
+ methods.marker = &mark_authors_own_uninteresting;
+ if (sort_list_in_merge_order(list, &methods))
+ die("merge order sort failed\n");
}
return 0;
diff --git a/t/t6001-rev-list-merge-order.sh b/t/t6001-rev-list-merge-order.sh
--- a/t/t6001-rev-list-merge-order.sh
+++ b/t/t6001-rev-list-merge-order.sh
@@ -9,7 +9,33 @@ test_description='Test rev-list --merge-
function do_commit
{
- git-commit-tree "$@" </dev/null
+ (
+ # add some entropy to commits so that the so that
+ # two closely timed commits have different sha1 hashes
+ # fall back to a slower technique if urandom, md5sum or
+ # dd don't exist
+ if [ -c /dev/urandom -a -x "$(which md5sum)" -a -x "$(which dd)" ]
+ then
+ dd if=/dev/urandom bs=1024 count=1 2>/dev/null | md5sum
+ else
+ sleep 1;
+ date
+ fi
+ ) | git-commit-tree "$@" 2>/dev/null
+}
+
+function e_commit
+{
+ export GIT_AUTHOR_EMAIL="author-e@example.com"
+ do_commit "$@"
+ export GIT_AUTHOR_EMAIL=
+}
+
+function f_commit
+{
+ export GIT_AUTHOR_EMAIL="author-f@example.com"
+ do_commit "$@"
+ export GIT_AUTHOR_EMAIL=
}
function check_adjacency
@@ -30,9 +56,10 @@ function check_adjacency
function sed_script
{
- for c in root a0 a1 a2 a3 a4 b1 b2 b3 b4 c1 c2 c3 l0 l1 l2 l3 l4 l5
+ for c in root a0 a1 a2 a3 a4 b1 b2 b3 b4 c1 c2 c3 l0 l1 l2 l3 l4 l5 e1 e2 e3 e4 e5 e6 e7 e8 e9 e10 f1 f2 f3 f4 f5 f6 f7 f8 f9 f10
+
do
- echo -n "s/${!c}/$c/;"
+ echo -n "s/${!c}/$c/g;"
done
}
@@ -40,17 +67,16 @@ date >path0
git-update-cache --add path0
tree=$(git-write-tree)
root=$(do_commit $tree 2>/dev/null)
-export GIT_COMMITTER_NAME=foobar # to guarantee that the commit is different
l0=$(do_commit $tree -p $root)
l1=$(do_commit $tree -p $l0)
l2=$(do_commit $tree -p $l1)
a0=$(do_commit $tree -p $l2)
a1=$(do_commit $tree -p $a0)
-export GIT_COMMITTER_NAME=foobar2 # to guarantee that the commit is different
b1=$(do_commit $tree -p $a0)
c1=$(do_commit $tree -p $b1)
-export GIT_COMMITTER_NAME=foobar3 # to guarantee that the commit is different
+export GIT_AUTHOR_EMAIL=foobar@example.com
b2=$(do_commit $tree -p $b1)
+export GIT_AUTHOR_EMAIL=
b3=$(do_commit $tree -p $b2)
c2=$(do_commit $tree -p $c1 -p $b2)
c3=$(do_commit $tree -p $c2)
@@ -63,6 +89,32 @@ l4=$(do_commit $tree -p $l3)
l5=$(do_commit $tree -p $l4)
echo $l5 > .git/HEAD
+e1=$(e_commit $tree 2>/dev/null)
+e2=$(e_commit $tree -p $e1)
+f1=$(f_commit $tree -p $e1)
+e3=$(e_commit $tree -p $e2)
+f2=$(f_commit $tree -p $f1)
+e4=$(e_commit $tree -p $e3 -p $f2)
+e5=$(e_commit $tree -p $e4)
+f3=$(f_commit $tree -p $f2)
+f4=$(f_commit $tree -p $f3)
+e6=$(e_commit $tree -p $e5 -p $f4)
+f5=$(f_commit $tree -p $f4)
+f6=$(f_commit $tree -p $f5 -p $e6)
+e7=$(e_commit $tree -p $e6)
+e8=$(e_commit $tree -p $e7)
+e9=$(e_commit $tree -p $e8)
+f7=$(f_commit $tree -p $f6)
+f8=$(f_commit $tree -p $f7)
+f9=$(f_commit $tree -p $f8)
+e10=$(e_commit $tree -p $e9 -p $f8)
+
+
+#> e1 -- e2 -- e3 -- e4 -- e5 -- e6 -- e7 -- e8 -- e9 -- e10
+#> \ / / \ /
+#> - f1 -- f2 -- f3 -- f4 -- f5 -- f6 -- f7 -- f8 -- f9
+
+
git-rev-list --merge-order --show-breaks HEAD | sed "$(sed_script)" > actual-merge-order
cat > expected-merge-order <<EOF
= l5
@@ -86,6 +138,15 @@ cat > expected-merge-order <<EOF
= root
EOF
+
+#
+# cd to t/trash and use
+#
+# git-rev-list ... 2>&1 | sed "$(cat sed.script)"
+#
+# if you ever want to manually debug the operation of git-rev-list
+#
+sed_script > sed.script
git-rev-list HEAD | check_adjacency | sed "$(sed_script)" > actual-default-order
normal_adjacency_count=$(git-rev-list HEAD | check_adjacency | grep -c "\^" | tr -d ' ')
merge_order_adjacency_count=$(git-rev-list --merge-order HEAD | check_adjacency | grep -c "\^" | tr -d ' ')
@@ -172,4 +233,125 @@ test_expect_success 'Testing duplicated
test_expect_success 'Testing exclusion near merge' 'git-rev-list --merge-order $a4 ^$c3 2>/dev/null'
+cat > expected-merge-order-6 <<EOF
+= l5
+| l4
+| l3
+= a4
+| c3
+| c2
+| c1
+^ b4
+| a3
+| a2
+| a1
+^ b3
+| b2
+| b1
+= a0
+| l2
+| l1
+| l0
+= root
+EOF
+
+git-rev-list --merge-order --show-breaks --wrt-author --author=foobar@example.com HEAD 2>/dev/null | sed "$(sed_script)" > actual-merge-order-6
+test_expect_success 'Testing --wrt-author --author=foobar@example.com' 'diff expected-merge-order-6 actual-merge-order-6'
+
+cat > expected-merge-order-7 <<EOF
+= l5
+| l4
+| l3
+= a4
+| c3
+| c2
+| c1
+^ b4
+| a3
+| a2
+| a1
+^ b3
+EOF
+
+git-rev-list --show-breaks --wrt-author --author=foobar@example.com --prune-at-author HEAD | sed "$(sed_script)" > actual-merge-order-7
+test_expect_success 'Testing --prune-at-author' 'diff expected-merge-order-7 actual-merge-order-7'
+
+cat > expected-wrt-e <<EOF
+| f9
+^ e10
+| f8
+| f7
+| f6
+| f5
+^ e9
+| e8
+| e7
+| e6
+| f4
+| f3
+^ e5
+| e4
+| f2
+| f1
+^ e3
+| e2
+= e1
+EOF
+
+git-rev-list --show-breaks --wrt-author --author=author-e@example.com $e10 $f9 | sed "$(sed_script)" > actual-wrt-e
+test_expect_success 'Testing --wrt-author --author=author-e' 'diff expected-wrt-e actual-wrt-e'
+
+cat > expected-wrt-f <<EOF
+| f9
+^ e10
+| f8
+| f7
+| f6
+| f5
+^ e9
+| e8
+| e7
+| e6
+| f4
+| f3
+^ e5
+| e4
+| e3
+| e2
+^ f2
+| f1
+= e1
+EOF
+
+cat > expected-wrt-nobody <<EOF
+| e10
+| e9
+| e8
+| e7
+^ f9
+| f8
+| f7
+| f6
+| e6
+| e5
+| e4
+| e3
+| e2
+^ f5
+| f4
+| f3
+| f2
+| f1
+= e1
+EOF
+
+git-rev-list --show-breaks --wrt-author --author=author-f@example.com $e10 $f9 | sed "$(sed_script)" > actual-wrt-f
+test_expect_success 'Testing --wrt-author --author=author-f' 'diff expected-wrt-f actual-wrt-f'
+
+git-rev-list --show-breaks $f9 $e10 | sed "$(sed_script)" > actual-wrt-nobody
+test_expect_success 'Testing --wrt-author' 'diff expected-wrt-nobody actual-wrt-nobody'
+
+#
+#
+#
test_done
diff --git a/user.c b/user.c
new file mode 100644
--- /dev/null
+++ b/user.c
@@ -0,0 +1,33 @@
+#include "cache.h"
+#include "user.h"
+#include <string.h>
+#include <pwd.h>
+
+void get_real_identity(char **email, char **gecos)
+{
+ static char buffer[1000];
+ struct passwd *pw;
+ int len;
+
+ pw = getpwuid(getuid());
+ if (!pw)
+ die("You don't exist. Go away!");
+
+ len = strlen(pw->pw_name);
+ memcpy(buffer, pw->pw_name, len);
+ buffer[len] = '@';
+ gethostname(buffer + len + 1, sizeof(buffer) - len - 1);
+ if (!strchr(buffer + len + 1, '.')) {
+ strcat(buffer, ".");
+ getdomainname(buffer + strlen(buffer),
+ sizeof(buffer) - strlen(buffer) - 1);
+ }
+
+ if (gecos) {
+ *gecos = strdup(pw->pw_gecos);
+ }
+
+ if (email) {
+ *email = strdup(buffer);
+ }
+}
diff --git a/user.h b/user.h
new file mode 100644
--- /dev/null
+++ b/user.h
@@ -0,0 +1,8 @@
+#ifndef USER_H
+#define USER_H
+/*
+ * Allocates two new strings to contain the real email and
+ * name of the current user.
+ */
+extern void get_real_identity(char **email, char **gecos);
+#endif
^ permalink raw reply
* [ANNOUNCE] Cogito-0.11.3
From: Petr Baudis @ 2005-06-09 9:19 UTC (permalink / raw)
To: git
Hello,
it turned out that Cogito was so broken w.r.t. three-way merging that
I had to really release a bugfix version. So this version contains some
cleanups of the merge script, some more portability fixes (it actually
runs on Debian old stable now!), and especially few bugfixes - cg-log
works with individual files passed to it now, and merging should
hopefully work correctly now too.
You know what to do. :-)
Junio C Hamano:
Add read-tree -m 3-way merge tests.
Linus Torvalds:
One more time.. Clean up git-merge-one-file-script
Fix up git-merge-one-file-script
Merge my and Petr's git-merge-one-file-script modifications
Make sure we error out if we can't remove a file on automatic merges.
Petr Baudis:
cogito-0.11.3
Fix cg-merge's three-way content merge
Support for compilation w/o OpenSSL
Portable stub for the stat call
Fix cg-diff -p to work with no -r specified
git-merge-one-file-script cleanups from Cogito
Further converge git-merge-one-file-script and cg-Xmergefile
Remove useless ret=0 in the onefile merge scripts
Make git-merge-one-file-script and cg-Xmergefile converge even more
Fix cg-log called on specified files
Tidy up some rev-list-related stuff
Fix git-merge-one-file permissions auto-merging
--
Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
<Espy> be careful, some twit might quote you out of context..
^ permalink raw reply
* Re: [PATCH] Add support for author-oriented git-rev-list switches [rev 6]
From: Jon Seymour @ 2005-06-09 9:37 UTC (permalink / raw)
To: git; +Cc: jon.seymour
In-Reply-To: <20050609090141.21555.qmail@blackcubes.dyndns.org>
A minor edit with some more whitespace and brace cleanups is available here.
http://blackcubes.dyndns.org/epoch/wrt-patch-rev-7.patch
Linus: once your or someone else ok's the patch, you may pull the
revised patch from there.
jon.
^ permalink raw reply
* Re: [ANNOUNCE] Cogito-0.11.3
From: Konstantin Antselovich @ 2005-06-09 10:10 UTC (permalink / raw)
To: Petr Baudis; +Cc: git
In-Reply-To: <20050609091909.GF29665@pasky.ji.cz>
Petr Baudis wrote:
> Hello,
>
> it turned out that Cogito was so broken w.r.t. three-way merging that
> I had to really release a bugfix version. So this version contains some
> cleanups of the merge script, some more portability fixes (it actually
> runs on Debian old stable now!), and especially few bugfixes - cg-log
> works with individual files passed to it now, and merging should
> hopefully work correctly now too.
>
> You know what to do. :-)
Hi Petr,
I have updated to Cogito-0.11.3, it compiles and runs
but make test returns multiple error messages (see below)
Rgds,
Konstantin
*** t6001-rev-list-merge-order.sh ***
fatal: merge order sort unsupported, OpenSSL not linked
fatal: merge order sort unsupported, OpenSSL not linked
* ok 1: Testing that the rev-list has correct number of entries
* FAIL 2: Testing that --merge-order produces the correct result diff
expected-merge-order actual-merge-order
* ok 3: Testing that --merge-order produces as many or fewer
discontinuities
fatal: merge order sort unsupported, OpenSSL not linked
* FAIL 4: Testing multiple heads diff expected-merge-order-1
actual-merge-order-1
fatal: merge order sort unsupported, OpenSSL not linked
* FAIL 5: Testing stop diff expected-merge-order-2 actual-merge-order-2
fatal: merge order sort unsupported, OpenSSL not linked
* FAIL 6: Testing stop in linear epoch diff expected-merge-order-3
actual-merge-order-3
fatal: merge order sort unsupported, OpenSSL not linked
* FAIL 7: Testing start in linear epoch, stop after non-linear epoch
diff expected-merge-order-4 actual-merge-order-4
* FAIL 8: Testing duplicated start arguments diff expected-merge-order-4
actual-merge-order-5
* FAIL 9: Testing exclusion near merge git-rev-list --merge-order $a4
^$c3 2>/dev/null
* failed 7 among 9 test(s)
make[1]: *** [all] Error 1
make[1]: Leaving directory `/home/konstantin/git/cogito/t'
make: *** [test] Error 2
>
--
Konstantin Antselovich
mailto: konstantin@antselovich.com
http://konstantin.antselovich.com
^ permalink raw reply
* Re: Upstream merging and conflicts (was Re: Using cvs2git to track an external CVS project)
From: Martin Langhoff @ 2005-06-09 11:03 UTC (permalink / raw)
To: Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0506081918050.2286@ppc970.osdl.org>
On 6/9/05, Linus Torvalds <torvalds@osdl.org> wrote:
> Well, it's done, and it's now "much easier" in the sense that anything
> that doesn't have metadata changes should be picked up pretty trivially by
> the three-way merge thing.
>
> But if you move things around, then you'd need to have a merge that is
> aware of movement, ie something much more sophisticated than just 3way
> merge.
I've followed the discussion and it's really good. I'll be playing
with the code on a cvs2git import I'm doing.
Two questions:
- Is there a way to ask "what is the patchlog that you'll merge if I
ask you to merge from X"?
- When an "automatic" merge happens, is there anything that
identifies commit with the commit that is being merged if the trees
are not identical? Is there a way to do that
What I am thinking of doing is having a perl (or bash?) script that
looks at the changelog of two branches since they last converged,
looks at each commit and makes an educated guess of what patches are
in both and /facilitates/ extracting a patch the remote branch and
applying it locally with the same commit msg.
There are no promises on the guess -- it has to be reviewed & checked
-- but I find that is always true when trading patches across
branches. A lightweight, best-effort script to help someone who is
going to backport some patches from HEAD to the stable branch.
> > > It's true that you can't skip patches, since a merge is always
> > > all-or-nothing, and the global history really requires that (in a very
> > > fundamental way). However, you _can_ merge from the HEAD, and then say
> > > "that patch is no longer relevant in this tree", and remove it. Then
> > > you'll never need to worry about that patch any more, because you've
> > > merged it and in your merged result, it no longer exists).
> >
> > I had that strategy in my back-pocket already, but it doesn't sound right.
>
> With global history, you really don't end up having much choice.
>
> The alternative is to have per-file history, which sucks pretty bad
I don't quite follow you on why the per-file or per-tree worldview
affects this. Not very important though ;)
> My gut feel is that it should be possible to have a hybrid system that
> handles both the solid "crystalline" phase (aka git) and the "gas" phase
> (aka free-lowing patches) and have them integrate with each other well.
> That's kind of the way the kernel works, with people using quilt as a way
> to capture the patches in between.
Absolutement. For most projects I suspect that the gas (fluid?) phase
can be quite simple. In fact, simple is better.
> My read is that this analogy arch and darcs try to avoid the really solid
> crystalline phase entirely and end up being amorphous. You can probably
> have that too, but on the other hand it's fairly easy to merge between two
> "crystallized" repositories and be totally unambigious about what the
> result is, but if there's a lot of the amorpous stuff going on, it's not
> clear any more.
After a while, and a few thousand patches since you "branched",
patch-based SCMs don't help converge. At no point can they recognized
that 2 trees have converged and that the patch track becomes
irrelevant.
> > > Whether the kernel model is applicable to anything else, I dunno.
> >
> > I don't know either -- but I'm sure the toolset around git can support
> > a range of dev models. I don't think any other project has such a
> > large pool and strong convergence dynamics as the kernel. But git and
> > its tools and practices can be (I'm hoping) quite flexible to support
> > a range of dev models.
>
> Hey, I obviously think you're right. Using git gives good ways of
> communicating the core infrastructure between two (or more) groups, while
> then internally within the group they may use loser patch-tracking systems
> that don't have the same kind of convergence requirements (but which you
> don't need for a "small" set of patches anyway, where "small" can
> obviously be hundreds of internal patches).
Interesting concept -- git as the head branch where everyone converges
while perhaps using other tools. Still... looser patch tracking
strategies can be based on git primitives, like some kind of commit
identity that travels with the merges, perhaps not to be used by git
but as a hooking point for naive patch tracking. But I suspect it may
be anathema to the git philosophy ;-)
cheers,
martin
^ permalink raw reply
* Re: [ANNOUNCE] Cogito-0.11.2
From: Dan Holmsand @ 2005-06-09 10:45 UTC (permalink / raw)
To: git; +Cc: Petr Baudis
In-Reply-To: <20050609083532.GE29665@pasky.ji.cz>
Petr Baudis wrote:
> Dear diary, on Thu, Jun 09, 2005 at 01:35:03AM CEST, I got a letter
> where Dan Holmsand <holmsand@gmail.com> told me that...
>
>>Well, since I try to use the same output format as git-rev-list --pretty
>>does (most of the time, it *is* just that...), I can actually already do
>>
>> cg-log -r:cogito-0.11.1 | git-shortlog
>>
>>and get the same thing.
>
>
> I'll see how I like it, but currently I find myself use all of the
> commit header items a lot, so I'm not too keen to losing some of them,
> perhaps except the tree line. Ok, and it might be enough to have the
> parent lines only for the merge commits. Nothing against things like
> s/^author/Author:/.
>
Oh, all the headers are still available, for sure, just not by default.
"-v" or "--verbose" is the magic incantation.
The parent is also somewhat visible by default whenever it's not obvious
who the parent is, as I keep git-diff-tree's "(from sha1)" thing.
/dan
^ permalink raw reply
* [PATCH 0/6] Bunch of new features for cg-log and cg-diff
From: Dan Holmsand @ 2005-06-09 11:03 UTC (permalink / raw)
To: git; +Cc: Petr Baudis
This series adds optget-style option parsing, support for almost all
git-diff-* features, git-apply --stat support, common colorization code,
better performance for cg-log and some other stuff.
/dan
^ permalink raw reply
* [PATCH 2/6] Factor out color settings to cg-Xlib
From: Dan Holmsand @ 2005-06-09 11:17 UTC (permalink / raw)
To: git; +Cc: Petr Baudis
[-- Attachment #1: Type: text/plain, Size: 476 bytes --]
[PATCH 2/6] Factor out color settings to cg-Xlib
setup_colors sets color variables from COGITO_COLORS, and
adds a sed script for diff colorization to color_rules.
Note that this changes the default colors a little from the
ones in cg-diff: in particular there's no bold any more. Bold
isn't really necessary if the "less search" thing in the new
cg-diff/cg-log is used (and bold is really ugly in
gnome-terminal :-) ).
Signed-off-by: Dan Holmsand <holmsand@gmail.com>
---
[-- Attachment #2: 2-setupcolors.patch.txt --]
[-- Type: text/plain, Size: 1052 bytes --]
cg-Xlib | 23 +++++++++++++++++++++++
1 files changed, 23 insertions(+), 0 deletions(-)
diff --git a/cg-Xlib b/cg-Xlib
--- a/cg-Xlib
+++ b/cg-Xlib
@@ -136,6 +136,29 @@ print_help () {
exit
}
+setup_colors() {
+ local C="header=32:author=36:signoff=33:committer=35:files=34"
+ C="$C:commit=34:date=32:trim=35"
+ C="$C:diffhdr=34:diffhdradd=32:diffadd=32:diffhdrmod=35"
+ C="$C:diffmod=35:diffhdrrem=31:diffrem=31:diffhunk=36"
+ C="$C:diffctx=34:diffcctx=33:default=0"
+ [ -n "$COGITO_COLORS" ] && C="$C:$COGITO_COLORS"
+
+ C=${C//=/=\'$'\e'[}
+ C=col${C//:/m\'; col}m\'
+ #coldefault=$(tput op)
+ eval $C
+
+ color_rules="
+s,^+++.*,$coldiffhdradd&$coldefault,
+s,^---.*,$coldiffhdrrem&$coldefault,
+s,^[+].*,$coldiffadd&$coldefault,
+s,^[-].*,$coldiffrem&$coldefault,
+s,^\\(@@.*@@\\)\\(.*\\),$coldiffhunk\\1$coldiffctx\\2$coldefault,
+s,^=*$',$coldiffhdr&$coldefault,
+s,^\\(Index:\\|===\\|diff\\) .*,$coldiffhdr&$coldefault,"
+}
+
for option in "$@"; do
if [ "$option" = "-h" -o "$option" = "--help" ]; then
print_help ${_cg_cmd##cg-}
^ permalink raw reply
* [PATCH 1/6] Add infrastructure for option parsing to cg-Xlib
From: Dan Holmsand @ 2005-06-09 11:15 UTC (permalink / raw)
To: git; +Cc: Petr Baudis
[-- Attachment #1: Type: text/plain, Size: 837 bytes --]
Adds a new function, optparse, that handles combined options
just like optget (cg-log -cf, for example).
When called without any argument, it returns 0 if there is any
option to parse.
When called as "optparse -f" or "optparse --foo" it returns 0
if the current option in $ARGS (at $ARGPOS) matches.
When called as "optparse -f= or "optparse --foo=" it requires
an argument.
For long options, it takes an optional second argument, specifying
the number of characters in the option name that has to be present
(defaults to 1). So "optparse --foo 2" will match "--fo", but not
"--f".
Just as with optget, options can be given after arguments as
well, as in "cg-log Documentation -rorigin". Option parsing
stops after "--".
optparse leaves unparsed arguments in the $ARGS array.
Signed-off-by: Dan Holmsand <holmsand@gmail.com>
---
[-- Attachment #2: 1-optparse.patch.txt --]
[-- Type: text/plain, Size: 1740 bytes --]
cg-Xlib | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 49 insertions(+), 0 deletions(-)
diff --git a/cg-Xlib b/cg-Xlib
--- a/cg-Xlib
+++ b/cg-Xlib
@@ -137,11 +137,60 @@ print_help () {
}
for option in "$@"; do
+ [ "$option" = -- ] && break
if [ "$option" = "-h" -o "$option" = "--help" ]; then
print_help ${_cg_cmd##cg-}
fi
done
+ARGS=("$@")
+ARGPOS=0
+
+optshift() {
+ unset ARGS[$ARGPOS]
+ ARGS=("${ARGS[@]}")
+ [ -z "$1" -o -n "${ARGS[$ARGPOS]}" ] ||
+ die "option \`$1' requires an argument"
+}
+
+optfail() {
+ die "unrecognized option \`${ARGS[$ARGPOS]}'"
+}
+
+optconflict() {
+ die "conflicting option \`$CUROPT'"
+}
+
+optparse() {
+ unset OPTARG
+ [ -z "$1" ] && case ${ARGS[$ARGPOS]} in
+ --) optshift; return 1 ;;
+ -*) return 0 ;;
+ *) while (( ${#ARGS[@]} > ++ARGPOS )); do
+ [[ "${ARGS[$ARGPOS]}" == -- ]] && return 1
+ [[ "${ARGS[$ARGPOS]}" == -* ]] && return 0
+ done; return 1 ;;
+ esac
+
+ CUROPT=${ARGS[$ARGPOS]}
+ local match=${1%=} minmatch=${2:-1} opt=$CUROPT o=$CUROPT val
+ [[ $1 == *= ]] && val=$match
+ case $match in
+ --*) [ "$val" ] && o=${o%%=*}
+ [ ${#o} -ge $((2 + $minmatch)) -a \
+ "${match:0:${#o}}" = "$o" ] || return 1
+ [[ -n "$val" && "$opt" == *=?* ]] && ARGS[$ARGPOS]=${opt#*=} ||
+ optshift $val ;;
+ -?) [[ $o == $match* ]] || return 1
+ [[ $o != -?-* || -n "$val" ]] || optfail
+ ARGS[$ARGPOS]=${o#$match}
+ [ "${ARGS[$ARGPOS]}" ] &&
+ { [ "$val" ] || ARGS[$ARGPOS]=-${ARGS[$ARGPOS]}; } ||
+ optshift $val ;;
+ *) die "optparse cannot handle $1" ;;
+ esac
+ [ -z "$val" ] || { OPTARG=${ARGS[$ARGPOS]}; optshift; }
+}
# Check if we have something to work on, unless the script can do w/o it.
if [ ! "$_git_repo_unneeded" ]; then
^ permalink raw reply
* [PATCH 4/6] Only invoke pager if there's any output
From: Dan Holmsand @ 2005-06-09 11:21 UTC (permalink / raw)
To: git; +Cc: Petr Baudis
[-- Attachment #1: Type: text/plain, Size: 144 bytes --]
This avoids annoying empty "less" window if there's no output
from cg-diff, for example.
Signed-off-by: Dan Holmsand <holmsand@gmail.com>
---
[-- Attachment #2: 4-pager.patch.txt --]
[-- Type: text/plain, Size: 382 bytes --]
cg-Xlib | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/cg-Xlib b/cg-Xlib
--- a/cg-Xlib
+++ b/cg-Xlib
@@ -22,7 +22,11 @@ usage() {
}
pager () {
- LESS="R$LESS" ${PAGER:-less} $PAGER_FLAGS
+ # Invoke $PAGER if there is any output
+ local line
+ if read -r line; then
+ ( echo "$line"; cat ) | LESS="-R $LESS" ${PAGER:-less}
+ fi
}
mktemp () {
^ permalink raw reply
* [PATCH 3/6] Make showdate use "Linus format"
From: Dan Holmsand @ 2005-06-09 11:19 UTC (permalink / raw)
To: git; +Cc: Petr Baudis
[-- Attachment #1: Type: text/plain, Size: 433 bytes --]
This makes showdate use the same date format as
git-rev-list --pretty, and gives some speedup. It might also
be more portable.
Note that this changes the calling convention: the previous
version used seconds from $1, but timezone from the global
variable $date. cg-mkpatch is modified to the new way.
Also fixes bash's belief that number literals starting with
zero are octal.
Signed-off-by: Dan Holmsand <holmsand@gmail.com>
---
[-- Attachment #2: 3-showdate.patch.txt --]
[-- Type: text/plain, Size: 1421 bytes --]
cg-Xlib | 18 ++++++++----------
cg-mkpatch | 2 +-
2 files changed, 9 insertions(+), 11 deletions(-)
diff --git a/cg-Xlib b/cg-Xlib
--- a/cg-Xlib
+++ b/cg-Xlib
@@ -60,19 +60,17 @@ stat () {
}
showdate () {
- date="$1"
- format="$2"
- [ "$format" ] || format=-R
- sec=${date[0]}; tz=${date[1]}
+ local secs=$1 tzhours=${2:0:3} tzmins=${2:0:1}${2:3} format=$3
+ # bash doesn't like leading zeros
+ [ "${tzhours:1:1}" = 0 ] && tzhours=${2:0:1}${2:2:1}
+ secs=$((secs + tzhours * 3600 + tzmins * 60))
if [ "$has_gnudate" ]; then
- dtz=${tz/+/}
- lsec=$(($dtz / 100 * 3600 + $dtz % 100 * 60 + $sec))
- pdate="$(date -ud "1970-01-01 UTC + $lsec sec" "$format" 2>/dev/null)"
+ [ "$format" ] || format="+%a %b %-d %H:%M:%S %Y $2"
+ LANG=C date -ud "1970-01-01 UTC + $secs sec" "$format"
else
- # FIXME: $format
- pdate="$(date -u -r ${date[0]} 2>/dev/null)"
+ [ "$format" ] || format="+%a %b %d %H:%M:%S %Y $2"
+ date -u -r $secs "$format"
fi
- echo "${pdate/+0000/$tz}"
}
# Usage: tree_timewarp [--no-head-update] DIRECTION_STR ROLLBACK_BOOL BASE BRANCH
diff --git a/cg-mkpatch b/cg-mkpatch
--- a/cg-mkpatch
+++ b/cg-mkpatch
@@ -52,7 +52,7 @@ showpatch () {
case "$key" in
"author"|"committer")
date=(${rest#*> })
- pdate="$(showdate $date)"
+ pdate="$(showdate ${date[*]})"
[ "$pdate" ] && rest="${rest%> *}> $pdate"
echo $key $rest >>$header
;;
^ permalink raw reply
* [PATCH 5/6] Make cg-diff use optparse, and add features
From: Dan Holmsand @ 2005-06-09 11:24 UTC (permalink / raw)
To: git; +Cc: Petr Baudis
[-- Attachment #1: Type: text/plain, Size: 787 bytes --]
This adds some new features to cg-diff:
- diffstat (or rather git-apply --stat) support. The
"-d" option outputs (optionally colorized) diff stats
before the diff.
- support for more git-diff-[tree/cache] options:
-B, -R, -M, -C are now all passed on.
- The COGITO_AUTO_COLOR environment variable makes output
automatically colorized, if set and if we're on a color
capable terminal.
We also use the new optparse function from cg-Xlib, to allow
for e.g. "cg-diff -drorigin" and stuff.
Reuse colorization logic from cg-Xlib.
And use LESS to make "less" search for chunks and diff --git
markers. This allows you to "n" your way through a series
of diffs, and gives a nice visual separation of patches.
Signed-off-by: Dan Holmsand <holmsand@gmail.com>
---
[-- Attachment #2: 5-cg-diff.patch.txt --]
[-- Type: text/plain, Size: 6694 bytes --]
cg-diff | 222 +++++++++++++++++++++++++++++----------------------------------
1 files changed, 102 insertions(+), 120 deletions(-)
diff --git a/cg-diff b/cg-diff
--- a/cg-diff
+++ b/cg-diff
@@ -5,13 +5,16 @@
#
# Outputs a diff for converting the first tree to the second one.
# By default compares the current working tree to the state at the
-# last commit. The output will automatically be displayed in a pager
-# unless it is piped to a program.
+# last commit.
#
# OPTIONS
# -------
-# -c::
-# Colorize the diff output
+# -c, --color::
+# Colorize the diff output and use a pager for output (less by
+# default).
+#
+# -d, --diffstat::
+# Show `diffstat' before diff.
#
# -p::
# Instead of one ID denotes a parent commit to the specified ID
@@ -24,153 +27,132 @@
# empty revision which means '-r rev:' compares between 'rev' and
# 'HEAD', while '-r rev' compares between 'rev' and working tree.
#
+# -R::
+# Output diff in reverse.
+#
+# -M::
+# Detect renames.
+#
+# -C::
+# Detect copies (as well as renames).
+#
+# -B::
+# Detect rewrites.
+#
# -m::
# Base the diff at the merge base of the -r arguments (defaulting
-# to master and origin).
+# to HEAD and origin).
#
# ENVIRONMENT VARIABLES
# ---------------------
# PAGER::
# The pager to display log information in, defaults to `less`.
#
-# PAGER_FLAGS::
-# Flags to pass to the pager. By default `R` is added to the `LESS`
-# environment variable to allow displaying of colorized output.
+# COGITO_AUTO_COLOR::
+# If set, colorized output is used automatically on color-capable
+# terminals.
USAGE="cg-diff [-c] [-m] [-p] [-r FROM_ID[:TO_ID]] [FILE]..."
. ${COGITO_LIB}cg-Xlib
-id1=" "
-id2=" "
-parent=
-opt_color=
-mergebase=
-
-# TODO: Make cg-log use this too.
-setup_colors()
-{
- local C="diffhdr=1;36:diffhdradd=1;32:diffadd=32:diffhdrmod=1;35:diffmod=35:diffhdrrem=1;31:diffrem=31:diffhunk=36:diffctx=34:diffcctx=33:default=0"
- [ -n "$COGITO_COLORS" ] && C="$C:$COGITO_COLORS"
-
- C=${C//=/=\'$'\e'[}
- C=col${C//:/m\'; col}m\'
- #coldefault=$(tput op)
- eval $C
+unset id1 id2 parent diffprog sedprog diffstat difftmp opt_color renames
+dtargs=()
+
+show_diffstat() {
+ [ -s "$difftmp" ] || return
+ git-apply --stat "$difftmp"
+ echo
+ cat "$difftmp"
}
-while [ "$1" ]; do
- case "$1" in
- -c)
- opt_color=1
- setup_colors
- ;;
- -p)
+while optparse; do
+ if optparse -p; then
parent=1
- ;;
- -r)
- shift
- if echo "$1" | grep -q ':'; then
- id2=$(echo "$1" | cut -d : -f 2)
- [ "$id2" ] || log_end="HEAD"
- id1=$(echo "$1" | cut -d : -f 1)
- elif [ "$id1" = " " ]; then
- id1="$1"
+ elif optparse -m; then
+ incoming=1
+ elif optparse -r=; then
+ if [ -z "${id1+set}" ]; then
+ id1=$OPTARG
+ if [[ "$id1" == *:* ]]; then
+ id2=${id1#*:}
+ id1=${id1%:*}
+ fi
else
- id2="$1"
+ [ -z "${id2+set}" ] || die "too many revisions"
+ id2=$OPTARG
fi
- ;;
- -m)
- mergebase=1
- ;;
- *)
- break
- ;;
- esac
- shift
-done
-
-colorize() {
- if [ "$opt_color" ]; then
- gawk '
- { if (/^(Index:|diff --git) /)
- print "'$coldiffhdr'" $0 "'$coldefault'"
- else if (/^======*$/)
- print "'$coldiffhdr'" $0 "'$coldefault'"
- else if (/^\+\+\+/)
- print "'$coldiffhdradd'" $0 "'$coldefault'"
- else if (/^\*\*\*/)
- print "'$coldiffhdrmod'" $0 "'$coldefault'"
- else if (/^---/)
- print "'$coldiffhdrrem'" $0 "'$coldefault'"
- else if (/^(\+|new( file)? mode )/)
- print "'$coldiffadd'" $0 "'$coldefault'"
- else if (/^(-|(deleted file|old) mode )/)
- print "'$coldiffrem'" $0 "'$coldefault'"
- else if (/^!/)
- print "'$coldiffmod'" $0 "'$coldefault'"
- else if (/^@@ \-[0-9]+(,[0-9]+)? \+[0-9]+(,[0-9]+)? @@/)
- print gensub(/^(@@[^@]*@@)([ \t]*)(.*)/,
- "'$coldiffhunk'" "\\1" "'$coldefault'" \
- "\\2" \
- "'$coldiffctx'" "\\3" "'$coldefault'", "")
- else if (/^\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/)
- print "'$coldiffcctx'" $0 "'$coldefault'"
- else
- print
- }'
+ elif optparse -c; then
+ opt_color=1
+ elif optparse -d || optparse --diffstat; then
+ diffstat=1
+ elif optparse -R; then
+ dtargs[${#dtargs[@]}]="-R"
+ elif optparse -M; then
+ [ "$renames" ] && optconflict
+ renames=1
+ dtargs[${#dtargs[@]}]="-M"
+ elif optparse -C; then
+ [ "$renames" ] && optconflict
+ renames=1
+ dtargs[${#dtargs[@]}]="-C"
+ elif optparse -B; then
+ dtargs[${#dtargs[@]}]="-B"
else
- cat
+ optfail
fi
-}
+done
-if [ "$parent" ]; then
- id2="$id1"
- id="$id2"; [ "$id" = " " ] && id=""
- id1=$(parent-id "$id" | head -n 1) || exit 1
-fi
+[ -n "$COGITO_AUTO_COLOR" -a -t 1 ] && [ "$(tput setaf 1 2>/dev/null)" ] &&
+opt_color=1
-if [ "$mergebase" ]; then
- [ "$id1" != " " ] || id1="master"
- [ "$id2" != " " ] || id2="origin"
- id1=$(git-merge-base $(commit-id "$id1") $(commit-id "$id2"))
-fi
+LESS=$'+/\013^@@.*@@|^diff.--git..*$'" $LESS"
+diffprog=git-diff-tree
-filter=$(mktemp -t gitdiff.XXXXXX)
-for file in "$@"; do
- echo "$file" >>$filter
-done
+if [ "$parent" ]; then
+ [ -z "${id2+set}" ] || die "too many revisions"
+ id2="$id1"
+ id1=$(parent-id "$id2" | head -n 1) || exit 1
+elif [ "$incoming" ]; then
+ tmp=$id1
+ id1="$(commit-id "${id2:-HEAD}")" || exit 1
+ id2="$(commit-id "${tmp:-origin}")" || exit 1
+ id1="$(git-merge-base "$id1" "$id2")" || exit 1
+fi
-if [ "$id2" = " " ]; then
- if [ "$id1" != " " ]; then
- tree=$(tree-id "$id1") || exit 1
- else
- tree=$(tree-id) || exit 1
- fi
+id1=$(tree-id "$id1") || exit 1
+if [ -z "${id2+set}" ]; then
# Ensure to only diff modified files
git-update-cache --refresh >/dev/null
-
- # FIXME: Update ret based on what did we match. And take "$@"
- # to account after all.
- ret=
- cat $filter | xargs git-diff-cache -r -p $tree | colorize | pager
-
- rm $filter
-
- [ "$ret" ] && die "no files matched"
- exit $ret
+ diffprog=git-diff-cache
+else
+ id2=$(tree-id "$id2") || exit 1
fi
-
-id1=$(tree-id "$id1") || exit 1
-id2=$(tree-id "$id2") || exit 1
-
[ "$id1" = "$id2" ] && die "trying to diff $id1 against itself"
+diffopts=(-r -p "${dtargs[@]}" $id1 $id2 "${ARGS[@]}")
+
+if [ "$diffstat" ]; then
+ difftmp=$(mktemp -t cgdiff.XXXXXX) || exit 1
+ trap "rm '$difftmp'" SIGTERM EXIT
+ $diffprog "${diffopts[@]}" > $difftmp
-cat $filter | xargs git-diff-tree -r -p $id1 $id2 | colorize | pager
+ diffprog=show_diffstat
+ diffopts=
+fi
-rm $filter
-exit 0
+if [ "$opt_color" ]; then
+ setup_colors
+ sedprog="$color_rules"
+
+ [ "$diffstat" ] && sedprog="$sedprog
+s,^\\( [^ ].*\\)\\( | *[0-9][0-9]* \\),$colfiles\\1$coldefault\\2,"
+
+ $diffprog "${diffopts[@]}" | sed -e "$sedprog" | pager
+ exit $PIPESTATUS
+else
+ $diffprog "${diffopts[@]}"
+fi
^ permalink raw reply
* [PATCH 6/6] Make cg-log use optparse, and add features
From: Dan Holmsand @ 2005-06-09 11:29 UTC (permalink / raw)
To: git; +Cc: Petr Baudis
[-- Attachment #1: Type: text/plain, Size: 2839 bytes --]
This is more or less a rewrite of cg-log, that adds a bunch
of new features and gives a substantial speedup.
cg-log now lets git-rev-list and git-diff-tree do as much
of the heavy lifting as possible. When possible, cg-log
pretty much reduces to a sed script, that does some fixup
and adds colorization. Otherwise, cg-log uses
--pretty=[raw|medium|short] output, that's augmented as
necessary.
New features:
- New, human friendlier output format. cg-log now uses
the same format as git-rev-list --pretty. It also
doesn't show tree, committer and parents by default.
These are all shown with "-v or --verbose", however.
- Slightly more readable summary format. sha1s are hidden
unless -v is given. Date format is condenced to HH:MM
for the last day, and Month Day otherwise. (+year is
shown for really old stuff).
summary format can also be combined with other options,
like "-f" and "-d" (diffstat).
- "-u" (or --search=) now searches the entire commit
message. This is more flexible, as you can still do e.g.
"cg-log -u 'author Petr Baudis'". Search is now also
a lot faster, done with a single grep invocation.
- Output can be limited by date or count.
- New format for "-f". This is pretty much the same
output as cg-status gives, and allows for the
rename/copy detection things to work. New and
deleted files are optionally color coded.
The old format is still available as -F.
- Merge commits are hidden by default when displaying
differences between commits, or when displaying disjoint
sets of commits. "-a" makes them show again. So
"cg-log -caF" gives you pretty much the same output
as the old "cg-log -c -f" would.
- Shows diffs between commits with the "-p" options.
The diffs use the same colorization rules as cg-diff,
and can be skipped through with "n" in less if "-c"
is given.
- "--stdin" reads sha1s from stdin instead of from
git-rev-list, for some extra flexibility.
New features in common with cg-diff:
- diffstat support (or rather git-apply --stat). The
"-d" option outputs (optionally colorized) diff stats
before the diff.
- support for more git-diff-[tree/cache] options:
-B, -R, -M, -C are now all passed on.
- The COGITO_AUTO_COLOR environment variable makes output
automatically colorized, if set and if we're on a color
capable terminal.
We also use the new optparse function from cg-Xlib, to allow
for e.g. "cg-log -sfa" and stuff.
Reuse colorization logic from cg-Xlib. All the colors are
added in a single sed invocation.
And use LESS to make "less" search for chunks and diff --git
markers. This allows you to "n" your way through a series
of diffs, and gives a nice visual separation of patches.
Signed-off-by: Dan Holmsand <holmsand@gmail.com>
---
[-- Attachment #2: 6-cg-log.patch.txt --]
[-- Type: text/plain, Size: 16296 bytes --]
cg-log | 545 +++++++++++++++++++++++++++++++++++++++-------------------------
1 files changed, 331 insertions(+), 214 deletions(-)
diff --git a/cg-log b/cg-log
--- a/cg-log
+++ b/cg-log
@@ -3,6 +3,7 @@
# Make a log of changes in a GIT branch.
# Copyright (c) Petr Baudis, 2005.
# Copyright (c) David Woodhouse, 2005.
+# Copyright (c) Dan Holmsand, 2005.
#
# Display log information for files or a range of commits. The output
# will automatically be displayed in a pager unless it is piped to
@@ -13,7 +14,7 @@
# Arguments not interpreted as options will be interpreted as filenames;
# cg-log then displays only changes in those files.
#
-# -c::
+# -c, --color::
# Colorize to the output. The used colors are listed below together
# with information about which log output (summary, full or both)
# they apply to:
@@ -26,8 +27,15 @@
# - `date`: 'green' (summary)
# - `trim_mark`: 'magenta' (summary)
#
+# -d, --diffstat::
+# Show `diffstat' for every commit.
+#
# -f::
-# List affected files. (No effect when passed along `-s`.)
+# Show list of files modified in each commit.
+#
+# -F::
+# Show ChangeLog-style list of modified files, instead of the
+# default listing.
#
# -r FROM_ID[:TO_ID]::
# Limit the log information to a set of revisions using either
@@ -37,29 +45,61 @@
# to the initial commit is shown. If no revisions is specified,
# the log information starting from 'HEAD' will be shown.
#
-# -m::
-# End the log listing at the merge base of the -r arguments
-# (defaulting to master and origin).
-#
-# -s::
+# -s, --summary::
# Show a one line summary for each log entry. The summary contains
# information about the commit date, the author, the first line
# of the commit log and the commit ID. Long author names and commit
# IDs are trimmed and marked with an ending tilde (~).
#
-# -uUSERNAME::
-# List only commits where author or committer contains 'USERNAME'.
-# The search for 'USERNAME' is case-insensitive.
+# -p, --patches::
+# Show diffs for files modified by each commit.
+#
+# -v, --verbose::
+# Show committer, tree and parents for each commit.
+#
+# -a, --all::
+# Show merge commits as well.
+#
+# -m::
+# End the log listing at the merge base of the -r arguments
+# (defaulting to master and origin).
+#
+# -u, --search=TEXT::
+# List only commits where author, committer or commit message
+# contains 'TEXT'. The search for 'TEXT' is case-insensitive.
+#
+# -S, --match=TEXT::
+# Look for commits that introduces TEXT in a file.
+#
+# -M::
+# Detect renames.
+#
+# -C::
+# Detect copies (as well as renames).
+#
+# -B::
+# Detect rewrites.
+#
+# -y, --max-age=DATE::
+# Limit output to commits younger than DATE.
+#
+# -o, --min-age=DATE::
+# Limit output to commits older than DATE.
+#
+# --max-count=N::
+# Show at most N commits.
+#
+# --stdin::
+# Show commits for sha1s read from stdin.
#
# ENVIRONMENT VARIABLES
# ---------------------
# PAGER::
# The pager to display log information in, defaults to `less`.
#
-# PAGER_FLAGS::
-# Flags to pass to the pager. By default `R` and `S` is added to the
-# `LESS` environment variable to allow displaying of colorized output
-# and to avoid long lines from wrapping when using `-s`.
+# COGITO_AUTO_COLOR::
+# If set, colorized output is used automatically on color-capable
+# terminals.
#
# EXAMPLE USAGE
# -------------
@@ -75,228 +115,305 @@ USAGE="cg-log [-c] [-f] [-m] [-s] [-uUSE
# at least somewhere it does. Bash is broken.
trap exit SIGPIPE
-[ "$COLUMNS" ] || COLUMNS="$(tput cols)"
+# speed bash up on utf-8 locales
+LANG=C
-colheader=
-colauthor=
-colcommitter=
-colfiles=
-colsignoff=
-colcommit=
-coldate=
-coltrim=
-coldefault=
-
-list_files=
-log_start=
-log_end=
-summary=
-user=
-mergebase=
-files=()
-
-while [ "$1" ]; do
- case "$1" in
- -c)
- # See terminfo(5), "Color Handling"
- colheader="$(tput setaf 2)" # Green
- colauthor="$(tput setaf 6)" # Cyan
- colcommitter="$(tput setaf 5)" # Magenta
- colfiles="$(tput setaf 4)" # Blue
- colsignoff="$(tput setaf 3)" # Yellow
-
- colcommit="$(tput setaf 4)"
- coldate="$(tput setaf 2)"
- coltrim="$(tput setaf 5)"
-
- coldefault="$(tput op)" # Restore default
- ;;
- -f)
- list_files=1
- ;;
- -u*)
- user="${1#-u}"
- ;;
- -r)
- shift
- if echo "$1" | grep -q ':'; then
- log_end=$(echo "$1" | cut -d : -f 2)
- [ "$log_end" ] || log_end="HEAD"
- log_start=$(echo "$1" | cut -d : -f 1)
- elif [ -z "$log_start" ]; then
- log_start="$1"
- else
- log_end="$1"
- fi
- ;;
- -m)
- mergebase=1
- ;;
- -s)
- summary=1
- ;;
- *)
- files=("$@")
- break
- ;;
- esac
- shift
-done
+unset id1 id2 user verbose filelist patches sedprog incoming pretty maxn
+unset diffstat stdin opt_color renames dtmode quiet summary longsummary files
+unset needparse
+filemode=-s
+dtargs=() rlargs=()
+
+revlist() {
+ if [ "$stdin" ]; then
+ # read sha1s from stdin. remove leading stuff from
+ # git-rev-tree output.
+ sed 's/^[0-9]* \([a-f0-9]\{40\}\)/\1/'
+ elif [ "$user" ]; then
+ git-rev-list --header "${rlargs[@]}" |
+ LANG=C grep -iz "$user" | tr '\n\0' '\t\n' | cut -f1
+ else
+ git-rev-list "${rlargs[@]}" "$@"
+ fi
+}
-list_commit_files()
-{
- tree1="$1"
- tree2="$2"
- line=
- sep=" * $colfiles"
- # List all files for for the initial commit
- if [ -z $tree2 ]; then
- list_cmd="git-ls-tree $tree1"
+dolog() {
+ if [ "$dtmode" = diff-tree ]; then
+ revlist |
+ git-diff-tree --stdin -v -r $filemode "${dtargs[@]}" \
+ -- "${ARGS[@]}"
else
- list_cmd="git-diff-tree -r $tree1 $tree2"
+ revlist --pretty${pretty+=}$pretty
fi
+}
+
+showstat() {
+ git-diff-tree -r -p "${dtargs[@]}" $1 $2 -- "${ARGS[@]}" |
+ git-apply --stat 2>/dev/null
echo
- $list_cmd | cut -f 2- | while read file; do
- echo -n "$sep"
- sep=", "
- line="$line$sep$file"
- if [ ${#line} -le 74 ]; then
- echo -n "$file"
+}
+
+showfiles() {
+ local p=6 sep=" * $colfiles" IFS=$'\n' file end=":$coldefault"
+ [ "$summary" ] && { end="$coldefault"$'\n'; }
+ for i in $(git-diff-tree -m -r $1 $2); do
+ [[ "$i" == :* ]] || continue
+ i=${i##*$'\t'}
+ if (( (p += 2 + ${#i}) < 75 )); then
+ echo -n "$sep$i"
else
- line=" $file"
- echo "$coldefault"
- echo -n " $colfiles$file"
+ (( p = 6 + ${#i} ))
+ echo ",$coldefault"
+ echo -n " $colfiles$i"
fi
+ sep=", "
done
- echo "$coldefault:"
+ echo "$end"
}
-process_commit_line()
-{
- if [ "$key" = "%" ] || [ "$key" = "%$colsignoff" ]; then
- # The fast common case
- [ "$summary" ] || [ "$skip_commit" ] || echo " $rest"
- return
+toepoch() {
+ expr "$1" : "[0-9]*$" >/dev/null && echo "$1" ||
+ date -ud "$1" +%s || die "invalid date $1"
+}
+
+showsummary() {
+ local commit=$1 author=$2 date=$3 text=$4 da
+ author=${author#Author: }
+ author=${author% <*}
+ [ ${#author} -gt 14 ] && author=${author:0:13}$coltrim~
+ if [ -z "$longsummary" ]; then
+ commit=
+ elif [ "${COLUMNS:-0}" -le 90 ]; then
+ commit="$colcommit${commit:0:12}$coltrim~ "
+ else
+ commit="$colcommit$commit "
fi
- case "$key" in
- "commit")
- [ "$summary" ] || [ "$skip_commit" ] || { [ "$commit" ] && echo; }
- commit="$rest"
- parents=()
- skip_commit=
- ;;
- "tree")
- tree="$rest"
- ;;
- "parent")
- parents[${#parents[@]}]="$rest"
- ;;
- "committer")
- committer="$rest"
- ;;
- "author")
- author="$rest"
- ;;
- "")
- if [ ! "$commit" ]; then
- # Next commit is coming
- [ "$summary" ] || echo
- return
- fi
+ # find suitable short date format, assumes date in "std linus format"
+ da=(${date#Date:})
+ local dyear=${da[4]}
+ if [ "$year" -ne "$dyear" ]; then
+ # year month day
+ date="${da[1]} ${da[2]} $dyear"
+ elif [ "$month" != "${da[1]}" -o "$day" -ne "${da[2]}" ]; then
+ # month day
+ date="${da[1]} ${da[2]}"
+ else
+ # time
+ date=${da[3]%:*}
+ fi
+ line=${line# }
+ printf "%s$colauthor%-14s $coldate%-6s $coldefault%s\n" \
+ "$commit" "$author" "$date" "$line"
+}
- if [ "$user" ]; then
- if ! echo -e "author $author\ncommitter $committer" \
- | grep -qi "$user"; then
- skip_commit=1
- return
- fi
- fi
- if [ "$files" ]; then
- parent="${parents[0]}"
- diff_ops=
- [ "$parent" ] || diff_ops=--root
- if ! [ "$(git-diff-tree -r $diff_ops $commit $parent "${files[@]}")" ]; then
- skip_commit=1
- return
- fi
- fi
- if [ "$summary" ]; then
- # Print summary
- commit="${commit%:*}"
- author="${author% <*}"
- date=(${committer#*> })
- date="$(showdate $date '+%F %H:%M')"
- read title
- if [ "${#author}" -gt 15 ]; then
- author="${author:0:14}$coltrim~"
- fi
- if [ "${COLUMNS:-0}" -le 90 ]; then
- commit="${commit:0:12}$coltrim~"
+while optparse; do
+ if optparse -c || optparse --color; then
+ opt_color=1
+ elif optparse -f; then
+ [ "$patches" ] && optconflict
+ files=1
+ filemode=
+ elif optparse -F; then
+ filelist=1
+ dtmode=diff-tree
+ elif optparse -p || optparse --patches; then
+ [ "$files" ] && optconflict
+ patches=1
+ filemode=-p
+ elif optparse -d || optparse --diffstat; then
+ diffstat=1
+ dtmode=diff-tree
+ elif optparse -u= || optparse --search= 2; then
+ user=$OPTARG
+ dtmode=diff-tree
+ elif optparse -q || optparse --short; then
+ [ "$verbose$summary" ] && optconflict
+ quiet=1
+ elif optparse -v || optparse --verbose; then
+ [ "$quiet" ] && optconflict
+ verbose=1
+ elif optparse -s || optparse --summary 2; then
+ [ "$quiet" ] && optconflict
+ summary=1
+ elif optparse --stdin 2; then
+ stdin=1
+ dtmode=diff-tree
+ elif optparse -y= || optparse --max-age= 5; then
+ rlargs[${#rlargs[@]}]=--max-age=$(toepoch "$OPTARG") || exit 1
+ elif optparse -o= || optparse --min-age= 2; then
+ rlargs[${#rlargs[@]}]=--min-age=$(toepoch "$OPTARG") || exit 1
+ elif optparse --max-count= 5; then
+ maxn=$OPTARG
+ elif optparse -a || optparse --all; then
+ dtargs[${#dtargs[@]}]="-m"
+ dtargs[${#dtargs[@]}]="--root"
+ elif optparse -m || optparse --mergebase 2; then
+ incoming=1
+ elif optparse -S= || optparse --match= 3; then
+ dtargs[${#dtargs[@]}]="-S$OPTARG"
+ elif optparse -M; then
+ [ "$renames" ] && optconflict
+ [ "$patches" ] || filemode=
+ dtargs[${#dtargs[@]}]="-M"
+ renames=1
+ elif optparse -C; then
+ [ "$renames" ] && optconflict
+ [ "$patches" ] || filemode=
+ dtargs[${#dtargs[@]}]="-C"
+ renames=1
+ elif optparse -B; then
+ [ "$patches" ] || filemode=
+ dtargs[${#dtargs[@]}]="-B"
+ elif optparse -r= || optparse --revision=; then
+ if [ -z "${id1+set}" ]; then
+ id1=$OPTARG
+ if [[ "$id1" == *:* ]]; then
+ id2=${id1#*:}
+ id1=${id1%:*}
fi
-
- printf "$colcommit%s $colauthor%-15s $coldate%s $coldefault%s\n" \
- "${commit%:*}" "$author" "$date" "${title:2}"
- commit=
- return
+ else
+ [ -z "${id2+set}" ] || die "too many revisions"
+ id2=$OPTARG
fi
+ else
+ optfail
+ fi
+done
- echo ${colheader}commit ${commit%:*} $coldefault
- echo ${colheader}tree $tree $coldefault
+[ -n "$COGITO_AUTO_COLOR" -a -t 1 ] && [ "$(tput setaf 1 2>/dev/null)" ] &&
+ opt_color=1
- for parent in "${parents[@]}"; do
- echo ${colheader}parent $parent $coldefault
- done
+LESS="-S $LESS"
+[ "$COLUMNS" ] || COLUMNS="$(tput cols)"
- date=(${author#*> })
- pdate="$(showdate $date)"
- [ "$pdate" ] && author="${author%> *}> $pdate"
- echo ${colauthor}author $author $coldefault
-
- date=(${committer#*> })
- pdate="$(showdate $date)"
- [ "$pdate" ] && committer="${committer%> *}> $pdate"
- echo ${colcommitter}committer $committer $coldefault
+if [ "$incoming" ]; then
+ id1="$(commit-id "${id1:-origin}")" || exit 1
+ id2="$(commit-id "${id2:-HEAD}")" || exit 1
+ id2="$(git-merge-base "$id1" "$id2")" || exit 1
+fi
- if [ -n "$list_files" ]; then
- list_commit_files "$tree" "${parents[0]}"
- fi
- echo
- commit=
- ;;
- esac
-}
+id1=$(commit-id "$id1") || exit 1
+[ -z "${id2+set}" ] || id2=$(commit-id "$id2") || exit 1
+rlargs=( "${rlargs[@]}" $id1 ${id2+^}$id2 )
-print_commit_log()
-{
- commit=
- author=
- committer=
- tree=
-
- sed -e '
- s/^ \(.*\)/% \1/
- /^% *[Ss]igned-[Oo]ff-[Bb]y:.*/ s/^% \(.*\)/% '$colsignoff'\1'$coldefault'/
- /^% *[Aa]cked-[Bb]y:.*/ s/^% \(.*\)/% '$colsignoff'\1'$coldefault'/
- ' | while read key rest; do
- trap exit SIGPIPE
- process_commit_line
- done
-}
+if [ -n "${dtargs[*]}${ARGS[*]}" -o "$filemode" != -s -o "$dtmode" ]; then
+ dtmode=diff-tree
+else
+ dtmode=commit
+ [ "$verbose" -a -z "$summary" ] && pretty=raw
+ [ "$quiet" ] && { pretty=short; quiet=; }
+fi
-if [ "$mergebase" ]; then
- [ "$log_start" ] || log_start="master"
- [ "$log_end" ] || log_end="origin"
- log_start=$(git-merge-base $(commit-id "$log_start") $(commit-id "$log_end"))
+if [ "$summary" ]; then
+ year=$(date +%Y)
+ month=$(LANG=C date +%b)
+ day=$(date +%d)
+ [ "$verbose" ] && { longsummary=1; verbose=; }
fi
-id1="$(commit-id "$log_start")" || exit 1
-if [ "$log_end" ]; then
- id2="$(commit-id "$log_end")" || exit 1
- revls="git-rev-list --pretty=raw $id2 ^$id1"
+if [ "$filelist$diffstat$summary" -o "$pretty" = raw -o $dtmode = diff-tree ]
+then
+ needparse=1
else
- revls="git-rev-list --pretty=raw $id1"
+ [ "$maxn" ] && rlargs=(--max-count="$maxn" "${rlargs[@]}")
+fi
+
+if [ "$opt_color" ]; then
+ setup_colors
+
+ if [ ! "$summary" ]; then
+ LESS=$'+/\013^Commit:.[a-f0-9]*|^diff.--git..*$'" $LESS"
+ sedprog="
+s,^\\(Commit: [^ ]*\\)\\(.*\\),$colheader\\1$coldefault\\2,
+s,^Author:.*,$colauthor&$coldefault,
+s,^Date:.*,$colauthor&$coldefault,
+s,^Tree:.*,$colheader&$coldefault,
+s,^Parent:.*,$colheader&$coldefault,
+s,^Committer:.*,$colcommitter&$coldefault,
+s,^Commitdate:.*,$colcommitter&$coldefault,
+s,^ Signed-[Oo]ff[- ][Bb]y:.*,$colsignoff&$coldefault,
+s,^ Acked[- ][Bb]y:.*,$colsignoff&$coldefault,"
+ fi
+
+ [ "$patches" ] && sedprog="$sedprog;$color_rules"
+
+ [ "$diffstat" ] && sedprog="$sedprog
+s,^\\( [^ ].*\\)\\( | *[0-9][0-9]* \\),$colfiles\\1$coldefault\\2,"
+fi
+
+if [ $dtmode = diff-tree -a -z "$filemode" ]; then
+ filediffstart=$'s,^:[0-9]* [^ ]* [^ ]* [^ ]* \('
+ filediffend=$'[^\t]*\)\t\(.*\)'
+ sedprog="$sedprog
+${filediffstart}[NCR]$filediffend,$coldiffadd\\1 \\2$coldefault,
+${filediffstart}D$filediffend,$coldiffrem\\1 \\2$coldefault,
+${filediffstart}.$filediffend,$colfiles\\1 \\2$coldefault,"
fi
-# LESS="S" will prevent less to wrap too long titles to multiple lines;
-# you can scroll horizontally.
-$revls | print_commit_log | LESS="S$LESS" pager
+if [ "$needparse" ]; then
+ # Slow version, parsing output from diff-tree or rev-list
+ unset commit showds from lineno line
+ n=0
+ dolog | ( trap exit SIGPIPE
+ while [ "$line" ] || IFS='' read -r line; do
+ case $line in
+ $dtmode\ *)
+ [ "$maxn" ] && (( ++n > maxn )) && exit
+ [ "$showds" ] && showstat $commit $from
+ line=${line#$dtmode } f="%s\n" showds=$diffstat lineno=0
+ commit=${line% (*} from=${line#*(from }; from=${from%)}
+ [ "$summary" ] && f= || echo "Commit: $line" ;;
+
+ Author:*) printf "$f" "$line"; author=$line ;;
+
+ Date:*) printf "$f" "$line"; date=$line ;;
+
+ author\ *)
+ line=${line#author }; author=${line% [0-9]*}; date=${line##*> }
+ if [ "$f" ]; then
+ printf "$f" "Author: $author"
+ [ "$quiet" ] || printf "$f" "Date: $(showdate $date)"
+ elif [ "$summary" ]; then
+ date=$(showdate $date)
+ fi ;;
+
+ committer\ *)
+ if [ "$verbose" ]; then
+ line=${line#committer }
+ echo "Committer: ${line% [0-9]*}"
+ echo "Commitdate: $(showdate ${line##*> })"
+ fi ;;
+
+ tree\ *) [ "$verbose" ] && echo "Tree: ${line#tree }" ;;
+
+ parent\ *) [ "$verbose" ] && echo "Parent: ${line#parent }" ;;
+
+ \ \ \ \ * | '')
+ if (( ++lineno == 2 )); then
+ [ "$summary" ] &&
+ showsummary $commit "$author" "$date" "$line"
+ [ "$filelist" ] && showfiles $commit $from
+ printf "$f" "$line"
+ [ "$quiet" ] && { f=; echo; }
+ else
+ printf "$f" "$line"
+ fi ;;
+
+ *)
+ [ "$showds" ] && { showstat $commit $from; showds=; }
+ printf "%s\n" "$line"
+ while IFS='' read -r line; do
+ case $line in $dtmode\ *) break ;; esac
+ printf "%s\n" "$line"
+ done
+ continue ;;
+ esac
+ line=
+ done
+ [ "$showds" ] && showstat $commit $from
+ ) | LANG=C sed -e "$sedprog" | pager
+else
+ # Fast version, using sed only.
+ sedprog="s,^$dtmode ,Commit: ,;$sedprog"
+ dolog | LANG=C sed -e "$sedprog" | pager
+fi
^ permalink raw reply
* Re: gitk-1.1 out
From: Paul Mackerras @ 2005-06-09 12:50 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
In-Reply-To: <Pine.LNX.4.58.0506080743040.2286@ppc970.osdl.org>
Linus Torvalds writes:
> Can you do the same for things in .git/refs/heads? Possibly using another
> color?
Try this patch against gitk-1.1 and let me know what you think. I
draw the heads as a green rectangle with a black border.
I also added a -t option to gitk which makes it use gitk-rev-tree
instead of git-rev-list, so that you can do
gitk -t $(ls .git/refs/heads)
Paul.
---
diff -urN gitk-1.1/gitk current/gitk
--- gitk-1.1/gitk 2005-06-01 18:14:51.000000000 +1000
+++ current/gitk 2005-06-09 22:41:56.000000000 +1000
@@ -10,13 +10,18 @@
# CVS $Revision: 1.20 $
proc getcommits {rargs} {
- global commits commfd phase canv mainfont
+ global commits commfd phase canv mainfont treemode
if {$rargs == {}} {
set rargs HEAD
}
set commits {}
set phase getcommits
- if [catch {set commfd [open "|git-rev-list $rargs" r]} err] {
+ if {$treemode} {
+ set cmd "git-rev-tree"
+ } else {
+ set cmd "git-rev-list"
+ }
+ if [catch {set commfd [open "|$cmd $rargs" r]} err] {
puts stderr "Error executing git-rev-list: $err"
exit 1
}
@@ -28,7 +33,7 @@
}
proc getcommitline {commfd} {
- global commits parents cdate nparents children nchildren
+ global commits parents cdate nparents children nchildren treemode
set n [gets $commfd line]
if {$n < 0} {
if {![eof $commfd]} return
@@ -39,8 +44,9 @@
return
}
if {[string range $err 0 4] == "usage"} {
+ set cmd [expr {$treemode? "tree": "list"}]
set err "\
-Gitk: error reading commits: bad arguments to git-rev-list.\n\
+Gitk: error reading commits: bad arguments to git-rev-$cmd.\n\
(Note: arguments to gitk are passed to git-rev-list\
to allow selection of commits to be displayed.)"
} else {
@@ -49,11 +55,19 @@
error_popup $err
exit 1
}
- if {![regexp {^[0-9a-f]{40}$} $line]} {
- error_popup "Can't parse git-rev-tree output: {$line}"
- exit 1
+ if {$treemode} {
+ if {![regexp {^[0-9]* ([0-9a-f]{40}):} $line match id]} {
+ error_popup "Can't parse git-rev-tree output: {$line}"
+ exit 1
+ }
+ lappend commits $id
+ } else {
+ if {![regexp {^[0-9a-f]{40}$} $line]} {
+ error_popup "Can't parse git-rev-list output: {$line}"
+ exit 1
+ }
+ lappend commits $line
}
- lappend commits $line
}
proc readallcommits {} {
@@ -130,7 +144,7 @@
}
proc readrefs {} {
- global tagids idtags
+ global tagids idtags headids idheads
set tags [glob -nocomplain -types f .git/refs/tags/*]
foreach f $tags {
catch {
@@ -154,6 +168,20 @@
lappend idtags($obj) $tag
}
}
+ close $fd
+ }
+ }
+ set heads [glob -nocomplain -types f .git/refs/heads/*]
+ foreach f $heads {
+ catch {
+ set fd [open $f r]
+ set line [read $fd 40]
+ if {[regexp {^[0-9a-f]{40}} $line id]} {
+ set head [file tail $f]
+ set headids($head) $line
+ lappend idheads($line) $head
+ }
+ close $fd
}
}
}
@@ -537,6 +565,7 @@
global lineid linehtag linentag linedtag commitinfo
global nextcolor colormap numcommits
global stopped phase redisplaying selectedline idtags idline
+ global idheads
allcanvs delete all
set start {}
@@ -616,13 +645,22 @@
if {$nparents($id) > 2} {
set xt [expr {$xt + ($nparents($id) - 2) * $linespc}]
}
- if {[info exists idtags($id)] && $idtags($id) != {}} {
+ set marks {}
+ set ntags 0
+ if {[info exists idtags($id)]} {
+ set marks $idtags($id)
+ set ntags [llength $marks]
+ }
+ if {[info exists idheads($id)]} {
+ set marks [concat $marks $idheads($id)]
+ }
+ if {$marks != {}} {
set delta [expr {int(0.5 * ($linespc - $lthickness))}]
set yt [expr $canvy - 0.5 * $linespc]
set yb [expr $yt + $linespc - 1]
set xvals {}
set wvals {}
- foreach tag $idtags($id) {
+ foreach tag $marks {
set wid [font measure $mainfont $tag]
lappend xvals $xt
lappend wvals $wid
@@ -631,12 +669,20 @@
set t [$canv create line $x $canvy [lindex $xvals end] $canvy \
-width $lthickness -fill black]
$canv lower $t
- foreach tag $idtags($id) x $xvals wid $wvals {
+ foreach tag $marks x $xvals wid $wvals {
set xl [expr $x + $delta]
set xr [expr $x + $delta + $wid + $lthickness]
- $canv create polygon $x [expr $yt + $delta] $xl $yt\
- $xr $yt $xr $yb $xl $yb $x [expr $yb - $delta] \
- -width 1 -outline black -fill yellow
+ if {[incr ntags -1] >= 0} {
+ # draw a tag
+ $canv create polygon $x [expr $yt + $delta] $xl $yt\
+ $xr $yt $xr $yb $xl $yb $x [expr $yb - $delta] \
+ -width 1 -outline black -fill yellow
+ } else {
+ # draw a head
+ set xl [expr $xl - $delta/2]
+ $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
+ -width 1 -outline black -fill green
+ }
$canv create text $xl $canvy -anchor w -text $tag \
-font $mainfont
}
@@ -1334,7 +1380,8 @@
set textfont {Courier 9}
set colors {green red blue magenta darkgrey brown orange}
-set colorbycommitter false
+set colorbycommitter 0
+set treemode 0
catch {source ~/.gitk}
@@ -1350,6 +1397,7 @@
"^-b" { set boldnames 1 }
"^-c" { set colorbycommitter 1 }
"^-d" { set datemode 1 }
+ "^-t" { set treemode 1 }
default {
lappend revtreeargs $arg
}
^ permalink raw reply
* Re: [ANNOUNCE] Cogito-0.11.3
From: Dan Holmsand @ 2005-06-09 13:07 UTC (permalink / raw)
To: git; +Cc: Konstantin Antselovich, Petr Baudis
In-Reply-To: <42A81584.90605@antselovich.com>
Konstantin Antselovich wrote:
> I have updated to Cogito-0.11.3, it compiles and runs
> but make test returns multiple error messages (see below)
There's a typo in rev-list.c. This fixes the tests for me:
diff --git a/rev-list.c b/rev-list.c
--- a/rev-list.c
+++ b/rev-list.c
@@ -218,7 +218,7 @@ int main(int argc, char **argv)
list = limit_list(list);
show_commit_list(list);
} else {
-#ifdef NO_OPENSSL
+#ifndef NO_OPENSSL
if (sort_list_in_merge_order(list, &process_commit)) {
die("merge order sort failed\n");
}
^ permalink raw reply
* Re: gitk-1.1 out
From: Linus Torvalds @ 2005-06-09 14:22 UTC (permalink / raw)
To: Paul Mackerras; +Cc: git
In-Reply-To: <17063.51611.467615.685723@cargo.ozlabs.ibm.com>
On Thu, 9 Jun 2005, Paul Mackerras wrote:
>
> > I realize that that may sound silly, but a tree that has many branches can
> > validly be used with gitk with something like this:
> >
> > gitk $(ls .git/refs/heads)
>
> Um, did I break that when I changed to using git-rev-list instead of
> git-rev-tree?
No, I fixed git-rev-list to do multiple heads.
Linus
^ permalink raw reply
* Re: [PATCH] Fix cogito handling of timezones
From: Frank Sorenson @ 2005-06-09 14:20 UTC (permalink / raw)
To: Petr Baudis; +Cc: Git Mailing List
In-Reply-To: <20050609082949.GC29665@pasky.ji.cz>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Petr Baudis wrote:
> It looks ok, but shouldn't this be
> + lsec=$(($sec + $dtz % 100 * 60 $sign $dtz / 100 * 3600))
Ah, yes, of course. I see that Dan Holmsand has also sent a patch (3/6)
to fix this. I haven't tested his very much, but it looks like a
cleaner and more comprehensive solution. Sigh.
Frank
- --
Frank Sorenson - KD7TZK
Systems Manager, Computer Science Department
Brigham Young University
frank@tuxrocks.com
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.6 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org
iD8DBQFCqFAtaI0dwg4A47wRAlaEAJ0Ui38Yrwp7r0QUseI5/7n4kbnRaACfZBv0
M5TClhJuvD2y7fz/Fm9pNes=
=SOdU
-----END PGP SIGNATURE-----
^ permalink raw reply
* Re: [PATCH 0/6] Bunch of new features for cg-log and cg-diff
From: Jonas Fonseca @ 2005-06-09 14:22 UTC (permalink / raw)
To: Dan Holmsand; +Cc: git, Petr Baudis
In-Reply-To: <42A82211.9060305@gmail.com>
Dan Holmsand <holmsand@gmail.com> wrote Thu, Jun 09, 2005:
> This series adds optget-style option parsing, support for almost all
> git-diff-* features, git-apply --stat support, common colorization code,
> better performance for cg-log and some other stuff.
I tried out your patchset and have a few comments ...
cg-diff:
- The pager is only used when passing -c. Is that intentional?
- Nice with the diffstat option.
cg-log:
- In the non-verbose summary you use the author date. One motivation
for using the commit date is that the summary output makes it easy to
track 'activity' and see if/when your patch made it in. Maybe I've
just become too used to CVS changelogs.
- Even though the more dense time format in the summary output is a
nice idea the new date information is unfortunately also makes the
summary output less useful, IMO. It can even make the by-date
scanning harder because you have to jump between two significantly
different date formats. With the new verbose distinction there should
be no need for making the date so dense.
I don't much like the inverted colors caused by the searching. Although
the quick goto next entry thing is nice the colors can be very
intrusive, and having to search for some nonsense string to remove them
is terrible.
What about a COGITO_COLORS environment variable for configuring what
string setup_colors() will work on. It could maybe take the place of the
COGITO_AUTO_COLOR environment variable although this is two different
things.
With the long help output of cg-log maybe we should consider also
displaying it in a pager.
A minor note about the option parsing. cg-log -sh will give the error
cg-log: unrecoginized option `-h'
--
Jonas Fonseca
^ permalink raw reply
* Re: gitk-1.1 out
From: Linus Torvalds @ 2005-06-09 14:58 UTC (permalink / raw)
To: Paul Mackerras; +Cc: git
In-Reply-To: <17064.15104.847009.390149@cargo.ozlabs.ibm.com>
On Thu, 9 Jun 2005, Paul Mackerras wrote:
>
> Try this patch against gitk-1.1 and let me know what you think. I
> draw the heads as a green rectangle with a black border.
Works for me. My cvs-import testing "syslinux" tree now has the heads of
the different branches nicely named.
However, I also changed it to allow the "direct" kinds of tags which the
cvs2git thing creates, where the tag file contains just a direct pointer
to the commit, rather than a pointer to a tag object.
I'm not a TCL weenie, so this is just a very quick hack, and you may want
to re-do things properly, but something like the appended works for me..
> I also added a -t option to gitk which makes it use gitk-rev-tree
> instead of git-rev-list, so that you can do
>
> gitk -t $(ls .git/refs/heads)
No need, I think yoy may have an old version of git.
Linus
--- hacky hack hack ---
--- gitk 2005-06-09 07:26:50.000000000 -0700
+++ gitk 2005-06-09 07:55:00.000000000 -0700
@@ -151,6 +151,11 @@
set fd [open $f r]
set line [read $fd]
if {[regexp {^[0-9a-f]{40}} $line id]} {
+ if {[regexp {[^/]*$} $f direct]} {
+ set tagids($direct) $id
+ lappend idtags($id) $direct
+ }
+
set contents [split [exec git-cat-file tag $id] "\n"]
set obj {}
set type {}
^ permalink raw reply
* Re: [PATCH 3/3] read-tree -m 3-way: handle more trivial merges internally
From: Linus Torvalds @ 2005-06-09 15:15 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7v64woroui.fsf_-_@assigned-by-dhcp.cox.net>
On Thu, 9 Jun 2005, Junio C Hamano wrote:
>
> This patch teaches "read-tree -m O A B" that some more trivial
> cases can be handled internally.
No, I think this is quite possibly wrong for several reasons.
For one, it makes the rest of the system unaware of the deleted files, so
nothing ever deletes them from the working directory.
And that's not entirely trivial to fix either, since the obvious fix
(which is to just do a
if (update)
unlink(b->name);
or something like that) is wrong. It's wrong because we must not do the
update until the very end, when we've either merged all entries or we've
failed on an entry that couldn't be merged (that's why I did the extra
CE_UPDATE flag, instead of updating as we go along).
Now, you could fix that by creating a separate list of files to be
deleted (so this is not fundamental, it's just more complicated than the
trivial case), but that doesn't help, because there's _another_ reason why
read-tree shouldn't handle these cases.
Namely that read-tree doesn't have a frigging clue about renames, and
shouldn't have.
But a real merge program _could_ have a frigging clue, and might notice
patterns like
- file got modified in one branch, removed in the other
- a file got added in the other branch
- "Hey, that added file looks like the removed one!"
- Let's merge the modifications from the first branch into the move of
the second branch!
See? Now, git-read-tree won't handle the first case anyway, but your
change _does_ make it handle the "file got added" case, which means that
now the added file is invisible the the "smart merger", and the smart
merger can't really tell that it was a rename any more.
So our current stupid file-by-file "git-merge-cache" will never do this,
but that's a limitation of me being less than the intellectual giant I
wish I was. So I just do the stupid merges. But I _know_ they are stupid,
and I would like to leave the door open for somebody else to fix up the
cases I don't handle.
You're basically closing that door.
Now, you can (validly) argue that you could still just look at the
original trees ("git-diff-tree -C $O $M") and grep for copies/movement and
do it by hand _there_ instead of looking at the result of the read-tree,
and you may well be right. So again, this is not a _fundamental_ problem,
although it's a bit more fundamental than the first one.
So if you want to convince me that it's better to do the rename detection
outside of the index file, go wild. Alternatively, you can argue that we
can always undo this later, when once we _do_ have rename and copy
detection and can try to merge things automatically (what _do_ you do if a
file is copied in one branch and modified in the other? Just warn the poor
user, I guess).
So I just need a little convincing that this is a good idea.
Linus
^ permalink raw reply
* Re: [PATCH 0/6] Bunch of new features for cg-log and cg-diff
From: Dan Holmsand @ 2005-06-09 15:14 UTC (permalink / raw)
To: Jonas Fonseca; +Cc: git, Petr Baudis
In-Reply-To: <20050609142243.GA26524@diku.dk>
Jonas Fonseca wrote:
> I tried out your patchset and have a few comments ...
>
> cg-diff:
>
> - The pager is only used when passing -c. Is that intentional?
Yes. The reasoning was that people wanting more features probably will
use color as well (and I didn't want to force the pager on people who
use cg-diff just to check *if* something changed).
But I'm not 100% convinced I'm doing the right thing. Any suggestions?
> cg-log:
>
> - In the non-verbose summary you use the author date. One motivation
> for using the commit date is that the summary output makes it easy to
> track 'activity' and see if/when your patch made it in. Maybe I've
> just become too used to CVS changelogs.
Yeah, maybe :-) Seriously, I think author date carries more information,
particularly since the log is already (most of the time) ordered by
commit date. So, when you see an old date before a newer one, you
immediately know that some old stuff was incorporated into the repository.
Also, git-rev-list --pretty uses author date, and I wanted to be consistent.
Of course, I could always add one more option :-)
> - Even though the more dense time format in the summary output is a
> nice idea the new date information is unfortunately also makes the
> summary output less useful, IMO. It can even make the by-date
> scanning harder because you have to jump between two significantly
> different date formats. With the new verbose distinction there should
> be no need for making the date so dense.
This is also a matter of taste, obviously. I actually *like* having two
different formats, as it makes the difference between "today" and
"earlier" more obvious. And I don't really care what time of day
something was written three weeks ago.
Perhaps this should be customisable as well, if it's felt to be
important enough?
> I don't much like the inverted colors caused by the searching. Although
> the quick goto next entry thing is nice the colors can be very
> intrusive, and having to search for some nonsense string to remove them
> is terrible.
"export LESS=-G" will do what you want. Maybe that should be the default?
> What about a COGITO_COLORS environment variable for configuring what
> string setup_colors() will work on. It could maybe take the place of the
> COGITO_AUTO_COLOR environment variable although this is two different
> things.
That should already be there. "COGITO_COLORS='header=31' cg-log" should
give very red headers, for example.
> With the long help output of cg-log maybe we should consider also
> displaying it in a pager.
Good idea.
> A minor note about the option parsing. cg-log -sh will give the error
>
> cg-log: unrecoginized option `-h'
>
Yeah, I know. "-h" and "--help" are the only options not handled by
optparse in cg-log. I thought I could rely on the general help-finding
logic in cg-Xlib for that.
On the other hand, you really shouldn't say "cg-log -sh" :-)
/dan
^ permalink raw reply
* Re: [PATCH 0/6] Bunch of new features for cg-log and cg-diff
From: Dan Holmsand @ 2005-06-09 15:14 UTC (permalink / raw)
To: Jonas Fonseca; +Cc: git, Petr Baudis
In-Reply-To: <20050609142243.GA26524@diku.dk>
Jonas Fonseca wrote:
> I tried out your patchset and have a few comments ...
>
> cg-diff:
>
> - The pager is only used when passing -c. Is that intentional?
Yes. The reasoning was that people wanting more features probably will
use color as well (and I didn't want to force the pager on people who
use cg-diff just to check *if* something changed).
But I'm not 100% convinced I'm doing the right thing. Any suggestions?
> cg-log:
>
> - In the non-verbose summary you use the author date. One motivation
> for using the commit date is that the summary output makes it easy to
> track 'activity' and see if/when your patch made it in. Maybe I've
> just become too used to CVS changelogs.
Yeah, maybe :-) Seriously, I think author date carries more information,
particularly since the log is already (most of the time) ordered by
commit date. So, when you see an old date before a newer one, you
immediately know that some old stuff was incorporated into the repository.
Also, git-rev-list --pretty uses author date, and I wanted to be consistent.
Of course, I could always add one more option :-)
> - Even though the more dense time format in the summary output is a
> nice idea the new date information is unfortunately also makes the
> summary output less useful, IMO. It can even make the by-date
> scanning harder because you have to jump between two significantly
> different date formats. With the new verbose distinction there should
> be no need for making the date so dense.
This is also a matter of taste, obviously. I actually *like* having two
different formats, as it makes the difference between "today" and
"earlier" more obvious. And I don't really care what time of day
something was written three weeks ago.
Perhaps this should be customisable as well, if it's felt to be
important enough?
> I don't much like the inverted colors caused by the searching. Although
> the quick goto next entry thing is nice the colors can be very
> intrusive, and having to search for some nonsense string to remove them
> is terrible.
"export LESS=-G" will do what you want. Maybe that should be the default?
> What about a COGITO_COLORS environment variable for configuring what
> string setup_colors() will work on. It could maybe take the place of the
> COGITO_AUTO_COLOR environment variable although this is two different
> things.
That should already be there. "COGITO_COLORS='header=31' cg-log" should
give very red headers, for example.
> With the long help output of cg-log maybe we should consider also
> displaying it in a pager.
Good idea.
> A minor note about the option parsing. cg-log -sh will give the error
>
> cg-log: unrecoginized option `-h'
>
Yeah, I know. "-h" and "--help" are the only options not handled by
optparse in cg-log. I thought I could rely on the general help-finding
logic in cg-Xlib for that.
On the other hand, you really shouldn't say "cg-log -sh" :-)
/dan
^ permalink raw reply
* [COGITO PATCH] Improvements for cg and cg-help
From: Pavel Roskin @ 2005-06-09 15:17 UTC (permalink / raw)
To: git
Hello!
cg should not run cg-help with arguments unless they were specified
after an explicit "help" command
("cg help -option")
cg without arguments should run cg-help.
cg with any option before the command ("cg -foo cmd") should also run
cg-help.
cg-help should print help for cg-help if specific help is not available,
e.g.:
$ cg-help foo
Error: no help available for "foo"
Usage: cg-help [COMMAND]
...
Signed-off-by: Pavel Roskin <proski@gnu.org>
diff --git a/cg b/cg
--- a/cg
+++ b/cg
@@ -1,7 +1,10 @@
#!/bin/sh
cmd="$1"; shift
-[ x"$cmd" = x"--help" ] && cmd="help"
+case x$cmd in
+ x-*) exec cg-help;;
+ x) exec cg-help;;
+esac
exe="cg-$cmd"
exec $exe "$@"
diff --git a/cg-help b/cg-help
--- a/cg-help
+++ b/cg-help
@@ -19,7 +19,9 @@ _git_repo_unneeded=1
if [ "$1" ]; then
cmd=$(echo "$1" | sed 's/^cg-//')
- print_help $cmd
+ (print_help $cmd) && exit
+ echo "Error: no help available for \"$1\""
+ print_help help
fi
--
Regards,
Pavel Roskin
^ permalink raw reply
* Re: [PATCH] Tidy up some rev-list-related stuff
From: Jon Seymour @ 2005-06-09 9:57 UTC (permalink / raw)
To: Git Mailing List
In-Reply-To: <2cfc4032050608180650e5ef12@mail.gmail.com>
> Speaking of which, I can't emacs to indent properly even when I use
> this suggestion from that document:
>
> (defun linux-c-mode ()
> "C mode with adjusted defaults for use with the Linux kernel."
> (interactive)
> (c-mode)
> (c-set-style "K&R")
> (setq tab-width 8)
> (setq indent-tabs-mode t)
> (setq c-basic-offset 8))
>
> This will define the M-x linux-c-mode command. When hacking on a
> module, if you put the string -*- linux-c -*- somewhere on the first
> two lines, this mode will be automatically invoked. Also, you may want
> to add
>
> (setq auto-mode-alist (cons '("/home/jon/.*/.*\\.[ch]$" . linux-c-mode)
> auto-mode-alist))
>
Actually, these instructions do work for me. An earlier version of the
instructions I pulled from a google hit didn't work.
jon.
^ permalink raw reply
* Re: [PATCH] Add support for author-oriented git-rev-list switches [rev 8]
From: Jon Seymour @ 2005-06-09 15:53 UTC (permalink / raw)
To: git; +Cc: jon.seymour, Junio C Hamano, Linus Torvalds
In-Reply-To: <2cfc403205060902373e5c284f@mail.gmail.com>
Since no-one had pulled the rev-7 patch to review, I have tweaked and
simplified the test cases some more. In particular, I removed the need
for /dev/urandom.
http://blackcubes.dyndns.org/epoch/wrt-patch-latest.patch
Junio: you might be interested in a new bash function I wrote in
t/t6001-rev-list-merge-order.sh
called: test_output_expect_success
If you like, I'll generalize it and move it into testlib.sh
Linus: when you are ready, the URI above contains the latest patch.
jon.
^ 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