* Re: cg-init bug
From: Radoslaw Szkodzinski @ 2005-06-05 16:10 UTC (permalink / raw)
To: Zack Brown; +Cc: pasky, git
In-Reply-To: <20050605153053.GA6890@tumblerings.org>
Zack Brown wrote:
>Hi,
>
>I've been tracking Cogito. This problem occurred with Cogito version
>1e2673d606dd39dc44b4eed2204ba349a448bc4d
>
>I have a directory tree with several layers of subdirectories and about 1700
>files. I tried to convert it to a git repository with 'cg-init'.
>
>
I think an ls -l of the repo would be useful... if it's not that big.
If it is, you may have hit the limitation of the length of command line.
If it's so, repeat the commands.
However, if I'd want to add everything to the repo, I'd do:
cg-status | sed "s/? //" | xargs cg-add
AstralStorm
^ permalink raw reply
* Re: [PATCH-CAREFUL/RENAME] rename git-rpush and git-rpull to git-ssh-push and git-ssh-pull
From: Daniel Barkalow @ 2005-06-05 16:06 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Linus Torvalds, git
In-Reply-To: <7vfyvxb89m.fsf_-_@assigned-by-dhcp.cox.net>
On Sat, 4 Jun 2005, Junio C Hamano wrote:
> In preparation for 1.0 release, this make the command names
> consistent with others in git-*-pull family.
>
> Signed-off-by: Junio C Hamano <junkio@cox.net>
This looks right to me (assuming git-apply produces the expected results,
of course).
Acked-by: Daniel Barkalow <barkalow@iabervon.org>
^ permalink raw reply
* Re: [PATCH] git-daemon server
From: Daniel Barkalow @ 2005-06-05 16:03 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Linus Torvalds, git
In-Reply-To: <7vzmu5b8o6.fsf@assigned-by-dhcp.cox.net>
On Sat, 4 Jun 2005, Junio C Hamano wrote:
> Ah (lightbulb!), are you asking me to send the patch in the
> git-extended rename diff format, like this?
>
> $ git-diff-cache -p -B -C HEAD
> diff --git a/Documentation/git-rpull.txt b/Documentation/git-ssh-pull.txt
> similarity index 89%
> rename old Documentation/git-rpull.txt
> rename new Documentation/git-ssh-pull.txt
> --- a/Documentation/git-rpull.txt
> +++ b/Documentation/git-ssh-pull.txt
> @@ -1,20 +1,20 @@
> -git-rpull(1)
> -============
> +git-ssh-pull(1)
> +===============
> v0.1, May 2005
> ...
Right. I hadn't actually been following the discussion entirely, but it
certainly seems like one major benefit of the rename/copy stuff is that it
will generate more readable diffs. That is, I can see what you changed in
the process of renaming the file, and don't have to inspect all the lines
you didn't change to see that they're the same.
> I am not sure if this is suitable for patch submission. I did
> the rename/copy stuff mostly for software archaeology purposes
> (meaning, you examine what is in your repository), not to
> generate patches for submission via e-mail.
I think that examination by mailing list subscribers is a very similar
situation, and it makes sense to apply exactly the patch that has been
checked.
> It certainly is a good test for the git-apply stuff Linus has
> been working on, and in addition it would have a good amusement
> value to see how well it would work (or how badly it would barf
> ;-), but I suspect Linus (or, rather, his "dotest" script) would
> appreciate it more if it came in the traditional diff format
> that does not use the rename stuff. I dunno. Let's ask Linus
> first.
I think I like best what you actually did: send it both ways. Having a
test case that's a real change someone wants to make as well as a
reference for how it should come out helps a lot in debugging. And I can
tell that the rename-using version is what I wanted, and the
non-rename-using version is also must be good if the results match.
-Daniel
*This .sig left intentionally blank*
^ permalink raw reply
* cg-init bug
From: Zack Brown @ 2005-06-05 15:30 UTC (permalink / raw)
To: pasky; +Cc: git
Hi,
I've been tracking Cogito. This problem occurred with Cogito version
1e2673d606dd39dc44b4eed2204ba349a448bc4d
I have a directory tree with several layers of subdirectories and about 1700
files. I tried to convert it to a git repository with 'cg-init'.
The first thing I noticed was that not all the files and subdirectories had been
added. Doing a cg-status listed many files with '?' in front of them.
I tried adding these files with "cg-add */*", "cg-add */*/*" etc, followed by
"cg-commit", and this seemed at first glance to work. I was able to reduce
the number of files reported by "cg-status". But eventually "cg-add" would
say there were no files left to add, while "cg-status" would still list many
files unadded. It seemed that "cg-add" would only selectively add files.
At that point I blew away the .git directory and gave up on repo-izing that
project for now.
I can reproduce this problem easily. Please let me know if you need any more
information.
Be well,
Zack
--
Zack Brown
^ permalink raw reply
* Re: [PATCH] Improve git-rev-list --header output
From: Linus Torvalds @ 2005-06-05 15:17 UTC (permalink / raw)
To: Petr Baudis; +Cc: git
In-Reply-To: <20050605132515.GB17462@pasky.ji.cz>
On Sun, 5 Jun 2005, Petr Baudis wrote:
>
> Indent the commit header by four spaces like in the --pretty output;
> the commit ID is still shown unaligned. This makes the --header output
> actually usable for further processing.
I'm actually going to do a few different "header formats", and have
pretty_print_commit() able to switch between them. IOW, this would count
as just one form of pretty-printing.
I want to have the "dense" format, which has only the author and the top
line(s) of the description (stop at the first empty line), and a "raw"
format, which has all the parent information etc (ie this one).
Linus
^ permalink raw reply
* Re: git full diff output issues..
From: Linus Torvalds @ 2005-06-05 15:11 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vu0kd42dm.fsf@assigned-by-dhcp.cox.net>
On Sun, 5 Jun 2005, Junio C Hamano wrote:
>
> The case you mentioned (i.e. /dev/null) is fine but rename/copy
> is "broken" according to the definition by git-apply.
No problem, the renames always get the names from the "rename" line, not
the header. Same goes for copies.
It's only modified files that keep the same name _and_ the same content
that don't have the name uniquely on a line somewhere.
> What do you want the diff-patch format to say for this one?
>
> :100644 100644 SHA1-OLD SHA1-NEW R frotz.c nitfol.c
>
> Currently I am saying:
>
> diff --git a/frotz.c b/nitfol.c
> similarity index 89%
> rename old frotz.c
> rename new nitfol.c
> --- a/frotz.c
> +++ b/nitfol.c
This finds the old names unambiguously in _two_ places: in the "--- " line
(no question about where it begins: it's -p1, or where it ends - at the
newline) _and_ on the "rename old xxxx" line.
The only case that was special was literally the "same name, no content
changes, new mode" case, which looked like
diff --git a/oldname.c b/oldname.c
new mode 100755
old mode 100644
and thus _only_ had the name in the (normally ambiguous wrt whitepsace)
header line.
But by having the requirement that the format of the header line for that
case is "-p1" together with both names being the same, it's not ambigious
any more.
Linus
^ permalink raw reply
* [PATCH] Modify git-rev-list ... in merge order [ repost with bug fixes ]
From: jon @ 2005-06-05 13:47 UTC (permalink / raw)
To: git; +Cc: jon.seymour, torvalds
[PATCH] Modify git-rev-list to linearise the commit history in merge order.
This patch linearises the GIT commit history graph into merge order
which is defined by invariants specified in Documentation/git-rev-list.txt.
The linearisation produced by this patch is superior in an objective sense
to that produced by the existing git-rev-list implementation in that
the linearisation produced is guaranteed to have the minimum number of
discontinuities, where a discontinuity is defined as an adjacent pair of
commits in the output list which are not related in a direct child-parent
relationship.
With this patch a graph like this:
a4 ---
| \ \
| b4 |
|/ | |
a3 | |
| | |
a2 | |
| | c3
| | |
| | c2
| b3 |
| | /|
| b2 |
| | c1
| | /
| b1
a1 |
| |
a0 |
| /
root
Sorts like this:
= a4
| c3
| c2
| c1
^ b4
| b3
| b2
| b1
^ a3
| a2
| a1
| a0
= root
Instead of this:
= a4
| c3
^ b4
| a3
^ c2
^ b3
^ a2
^ b2
^ c1
^ a1
^ b1
^ a0
= root
A test script, t/t6000-rev-list.sh, includes a test which demonstrates
that the linearisation produced by --merge-order has less discontinuities
than the linearisation produced by git-rev-list without the --merge-order
flag specified. To see this, do the following:
cd t
./t6000-rev-list.sh
cd trash
cat actual-default-order
cat actual-merge-order
The existing behaviour of git-rev-list is preserved, by default. To obtain
the modified behaviour, specify --merge-order or --merge-order --show-breaks
on the command line.
This version of the patch has been tested on the git repository and also on the linux-2.6
repository and has reasonable performance on both - ~50-100% slower than the original algorithm.
This version of the patch has incorporated a functional equivalent of the Linus' output limiting
algorithm into the merge-order algorithm itself. This operates per the notes associated
with Linus' commit 337cb3fb8da45f10fe9a0c3cf571600f55ead2ce [ note: some bugs in my patch's
support of this functionality have now been fixed ]
For more details about this change, refer to Documentation/git-rev-list.txt
and http://blackcubes.dyndns.org/epoch/.
Signed-off-by: Jon Seymour <jon.seymour@gmail.com>
Diverged from 337cb3fb8da45f10fe9a0c3cf571600f55ead2ce 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,7 +9,7 @@ git-rev-list - Lists commit objects in r
SYNOPSIS
--------
-'git-rev-list' <commit>
+'git-rev-list' [ *--max-count*=number ] [ *--max-age*=timestamp ] [ *--min-age*=timestamp ] [ *--merge-order* [ *--show-breaks* ] ] <commit>
DESCRIPTION
-----------
@@ -17,11 +17,39 @@ Lists commit objects in reverse chronolo
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
+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 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.
+
+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.
+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 represent an arbtirary DAG in a linear form.
+
+*--show-breaks* is only valid if *--merge-order* is also specified.
Author
------
Written by Linus Torvalds <torvalds@osdl.org>
+Original *--merge-order* logic by Jon Seymour <jon.seymour@gmail.com>
+
Documentation
--------------
Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -40,9 +40,9 @@ install: $(PROG) $(SCRIPTS)
$(INSTALL) $(PROG) $(SCRIPTS) $(dest)$(bin)
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
+ tag.o delta.o date.o index.o diff-delta.o patch-delta.o epoch.o
LIB_FILE=libgit.a
-LIB_H=cache.h object.h blob.h tree.h commit.h tag.h delta.h
+LIB_H=cache.h object.h blob.h tree.h commit.h tag.h delta.h epoch.h
LIB_H += strbuf.h
LIB_OBJS += strbuf.o
@@ -134,6 +134,7 @@ diffcore-pathspec.o : $(LIB_H) diffcore.
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)
test: all
$(MAKE) -C t/ all
diff --git a/commit.c b/commit.c
--- a/commit.c
+++ b/commit.c
@@ -129,7 +129,7 @@ void free_commit_list(struct commit_list
}
}
-static void insert_by_date(struct commit_list **list, struct commit *item)
+void insert_by_date(struct commit_list **list, struct commit *item)
{
struct commit_list **pp = list;
struct commit_list *p;
@@ -254,3 +254,25 @@ unsigned long pretty_print_commit(const
buf[offset] = '\0';
return offset;
}
+
+struct commit *pop_commit(struct commit_list **stack)
+{
+ struct commit_list *top = *stack;
+ struct commit *item = top ? top->item : NULL;
+
+ if (top) {
+ *stack = top->next;
+ free(top);
+ }
+ return item;
+}
+
+int count_parents(struct commit * commit)
+{
+ int count = 0;
+ struct commit_list * parents = commit->parents;
+ for (count=0;parents; parents=parents->next,count++)
+ ;
+ return count;
+}
+
diff --git a/commit.h b/commit.h
--- a/commit.h
+++ b/commit.h
@@ -32,6 +32,8 @@ void free_commit_list(struct commit_list
void sort_by_date(struct commit_list **list);
+void insert_by_date(struct commit_list **list, struct commit *item);
+
extern unsigned long pretty_print_commit(const char *msg, unsigned long len, char *buf, unsigned long space);
@@ -41,4 +43,7 @@ extern unsigned long pretty_print_commit
struct commit *pop_most_recent_commit(struct commit_list **list,
unsigned int mark);
+struct commit *pop_commit(struct commit_list **stack);
+
+int count_parents(struct commit * commit);
#endif /* COMMIT_H */
diff --git a/epoch.c b/epoch.c
new file mode 100644
--- /dev/null
+++ b/epoch.c
@@ -0,0 +1,640 @@
+/*
+ * Copyright (c) 2005, Jon Seymour
+ *
+ * For more information about epoch theory on which this module is based,
+ * refer to http://blackcubes.dyndns.org/epoch/. That web page defines
+ * terms such as "epoch" and "minimal, non-linear epoch" and provides rationales
+ * for some of the algorithms used here.
+ *
+ */
+#include <stdlib.h>
+#include <openssl/bn.h> // provides arbitrary precision integers required to accurately represent fractional mass
+#include "cache.h"
+#include "commit.h"
+#include "epoch.h"
+
+struct fraction {
+ BIGNUM numerator;
+ BIGNUM denominator;
+};
+
+#define HAS_EXACTLY_ONE_PARENT(n) ((n)->parents && !(n)->parents->next)
+
+static BN_CTX *context = NULL;
+static struct fraction *one = NULL;
+static struct fraction *zero = NULL;
+
+static BN_CTX *get_BN_CTX()
+{
+ if (!context) {
+ context = BN_CTX_new();
+ }
+ return context;
+}
+
+static struct fraction *new_zero()
+{
+ struct fraction *result = xmalloc(sizeof(*result));
+ BN_init(&result->numerator);
+ BN_init(&result->denominator);
+ BN_zero(&result->numerator);
+ BN_one(&result->denominator);
+ return result;
+}
+
+static void clear_fraction(struct fraction *fraction)
+{
+ BN_clear(&fraction->numerator);
+ BN_clear(&fraction->denominator);
+}
+
+static struct fraction *divide(struct fraction *result, struct fraction *fraction, int divisor)
+{
+ BIGNUM bn_divisor;
+
+ 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_clear(&bn_divisor);
+ return result;
+}
+
+static struct fraction *init_fraction(struct fraction *fraction)
+{
+ BN_init(&fraction->numerator);
+ BN_init(&fraction->denominator);
+ BN_zero(&fraction->numerator);
+ BN_one(&fraction->denominator);
+ return fraction;
+}
+
+static struct fraction *get_one()
+{
+ if (!one) {
+ one = new_zero();
+ BN_one(&one->numerator);
+ }
+ return one;
+}
+
+static struct fraction *get_zero()
+{
+ if (!zero) {
+ zero = new_zero();
+ }
+ return zero;
+}
+
+static struct fraction *copy(struct fraction *to, struct fraction *from)
+{
+ BN_copy(&to->numerator, &from->numerator);
+ BN_copy(&to->denominator, &from->denominator);
+ return to;
+}
+
+static struct fraction *add(struct fraction *result, 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_clear(&a);
+ BN_clear(&b);
+ BN_clear(&gcd);
+
+ return result;
+}
+
+static int compare(struct fraction *left, struct fraction *right)
+{
+ BIGNUM a, b;
+
+ int result;
+
+ 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;
+}
+
+struct mass_counter {
+ struct fraction seen;
+ struct fraction pending;
+};
+
+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));
+
+ 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\n", sha1_to_hex(commit->object.sha1));
+ }
+
+ commit->object.util = mass_counter;
+
+ return mass_counter;
+}
+
+static void free_mass_counter(struct mass_counter *counter)
+{
+ clear_fraction(&counter->seen);
+ clear_fraction(&counter->pending);
+ free(counter);
+}
+
+//
+// Finds the base commit of a list of commits.
+//
+// One property of the commit being searched for is that every commit reachable from the base commit is reachable
+// from the commits in the starting list only via paths that include the base commit.
+//
+// This algorithm uses a conservation of mass approach to find the base commit.
+//
+// We start by injecting one unit of mass into the graph at each
+// of the commits in the starting list. Injecting mass into a commit
+// is achieved by adding to its pending mass counter and, if it is not already
+// enqueued, enqueuing the commit in a list of pending commits, in latest commit date
+// first order.
+//
+// The algorithm then preceeds to visit each commit in the pending queue.
+// Upon each visit, the pending mass is added to the mass already seen for that commit and
+// then divided into N equal portions, where N is the number of parents
+// of the commit being visited. The divided portions are then injected into each of
+// the parents.
+//
+// The algorithm continues until we discover a commit which has seen all the
+// mass originally injected or until we run out of things to do.
+//
+// If we find a commit that has seen all the original mass, we have found
+// the common base of all the commits in the starting list.
+//
+// The algorithm does _not_ depend on accurate timestamps for correct operation.
+// However, reasonably sane (e.g. non-random) timestamps are required in order to prevent an exponential
+// performance characteristic. The occasional timestamp inaccuracy will not dramatically
+// affect performance but may result in more nodes being processed than strictly necessary.
+//
+// This procedure sets *boundary to the address of the base commit. It returns non-zero if, and only if,
+// there was a problem parsing one of the commits discovered during the traversal.
+//
+static int find_base_for_list(struct commit_list *list, struct commit **boundary)
+{
+
+ int ret = 0;
+
+ struct commit_list *cleaner = NULL;
+ struct commit_list *pending = NULL;
+
+ *boundary = NULL;
+
+ struct fraction injected;
+
+ init_fraction(&injected);
+
+ for (; list; list = list->next) {
+
+ if (list->item->object.util) {
+ fprintf(stderr,
+ "%s:%d:duplicate commit %s discarded.\n",
+ __FILE__,
+ __LINE__,
+ sha1_to_hex(list->item->object.sha1));
+ continue;
+ }
+
+ if (!(list->item->object.flags & UNINTERESTING)) {
+
+ new_mass_counter(list->item, get_one());
+ add(&injected, &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;
+
+ if ((ret = parse_commit(latest)))
+ continue;
+
+ add(&latest_node->seen, &latest_node->seen, &latest_node->pending);
+
+ int num_parents = count_parents(latest);
+
+ if (num_parents) {
+
+ struct fraction distribution;
+ struct commit_list *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);
+
+ }
+ }
+
+ clear_fraction(&distribution);
+
+ }
+
+ 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;
+
+}
+
+
+//
+// Finds the base of an minimal, non-linear epoch, headed at head, by
+// applying the find_base_for_list to a list consisting of the head.
+//
+static int find_base(struct commit *head, struct commit **boundary)
+{
+ int ret = 0;
+ struct commit_list *pending = NULL;
+ struct commit_list *next;
+
+ commit_list_insert(head, &pending);
+ for (next = head->parents; next; next = next->next) {
+ commit_list_insert(next->item, &pending);
+ }
+ ret = find_base_for_list(pending, boundary);
+ free_commit_list(pending);
+
+ return ret;
+}
+
+//
+// This procedure traverses to the boundary of the first epoch in the epoch
+// sequence of the epoch headed at head_of_epoch. This is either the end of
+// the maximal linear epoch or the base of a minimal non-linear epoch.
+//
+// The queue of pending nodes is sorted in reverse date order and each node
+// is currently in the queue at most once.
+//
+static int find_next_epoch_boundary(struct commit *head_of_epoch, struct commit **boundary)
+{
+ int ret;
+ struct commit *item = head_of_epoch;
+
+ ret = parse_commit(item);
+ if (ret)
+ return ret;
+
+ if (HAS_EXACTLY_ONE_PARENT(item)) {
+
+ // we are at the start of a maximimal linear epoch .. traverse to the end
+
+ // traverse to the end of a maximal linear epoch
+ while (HAS_EXACTLY_ONE_PARENT(item) && !ret) {
+ item = item->parents->item;
+ ret = parse_commit(item);
+ }
+ *boundary = item;
+
+ } else {
+
+ // otherwise, we are at the start of a minimal, non-linear
+ // epoch - find the common base of all parents.
+
+ ret = find_base(item, boundary);
+
+ }
+
+ return ret;
+}
+
+//
+// Returns non-zero if parent is known to be a parent of child.
+//
+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, sizeof(parents->item->object.sha1)))
+ return 1;
+ }
+ return 0;
+}
+
+//
+// Pushes an item onto the merge order stack. If the top of the stack is
+// marked as being a possible "break", we check to see whether it actually
+// is a break.
+//
+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;
+ }
+ }
+ commit_list_insert(item, stack);
+}
+
+//
+// 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)
+{
+ struct commit_list *parents;
+ struct commit_list *reversed_parents = NULL;
+
+ 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.
+ //
+
+ for (parents = head->parents; parents; parents = parents->next)
+ commit_list_insert(parents->item, &reversed_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.
+ //
+
+ while (reversed_parents) {
+
+ struct commit *parent = pop_commit(&reversed_parents);
+
+ if (head->object.flags & UNINTERESTING) {
+ parent->object.flags |= UNINTERESTING;
+ }
+
+ if (!(parent->object.flags & VISITED)) {
+ if (parent->object.flags & BOUNDARY) {
+
+ if (*stack) {
+ die("something else is on the stack - %s\n", sha1_to_hex((*stack)->item->object.sha1));
+ }
+
+ push_onto_merge_order_stack(stack, parent);
+ parent->object.flags |= VISITED;
+
+ } else {
+
+ sort_first_epoch(parent, stack);
+
+ if (reversed_parents) {
+ //
+ // this indicates a possible discontinuity
+ // it may not be be actual discontinuity if
+ // the head of parent N happens to be the tail
+ // of parent N+1
+ //
+ // the next push onto the stack will resolve the question
+ //
+ (*stack)->item->object.flags |= DISCONTINUITY;
+ }
+ }
+ }
+ }
+
+ push_onto_merge_order_stack(stack, head);
+}
+
+//
+// Emit the contents of the stack.
+//
+// Set a non-zero value if we have seen an uninteresting element while
+// writing out the stack.
+//
+static int emit_stack(struct commit_list ** stack, emitter_func emitter)
+{
+ unsigned int uninteresting = 0;
+ unsigned int seen = 0;
+ int stop = 0;
+
+ while (*stack && !stop) {
+
+ struct commit * next = pop_commit(stack);
+
+ if (next->object.flags & DISCONTINUITY) {
+ // reset the uninteresting flag
+ uninteresting = (next->object.flags & UNINTERESTING);
+ } else {
+ // inherit the uninteresting flag from parent that was on the stack
+ uninteresting |= (next->object.flags & UNINTERESTING);
+ }
+
+ next->object.flags |= uninteresting;
+ seen |= next->object.flags;
+
+ if (*stack) {
+ stop = (*emitter) (next);
+ }
+ }
+
+ if (*stack) {
+ free_commit_list(*stack);
+ *stack = NULL;
+ }
+
+ return stop || (seen & UNINTERESTING);
+}
+
+//
+// Sorts an arbitrary epoch into merge order by sorting each epoch
+// of its epoch sequence into order.
+//
+// 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)
+{
+ struct commit *next = head_of_epoch;
+ int ret = 0;
+ int stop = 0;
+
+ ret = parse_commit(head_of_epoch);
+
+ while (next && next->parents && !ret && !stop) {
+
+ struct commit *base = NULL;
+
+ if ((ret = find_next_epoch_boundary(next, &base)))
+ return ret;
+
+ next->object.flags |= BOUNDARY;
+ if (base) {
+ base->object.flags |= BOUNDARY;
+ }
+
+ if (HAS_EXACTLY_ONE_PARENT(next)) {
+
+ while (HAS_EXACTLY_ONE_PARENT(next) && !stop && !ret) {
+
+ stop = (next->object.flags & UNINTERESTING) || (*emitter) (next);
+ next = next->parents->item;
+ ret = parse_commit(next);
+
+ }
+
+ } else {
+
+ struct commit_list *stack = NULL;
+ sort_first_epoch(next, &stack);
+ stop = emit_stack(&stack, emitter);
+ next = base;
+ }
+
+ }
+
+ if (next && !stop) {
+ stop = (*emitter) (next);
+ }
+
+ return ret;
+}
+
+//
+// Sorts the nodes reachable from a starting list in merge order, we
+// first find the base for the starting list and then sort all nodes 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)
+{
+ struct commit_list *stack = NULL;
+ struct commit *base;
+
+ int ret = 0;
+ int stop = 0;
+
+ struct commit_list *reversed = NULL;
+
+ 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 {
+ next->object.flags |= DUPCHECK;
+ commit_list_insert(list->item, &reversed);
+ }
+ }
+ }
+
+ if (!reversed->next) {
+
+ // if there is only one element in the list, we can sort it using
+ // sort_in_merge_order.
+
+ base = reversed->item;
+
+ } else {
+
+ // otherwise, we search for the base of the list
+
+ if ((ret = find_base_for_list(reversed, &base)))
+ return ret;
+
+ if (base) {
+ base->object.flags |= BOUNDARY;
+ }
+
+ while (reversed) {
+ sort_first_epoch(pop_commit(&reversed), &stack);
+ if (reversed) {
+ //
+ // if we have more parents to push, then the
+ // first push for the next parent may (or may not)
+ // represent a discontinuity with respect to the
+ // parent currently on the top of the stack.
+ //
+ // mark it for checking here, and check it
+ // with the next push...see sort_first_epoch for
+ // more details.
+ //
+ stack->item->object.flags |= DISCONTINUITY;
+ }
+ }
+
+ stop = emit_stack(&stack, emitter);
+ }
+
+ if (!stop && base) {
+ ret = sort_in_merge_order(base, emitter);
+ }
+
+ return ret;
+}
diff --git a/epoch.h b/epoch.h
new file mode 100644
--- /dev/null
+++ b/epoch.h
@@ -0,0 +1,14 @@
+#ifndef EPOCH_H
+#define EPOCH_H
+
+
+typedef int (*emitter_func) (struct commit *);
+int sort_list_in_merge_order(struct commit_list *list, emitter_func emitter);
+
+#define UNINTERESTING (1u<<2)
+#define BOUNDARY (1u<<3)
+#define VISITED (1u<<4)
+#define DISCONTINUITY (1u<<5)
+#define DUPCHECK (1u<<6)
+
+#endif /* EPOCH_H */
diff --git a/git-resolve-script b/git-resolve-script
old mode 100644
new mode 100755
diff --git a/object.h b/object.h
--- a/object.h
+++ b/object.h
@@ -15,6 +15,7 @@ struct object {
const char *type;
struct object_list *refs;
struct object_list *attached_deltas;
+ void *util;
};
extern int nr_objs;
diff --git a/rev-list.c b/rev-list.c
--- a/rev-list.c
+++ b/rev-list.c
@@ -1,9 +1,9 @@
#include "cache.h"
#include "commit.h"
+#include "epoch.h"
#define SEEN (1u << 0)
#define INTERESTING (1u << 1)
-#define UNINTERESTING (1u << 2)
static const char rev_list_usage[] =
"usage: git-rev-list [OPTION] commit-id <commit-id>\n"
@@ -11,7 +11,8 @@ static const char rev_list_usage[] =
" --max-age=epoch\n"
" --min-age=epoch\n"
" --header\n"
- " --pretty";
+ " --pretty\n"
+ " --merge-order [ --show-breaks ]";
static int verbose_header = 0;
static int show_parents = 0;
@@ -21,27 +22,49 @@ static const char *prefix = "";
static unsigned long max_age = -1;
static unsigned long min_age = -1;
static int max_count = -1;
+static int merge_order = 0;
+static int show_breaks = 0;
-static void show_commit(struct commit *commit)
+static int show_commit(struct commit *commit)
{
- printf("%s%s", prefix, sha1_to_hex(commit->object.sha1));
- if (show_parents) {
- struct commit_list *parents = commit->parents;
- while (parents) {
- printf(" %s", sha1_to_hex(parents->item->object.sha1));
- parents = parents->next;
- }
- }
- putchar('\n');
- if (verbose_header) {
- const char *buf = commit->buffer;
- if (pretty_print) {
- static char pretty_header[16384];
- pretty_print_commit(commit->buffer, ~0, pretty_header, sizeof(pretty_header));
- buf = pretty_header;
- }
- printf("%s%c", buf, hdr_termination);
+ if (commit->object.flags & UNINTERESTING)
+ return 0;
+ if (min_age != -1 && (commit->date > min_age))
+ return 0;
+ if (max_age != -1 && (commit->date < max_age))
+ return 1;
+ if (max_count != -1 && !max_count--)
+ return 1;
+
+ if (!show_breaks) {
+ printf("%s%s", prefix, sha1_to_hex(commit->object.sha1));
+ if (show_parents) {
+ struct commit_list *parents = commit->parents;
+ while (parents) {
+ printf(" %s", sha1_to_hex(parents->item->object.sha1));
+ parents = parents->next;
+ }
+ }
+ putchar('\n');
+ if (verbose_header) {
+ const char *buf = commit->buffer;
+ if (pretty_print) {
+ static char pretty_header[16384];
+ pretty_print_commit(commit->buffer, ~0, pretty_header, sizeof(pretty_header));
+ buf = pretty_header;
+ }
+ printf("%s%c", buf, hdr_termination);
+ }
+ } else {
+ char c='|';
+ if (commit->object.flags &DISCONTINUITY) {
+ c = '^';
+ } else if (commit->object.flags & BOUNDARY) {
+ c = '=';
+ }
+ printf("%c %s\n", c, sha1_to_hex(commit->object.sha1));
}
+ return 0;
}
static void show_commit_list(struct commit_list *list)
@@ -49,15 +72,8 @@ static void show_commit_list(struct comm
while (list) {
struct commit *commit = pop_most_recent_commit(&list, SEEN);
- if (commit->object.flags & UNINTERESTING)
- continue;
- if (min_age != -1 && (commit->date > min_age))
- continue;
- if (max_age != -1 && (commit->date < max_age))
- break;
- if (max_count != -1 && !max_count--)
- break;
- show_commit(commit);
+ if (show_commit(commit))
+ break;
}
}
@@ -141,6 +157,14 @@ int main(int argc, char **argv)
show_parents = 1;
continue;
}
+ if (!strncmp(arg, "--merge-order", 13)) {
+ merge_order = 1;
+ continue;
+ }
+ if (!strncmp(arg, "--show-breaks", 13)) {
+ show_breaks = 1;
+ continue;
+ }
flags = 0;
if (*arg == '^') {
@@ -148,7 +172,7 @@ int main(int argc, char **argv)
arg++;
limited = 1;
}
- if (get_sha1(arg, sha1))
+ if (get_sha1(arg, sha1) || (show_breaks && !merge_order))
usage(rev_list_usage);
commit = lookup_commit_reference(sha1);
if (!commit || parse_commit(commit) < 0)
@@ -160,9 +184,18 @@ int main(int argc, char **argv)
if (!list)
usage(rev_list_usage);
- if (limited)
- list = limit_list(list);
- show_commit_list(list);
+
+ if (!merge_order) {
+ if (limited)
+ list = limit_list(list);
+
+ show_commit_list(list);
+ } else {
+ if (sort_list_in_merge_order(list, &show_commit)) {
+ die("merge order sort failed\n");
+ }
+ }
+
return 0;
}
diff --git a/t/t6000-rev-list.sh b/t/t6000-rev-list.sh
new file mode 100755
--- /dev/null
+++ b/t/t6000-rev-list.sh
@@ -0,0 +1,173 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Jon Seymour
+#
+
+test_description='Test rev-list --merge-order
+'
+. ./test-lib.sh
+
+function do_commit
+{
+ git-commit-tree "$@" </dev/null
+}
+
+function check_adjacency
+{
+ read previous
+ echo "= $previous"
+ while read next
+ do
+ if ! (git-cat-file commit $previous | grep "^parent $next" >/dev/null)
+ then
+ echo "^ $next"
+ else
+ echo "| $next"
+ fi
+ previous=$next
+ done
+}
+
+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
+ do
+ echo -n "s/${!c}/$c/;"
+ done
+}
+
+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 $root)
+c1=$(do_commit $tree -p $b1)
+export GIT_COMMITTER_NAME=foobar3 # to guarantee that the commit is different
+b2=$(do_commit $tree -p $b1)
+b3=$(do_commit $tree -p $b2)
+c2=$(do_commit $tree -p $c1 -p $b2)
+c3=$(do_commit $tree -p $c2)
+a2=$(do_commit $tree -p $a1)
+a3=$(do_commit $tree -p $a2)
+b4=$(do_commit $tree -p $b3 -p $a3)
+a4=$(do_commit $tree -p $a3 -p $b4 -p $c3)
+l3=$(do_commit $tree -p $a4)
+l4=$(do_commit $tree -p $l3)
+l5=$(do_commit $tree -p $l4)
+echo $l5 > .git/HEAD
+
+git-rev-list --merge-order --show-breaks HEAD | sed "$(sed_script)" > actual-merge-order
+cat > expected-merge-order <<EOF
+= l5
+| l4
+| l3
+= a4
+| c3
+| c2
+| c1
+^ b4
+| b3
+| b2
+| b1
+^ a3
+| a2
+| a1
+| a0
+| l2
+| l1
+| l0
+= root
+EOF
+
+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 ' ')
+
+test_expect_success 'Testing that the rev-list has correct number of entries' '[ $(git-rev-list HEAD | wc -l) -eq 19 ]'
+test_expect_success 'Testing that --merge-order produces the correct result' 'diff expected-merge-order actual-merge-order'
+test_expect_success 'Testing that --merge-order produces as many or fewer discontinuities' '[ $merge_order_adjacency_count -le $normal_adjacency_count ]'
+
+cat > expected-merge-order-1 <<EOF
+c3
+c2
+c1
+b3
+b2
+b1
+a3
+a2
+a1
+a0
+l2
+l1
+l0
+root
+EOF
+
+git-rev-list --merge-order $a3 $b3 $c3 | sed "$(sed_script)" > actual-merge-order-1
+test_expect_success 'Testing multiple heads' 'diff expected-merge-order-1 actual-merge-order-1'
+
+cat > expected-merge-order-2 <<EOF
+c3
+c2
+c1
+b3
+b2
+b1
+a3
+a2
+EOF
+
+git-rev-list --merge-order $a3 $b3 $c3 ^$a1 | sed "$(sed_script)" > actual-merge-order-2
+test_expect_success 'Testing stop' 'diff expected-merge-order-2 actual-merge-order-2'
+
+cat > expected-merge-order-3 <<EOF
+c3
+c2
+c1
+b3
+b2
+b1
+a3
+a2
+a1
+a0
+l2
+EOF
+
+git-rev-list --merge-order $a3 $b3 $c3 ^$l1 | sed "$(sed_script)" > actual-merge-order-3
+test_expect_success 'Testing stop in linear epoch' 'diff expected-merge-order-3 actual-merge-order-3'
+
+cat > expected-merge-order-4 <<EOF
+l5
+l4
+l3
+a4
+c3
+c2
+c1
+b4
+b3
+b2
+b1
+a3
+a2
+a1
+a0
+l2
+EOF
+
+git-rev-list --merge-order $l5 ^$l1 | sed "$(sed_script)" > actual-merge-order-4
+test_expect_success 'Testing start in linear epoch, stop after non-linear epoch' 'diff expected-merge-order-4 actual-merge-order-4'
+
+git-rev-list --merge-order $l5 $l5 ^$l1 2>/dev/null | sed "$(sed_script)" > actual-merge-order-5
+test_expect_success 'Testing duplicated start arguments' 'diff expected-merge-order-4 actual-merge-order-5'
+
+test_done
^ permalink raw reply
* [PATCH] Improve git-rev-list --header output
From: Petr Baudis @ 2005-06-05 13:25 UTC (permalink / raw)
To: torvalds; +Cc: git
Indent the commit header by four spaces like in the --pretty output;
the commit ID is still shown unaligned. This makes the --header output
actually usable for further processing.
Signed-off-by: Petr Baudis <pasky@ucw.cz>
---
commit 7c99469f9820f41138e1bfe1a0f416fa6b56d09d
tree ea9091df859f279e920ec0f8ca9fdc8d9d307e33
parent 441258d08a4bc12121c998e0aa112c8453eb15fc
author Petr Baudis <pasky@ucw.cz> Sun, 05 Jun 2005 15:24:25 +0200
committer Petr Baudis <xpasky@machine.sinus.cz> Sun, 05 Jun 2005 15:24:25 +0200
rev-list.c | 26 ++++++++++++++++++++++++--
1 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/rev-list.c b/rev-list.c
--- a/rev-list.c
+++ b/rev-list.c
@@ -35,12 +35,34 @@ static void show_commit(struct commit *c
putchar('\n');
if (verbose_header) {
const char *buf = commit->buffer;
+ static char pretty_header[16384];
if (pretty_print) {
- static char pretty_header[16384];
pretty_print_commit(commit->buffer, ~0, pretty_header, sizeof(pretty_header));
buf = pretty_header;
+ } else {
+ /* Indent the commit contents by four chars */
+ char *buf2 = pretty_header;
+ char *eol;
+ do {
+ int len = -1;
+
+ eol = strchr(buf, '\n');
+ if (eol || *buf) {
+ strcpy(buf2, " ");
+ buf2 += 4;
+ }
+ if (eol) {
+ eol++;
+ len = eol - buf;
+ strncpy(buf2, buf, len);
+ buf2 += len;
+ buf = eol;
+ } else {
+ strcpy(buf2, buf);
+ }
+ } while (eol);
}
- printf("%s%c", buf, hdr_termination);
+ printf("%s%c", pretty_header, hdr_termination);
}
}
|
--
Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
C++: an octopus made by nailing extra legs onto a dog. -- Steve Taylor
^ permalink raw reply
* Re: git full diff output issues..
From: Junio C Hamano @ 2005-06-05 8:46 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
In-Reply-To: <7v64x5bt9n.fsf@assigned-by-dhcp.cox.net>
>>>>> "JCH" == Junio C Hamano <junkio@cox.net> writes:
>>>>> "LT" == Linus Torvalds <torvalds@osdl.org> writes:
LT> This means, btw, that the "git --diff" format must _not_ do
LT> diff --git a/file /dev/null
LT> deleted file mode 100644
JCH> I just checked, and both built-in and git-external-diff-script
JCH> should be safe about this issue.
Sorry, I spoke too soon about a week and half ago X-<, and I am
bugging you about this because this clearly belongs to "fix"
category not "new stuff".
The case you mentioned (i.e. /dev/null) is fine but rename/copy
is "broken" according to the definition by git-apply.
What do you want the diff-patch format to say for this one?
:100644 100644 SHA1-OLD SHA1-NEW R frotz.c nitfol.c
Currently I am saying:
diff --git a/frotz.c b/nitfol.c
similarity index 89%
rename old frotz.c
rename new nitfol.c
--- a/frotz.c
+++ b/nitfol.c
@@ ...
and this makes git-apply barf, because a/ and b/ names are
different. Is the following what you want? That is, do you
always want p->two->path (name in the right hand side tree)?
diff --git a/nitfol.c b/nitfol.c
similarity index 89%
rename old frotz.c
rename new nitfol.c
--- a/frotz.c
+++ b/nitfol.c
@@ ...
According to the current apply.c, git_header_name() does not
care as long as a/ and b/ names are the same (that is, I could
even say "diff --git a/junkio b/junkio" to make it grok the
above example, as long as I have the correct "rename old" and
"rename new" in the extended header part). In that sense, it
all boils down to which name you, as a human consumer of the
patch, would want to see on the header, if we go the route of
making a/ and b/ name always the same. However I suspect that
this slightly breaks patch reversibility.
If we do care about patch reversibility, having a/ and b/ names
to show the pre- and post- paths like my current output does
(which _does_ break the current apply.c name checking) is
probably the most sensible thing to keep things symmetric. I am
not sure if it is worth it to make the name checking logic in
apply.c more complicated only to support this rename symmetry,
though.
Another possibility; since "diff --git" is a git-specific header
format anyway, we could quote things to help apply.c parsing it,
without introducing too much clutter for ordinary cases. How
about taking advantage of the fact that most pathnames do not
contain spaces nor backslashes, and if we see them we simply
quote, like this?
# no need for quote
diff --git a/frotz.c b/nitfol.c
rename old frotz.c
rename new nitfol.c
# patch for "frotz and nitfol.c"
diff --git a/frotz\ and\ nitfol.c b/frotz\ and\ nitfol.c
# rename but filename has spaces and a backslash
diff --git a/old\ name\\with\ bs b/new\ name\\with\ bs
rename old old name\with bs
rename new new name\with bs
^ permalink raw reply
* [PATCH] cogitio: sh != bash
From: Chris Wedgwood @ 2005-06-05 7:45 UTC (permalink / raw)
To: Petr Baudis; +Cc: Git Mailing List
Without this users of dash (or similar?) as /bin/sh will get
'substitution' warnings.
diff --git a/cg-patch b/cg-patch
--- a/cg-patch
+++ b/cg-patch
@@ -128,7 +128,7 @@ wait
touchfiles="$(git-ls-files --deleted | join -v 2 $gonefile -)"
[ "$touchfiles" ] && touch $touchfiles
-cat $todo | xargs -0 sh -c '
+cat $todo | xargs -0 bash -c '
while [ "$1" ]; do
op="$1"; shift;
case "$op" in
^ permalink raw reply
* Re: [PATCH] git-rev-list --merge-order resynched with Linus' head
From: Jon Seymour @ 2005-06-05 7:33 UTC (permalink / raw)
To: Git Mailing List
In-Reply-To: <2cfc403205060500323de294dc@mail.gmail.com>
This latest revision of my merge-order patch adds support for
git-rev-list arguments of the form:
git-rev-list --merge-order a b c ^x ^y ^z
I believe the output is functionally equivalent to the existing
algorithm, though sorted differently. Please let me know if you find a
case where this is not so.
jon.
^ permalink raw reply
* [PATCH] git-rev-list --merge-order resynched with Linus' head
From: jon @ 2005-06-05 7:23 UTC (permalink / raw)
To: git, jon.seymour, torvalds
[PATCH] Modify git-rev-list to linearise the commit history in merge order.
This patch linearises the GIT commit history graph into merge order
which is defined by invariants specified in Documentation/git-rev-list.txt.
The linearisation produced by this patch is superior in an objective sense
to that produced by the existing git-rev-list implementation in that
the linearisation produced is guaranteed to have the minimum number of
discontinuities, where a discontinuity is defined as an adjacent pair of
commits in the output list which are not related in a direct child-parent
relationship.
With this patch a graph like this:
a4 ---
| \ \
| b4 |
|/ | |
a3 | |
| | |
a2 | |
| | c3
| | |
| | c2
| b3 |
| | /|
| b2 |
| | c1
| | /
| b1
a1 |
| |
a0 |
| /
root
Sorts like this:
= a4
| c3
| c2
| c1
^ b4
| b3
| b2
| b1
^ a3
| a2
| a1
| a0
= root
Instead of this:
= a4
| c3
^ b4
| a3
^ c2
^ b3
^ a2
^ b2
^ c1
^ a1
^ b1
^ a0
= root
A test script, t/t6000-rev-list.sh, includes a test which demonstrates
that the linearisation produced by --merge-order has less discontinuities
than the linearisation produced by git-rev-list without the --merge-order
flag specified. To see this, do the following:
cd t
./t6000-rev-list.sh
cd trash
cat actual-default-order
cat actual-merge-order
The existing behaviour of git-rev-list is preserved, by default. To obtain
the modified behaviour, specify --merge-order or --merge-order --show-breaks
on the command line.
This version of the patch has been tested on the git repository and also on the linux-2.6
repository and has reasonable performance on both - ~50-100% slower than the original algorithm.
This version of the patch has incorporated a functional equivalent of the Linus' output limiting
algorithm into the merge-order algorithm itself. This operates per the notes associated
with Linus' commit 337cb3fb8da45f10fe9a0c3cf571600f55ead2ce.
For more details about this change, refer to Documentation/git-rev-list.txt
and http://blackcubes.dyndns.org/epoch/.
Signed-off-by: Jon Seymour <jon.seymour@gmail.com>
Diverged from 337cb3fb8da45f10fe9a0c3cf571600f55ead2ce 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,7 +9,7 @@ git-rev-list - Lists commit objects in r
SYNOPSIS
--------
-'git-rev-list' <commit>
+'git-rev-list' [ *--max-count*=number ] [ *--max-age*=timestamp ] [ *--min-age*=timestamp ] [ *--merge-order* [ *--show-breaks* ] ] <commit>
DESCRIPTION
-----------
@@ -17,11 +17,39 @@ Lists commit objects in reverse chronolo
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
+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 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.
+
+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.
+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 represent an arbtirary DAG in a linear form.
+
+*--show-breaks* is only valid if *--merge-order* is also specified.
Author
------
Written by Linus Torvalds <torvalds@osdl.org>
+Original *--merge-order* logic by Jon Seymour <jon.seymour@gmail.com>
+
Documentation
--------------
Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -40,9 +40,9 @@ install: $(PROG) $(SCRIPTS)
$(INSTALL) $(PROG) $(SCRIPTS) $(dest)$(bin)
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
+ tag.o delta.o date.o index.o diff-delta.o patch-delta.o epoch.o
LIB_FILE=libgit.a
-LIB_H=cache.h object.h blob.h tree.h commit.h tag.h delta.h
+LIB_H=cache.h object.h blob.h tree.h commit.h tag.h delta.h epoch.h
LIB_H += strbuf.h
LIB_OBJS += strbuf.o
@@ -134,6 +134,7 @@ diffcore-pathspec.o : $(LIB_H) diffcore.
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)
test: all
$(MAKE) -C t/ all
diff --git a/commit.c b/commit.c
--- a/commit.c
+++ b/commit.c
@@ -129,7 +129,7 @@ void free_commit_list(struct commit_list
}
}
-static void insert_by_date(struct commit_list **list, struct commit *item)
+void insert_by_date(struct commit_list **list, struct commit *item)
{
struct commit_list **pp = list;
struct commit_list *p;
@@ -254,3 +254,25 @@ unsigned long pretty_print_commit(const
buf[offset] = '\0';
return offset;
}
+
+struct commit *pop_commit(struct commit_list **stack)
+{
+ struct commit_list *top = *stack;
+ struct commit *item = top ? top->item : NULL;
+
+ if (top) {
+ *stack = top->next;
+ free(top);
+ }
+ return item;
+}
+
+int count_parents(struct commit * commit)
+{
+ int count = 0;
+ struct commit_list * parents = commit->parents;
+ for (count=0;parents; parents=parents->next,count++)
+ ;
+ return count;
+}
+
diff --git a/commit.h b/commit.h
--- a/commit.h
+++ b/commit.h
@@ -32,6 +32,8 @@ void free_commit_list(struct commit_list
void sort_by_date(struct commit_list **list);
+void insert_by_date(struct commit_list **list, struct commit *item);
+
extern unsigned long pretty_print_commit(const char *msg, unsigned long len, char *buf, unsigned long space);
@@ -41,4 +43,7 @@ extern unsigned long pretty_print_commit
struct commit *pop_most_recent_commit(struct commit_list **list,
unsigned int mark);
+struct commit *pop_commit(struct commit_list **stack);
+
+int count_parents(struct commit * commit);
#endif /* COMMIT_H */
diff --git a/epoch.c b/epoch.c
new file mode 100644
--- /dev/null
+++ b/epoch.c
@@ -0,0 +1,568 @@
+/*
+ * Copyright (c) 2005, Jon Seymour
+ *
+ * For more information about epoch theory on which this module is based,
+ * refer to http://blackcubes.dyndns.org/epoch/. That web page defines
+ * terms such as "epoch" and "minimal, non-linear epoch" and provides rationales
+ * for some of the algorithms used here.
+ *
+ */
+#include <stdlib.h>
+#include <openssl/bn.h> // provides arbitrary precision integers required to accurately represent fractional mass
+#include "cache.h"
+#include "commit.h"
+#include "epoch.h"
+
+struct fraction {
+ BIGNUM numerator;
+ BIGNUM denominator;
+};
+
+#define HAS_EXACTLY_ONE_PARENT(n) ((n)->parents && !(n)->parents->next)
+
+static BN_CTX *context = NULL;
+static struct fraction *one = NULL;
+static struct fraction *zero = NULL;
+
+static BN_CTX *get_BN_CTX()
+{
+ if (!context) {
+ context = BN_CTX_new();
+ }
+ return context;
+}
+
+static struct fraction *new_zero()
+{
+ struct fraction *result = xmalloc(sizeof(*result));
+ BN_init(&result->numerator);
+ BN_init(&result->denominator);
+ BN_zero(&result->numerator);
+ BN_one(&result->denominator);
+ return result;
+}
+
+static void clear_fraction(struct fraction *fraction)
+{
+ BN_clear(&fraction->numerator);
+ BN_clear(&fraction->denominator);
+}
+
+static struct fraction *divide(struct fraction *result, struct fraction *fraction, int divisor)
+{
+ BIGNUM bn_divisor;
+
+ 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_clear(&bn_divisor);
+ return result;
+}
+
+static struct fraction *init_fraction(struct fraction *fraction)
+{
+ BN_init(&fraction->numerator);
+ BN_init(&fraction->denominator);
+ BN_zero(&fraction->numerator);
+ BN_one(&fraction->denominator);
+ return fraction;
+}
+
+static struct fraction *get_one()
+{
+ if (!one) {
+ one = new_zero();
+ BN_one(&one->numerator);
+ }
+ return one;
+}
+
+static struct fraction *get_zero()
+{
+ if (!zero) {
+ zero = new_zero();
+ }
+ return zero;
+}
+
+static struct fraction *copy(struct fraction *to, struct fraction *from)
+{
+ BN_copy(&to->numerator, &from->numerator);
+ BN_copy(&to->denominator, &from->denominator);
+ return to;
+}
+
+static struct fraction *add(struct fraction *result, 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_clear(&a);
+ BN_clear(&b);
+ BN_clear(&gcd);
+
+ return result;
+}
+
+static int compare(struct fraction *left, struct fraction *right)
+{
+ BIGNUM a, b;
+
+ int result;
+
+ 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;
+}
+
+struct mass_counter {
+ struct fraction seen;
+ struct fraction pending;
+};
+
+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));
+
+ 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\n", sha1_to_hex(commit->object.sha1));
+ }
+
+ commit->object.util = mass_counter;
+
+ return mass_counter;
+}
+
+static void free_mass_counter(struct mass_counter *counter)
+{
+ clear_fraction(&counter->seen);
+ clear_fraction(&counter->pending);
+ free(counter);
+}
+
+static int find_base_for_list(struct commit_list *list, struct commit **boundary)
+{
+
+ int ret = 0;
+
+ struct commit_list *cleaner = NULL;
+ struct commit_list *pending = NULL;
+ struct commit *head = (list->next ? NULL : list->item);
+
+ *boundary = NULL;
+
+ struct fraction injected;
+
+ init_fraction(&injected);
+
+ for (; list; list = list->next) {
+ if (!(list->item->object.flags & UNINTERESTING)) {
+ new_mass_counter(list->item, get_one());
+ commit_list_insert(list->item, &cleaner);
+ commit_list_insert(list->item, &pending);
+ add(&injected, &injected, get_one());
+ }
+ }
+
+ while (!*boundary && pending && !ret) {
+
+ struct commit *latest = pop_commit(&pending);
+ struct mass_counter *latest_node = (struct mass_counter *) latest->object.util;
+
+ if ((ret = parse_commit(latest)))
+ continue;
+
+ add(&latest_node->seen, &latest_node->seen, &latest_node->pending);
+
+ int num_parents = count_parents(latest);
+
+ if (num_parents) {
+
+ struct fraction distribution;
+
+ divide(init_fraction(&distribution), &latest_node->pending, num_parents);
+
+ struct commit_list *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);
+ commit_list_insert(parent, &cleaner);
+ insert_by_date(&pending, parent);
+ } else {
+ if (!compare(&parent_node->pending, get_zero())) {
+ copy(&parent_node->pending, &distribution);
+ insert_by_date(&pending, parent);
+ } else {
+ add(&parent_node->pending, &parent_node->pending, &distribution);
+ }
+ }
+ }
+ }
+
+ if (!compare(&latest_node->seen, &injected) && (latest != head)) {
+ // if this node has seen one unit of mass, it must be the base, so mark
+ // the boundary which will cause the loop to stop.
+ *boundary = latest;
+ }
+
+ copy(&latest_node->pending, get_zero());
+
+ }
+
+ for (; cleaner;) {
+ struct commit *next = pop_commit(&cleaner);
+ if (!next->object.util) {
+ die("unexpected NULL pointer found in %s @ line %d", __FILE__, __LINE__);
+ }
+ free_mass_counter((struct mass_counter *) next->object.util);
+ next->object.util = NULL;
+ }
+
+ if (pending)
+ free_commit_list(pending);
+
+ return ret;
+
+}
+
+
+//
+// Finds the base node of an epoch.
+//
+// This algorithm uses a conservation of mass approach.
+//
+// The head starts with a weight of 1 and distributes equal portions
+// along each edge to each of its parents. The process continues until
+// one node discovers that it has acquired a total mass of 1 from its
+// descendents. This node is the base node.
+//
+// Importantly the queue of nodes to be processed is sorted in
+// reverse date order and each node occupies at most one
+// position in the queue simultaneously. These two properties
+// do not effect the algorithm's correctness. However assuming most, but
+// not all, timestamps are sane they do prevent the algorithm becoming
+// exponential by ensuring that most nodes are only visited once.
+// This is true because mass is only propagated through the network
+// when we are pretty sure we are not going to see any more coming
+// our way. The occasional bad timestamp will mean that we are
+// wrong, occasionally, but this does not matter too much.
+//
+static int find_base(struct commit *head, struct commit **boundary)
+{
+ int ret = 0;
+
+ struct commit_list *pending = NULL;
+
+ commit_list_insert(head, &pending);
+
+ ret = find_base_for_list(pending, boundary);
+
+ free_commit_list(pending);
+
+ return ret;
+}
+
+//
+// This procedure traverses to the boundary of the first epoch in the epoch
+// sequence of the epoch headed at head_of_epoch. This is either the end of
+// the maximal linear epoch or the base of a minimal non-linear epoch.
+//
+// The queue of pending nodes is sorted in reverse date order and each node
+// is currently in the queue at most once.
+//
+static int find_next_epoch_boundary(struct commit *head_of_epoch, struct commit **boundary)
+{
+ int ret;
+ struct commit *item = head_of_epoch;
+
+ ret = parse_commit(item);
+ if (ret)
+ return ret;
+
+ if (HAS_EXACTLY_ONE_PARENT(item)) {
+
+ // we are at the start of a maximimal linear epoch .. traverse to the end
+
+ // traverse to the end of a maximal linear epoch
+ while (HAS_EXACTLY_ONE_PARENT(item) && !ret) {
+ item = item->parents->item;
+ ret = parse_commit(item);
+ }
+ *boundary = item;
+
+ } else {
+
+ // otherwise, we are at the start of a minimal, non-linear
+ // epoch - find the common base of all parents.
+
+ ret = find_base(item, boundary);
+
+ }
+
+ return ret;
+}
+
+//
+// Returns non-zero if parent is known to be a parent of child.
+//
+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, sizeof(parents->item->object.sha1)))
+ return 1;
+ }
+ return 0;
+}
+
+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 & MERGE_ORDER_BREAK_FLAG)) {
+ if (is_parent_of(top->item, item)) {
+ top->item->object.flags &= ~MERGE_ORDER_BREAK_FLAG;
+ }
+ }
+ commit_list_insert(item, stack);
+}
+
+//
+// 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)
+{
+ struct commit_list *parents;
+ struct commit_list *reversed_parents = NULL;
+
+ head->object.flags |= MERGE_ORDER_VISIT_FLAG;
+
+
+ //
+ // 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.
+ //
+
+ for (parents = head->parents; parents; parents = parents->next)
+ commit_list_insert(parents->item, &reversed_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.
+ //
+
+ for (; reversed_parents;) {
+
+ struct commit *parent = pop_commit(&reversed_parents);
+
+ if (head->object.flags & UNINTERESTING) {
+ parent->object.flags |= UNINTERESTING;
+ }
+
+ if (!(parent->object.flags & MERGE_ORDER_VISIT_FLAG)) {
+ if (parent->object.flags & MERGE_ORDER_EPOCH_FLAG) {
+ if (*stack) {
+ die("something else is on the stack - %s\n", sha1_to_hex((*stack)->item->object.sha1));
+ }
+ push_onto_merge_order_stack(stack, parent);
+ parent->object.flags |= MERGE_ORDER_VISIT_FLAG;
+ } else {
+
+ sort_first_epoch(parent, stack);
+
+ if (reversed_parents) {
+ //
+ // this indicates a possible discontinuity
+ // it may not be be actual discontinuity if
+ // the head of parent N happens to be the tail
+ // of parent N+1
+ //
+ // the next push onto the stack will resolve the question
+ //
+ (*stack)->item->object.flags |= MERGE_ORDER_BREAK_FLAG;
+ }
+ }
+ }
+ }
+
+ push_onto_merge_order_stack(stack, head);
+}
+
+//
+// Sorts an arbitrary epoch into merge order by sorting each epoch
+// of its epoch sequence into order.
+//
+// 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)
+{
+ struct commit *next;
+ int ret = 0;
+ int stop = 0;
+ unsigned int flags_seen = 0;
+
+ ret = parse_commit(head_of_epoch);
+
+ for (next = head_of_epoch; next && next->parents && !ret && !stop;) {
+
+ struct commit *base = NULL;
+ unsigned int uninteresting = 0;
+
+
+ ret = find_next_epoch_boundary(next, &base);
+ if (ret)
+ return ret;
+
+ next->object.flags |= MERGE_ORDER_EPOCH_FLAG;
+ if (base) {
+ base->object.flags |= MERGE_ORDER_EPOCH_FLAG;
+ }
+
+ if (HAS_EXACTLY_ONE_PARENT(next)) {
+
+ while (HAS_EXACTLY_ONE_PARENT(next) && !stop && !ret) {
+ next->object.flags |= (uninteresting |= (next->object.flags & UNINTERESTING));
+ stop = (*emitter) (next);
+ next = next->parents->item;
+ ret = parse_commit(next);
+ }
+
+ } else {
+
+ struct commit_list *stack = NULL;
+ sort_first_epoch(next, &stack);
+ while (stack && !stop) {
+ next = pop_commit(&stack);
+
+ if (next->object.flags & MERGE_ORDER_BREAK_FLAG) {
+ uninteresting = (next->object.flags & UNINTERESTING);
+ } else {
+ next->object.flags |= (uninteresting |= (next->object.flags & UNINTERESTING));
+ }
+
+ flags_seen |= next->object.flags;
+ if (stack) {
+ stop = (*emitter) (next);
+ }
+ }
+ if (stack) {
+ free_commit_list(stack);
+ }
+ stop = stop || (flags_seen & UNINTERESTING);
+ }
+
+ }
+
+ if (next && !stop) {
+ stop = (*emitter) (next);
+ }
+
+ return ret;
+}
+
+int sort_list_in_merge_order(struct commit_list *list, emitter_func emitter)
+{
+ struct commit_list *stack = NULL;
+ struct commit *base;
+ struct commit *next;
+ unsigned int flags_seen = 0;
+ int ret = 0;
+ int stop = 0;
+
+ if (!list->next) {
+
+ base = list->item;
+
+ } else {
+
+ if ((ret = find_base_for_list(list, &base)))
+ return ret;
+
+ if (base) {
+ base->object.flags |= MERGE_ORDER_EPOCH_FLAG;
+ }
+
+ struct commit_list *reversed = NULL;
+
+ for (; list; list = list->next)
+ commit_list_insert(list->item, &reversed);
+
+ while (reversed) {
+ sort_first_epoch(pop_commit(&reversed), &stack);
+ if (reversed) {
+ // this may or may not be a break - see sort_first_epoch
+ stack->item->object.flags |= MERGE_ORDER_BREAK_FLAG;
+ }
+ }
+
+ unsigned int uninteresting = 0;
+
+ while (stack && !stop) {
+
+ next = pop_commit(&stack);
+
+ if (next->object.flags & MERGE_ORDER_BREAK_FLAG) {
+ uninteresting = (next->object.flags & UNINTERESTING);
+ } else {
+ next->object.flags |= (uninteresting |= (next->object.flags & UNINTERESTING));
+ }
+
+ flags_seen |= next->object.flags;
+
+ if (stack) {
+ stop = (*emitter) (next);
+ }
+ }
+
+ if (stack) {
+ free_commit_list(stack);
+ }
+ }
+
+ if (!stop && base && !(flags_seen & UNINTERESTING)) {
+ ret = sort_in_merge_order(base, emitter);
+ }
+
+ return ret;
+}
diff --git a/epoch.h b/epoch.h
new file mode 100644
--- /dev/null
+++ b/epoch.h
@@ -0,0 +1,13 @@
+#ifndef EPOCH_H
+#define EPOCH_H
+
+typedef int (*emitter_func) (struct commit *);
+
+int sort_list_in_merge_order(struct commit_list *list, emitter_func emitter);
+
+#define UNINTERESTING (1u<<2)
+#define MERGE_ORDER_EPOCH_FLAG (1u<<3)
+#define MERGE_ORDER_VISIT_FLAG (1u<<4)
+#define MERGE_ORDER_BREAK_FLAG (1u<<5)
+
+#endif /* EPOCH_H */
diff --git a/object.h b/object.h
--- a/object.h
+++ b/object.h
@@ -15,6 +15,7 @@ struct object {
const char *type;
struct object_list *refs;
struct object_list *attached_deltas;
+ void *util;
};
extern int nr_objs;
diff --git a/rev-list.c b/rev-list.c
--- a/rev-list.c
+++ b/rev-list.c
@@ -1,9 +1,9 @@
#include "cache.h"
#include "commit.h"
+#include "epoch.h"
#define SEEN (1u << 0)
#define INTERESTING (1u << 1)
-#define UNINTERESTING (1u << 2)
static const char rev_list_usage[] =
"usage: git-rev-list [OPTION] commit-id <commit-id>\n"
@@ -11,7 +11,8 @@ static const char rev_list_usage[] =
" --max-age=epoch\n"
" --min-age=epoch\n"
" --header\n"
- " --pretty";
+ " --pretty\n"
+ " --merge-order [ --show-breaks ]";
static int verbose_header = 0;
static int show_parents = 0;
@@ -21,27 +22,49 @@ static const char *prefix = "";
static unsigned long max_age = -1;
static unsigned long min_age = -1;
static int max_count = -1;
+static int merge_order = 0;
+static int show_breaks = 0;
-static void show_commit(struct commit *commit)
+static int show_commit(struct commit *commit)
{
- printf("%s%s", prefix, sha1_to_hex(commit->object.sha1));
- if (show_parents) {
- struct commit_list *parents = commit->parents;
- while (parents) {
- printf(" %s", sha1_to_hex(parents->item->object.sha1));
- parents = parents->next;
- }
- }
- putchar('\n');
- if (verbose_header) {
- const char *buf = commit->buffer;
- if (pretty_print) {
- static char pretty_header[16384];
- pretty_print_commit(commit->buffer, ~0, pretty_header, sizeof(pretty_header));
- buf = pretty_header;
- }
- printf("%s%c", buf, hdr_termination);
+ if (commit->object.flags & UNINTERESTING)
+ return 0;
+ if (min_age != -1 && (commit->date > min_age))
+ return 0;
+ if (max_age != -1 && (commit->date < max_age))
+ return 1;
+ if (max_count != -1 && !max_count--)
+ return 1;
+
+ if (!show_breaks) {
+ printf("%s%s", prefix, sha1_to_hex(commit->object.sha1));
+ if (show_parents) {
+ struct commit_list *parents = commit->parents;
+ while (parents) {
+ printf(" %s", sha1_to_hex(parents->item->object.sha1));
+ parents = parents->next;
+ }
+ }
+ putchar('\n');
+ if (verbose_header) {
+ const char *buf = commit->buffer;
+ if (pretty_print) {
+ static char pretty_header[16384];
+ pretty_print_commit(commit->buffer, ~0, pretty_header, sizeof(pretty_header));
+ buf = pretty_header;
+ }
+ printf("%s%c", buf, hdr_termination);
+ }
+ } else {
+ char c='|';
+ if (commit->object.flags & MERGE_ORDER_BREAK_FLAG) {
+ c = '^';
+ } else if (commit->object.flags & MERGE_ORDER_EPOCH_FLAG) {
+ c = '=';
+ }
+ printf("%c %s\n", c, sha1_to_hex(commit->object.sha1));
}
+ return 0;
}
static void show_commit_list(struct commit_list *list)
@@ -49,15 +72,8 @@ static void show_commit_list(struct comm
while (list) {
struct commit *commit = pop_most_recent_commit(&list, SEEN);
- if (commit->object.flags & UNINTERESTING)
- continue;
- if (min_age != -1 && (commit->date > min_age))
- continue;
- if (max_age != -1 && (commit->date < max_age))
- break;
- if (max_count != -1 && !max_count--)
- break;
- show_commit(commit);
+ if (show_commit(commit))
+ break;
}
}
@@ -141,6 +157,14 @@ int main(int argc, char **argv)
show_parents = 1;
continue;
}
+ if (!strncmp(arg, "--merge-order", 13)) {
+ merge_order = 1;
+ continue;
+ }
+ if (!strncmp(arg, "--show-breaks", 13)) {
+ show_breaks = 1;
+ continue;
+ }
flags = 0;
if (*arg == '^') {
@@ -148,7 +172,7 @@ int main(int argc, char **argv)
arg++;
limited = 1;
}
- if (get_sha1(arg, sha1))
+ if (get_sha1(arg, sha1) || (show_breaks && !merge_order))
usage(rev_list_usage);
commit = lookup_commit_reference(sha1);
if (!commit || parse_commit(commit) < 0)
@@ -160,9 +184,18 @@ int main(int argc, char **argv)
if (!list)
usage(rev_list_usage);
- if (limited)
- list = limit_list(list);
- show_commit_list(list);
+
+ if (!merge_order) {
+ if (limited)
+ list = limit_list(list);
+
+ show_commit_list(list);
+ } else {
+ if (sort_list_in_merge_order(list, &show_commit)) {
+ die("merge order sort failed\n");
+ }
+ }
+
return 0;
}
diff --git a/t/t6000-rev-list.sh b/t/t6000-rev-list.sh
new file mode 100755
--- /dev/null
+++ b/t/t6000-rev-list.sh
@@ -0,0 +1,116 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Jon Seymour
+#
+
+test_description='Test rev-list --merge-order
+'
+. ./test-lib.sh
+
+function do_commit
+{
+ git-commit-tree "$@" </dev/null
+}
+
+function check_adjacency
+{
+ read previous
+ echo "= $previous"
+ while read next
+ do
+ if ! (git-cat-file commit $previous | grep "^parent $next" >/dev/null)
+ then
+ echo "^ $next"
+ else
+ echo "| $next"
+ fi
+ previous=$next
+ done
+}
+
+function sed_script
+{
+ for c in root a0 a1 a2 a3 a4 b1 b2 b3 b4 c1 c2 c3
+ do
+ echo -n "s/${!c}/$c/;"
+ done
+}
+
+date >path0
+git-update-cache --add path0
+tree=$(git-write-tree)
+root=$(do_commit $tree)
+export GIT_COMMITTER_NAME=foobar # to guarantee that the commit is different
+a0=$(do_commit $tree -p $root)
+a1=$(do_commit $tree -p $a0)
+export GIT_COMMITTER_NAME=foobar2 # to guarantee that the commit is different
+b1=$(do_commit $tree -p $root)
+c1=$(do_commit $tree -p $b1)
+export GIT_COMMITTER_NAME=foobar3 # to guarantee that the commit is different
+b2=$(do_commit $tree -p $b1)
+b3=$(do_commit $tree -p $b2)
+c2=$(do_commit $tree -p $c1 -p $b2)
+c3=$(do_commit $tree -p $c2)
+a2=$(do_commit $tree -p $a1)
+a3=$(do_commit $tree -p $a2)
+b4=$(do_commit $tree -p $b3 -p $a3)
+a4=$(do_commit $tree -p $a3 -p $b4 -p $c3)
+echo $a4 > .git/HEAD
+
+git-rev-list --merge-order --show-breaks HEAD | sed "$(sed_script)" > actual-merge-order
+cat > expected-merge-order <<EOF
+= a4
+| c3
+| c2
+| c1
+^ b4
+| b3
+| b2
+| b1
+^ a3
+| a2
+| a1
+| a0
+= root
+EOF
+
+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 ' ')
+
+test_expect_success 'Testing that the rev-list has correct number of entries' '[ $(git-rev-list HEAD | wc -l) -eq 13 ]'
+test_expect_success 'Testing that --merge-order produces the correct result' 'diff expected-merge-order actual-merge-order'
+test_expect_success 'Testing that --merge-order produces as many or fewer discontinuities' '[ $merge_order_adjacency_count -le $normal_adjacency_count ]'
+
+cat > expected-merge-order-1 <<EOF
+c3
+c2
+c1
+b3
+b2
+b1
+a3
+a2
+a1
+a0
+root
+EOF
+
+git-rev-list --merge-order $a3 $b3 $c3 | sed "$(sed_script)" > actual-merge-order-1
+test_expect_success 'Testing multiple heads' 'diff expected-merge-order-1 actual-merge-order-1'
+
+cat > expected-merge-order-2 <<EOF
+c3
+c2
+c1
+b3
+b2
+b1
+a3
+a2
+EOF
+
+git-rev-list --merge-order $a3 $b3 $c3 ^$a1 | sed "$(sed_script)" > actual-merge-order-2
+test_expect_success 'Testing stop' 'diff expected-merge-order-2 actual-merge-order-2'
+
+test_done
^ permalink raw reply
* [PATCH-CAREFUL/RENAME] rename git-rpush and git-rpull to git-ssh-push and git-ssh-pull
From: Junio C Hamano @ 2005-06-05 6:57 UTC (permalink / raw)
To: Daniel Barkalow; +Cc: Linus Torvalds, git
In-Reply-To: <Pine.LNX.4.21.0506050132590.30848-100000@iabervon.org>
In preparation for 1.0 release, this make the command names
consistent with others in git-*-pull family.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
*** Linus, please be careful with this patch. This uses
*** extended git "rename" diff format, and I doubt that
*** traditional patch -p1 would do the right thing. I am
*** sending it because Dan asked me to, and also it may serve as
*** a good example if your plan is to support git extended diff
*** format in your "dotest" toolchain. I'll send the same patch
*** in the traditional diff format in a separate message.
Documentation/git-ssh-pull.txt | 10 +++++-----
Documentation/git-ssh-push.txt | 10 +++++-----
Documentation/git.txt | 6 +++---
Makefile | 6 +++---
ssh-pull.c | 4 ++--
ssh-push.c | 14 +++++++-------
6 files changed, 25 insertions(+), 25 deletions(-)
git-diff-cache -p -B -C HEAD
diff --git a/Documentation/git-rpull.txt b/Documentation/git-ssh-pull.txt
similarity index 89%
rename old Documentation/git-rpull.txt
rename new Documentation/git-ssh-pull.txt
--- a/Documentation/git-rpull.txt
+++ b/Documentation/git-ssh-pull.txt
@@ -1,20 +1,20 @@
-git-rpull(1)
-============
+git-ssh-pull(1)
+===============
v0.1, May 2005
NAME
----
-git-rpull - Pulls from a remote repository over ssh connection
+git-ssh-pull - Pulls from a remote repository over ssh connection
SYNOPSIS
--------
-'git-rpull' [-c] [-t] [-a] [-d] [-v] commit-id url
+'git-ssh-pull' [-c] [-t] [-a] [-d] [-v] commit-id url
DESCRIPTION
-----------
-Pulls from a remote repository over ssh connection, invoking git-rpush on
+Pulls from a remote repository over ssh connection, invoking git-ssh-push on
the other end.
OPTIONS
diff --git a/Documentation/git-rpush.txt b/Documentation/git-ssh-push.txt
similarity index 71%
rename old Documentation/git-rpush.txt
rename new Documentation/git-ssh-push.txt
--- a/Documentation/git-rpush.txt
+++ b/Documentation/git-ssh-push.txt
@@ -1,19 +1,19 @@
-git-rpush(1)
-============
+git-ssh-push(1)
+===============
v0.1, May 2005
NAME
----
-git-rpush - Helper "server-side" program used by git-rpull
+git-ssh-push - Helper "server-side" program used by git-ssh-pull
SYNOPSIS
--------
-'git-rpush'
+'git-ssh-push'
DESCRIPTION
-----------
-Helper "server-side" program used by git-rpull.
+Helper "server-side" program used by git-ssh-pull.
Author
diff --git a/Documentation/git.txt b/Documentation/git.txt
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -148,7 +148,7 @@ link:git-resolve-script.html[git-resolve
link:git-tag-script.html[git-tag-script]::
An example script to create a tag object signed with GPG
-link:git-rpull.html[git-rpull]::
+link:git-ssh-pull.html[git-ssh-pull]::
Pulls from a remote repository over ssh connection
Interogators:
@@ -156,8 +156,8 @@ Interogators:
link:git-diff-helper.html[git-diff-helper]::
Generates patch format output for git-diff-*
-link:git-rpush.html[git-rpush]::
- Helper "server-side" program used by git-rpull
+link:git-ssh-push.html[git-ssh-push]::
+ Helper "server-side" program used by git-ssh-pull
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -30,7 +30,7 @@ PROG= git-update-cache git-diff-files
git-checkout-cache git-diff-tree git-rev-tree git-ls-files \
git-check-files git-ls-tree git-merge-base git-merge-cache \
git-unpack-file git-export git-diff-cache git-convert-cache \
- git-http-pull git-rpush git-rpull git-rev-list git-mktag \
+ git-http-pull git-ssh-push git-ssh-pull git-rev-list git-mktag \
git-diff-helper git-tar-tree git-local-pull git-write-blob \
git-get-tar-commit-id git-mkdelta git-apply git-stripspace
@@ -105,8 +105,8 @@ git-diff-cache: diff-cache.c
git-convert-cache: convert-cache.c
git-http-pull: http-pull.c pull.c
git-local-pull: local-pull.c pull.c
-git-rpush: rsh.c
-git-rpull: rsh.c pull.c
+git-ssh-push: rsh.c
+git-ssh-pull: rsh.c pull.c
git-rev-list: rev-list.c
git-mktag: mktag.c
git-diff-helper: diff-helper.c
diff --git a/rpull.c b/ssh-pull.c
similarity index 97%
rename old rpull.c
rename new ssh-pull.c
--- a/rpull.c
+++ b/ssh-pull.c
@@ -62,13 +62,13 @@ int main(int argc, char **argv)
arg++;
}
if (argc < arg + 2) {
- usage("git-rpull [-c] [-t] [-a] [-v] [-d] commit-id url");
+ usage("git-ssh-pull [-c] [-t] [-a] [-v] [-d] commit-id url");
return 1;
}
commit_id = argv[arg];
url = argv[arg + 1];
- if (setup_connection(&fd_in, &fd_out, "git-rpush", url, arg, argv + 1))
+ if (setup_connection(&fd_in, &fd_out, "git-ssh-push", url, arg, argv + 1))
return 1;
if (get_version())
diff --git a/rpush.c b/ssh-push.c
similarity index 93%
rename old rpush.c
rename new ssh-push.c
--- a/rpush.c
+++ b/ssh-push.c
@@ -16,7 +16,7 @@ int serve_object(int fd_in, int fd_out)
do {
size = read(fd_in, sha1 + posn, 20 - posn);
if (size < 0) {
- perror("git-rpush: read ");
+ perror("git-ssh-push: read ");
return -1;
}
if (!size)
@@ -30,7 +30,7 @@ int serve_object(int fd_in, int fd_out)
buf = map_sha1_file(sha1, &objsize);
if (!buf) {
- fprintf(stderr, "git-rpush: could not find %s\n",
+ fprintf(stderr, "git-ssh-push: could not find %s\n",
sha1_to_hex(sha1));
remote = -1;
}
@@ -45,9 +45,9 @@ int serve_object(int fd_in, int fd_out)
size = write(fd_out, buf + posn, objsize - posn);
if (size <= 0) {
if (!size) {
- fprintf(stderr, "git-rpush: write closed");
+ fprintf(stderr, "git-ssh-push: write closed");
} else {
- perror("git-rpush: write ");
+ perror("git-ssh-push: write ");
}
return -1;
}
@@ -71,7 +71,7 @@ void service(int fd_in, int fd_out) {
retval = read(fd_in, &type, 1);
if (retval < 1) {
if (retval < 0)
- perror("rpush: read ");
+ perror("ssh-push: read ");
return;
}
if (type == 'v' && serve_version(fd_in, fd_out))
@@ -91,12 +91,12 @@ int main(int argc, char **argv)
arg++;
}
if (argc < arg + 2) {
- usage("git-rpush [-c] [-t] [-a] commit-id url");
+ usage("git-ssh-push [-c] [-t] [-a] commit-id url");
return 1;
}
commit_id = argv[arg];
url = argv[arg + 1];
- if (setup_connection(&fd_in, &fd_out, "git-rpull", url, arg, argv + 1))
+ if (setup_connection(&fd_in, &fd_out, "git-ssh-pull", url, arg, argv + 1))
return 1;
service(fd_in, fd_out);
^ permalink raw reply
* Re: [PATCH] git-daemon server
From: Junio C Hamano @ 2005-06-05 6:48 UTC (permalink / raw)
To: Daniel Barkalow; +Cc: Linus Torvalds, git
In-Reply-To: <Pine.LNX.4.21.0506050132590.30848-100000@iabervon.org>
>>>>> "DB" == Daniel Barkalow <barkalow@iabervon.org> writes:
DB> On Sat, 4 Jun 2005, Junio C Hamano wrote:
>> BTW, don't we want to have them renamed to git-ssh-pull (and
>> git-ssh-push) for consistency with other transports, before 1.0
>> happens?
DB> I think that would be good, but I'm still using a really old version of
DB> git for my development, so I don't have your diff rename support; could
DB> you send a patch to do it?
I am sorry, but you lost me. I am afraid that I do not
understand what you are asking me to send you: a patch to do
what? Tell your old git how to do -M/-C? That would be "the
tip of Linus repository"...
Ah (lightbulb!), are you asking me to send the patch in the
git-extended rename diff format, like this?
$ git-diff-cache -p -B -C HEAD
diff --git a/Documentation/git-rpull.txt b/Documentation/git-ssh-pull.txt
similarity index 89%
rename old Documentation/git-rpull.txt
rename new Documentation/git-ssh-pull.txt
--- a/Documentation/git-rpull.txt
+++ b/Documentation/git-ssh-pull.txt
@@ -1,20 +1,20 @@
-git-rpull(1)
-============
+git-ssh-pull(1)
+===============
v0.1, May 2005
...
I am not sure if this is suitable for patch submission. I did
the rename/copy stuff mostly for software archaeology purposes
(meaning, you examine what is in your repository), not to
generate patches for submission via e-mail.
It certainly is a good test for the git-apply stuff Linus has
been working on, and in addition it would have a good amusement
value to see how well it would work (or how badly it would barf
;-), but I suspect Linus (or, rather, his "dotest" script) would
appreciate it more if it came in the traditional diff format
that does not use the rename stuff. I dunno. Let's ask Linus
first.
Linus, can your workflow grok things like this, or do you prefer
patch submission to use traditional diff format without renames?
^ permalink raw reply
* [PATCH] pull: gracefully recover from delta retrieval failure.
From: Junio C Hamano @ 2005-06-05 6:11 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
This addresses a concern raised by Jason McMullan in the mailing
list discussion. After retrieving and storing a potentially
deltified object, pull logic tries to check and fulfil its delta
dependency. When the pull procedure is killed at this point,
however, there was no easy way to recover by re-running pull,
since next run would have found that we already have that
deltified object and happily reported success, without really
checking its delta dependency is satisfied.
This patch introduces --recover option to git-*-pull family
which causes them to re-validate dependency of deltified objects
we are fetching. A new test t5100-delta-pull.sh covers such a
failure mode.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
*** Linus, from now on I will go into "calming down" mode and
*** refrain myself from sending you too many "new" stuff, until
*** you tell me otherwise. I will concentrate on fixes like
*** this one and the "diff-* -B fix" patches I sent you earlier.
*** Perhaps I would also work on CVS migration documents if you
*** would like me to help you in that area as well.
*** Definitely things like the idea of diff-tree switching its
*** pathspec according rename detection results would not be
*** something I'll be bugging you about until 1.0 happens;
*** unless you tell me otherwise, that is.
Documentation/git-http-pull.txt | 5 ++
Documentation/git-local-pull.txt | 5 ++
Documentation/git-rpull.txt | 5 ++
pull.h | 4 +-
http-pull.c | 4 +-
local-pull.c | 4 +-
pull.c | 15 +++++--
rpull.c | 4 +-
t/t5100-delta-pull.sh | 79 ++++++++++++++++++++++++++++++++++++++
9 files changed, 113 insertions(+), 12 deletions(-)
diff --git a/Documentation/git-http-pull.txt b/Documentation/git-http-pull.txt
--- a/Documentation/git-http-pull.txt
+++ b/Documentation/git-http-pull.txt
@@ -9,7 +9,7 @@ git-http-pull - Downloads a remote GIT r
SYNOPSIS
--------
-'git-http-pull' [-c] [-t] [-a] [-v] [-d] commit-id url
+'git-http-pull' [-c] [-t] [-a] [-v] [-d] [--recover] commit-id url
DESCRIPTION
-----------
@@ -25,6 +25,9 @@ Downloads a remote GIT repository via HT
Do not check for delta base objects (use this option
only when you know the remote repository is not
deltified).
+--recover::
+ Check dependency of deltified object more carefully than
+ usual, to recover after earlier pull that was interrupted.
-v::
Report what is downloaded.
diff --git a/Documentation/git-local-pull.txt b/Documentation/git-local-pull.txt
--- a/Documentation/git-local-pull.txt
+++ b/Documentation/git-local-pull.txt
@@ -9,7 +9,7 @@ git-local-pull - Duplicates another GIT
SYNOPSIS
--------
-'git-local-pull' [-c] [-t] [-a] [-l] [-s] [-n] [-v] [-d] commit-id path
+'git-local-pull' [-c] [-t] [-a] [-l] [-s] [-n] [-v] [-d] [--recover] commit-id path
DESCRIPTION
-----------
@@ -27,6 +27,9 @@ OPTIONS
Do not check for delta base objects (use this option
only when you know the remote repository is not
deltified).
+--recover::
+ Check dependency of deltified object more carefully than
+ usual, to recover after earlier pull that was interrupted.
-v::
Report what is downloaded.
diff --git a/Documentation/git-rpull.txt b/Documentation/git-rpull.txt
--- a/Documentation/git-rpull.txt
+++ b/Documentation/git-rpull.txt
@@ -10,7 +10,7 @@ git-rpull - Pulls from a remote reposito
SYNOPSIS
--------
-'git-rpull' [-c] [-t] [-a] [-d] [-v] commit-id url
+'git-rpull' [-c] [-t] [-a] [-d] [-v] [--recover] commit-id url
DESCRIPTION
-----------
@@ -29,6 +29,9 @@ OPTIONS
Do not check for delta base objects (use this option
only when you know the remote repository is not
deltified).
+--recover::
+ Check dependency of deltified object more carefully than
+ usual, to recover after earlier pull that was interrupted.
-v::
Report what is downloaded.
diff --git a/pull.h b/pull.h
--- a/pull.h
+++ b/pull.h
@@ -13,7 +13,9 @@ extern int get_history;
/** Set to fetch the trees in the commit history. **/
extern int get_all;
-/* Set to zero to skip the check for delta object base. */
+/* Set to zero to skip the check for delta object base;
+ * set to two to check delta dependency even for objects we already have.
+ */
extern int get_delta;
/* Set to be verbose */
diff --git a/http-pull.c b/http-pull.c
--- a/http-pull.c
+++ b/http-pull.c
@@ -105,6 +105,8 @@ int main(int argc, char **argv)
get_history = 1;
} else if (argv[arg][1] == 'd') {
get_delta = 0;
+ } else if (!strcmp(argv[arg], "--recover")) {
+ get_delta = 2;
} else if (argv[arg][1] == 'a') {
get_all = 1;
get_tree = 1;
@@ -115,7 +117,7 @@ int main(int argc, char **argv)
arg++;
}
if (argc < arg + 2) {
- usage("git-http-pull [-c] [-t] [-a] [-d] [-v] commit-id url");
+ usage("git-http-pull [-c] [-t] [-a] [-d] [-v] [--recover] commit-id url");
return 1;
}
commit_id = argv[arg];
diff --git a/local-pull.c b/local-pull.c
--- a/local-pull.c
+++ b/local-pull.c
@@ -74,7 +74,7 @@ int fetch(unsigned char *sha1)
}
static const char *local_pull_usage =
-"git-local-pull [-c] [-t] [-a] [-l] [-s] [-n] [-v] [-d] commit-id path";
+"git-local-pull [-c] [-t] [-a] [-l] [-s] [-n] [-v] [-d] [--recover] commit-id path";
/*
* By default we only use file copy.
@@ -94,6 +94,8 @@ int main(int argc, char **argv)
get_history = 1;
else if (argv[arg][1] == 'd')
get_delta = 0;
+ else if (!strcmp(argv[arg], "--recover"))
+ get_delta = 2;
else if (argv[arg][1] == 'a') {
get_all = 1;
get_tree = 1;
diff --git a/pull.c b/pull.c
--- a/pull.c
+++ b/pull.c
@@ -6,6 +6,7 @@
int get_tree = 0;
int get_history = 0;
+/* 1 means "get delta", 2 means "really check delta harder */
int get_delta = 1;
int get_all = 0;
int get_verbosely = 0;
@@ -32,12 +33,16 @@ static void report_missing(const char *w
static int make_sure_we_have_it(const char *what, unsigned char *sha1)
{
- int status;
- if (has_sha1_file(sha1))
+ int status = 0;
+
+ if (!has_sha1_file(sha1)) {
+ status = fetch(sha1);
+ if (status && what)
+ report_missing(what, sha1);
+ }
+ else if (get_delta < 2)
return 0;
- status = fetch(sha1);
- if (status && what)
- report_missing(what, sha1);
+
if (get_delta) {
char delta_sha1[20];
status = sha1_delta_base(sha1, delta_sha1);
diff --git a/rpull.c b/rpull.c
--- a/rpull.c
+++ b/rpull.c
@@ -52,6 +52,8 @@ int main(int argc, char **argv)
get_history = 1;
} else if (argv[arg][1] == 'd') {
get_delta = 0;
+ } else if (!strcmp(argv[arg], "--recover")) {
+ get_delta = 2;
} else if (argv[arg][1] == 'a') {
get_all = 1;
get_tree = 1;
@@ -62,7 +64,7 @@ int main(int argc, char **argv)
arg++;
}
if (argc < arg + 2) {
- usage("git-rpull [-c] [-t] [-a] [-v] [-d] commit-id url");
+ usage("git-rpull [-c] [-t] [-a] [-v] [-d] [--recover] commit-id url");
return 1;
}
commit_id = argv[arg];
diff --git a/t/t5100-delta-pull.sh b/t/t5100-delta-pull.sh
new file mode 100644
--- /dev/null
+++ b/t/t5100-delta-pull.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Test pulling deltified objects
+
+'
+. ./test-lib.sh
+
+locate_obj='s|\(..\)|.git/objects/\1/|'
+
+test_expect_success \
+ setup \
+ 'cat ../README >a &&
+ git-update-cache --add a &&
+ a0=`git-ls-files --stage |
+ sed -e '\''s/^[0-7]* \([0-9a-f]*\) .*/\1/'\''` &&
+
+ sed -e 's/test/TEST/g' ../README >a &&
+ git-update-cache a &&
+ a1=`git-ls-files --stage |
+ sed -e '\''s/^[0-7]* \([0-9a-f]*\) .*/\1/'\''` &&
+ tree=`git-write-tree` &&
+ commit=`git-commit-tree $tree </dev/null` &&
+ a0f=`echo "$a0" | sed -e "$locate_obj"` &&
+ a1f=`echo "$a1" | sed -e "$locate_obj"` &&
+ echo commit $commit &&
+ echo a0 $a0 &&
+ echo a1 $a1 &&
+ ls -l $a0f $a1f &&
+ echo $commit >.git/HEAD &&
+ git-mkdelta -v $a0 $a1 &&
+ ls -l $a0f $a1f'
+
+# Now commit has a tree that records delitified "a" whose SHA1 is a1.
+# Create a new repo and pull this commit into it.
+
+test_expect_success \
+ 'setup and cd into new repo' \
+ 'mkdir dest && cd dest && rm -fr .git && git-init-db'
+
+test_expect_success \
+ 'pull from deltified repo into a new repo without -d' \
+ 'rm -fr .git a && git-init-db &&
+ git-local-pull -v -a $commit ../.git/ &&
+ git-cat-file blob $a1 >a &&
+ diff -u a ../a'
+
+test_expect_failure \
+ 'pull from deltified repo into a new repo with -d' \
+ 'rm -fr .git a && git-init-db &&
+ git-local-pull -v -a -d $commit ../.git/ &&
+ git-cat-file blob $a1 >a &&
+ diff -u a ../a'
+
+test_expect_failure \
+ 'pull from deltified repo after delta failure without --recover' \
+ 'rm -f a &&
+ git-local-pull -v -a $commit ../.git/ &&
+ git-cat-file blob $a1 >a &&
+ diff -u a ../a'
+
+test_expect_success \
+ 'pull from deltified repo after delta failure with --recover' \
+ 'rm -f a &&
+ git-local-pull -v -a --recover $commit ../.git/ &&
+ git-cat-file blob $a1 >a &&
+ diff -u a ../a'
+
+test_expect_success \
+ 'missing-tree or missing-blob should be re-fetched without --recover' \
+ 'rm -f a $a0f $a1f &&
+ git-local-pull -v -a $commit ../.git/ &&
+ git-cat-file blob $a1 >a &&
+ diff -u a ../a'
+
+test_done
+
------------
^ permalink raw reply
* Re: [PATCH] git-daemon server
From: Daniel Barkalow @ 2005-06-05 5:38 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vk6l9flzr.fsf@assigned-by-dhcp.cox.net>
On Sat, 4 Jun 2005, Junio C Hamano wrote:
> I'd like to see your "-w" extention merged into mainline soon
> (both push and pull).
I have the patches worked out now, and I'm planning to send them on
Monday.
> BTW, don't we want to have them renamed to git-ssh-pull (and
> git-ssh-push) for consistency with other transports, before 1.0
> happens?
I think that would be good, but I'm still using a really old version of
git for my development, so I don't have your diff rename support; could
you send a patch to do it?
-Daniel
*This .sig left intentionally blank*
^ permalink raw reply
* Re: [PATCH] git-daemon server
From: Junio C Hamano @ 2005-06-05 4:47 UTC (permalink / raw)
To: Daniel Barkalow; +Cc: git
In-Reply-To: <Pine.LNX.4.21.0506031927000.30848-100000@iabervon.org>
>>>>> "DB" == Daniel Barkalow <barkalow@iabervon.org> writes:
DB> With patches I have (but have to rebase and such), you could do:
DB> git-rpush -a -w heads/master heads/master //master.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
I'd like to see your "-w" extention merged into mainline soon
(both push and pull).
BTW, don't we want to have them renamed to git-ssh-pull (and
git-ssh-push) for consistency with other transports, before 1.0
happens?
^ permalink raw reply
* Re: git-shortlog script
From: Jeff Garzik @ 2005-06-05 0:19 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Git Mailing List, Linux Kernel
In-Reply-To: <Pine.LNX.4.58.0506041715170.1876@ppc970.osdl.org>
Linus Torvalds wrote:
>
> On Sat, 4 Jun 2005, Jeff Garzik wrote:
>
>>I'm surprised git doesn't fall back to GIT_COMMITTER_NAME if
>>GIT_AUTHOR_NAME doesn't exist, though.
>
>
> GIT_AUTHOR_NAME existed first ;)
>
> Btw, what does your /etc/passwd look like, and I'll try to hack it up to
> just get that case right by default too..
ah, it looks like I forget the name when I was creating the account.
> [jgarzik@pretzel libata-dev]$ grep jgarzik /etc/passwd
> jgarzik:x:500:500::/g/g:/bin/bash
> [jgarzik@pretzel libata-dev]$ chfn
> Changing finger information for jgarzik.
> Password:
> Name []: Jeff Garzik
> Office []:
> Office Phone []:
> Home Phone []:
>
> Finger information changed.
Fixed. :)
In any case, I'll set GIT_AUTHOR_NAME in .bash_profile, to get my proper
email addy rather than the local one.
Jeff
^ permalink raw reply
* Re: git-shortlog script
From: Linus Torvalds @ 2005-06-05 0:16 UTC (permalink / raw)
To: Jeff Garzik; +Cc: Git Mailing List, Linux Kernel
In-Reply-To: <42A24274.7040906@pobox.com>
On Sat, 4 Jun 2005, Jeff Garzik wrote:
>
> I'm surprised git doesn't fall back to GIT_COMMITTER_NAME if
> GIT_AUTHOR_NAME doesn't exist, though.
GIT_AUTHOR_NAME existed first ;)
Btw, what does your /etc/passwd look like, and I'll try to hack it up to
just get that case right by default too..
Linus
^ permalink raw reply
* Re: git-shortlog script
From: Jeff Garzik @ 2005-06-05 0:08 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Git Mailing List, Linux Kernel
In-Reply-To: <Pine.LNX.4.58.0506041642530.1876@ppc970.osdl.org>
Linus Torvalds wrote:
>
> On Sat, 4 Jun 2005, Jeff Garzik wrote:
>
>>Attached is the 'git-shortlog' script I whipped up, to mimic the
>>shortlog script that was used back in the BitKeeper days.
>
>
> Thanks, I'll add this to the git stuff, and next kernel release will have
> a proper shortlog.
cool
> Btw, it shows how broken your merge script is: you don't fill in the
> AUTHOR field properly for some reason:
>
> <jgarzik@pretzel.yyz.us>:
> Automatic merge of /spare/repo/netdev-2.6 branch r8169-fix
> Automatic merge of /spare/repo/linux-2.6/.git branch HEAD
> Automatic merge of /spare/repo/netdev-2.6 branch use-after-unmap
> Automatic merge of rsync://rsync.kernel.org/.../torvalds/linux-2.6.git branch HEAD
>
> but "committer" is right. Pls fix.
hehe, my merge script is Ctrl-R (recall last git-resolve-script invocation).
Committer is right because I set that in my .bash_profile. I'll do that
for author too, to provide a sane default.
I'm surprised git doesn't fall back to GIT_COMMITTER_NAME if
GIT_AUTHOR_NAME doesn't exist, though.
Jeff
^ permalink raw reply
* Re: git-shortlog script
From: Linus Torvalds @ 2005-06-04 23:47 UTC (permalink / raw)
To: Jeff Garzik; +Cc: Git Mailing List, Linux Kernel
In-Reply-To: <Pine.LNX.4.58.0506041642530.1876@ppc970.osdl.org>
On Sat, 4 Jun 2005, Linus Torvalds wrote:
>
> Btw, it shows how broken your merge script is: you don't fill in the
> AUTHOR field properly for some reason:
Oh, and the reason you didn't notice is that "git-whatchanged" normally
ignores merges. Do
git-rev-list --pretty HEAD ^v2.6.12-rc5 | git-shortlog | less -S
to see what I'm talking about ("show shortlog of all the changes since
v2.6.12-rc5").
Linus
^ permalink raw reply
* Re: git-shortlog script
From: Linus Torvalds @ 2005-06-04 23:45 UTC (permalink / raw)
To: Jeff Garzik; +Cc: Git Mailing List, Linux Kernel
In-Reply-To: <42A22C20.10002@pobox.com>
On Sat, 4 Jun 2005, Jeff Garzik wrote:
>
> Attached is the 'git-shortlog' script I whipped up, to mimic the
> shortlog script that was used back in the BitKeeper days.
Thanks, I'll add this to the git stuff, and next kernel release will have
a proper shortlog.
Btw, it shows how broken your merge script is: you don't fill in the
AUTHOR field properly for some reason:
<jgarzik@pretzel.yyz.us>:
Automatic merge of /spare/repo/netdev-2.6 branch r8169-fix
Automatic merge of /spare/repo/linux-2.6/.git branch HEAD
Automatic merge of /spare/repo/netdev-2.6 branch use-after-unmap
Automatic merge of rsync://rsync.kernel.org/.../torvalds/linux-2.6.git branch HEAD
but "committer" is right. Pls fix.
Linus
^ permalink raw reply
* git-shortlog script
From: Jeff Garzik @ 2005-06-04 22:33 UTC (permalink / raw)
To: Git Mailing List, Linux Kernel; +Cc: Linus Torvalds
[-- Attachment #1: Type: text/plain, Size: 785 bytes --]
Attached is the 'git-shortlog' script I whipped up, to mimic the
shortlog script that was used back in the BitKeeper days.
shortlog reads a changelog in the 'git-whatchanged' format, such as
git-whatchanged | git-shortlog
or
git-shortlog changes.txt
and outputs the changes sorted by author:
author1:
cset 1-line desc
cset 1-line desc
...
author2:
cset 1-line desc
...
...
Since git distinguishes 'author' from 'committer', I ran
git-whatchanged | git-shortlog > changes.txt
to look at the kernel authors throughout the entire history [of which is
in git].
It's fun to browse, since this is the first time we've been able to get
a better picture who is actually writing the patches, versus committing
them. See changes.txt.bz2, attached.
Jeff
[-- Attachment #2: git-shortlog --]
[-- Type: text/plain, Size: 1125 bytes --]
#!/usr/bin/perl -w
use strict;
my ($author, $desc, %map);
my $pstate = 1;
while (<>) {
# get author
if ($pstate == 1) {
next unless /^Author: (.*)$/;
$author = $1;
$pstate++;
}
# skip to blank line
elsif ($pstate == 2) {
next unless /^\s*$/;
$pstate++;
}
# skip to non-blank line
elsif ($pstate == 3) {
next if /^\s*$/;
chomp;
$desc = $_;
&shortlog_entry($author, $desc);
$pstate = 1;
}
else {
die "invalid parse state $pstate";
}
}
&shortlog_output;
exit(0);
sub shortlog_entry($$) {
my ($tmp_author, $tmp_desc) = @_;
$tmp_desc =~ s#/pub/scm/linux/kernel/git/#/.../#g;
$tmp_desc =~ s#\[PATCH\] ##g;
$tmp_desc =~ s#^\s+##g;
if (exists $map{$tmp_author}) {
# grab ref
my $obj = $map{$tmp_author};
# add desc to array
push(@$obj, $tmp_desc);
} else {
# create new array, containing 1 item
my @arr = ($tmp_desc);
# store ref to array
$map{$tmp_author} = \@arr;
}
}
sub shortlog_output {
my ($obj);
foreach $author (sort keys %map) {
print "$author:\n";
$obj = $map{$author};
foreach $desc (@$obj) {
print " $desc\n";
}
print "\n";
}
}
[-- Attachment #3: changes.txt.bz2 --]
[-- Type: application/x-bzip2, Size: 31825 bytes --]
^ permalink raw reply
* Re: [PATCH] Cleanup commit messages with git-stripspace
From: Petr Baudis @ 2005-06-04 22:10 UTC (permalink / raw)
To: Jonas Fonseca; +Cc: git
In-Reply-To: <20050604161531.GO12615@diku.dk>
Dear diary, on Sat, Jun 04, 2005 at 06:15:31PM CEST, I got a letter
where Jonas Fonseca <fonseca@diku.dk> told me that...
> Besides removing empty lines from the beginning and end (like the
> current sed command) git-stripspace will also turn multiple consecutive
> empty lines into just one empty line.
>
> Signed-off-by: Jonas Fonseca <fonseca@diku.dk>
I tried hard to prevent this exact behavior since I think this is Bad
Thing (tm), but after all, what the hell... The sed expression has some
portability problems associated.
Thanks, applied.
--
Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
C++: an octopus made by nailing extra legs onto a dog. -- Steve Taylor
^ permalink raw reply
* Re: git-rev-list: proper lazy reachability
From: Petr Baudis @ 2005-06-04 19:46 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Junio C Hamano, Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0506040847170.1876@ppc970.osdl.org>
Dear diary, on Sat, Jun 04, 2005 at 05:51:13PM CEST, I got a letter
where Linus Torvalds <torvalds@osdl.org> told me that...
> Would people prefer to have "git-rev-list" take arguments like
>
> git-rev-list a b ^c ^d
>
> the way git-rev-tree does?
Yes, please. I think that usage is more consistent and more flexible.
--
Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
C++: an octopus made by nailing extra legs onto a dog. -- Steve Taylor
^ 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