* Re: [PATCH] Only update the cygwin-related configuration during state auto-setup
From: Alex Riesen @ 2008-10-23 18:36 UTC (permalink / raw)
To: Mark Levedahl; +Cc: gitster, spearce, dpotapov, git
In-Reply-To: <81b0412b0810230607sfea05ddm62bd03f837fc922e@mail.gmail.com>
Err... It should be: "Only update the cygwin-related configuration
during stat auto-setup".
^ permalink raw reply
* Re: How to push to http(s) repository with authentication?
From: Alex Riesen @ 2008-10-23 19:05 UTC (permalink / raw)
To: Josef Wolf; +Cc: git
In-Reply-To: <20081023175336.GA3526@raven.wolf.lan>
Josef Wolf, Thu, Oct 23, 2008 19:53:37 +0200:
> On Mon, Oct 20, 2008 at 08:25:02PM +0200, Josef Wolf wrote:
>
> > I have set up a repository as described in
> >
> > http://www.kernel.org/pub/software/scm/git/docs/howto/setup-git-server-over-http.txt
> >
> > over SSL with basic authentication. DAV access works fine with konqueror,
> > cadaver and and curl, using this .curlrc:
> > [ ... ]
>
> Is nobody interested in this topic? Or am I in the wrong list?
--cacert <CA certificate>
(SSL) Tells curl to use the specified certificate file to verify
the peer. The file may contain multiple CA certificates. The
certificate(s) must be in PEM format.
curl recognizes the environment variable named ’CURL_CA_BUNDLE’
if that is set, and uses the given path as a path to a CA cert
bundle. This option overrides that variable.
The windows version of curl will automatically look for a CA
certs file named ´curl-ca-bundle.crt´, either in the same direc‐
tory as curl.exe, or in the Current Working Directory, or in any
folder along your PATH.
If curl is built against the NSS SSL library then this option
tells curl the nickname of the CA certificate to use within the
NSS database defined by the environment variable SSL_DIR (or by
default /etc/pki/nssdb). If the NSS PEM PKCS#11 module (lib‐
nsspem.so) is available then PEM files may be loaded.
If this option is used several times, the last one will be used.
Does this (setting $CURL_CA_BUNDLE) help?
^ permalink raw reply
* [PATCH] index-pack: don't leak leaf delta result
From: Nicolas Pitre @ 2008-10-23 19:05 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Another (but minor this time) fallout from commit 9441b61dc5.
Signed-off-by: Nicolas Pitre <nico@cam.org>
diff --git a/index-pack.c b/index-pack.c
index 7f97faa..fe75332 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -550,8 +550,10 @@ static void find_unresolved_deltas(struct base_data *base,
find_delta_children(&base_spec, &ofs_first, &ofs_last);
}
- if (ref_last == -1 && ofs_last == -1)
+ if (ref_last == -1 && ofs_last == -1) {
+ free(base->data);
return;
+ }
link_base_data(prev_base, base);
Nicolas
^ permalink raw reply related
* [PATCH] git-svn: change dashed git-config to git config
From: Deskin Miller @ 2008-10-23 19:21 UTC (permalink / raw)
To: git; +Cc: normalperson
---
When testing git-svn changes, I'll often use git-svn.perl directly on a
repository of mine, to see how things work. Recently I had this happen:
$ git-svn.perl info
Use of uninitialized value in scalar chomp at ../git/git-svn.perl line 1202.
Use of uninitialized value in concatenation (.) or string at ../git/git-svn.perl line 1203.
but the expected output showed up correctly, giving the SVN-like info.
git svn info appeared to work just fine though, despite there being essentially
no difference between the two. However, if I do the following, this is what I
see:
$ env /usr/libexec/git-core/git-svn info
Can't exec "git-config": No such file or directory at /usr/libexec/git-core/git-svn line 1202.
Use of uninitialized value in scalar chomp at /usr/libexec/git-core/git-svn line 1202.
Again, the SVN info would still appear.
Regardless, I think we want to change this call to use the non-dashed form.
Deskin Miller
git-svn.perl | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/git-svn.perl b/git-svn.perl
index 33e1b50..2e68c68 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -1126,7 +1126,7 @@ sub read_repo_config {
my $v = $opts->{$o};
my ($key) = ($o =~ /^([a-zA-Z\-]+)/);
$key =~ s/-//g;
- my $arg = 'git-config';
+ my $arg = 'git config';
$arg .= ' --int' if ($o =~ /[:=]i$/);
$arg .= ' --bool' if ($o !~ /[:=][sfi]$/);
if (ref $v eq 'ARRAY') {
--
1.6.0.2.554.g3041b
^ permalink raw reply related
* [RFC PATCH v2] fetch-pack: log(n)-transmission find_common()
From: Thomas Rast @ 2008-10-23 19:38 UTC (permalink / raw)
To: git
In-Reply-To: <1222217303-29792-1-git-send-email-trast@student.ethz.ch>
Replaces the existing simple history search with a more sophisticated
algorithm:
1) Walk history with exponentially increasing stride lengths; i.e.,
send the 1st commit, then the 2nd after that, then the 4th after
that, and so on.
2) Bisect the resulting intervals.
Combined with tracking the "outstanding haves" so that we can detect
which sha1s were never ACKed by upload-pack (and hence not common),
this gives O(log(n)) required "have" lines.
Unfortunately this cannot work if the server sends fake ACKs, so we
introduce a capability 'exp-stride' which instructs upload-pack to
disable ok_to_give_up(). (Which incidentally saves the server a lot
of work.)
Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---
This is a simple resend of v2, in the hope of attracting some
discussion or at least encouraging words this time around.
Here are the timings I took when I wrote the last mail:
-- 8< --
For the timings below, I ran the (new) daemon locally, with two repos:
- a 'clone --mirror' of my own git.git, with a branch 'next' 525
commits from the above cc185a6
- a 'clone --mirror' of the completely unrelated adps.git, which
shares no objects with git.git
Some not really statistically sound timings, usually best of 3-5:
## A stripped down git.git, with only one branch and no tags
$ git branch -a
* master
$ git tag
$ git log -1 --pretty=oneline master
cc185a6a8ac24737a26ec4b40cc401c2db8b2e97 Start draft release notes for 1.6.0.3
## I still have the old (dashed) forms in /usr/bin from RPMs
$ /usr/bin/git --version
git version 1.5.6
## (1a) "Updating" from the git.git that is further ahead (OLD)
$ time /usr/bin/git-fetch-pack -k git://localhost/git next
[...]
real 0m1.004s
user 0m0.228s
sys 0m0.028s
## (1b) "Updating" from the git.git that is further ahead (NEW)
$ time git fetch-pack -k git://localhost/git next
[...]
real 0m0.977s
user 0m0.208s
sys 0m0.068s
## (2a) Fetching the unrelated repo from scratch (OLD)
$ time /usr/bin/git-fetch-pack -k git://localhost/adps master
[...]
real 0m9.560s
user 0m0.720s
sys 0m0.128s
## (2b) Fetching the unrelated repo from scratch (NEW)
$ time git fetch-pack -k git://localhost/adps master
[...]
real 0m0.596s
user 0m0.372s
sys 0m0.008s
## (3a) Fetching over the network from real repo (OLD)
$ time /usr/bin/git-fetch-pack git://git.kernel.org/pub/scm/git/git.git next
[...]
user 0m0.528s
sys 0m0.136s
## (3b) Fetching over the network from real repo (NEW)
$ time git fetch-pack git://git.kernel.org/pub/scm/git/git.git next
[...]
user 0m0.540s
sys 0m0.180s
## Add more refs to make it more interesting
$ git remote add origin ~/dev/git
$ git fetch --tags origin
## (4a) like 1a, but with lots of tags
$ time /usr/bin/git-fetch-pack -k git://localhost/git next
[...]
real 0m1.075s
user 0m0.452s
sys 0m0.048s
## (4b) like 1b, but with lots of tags
$ time git fetch-pack -k git://localhost/git next
[...]
real 0m0.837s
user 0m0.236s
sys 0m0.040s
Clearly, this approach does solve the issue I mentioned in the
motivation earlier in the thread, where the initial fetch of
completely disjoint repositories takes _ages_. Somewhat to my own
surprise, it seems to do quite well in _all_ cases even though it
aggressively digs through history.
Note, however, that the timings aren't really solid; the final write
of the pack, which I assume is fsync()ed, seems to have a big impact
on the execution time. Test (3) obviously depends more on network
performance than anything else; and it uses the old (linear)
algorithm, but in the new implementation.
-- >8 --
builtin-fetch-pack.c | 477 +++++++++++++++++++++++++++++++++++++++++---------
upload-pack.c | 7 +-
2 files changed, 400 insertions(+), 84 deletions(-)
diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c
index 372bfa2..9ab84ab 100644
--- a/builtin-fetch-pack.c
+++ b/builtin-fetch-pack.c
@@ -25,6 +25,12 @@ static const char fetch_pack_usage[] =
#define COMMON_REF (1U << 2)
#define SEEN (1U << 3)
#define POPPED (1U << 4)
+#define BISECTED_FW (1U << 5)
+#define BISECTED_BW (1U << 6)
+#define NOTCOMMON (1U << 7)
+#define HAS_INFO (1U << 8)
+#define CLEAN_MARKS (COMMON | COMMON_REF | SEEN | POPPED | BISECTED_FW \
+ | BISECTED_BW | NOTCOMMON)
static int marked;
@@ -34,133 +40,412 @@ static int marked;
*/
#define MAX_IN_VAIN 256
-static struct commit_list *rev_list;
-static int non_common_revs, multi_ack, use_sideband;
+struct work_list
+{
+ struct work_list *next;
+ struct commit *commit;
+ unsigned int stride; /* current stride length between commits */
+ unsigned int steps; /* steps left to go before emitting again */
+ unsigned int depth; /* sideline depth */
+ unsigned int bisect;
+};
+
+/* "recursion stack" of our strategy of choosing sha1s to transmit */
+static struct work_list *work_list = NULL;
-static void rev_list_push(struct commit *commit, int mark)
+/* list of commits not ACKed yet */
+static struct commit_list *outstanding = NULL;
+static struct commit_list *outstanding_end = NULL;
+
+struct commit_info
{
- if (!(commit->object.flags & mark)) {
- commit->object.flags |= mark;
+ struct commit_list *children;
+};
- if (!(commit->object.parsed))
- if (parse_commit(commit))
- return;
- insert_by_date(commit, &rev_list);
+static int non_common_revs = 1, multi_ack, use_sideband, exp_stride_algorithm;
- if (!(commit->object.flags & COMMON))
- non_common_revs++;
+static struct commit_info *get_commit_info(struct commit *commit)
+{
+ if (commit->object.flags & HAS_INFO)
+ return commit->util;
+
+ struct commit_info *info = xmalloc(sizeof(struct commit_info));
+ info->children = NULL;
+ commit->util = info;
+ commit->object.flags |= HAS_INFO;
+ return info;
+}
+
+static void work_list_insert(struct work_list *work_item)
+{
+ struct work_list **pp = &work_list;
+ struct work_list *p;
+
+ while ((p = *pp)) {
+ if (p->bisect || work_item->bisect) {
+ if (p->bisect > work_item->bisect)
+ break;
+ } else {
+ if (p->depth > work_item->depth)
+ break;
+ else if (p->commit->date < work_item->commit->date)
+ break;
+ }
+
+ pp = &p->next;
}
+
+ work_item->next = p;
+ *pp = work_item;
}
-static int rev_list_insert_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static void add_child(struct commit *parent, struct commit *child)
+{
+ struct commit_info *info = get_commit_info(parent);
+ struct commit_list *item;
+ for (item = info->children; item; item = item->next)
+ if (item->item == child)
+ break;
+ if (!item)
+ commit_list_insert(child, &(info->children));
+}
+
+
+/*
+ Marks all (known) children of this commit NOTCOMMON. We call this
+ for all sha1s the server did not ACK.
+*/
+
+static void mark_not_common(struct commit *commit)
+{
+ struct commit_info *info = get_commit_info(commit);
+ struct commit_list *child;
+
+ assert(!(commit->object.flags & COMMON));
+
+ if (commit->object.flags & NOTCOMMON)
+ return;
+
+ commit->object.flags |= NOTCOMMON;
+
+ for (child = info->children; child; child = child->next)
+ mark_not_common(child->item);
+}
+
+
+static void check_parsed_and_mark(struct commit *commit)
+{
+ struct commit_list *parent;
+
+ if (!commit && commit->object.parsed)
+ return;
+
+ parse_commit(commit);
+
+ for (parent = commit->parents; parent; parent = parent->next) {
+ add_child(parent->item, commit);
+ if (parent->item->object.flags & NOTCOMMON)
+ mark_not_common(commit);
+ }
+}
+
+
+static void rev_list_push(struct commit *commit, unsigned int stride,
+ unsigned int steps, unsigned int depth)
+{
+ struct work_list *work_item;
+
+ check_parsed_and_mark(commit);
+
+ work_item = xmalloc(sizeof(struct work_list));
+ work_item->commit = commit;
+ work_item->stride = stride;
+ work_item->steps = steps;
+ work_item->depth = depth;
+ work_item->bisect = 0;
+ work_list_insert(work_item);
+}
+
+static int rev_list_insert_ref(const char *path, const unsigned char *sha1,
+ int flag, void *cb_data)
{
struct object *o = deref_tag(parse_object(sha1), path, 0);
- if (o && o->type == OBJ_COMMIT)
- rev_list_push((struct commit *)o, SEEN);
+ if (o && o->type == OBJ_COMMIT && !(o->flags & SEEN))
+ rev_list_push((struct commit *)o, 1, 1, 0);
return 0;
}
-static int clear_marks(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static int clear_marks(const char *path, const unsigned char *sha1, int flag,
+ void *cb_data)
{
struct object *o = deref_tag(parse_object(sha1), path, 0);
if (o && o->type == OBJ_COMMIT)
- clear_commit_marks((struct commit *)o,
- COMMON | COMMON_REF | SEEN | POPPED);
+ clear_commit_marks((struct commit *)o, CLEAN_MARKS);
return 0;
}
+
/*
- This function marks a rev and its ancestors as common.
- In some cases, it is desirable to mark only the ancestors (for example
- when only the server does not yet know that they are common).
+ Mark commits backwards through history as COMMON.
*/
-static void mark_common(struct commit *commit,
- int ancestors_only, int dont_parse)
+static void mark_common(struct commit *commit, int ancestors_only, int dont_parse)
{
- if (commit != NULL && !(commit->object.flags & COMMON)) {
- struct object *o = (struct object *)commit;
+ struct commit_list *parents;
+ struct object *o;
- if (!ancestors_only)
- o->flags |= COMMON;
+ if (commit == NULL || commit->object.flags & (COMMON_REF|COMMON))
+ return;
- if (!(o->flags & SEEN))
- rev_list_push(commit, SEEN);
- else {
- struct commit_list *parents;
-
- if (!ancestors_only && !(o->flags & POPPED))
- non_common_revs--;
- if (!o->parsed && !dont_parse)
- if (parse_commit(commit))
- return;
-
- for (parents = commit->parents;
- parents;
- parents = parents->next)
- mark_common(parents->item, 0, dont_parse);
- }
+ if (!dont_parse && !(commit->object.parsed))
+ check_parsed_and_mark(commit);
+
+ o = (struct object *)commit;
+
+ if (!ancestors_only)
+ o->flags |= COMMON;
+
+ for (parents = commit->parents; parents; parents = parents->next) {
+ add_child(parents->item, commit);
+ mark_common(parents->item, 0, dont_parse);
}
}
+
+
/*
- Get the next rev to send, ignoring the common.
+ Queue all revs in the list for simple backward search. Does not
+ requeue already SEEN commits.
*/
-static const unsigned char* get_rev(void)
+static void queue_list(struct commit *child, struct commit_list *list,
+ unsigned int stride, unsigned int steps,
+ unsigned int depth)
{
- struct commit *commit = NULL;
+ while (list) {
+ add_child(list->item, child);
+ if (!(list->item->object.flags & SEEN))
+ rev_list_push(list->item, stride, steps, depth);
+ list = list->next;
+ }
+}
+
+
+/*
+ Queues a commit for bisection
+*/
+
+static void setup_bisect(struct commit *commit, int depth)
+{
+ struct work_list *work_item = xmalloc(sizeof(struct work_list));
+
+ work_item->commit = commit;
+ work_item->bisect = 1+depth;
+ work_item->depth = 0;
+ work_list_insert(work_item);
+}
+
+
+/*
+ Queues a list of commits for bisection
+*/
+
+static void setup_bisect_all (struct commit_list* list, int depth)
+{
+ while (list) {
+ setup_bisect(list->item, depth);
+ list = list->next;
+ }
+}
+
+
+/*
+ Backwards search. If we use exp_stride_algorithm, it takes
+ exponential strides between commits chosen.
+*/
+
+static struct commit* get_rev_stride(struct work_list *work_item)
+{
+ struct commit *commit = work_item->commit;
+ unsigned int steps = work_item->steps;
+
+ while (steps++ < work_item->stride-1 && commit->parents
+ && !(commit->object.flags & (SEEN|COMMON|COMMON_REF))) {
+ /* all but the first parent line are queued for later */
+ if (commit->parents)
+ queue_list(commit, commit->parents->next,
+ work_item->stride, steps,
+ work_item->depth+1);
+
+ /* follow the first parent line directly */
+ commit->object.flags |= SEEN;
+ add_child(commit->parents->item, commit);
+ commit = commit->parents->item;
+ check_parsed_and_mark(commit);
+ }
+
+ if (commit->object.flags & (SEEN|COMMON)) {
+ /* already been here! */
+ commit = NULL;
+ } else if (commit->object.flags & COMMON_REF) {
+ /* this came from a ref; we stop here, but need to
+ * emit it so the server knows too */
+ commit->object.flags |= POPPED|SEEN|BISECTED_BW;
+ if (exp_stride_algorithm)
+ setup_bisect(commit, 0);
+ } else {
+ /* usual case: this is news to us, so we scan further
+ * back. note that the first-depth bisection only
+ * needs to go forward */
+ commit->object.flags |= POPPED|SEEN|BISECTED_BW;
+ queue_list(commit, commit->parents,
+ exp_stride_algorithm ? work_item->stride*2 : 1,
+ 0, work_item->depth);
+ if (exp_stride_algorithm)
+ setup_bisect(commit, 0);
+ }
+
+ free(work_item);
+
+ return commit;
+}
- while (commit == NULL) {
- unsigned int mark;
- struct commit_list *parents;
- if (rev_list == NULL || non_common_revs == 0)
+/*
+ Bisection state of the exponential stride algorithm: scans forward
+ (backward) for a non-common/already-sent (common/already-sent,
+ resp.) commit, then emits the middle of that range, and re-queues.
+
+ This means we bisect the discovered range in log(n) transmissions,
+ although we do make O(n) steps through history.
+*/
+
+static struct commit* get_rev_bisect(struct work_list *work_item)
+{
+ struct commit *full;
+ struct commit *half;
+ int half_step;
+ struct commit_info *info;
+ unsigned int flags = work_item->commit->object.flags;
+
+ if (!(flags & (COMMON|BISECTED_BW))) {
+ /* We don't know anything about the history _backward_
+ * from this, so search it */
+
+ full = work_item->commit;
+ full->object.flags |= BISECTED_BW;
+ half = work_item->commit;
+ half_step = 0;
+
+ while (full && full->parents) {
+ full = full->parents->item;
+ half_step ^= 1;
+ if (half_step)
+ half = half->parents->item;
+ if (full->object.flags & (POPPED|COMMON))
+ break;
+ }
+
+ /* also insert the same bisection again so we can try
+ * forward too */
+ work_list_insert(work_item);
+
+ if (full->object.flags & POPPED
+ && !(full->object.flags & NOTCOMMON)
+ && !(half->object.flags & (COMMON|POPPED))) {
+ setup_bisect(half, work_item->bisect);
+ half->object.flags |= POPPED;
+ return half;
+ } else {
return NULL;
+ }
+ }
- commit = rev_list->item;
- if (!commit->object.parsed)
- parse_commit(commit);
- parents = commit->parents;
-
- commit->object.flags |= POPPED;
- if (!(commit->object.flags & COMMON))
- non_common_revs--;
-
- if (commit->object.flags & COMMON) {
- /* do not send "have", and ignore ancestors */
- commit = NULL;
- mark = COMMON | SEEN;
- } else if (commit->object.flags & COMMON_REF)
- /* send "have", and ignore ancestors */
- mark = COMMON | SEEN;
- else
- /* send "have", also for its ancestors */
- mark = SEEN;
-
- while (parents) {
- if (!(parents->item->object.flags & SEEN))
- rev_list_push(parents->item, mark);
- if (mark & COMMON)
- mark_common(parents->item, 1, 0);
- parents = parents->next;
+ if (!(flags & (NOTCOMMON|BISECTED_FW))) {
+ /* We don't know anything about the history _forward_
+ * from this, so search it */
+
+ full = work_item->commit;
+ full->object.flags |= BISECTED_FW;
+ half = work_item->commit;
+ half_step = 0;
+
+ while (full && (info=get_commit_info(full))->children) {
+ setup_bisect_all(info->children->next, work_item->bisect);
+ full = info->children->item;
+ half_step ^= 1;
+ if (half_step) {
+ info = get_commit_info(half);
+ half = info->children->item;
+ }
+ if (full->object.flags & (POPPED|NOTCOMMON))
+ break;
}
- rev_list = rev_list->next;
+ if (full->object.flags & POPPED
+ && !(full->object.flags & COMMON)
+ && !(half->object.flags & (NOTCOMMON|POPPED))) {
+ setup_bisect(half, work_item->bisect);
+ half->object.flags |= POPPED;
+ free(work_item);
+ return half;
+ }
}
- return commit->object.sha1;
+ free(work_item);
+ return NULL;
}
+
+/*
+ Get the next revision to send.
+*/
+
+static struct commit *get_rev(void)
+{
+ struct commit *commit = NULL;
+
+ while (commit == NULL && work_list) {
+ struct work_list *work_item = NULL;
+
+ work_item = work_list;
+ /* we update the pointer early so the get_rev
+ * functions can free() or reuse the work_item as
+ * required */
+ work_list = work_item->next;
+
+ commit = work_item->commit;
+
+ check_parsed_and_mark(commit);
+
+ if (work_item->bisect) {
+ commit = get_rev_bisect(work_item);
+ } else {
+ commit = get_rev_stride(work_item);
+ }
+ }
+
+ return commit;
+}
+
+static void pop_outstanding()
+{
+ struct commit_list *item = outstanding;
+ outstanding = item->next;
+ free(item);
+}
+
+
static int find_common(int fd[2], unsigned char *result_sha1,
struct ref *refs)
{
int fetching;
int count = 0, flushes = 0, retval;
+ struct commit *commit;
const unsigned char *sha1;
unsigned in_vain = 0;
int got_continue = 0;
@@ -192,7 +477,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
}
if (!fetching)
- packet_write(fd[1], "want %s%s%s%s%s%s%s%s\n",
+ packet_write(fd[1], "want %s%s%s%s%s%s%s%s%s\n",
sha1_to_hex(remote),
(multi_ack ? " multi_ack" : ""),
(use_sideband == 2 ? " side-band-64k" : ""),
@@ -200,6 +485,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
(args.use_thin_pack ? " thin-pack" : ""),
(args.no_progress ? " no-progress" : ""),
(args.include_tag ? " include-tag" : ""),
+ (exp_stride_algorithm ? " exp-stride" : ""),
" ofs-delta");
else
packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
@@ -243,13 +529,25 @@ static int find_common(int fd[2], unsigned char *result_sha1,
flushes = 0;
retval = -1;
- while ((sha1 = get_rev())) {
+ while ((commit = get_rev())) {
+ sha1 = commit->object.sha1;
packet_write(fd[1], "have %s\n", sha1_to_hex(sha1));
if (args.verbose)
fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
in_vain++;
+
+ if (outstanding) {
+ commit_list_insert(commit, &(outstanding_end->next));
+ outstanding_end = outstanding_end->next;
+ } else {
+ commit_list_insert(commit, &outstanding);
+ outstanding_end = outstanding;
+ }
+
if (!(31 & ++count)) {
int ack;
+ int unwound = 0;
+ struct commit_list *item;
packet_flush(fd[1]);
flushes++;
@@ -274,12 +572,23 @@ static int find_common(int fd[2], unsigned char *result_sha1,
} else if (ack == 2) {
struct commit *commit =
lookup_commit(result_sha1);
+ while (commit != outstanding->item) {
+ mark_not_common(outstanding->item);
+ pop_outstanding();
+ unwound++;
+ }
+ pop_outstanding();
+ unwound++;
mark_common(commit, 0, 1);
retval = 0;
in_vain = 0;
got_continue = 1;
}
} while (ack);
+ while (unwound++ < 32) {
+ mark_not_common(outstanding->item);
+ pop_outstanding();
+ }
flushes--;
if (got_continue && MAX_IN_VAIN < in_vain) {
if (args.verbose)
@@ -445,9 +754,8 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
continue;
if (!(o->flags & SEEN)) {
- rev_list_push((struct commit *)o, COMMON_REF | SEEN);
-
- mark_common((struct commit *)o, 1, 1);
+ rev_list_push((struct commit *)o, 1, 0, 0);
+ o->flags |= COMMON_REF;
}
}
@@ -597,6 +905,11 @@ static struct ref *do_fetch_pack(int fd[2],
fprintf(stderr, "Server supports side-band\n");
use_sideband = 1;
}
+ if (server_supports("exp-stride")) {
+ if (args.verbose)
+ fprintf(stderr, "Server supports exp-stride\n");
+ exp_stride_algorithm = 1;
+ }
if (everything_local(&ref, nr_match, match)) {
packet_flush(fd[1]);
goto all_done;
diff --git a/upload-pack.c b/upload-pack.c
index e5adbc0..14012ee 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -37,6 +37,7 @@ static unsigned int timeout;
*/
static int use_sideband;
static int debug_fd;
+static int exp_stride_algorithm;
static void reset_timeout(void)
{
@@ -414,7 +415,7 @@ static int get_common_commits(void)
if (!prefixcmp(line, "have ")) {
switch (got_sha1(line+5, sha1)) {
case -1: /* they have what we do not */
- if (multi_ack && ok_to_give_up())
+ if (multi_ack && !exp_stride_algorithm && ok_to_give_up())
packet_write(1, "ACK %s continue\n",
sha1_to_hex(sha1));
break;
@@ -501,6 +502,8 @@ static void receive_needs(void)
no_progress = 1;
if (strstr(line+45, "include-tag"))
use_include_tag = 1;
+ if (strstr(line+45, "exp-stride"))
+ exp_stride_algorithm = 1;
/* We have sent all our refs already, and the other end
* should have chosen out of them; otherwise they are
@@ -573,7 +576,7 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
{
static const char *capabilities = "multi_ack thin-pack side-band"
" side-band-64k ofs-delta shallow no-progress"
- " include-tag";
+ " include-tag exp-stride";
struct object *o = parse_object(sha1);
if (!o)
--
tg: (759ad19..) t/fetch-pack-speedup (depends on: origin/master)
^ permalink raw reply related
* clean/smudge filters for pdf files
From: Leo Razoumov @ 2008-10-23 19:44 UTC (permalink / raw)
To: git
I am trying to improve storage efficiency for PDF files in a git repo.
Following earlier discussions in this list I am trying to set up
proper clean/smudge filters. What follows is my current setup
# in ~/.gitconfig
[filter "pdf"]
clean = "pdftk - output - uncompress"
smudge = "pdftk - output - compress"
# in .gitattributes
*.pdf filter=pdf
Unfortunately, it seems as though that pdftk uncompress followed by
pdftk compress do not leave the file invariant. I tried several
uncompress+compress iterations and the file still keep changing (the
size though stays the same).
Is there any other alternative way to store PDF files in git repo more
efficiently?
Any alternative to pdftk on Linux?
--Leo--
^ permalink raw reply
* Re: [JGIT PATCH 8/8] Define a basic merge API, and a two-way tree merge strategy
From: Robin Rosenberg @ 2008-10-23 21:14 UTC (permalink / raw)
To: Shawn O. Pearce; +Cc: git
In-Reply-To: <1223932217-4771-9-git-send-email-spearce@spearce.org>
Hi, Shawn
Shouldn't testTrivialTwoWay_disjointhistories() work?
The two trees have nothing in common and so should be trivially mergeable.
-- robin
>From cef2695431e368da616a1e9c8de3e5e419854a4c Mon Sep 17 00:00:00 2001
From: Robin Rosenberg <robin.rosenberg@dewire.com>
Date: Thu, 23 Oct 2008 23:09:10 +0200
Subject: [EGIT PATCH] Simple merge test
---
.../org/spearce/jgit/merge/SimpleMergeTest.java | 28 ++++++++++++++++++++
1 files changed, 28 insertions(+), 0 deletions(-)
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/merge/SimpleMergeTest.java
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/merge/SimpleMergeTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/merge/SimpleMergeTest.java
new file mode 100644
index 0000000..8ec1c7f
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/merge/SimpleMergeTest.java
@@ -0,0 +1,28 @@
+package org.spearce.jgit.merge;
+
+import java.io.IOException;
+
+import org.spearce.jgit.lib.ObjectId;
+import org.spearce.jgit.lib.RepositoryTestCase;
+
+public class SimpleMergeTest extends RepositoryTestCase {
+
+ public void testTrivialTwoWay_noway() throws IOException {
+ Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c") });
+ assertFalse(merge);
+ }
+
+ public void testTrivialTwoWay_disjointhistories() throws IOException {
+ Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c~4") });
+ assertTrue(merge);
+ }
+
+ public void testTrivialTwoWay_ok() throws IOException {
+ Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a^0^0^0"), db.resolve("a^0^0^1") });
+ assertTrue(merge);
+ assertEquals(db.mapTree("a^0^0").getId(), ourMerger.getResultTreeId());
+ }
+}
--
1.6.0.2.308.gef4a
^ permalink raw reply related
* Re: clean/smudge filters for pdf files
From: Pierre Habouzit @ 2008-10-23 21:32 UTC (permalink / raw)
To: Leo Razoumov; +Cc: git
In-Reply-To: <ee2a733e0810231244u1510cdc8y7b27af9b8fdda1e0@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1182 bytes --]
On Thu, Oct 23, 2008 at 07:44:39PM +0000, Leo Razoumov wrote:
> I am trying to improve storage efficiency for PDF files in a git repo.
> Following earlier discussions in this list I am trying to set up
> proper clean/smudge filters. What follows is my current setup
>
> # in ~/.gitconfig
> [filter "pdf"]
> clean = "pdftk - output - uncompress"
> smudge = "pdftk - output - compress"
>
> # in .gitattributes
> *.pdf filter=pdf
>
> Unfortunately, it seems as though that pdftk uncompress followed by
> pdftk compress do not leave the file invariant. I tried several
> uncompress+compress iterations and the file still keep changing (the
> size though stays the same).
> Is there any other alternative way to store PDF files in git repo more
> efficiently?
> Any alternative to pdftk on Linux?
actually it uses some kind of zlib algorithm so that's pretty normal you
don't have the same result with a packer. Maybe one could write a tool
like pristine-tar for that purpose.
--
·O· Pierre Habouzit
··O madcoder@debian.org
OOO http://www.madism.org
[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* Conference in near
From: Anthony Murphy @ 2008-10-23 22:04 UTC (permalink / raw)
To: git
The 2008 Hard Money National Conference in near
All the information is at:
http://hardmoneyparty.com
or call us today at 858-736-7788 for the event details.
to be taken of our list please:
http://hardmoneyparty.com/uns/ or, send us a letter to PBMSII, 5580 la jolla blvd #153 La Jolla, Ca 92037
--------------------------------------------
.
^ permalink raw reply
* Re: git performance
From: Nanako Shiraishi @ 2008-10-23 22:24 UTC (permalink / raw)
To: Daniel Barkalow; +Cc: Edward Ned Harvey, git
In-Reply-To: <alpine.LNX.1.00.0810231346520.19665@iabervon.org>
Quoting Daniel Barkalow <barkalow@iabervon.org>:
> On Wed, 22 Oct 2008, Edward Ned Harvey wrote:
>
>> Out of curiosity, what are they talking about, when they say "git is
>> fast?" Just the fact that it's all local disk, or is there more to it
>> than that? I could see - git would probably outperform perforce for
>> versioning of large files (let's say iso files) to benefit from
>> sustained local disk IO, while perforce would probably outperform
>> anything I can think of, operating on thousands of tiny files, because
>> it will never walk the tree.
>
> It shouldn't be too hard to make git work like perforce with respect to
> walking the tree. git keeps an index of the stat() info it saw when it
> last looked at files, and only looks at the contents of files whose stat()
> info has changed. In order to have it work like perforce, it would just
> need to have a flag in the stat() info index for "don't even bother",
Are you describing the "assume unchanged bit"?
--
Nanako Shiraishi
http://ivory.ap.teacup.com/nanako3/
^ permalink raw reply
* Re: How to push to http(s) repository with authentication?
From: Josef Wolf @ 2008-10-23 22:32 UTC (permalink / raw)
To: git
In-Reply-To: <20081023190544.GA21079@blimp.localdomain>
On Thu, Oct 23, 2008 at 09:05:44PM +0200, Alex Riesen wrote:
Thanks for your response, Alex!
[ ... ]
> --cacert <CA certificate>
[ ... ]
> Does this (setting $CURL_CA_BUNDLE) help?
Well, as I wrote in my original mail, I already got the certificate
working. My problem is the usage of the .netrc file. I don't want
to store passwords in cleartext in files.
So the real question is: how do I make git-http to ask for credentials.
Sorry, I should have made more clearly what's the problem.
^ permalink raw reply
* Re: [JGIT RFC PATCH 3/3] Rate limit warnings spewed by RepositoryTestCase.recursiveDelete
From: Robin Rosenberg @ 2008-10-23 23:10 UTC (permalink / raw)
To: Jonas Fonseca; +Cc: Shawn O. Pearce, git
In-Reply-To: <20081022083420.GC17940@diku.dk>
onsdagen den 22 oktober 2008 10.34.20 skrev Jonas Fonseca:
> On Windows XP / NTFS / NetBeans 6.1 / Java 5 a lot of warnings are
> printed. In most cases the path is in fact deleted and it seems to just
> be a timing bug or something Windows or NTFS specific. Also, many of the
> warnings are for the same paths and therefore a bit redundant.
> Furthermore, when a lot of warnings are printed it gives the feeling of
> slowing down the tests. By rate limiting the warnings the problem is
> still made visible for developers to notice.
The problem is actually flaws in the unit tests and in the supporting RepositoryTestCase.
I think I'll fix that instead.
-- robin
^ permalink raw reply
* How does Git treat hardlinked files which are outside the working directory?
From: Weiwei Shu @ 2008-10-23 23:13 UTC (permalink / raw)
To: git
Hi,
Can someone point to me if there is any special care needed for
hardlink files if they are outside the versioned directory? Thanks!
Weiwei
--
Weiwei
--Hope Springs Eternal.
^ permalink raw reply
* Re: [RFC] Zit: the git-based single file content tracker
From: Jean-Luc Herren @ 2008-10-23 23:23 UTC (permalink / raw)
To: Giuseppe Bilotta, git
In-Reply-To: <gdok16$vh2$1@ger.gmane.org>
Hi!
Giuseppe Bilotta wrote:
> So today I decided to start hacking at a git-based but file-oriented
> content tracker, which I decided to name Zit.
This sounds great and would seem very useful to manage my ~/bin/
directory which contains a set of unrelated one-file-tools that
evolve over time. I haven't played with it yet though.
> when you choose to start tracking a file with Zit [...]
> Zit will create a directory .zit.file to hold a git repository
If you have many files you want to track in a single directory
(like ~/bin/), all those additional directories will quickly feel
like clutter. If you track every file, it will even double the
number of things you see with an "ls -a".
If you decide against a shared repository, maybe you want to
consider to not use ".zit.file/", but ".zit/file/" as the
repository? This would reduce the clutter to a single directory,
just like with ".git". And moving files around wouldn't be that
much complicated.
jlh
^ permalink raw reply
* Problem with Git.pm bidi_pipe methods
From: Philippe Bruhat (BooK) @ 2008-10-24 0:14 UTC (permalink / raw)
To: git
Hi,
To be able to call commit-tree from a Perl program, I had to use
command_bidi_pipe() to pass the message on standard input, and get the
new commit id on standard output. The only problem I have is that the
method doesn't work (or at least, doesn't work like the other
command_... methods).
sub command_bidi_pipe {
my ($pid, $in, $out);
$pid = open2($in, $out, 'git', @_);
return ($pid, $in, $out, join(' ', @_));
}
First, it calls git in the current working dir, when all the other
command_... methods do the appropriate chdir depending on the Git object.
Second, it can't be used as a method, since $self is not removed from @_
before calling open2, leading to errors such as this one:
git: 'Git=HASH(0x82f898c)' is not a git-command. See 'git --help'.
Third, no access is given to STDERR (open3 may help).
I'm very sorry to complain without proposing a patch.
I think solving the current directory problem is probably going to be
difficult with open2/open3, since open2 does the fork+exec, whereas Git.pm
handles the fork+chdir+exec itself for the other command_... methods.
And so there is no room to chdir when calling open2.
--
Philippe Bruhat (BooK)
Treat those you outrank well... you never know when they will outrank you.
(Moral from Groo #7 (Image))
^ permalink raw reply
* Re: [PATCH stgit] revised patch for importing series from tarball
From: Karl Hasselström @ 2008-10-24 1:17 UTC (permalink / raw)
To: Clark Williams; +Cc: Git Mailing List
In-Reply-To: <20081019141613.05cbd93e@gmail.com>
On 2008-10-19 14:16:13 -0500, Clark Williams wrote:
> I added a simple test to the test harness for import as well.
Great!
> Oh and I added '*.elc' to the .gitignore file. May not be that many
> folks using emacs with stgit, but hey, I am! :)
Me too (but I have *.elc in my personal ignore file, so I never saw
it). However, this is an unrelated change, and we don't need to ignore
.elc files at the top level, only in contrib. I've taken the liberty
of splitting this out to a separate commit and forging your signature
on it -- let me know if that's OK and I'll push it out.
> patch to allow importing a series from a tar archive
I turned this into a complete sentence, and removed "patch" since it's
entirely redundant.
> --- a/.gitignore
> +++ b/.gitignore
> @@ -6,3 +6,4 @@ patches-*
> release.sh
> setup.cfg.rpm
> snapshot.sh
> +*.elc
As I said, I put this in a separate patch, and put that ignore pattern
in contrib.gitignore instead.
> make_option('--commemail',
> - help = 'use COMMEMAIL as the committer e-mail')
> + help = 'use COMMEMAIL as the committer e-mail'),
> ] + make_sign_options()
This is just a noise change _and_ didn't apply, so I dropped it.
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
^ permalink raw reply
* Re: clean/smudge filters for pdf files
From: Leo Razoumov @ 2008-10-24 1:40 UTC (permalink / raw)
To: Pierre Habouzit; +Cc: git
In-Reply-To: <20081023213203.GB26104@artemis.corp>
On 10/23/08, Pierre Habouzit <madcoder@debian.org> wrote:
> On Thu, Oct 23, 2008 at 07:44:39PM +0000, Leo Razoumov wrote:
> > I am trying to improve storage efficiency for PDF files in a git repo.
> > Following earlier discussions in this list I am trying to set up
> > proper clean/smudge filters. What follows is my current setup
> >
> > # in ~/.gitconfig
> > [filter "pdf"]
> > clean = "pdftk - output - uncompress"
> > smudge = "pdftk - output - compress"
> >
> > # in .gitattributes
> > *.pdf filter=pdf
> >
> > Unfortunately, it seems as though that pdftk uncompress followed by
> > pdftk compress do not leave the file invariant. I tried several
> > uncompress+compress iterations and the file still keep changing (the
> > size though stays the same).
> > Is there any other alternative way to store PDF files in git repo more
> > efficiently?
> > Any alternative to pdftk on Linux?
>
>
> actually it uses some kind of zlib algorithm so that's pretty normal you
> don't have the same result with a packer. Maybe one could write a tool
> like pristine-tar for that purpose.
>
With zlib you get the same deterministic result as long as you use the
same zlib packer and unpacker. With pdftk compress/uncompress seem not
to form a bijection pair. This issue was briefly discussed on this
list back in April 2008 but no resolution emerged.
--Leo--
^ permalink raw reply
* Re: What's in git.git (Oct 2008, #03; Tue, 14)
From: Junio C Hamano @ 2008-10-24 2:44 UTC (permalink / raw)
To: Brandon Casey
Cc: git, Arjen Laarhoven, Jeff King, Shawn O. Pearce, Mike Ralphson
In-Reply-To: <bsftmRx17krWBpVlulipoJEO1fWsD0hZfF3HOZcajX6GV66RKW8W6A@cipher.nrlssc.navy.mil>
Brandon Casey <casey@nrlssc.navy.mil> writes:
> 2) Do we want to stick with compat/regex on
>
> Darwin: Arjen
> FreeBSD: Jeff
> AIX: Mike
>
> now that the builtin funcname patterns have been converted to Extended
> Regular Expressions?
-- >8 --
Subject: Stop using compat/regex.c on platforms with working regexp library
We used to have non-POSIX comformant BRE in our code, and linked with GNU
regexp library on a few platforms (Darwin, FreeBSD and AIX) to work it
around. This was backwards.
We've fixed the broken regexps to use ERE that native regexp libraries on
these platforms can handle just fine. There is no need to link with GNU
regexp library on these platforms anymore.
Tested-on-AIX-by: Mike Ralphson <mike@abacus.co.uk>
Tested-on-FreeBSD-by: Jeff King <peff@peff.net>
Tested-on-Darwin-by: Arjen Laarhoven <arjen@yaph.org>
Tested-on-Darwin-by: Pieter de Bie <pieter@frim.nl>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
Makefile | 6 ------
1 files changed, 0 insertions(+), 6 deletions(-)
diff --git c/Makefile w/Makefile
index d6f3695..40309e1 100644
--- c/Makefile
+++ w/Makefile
@@ -640,8 +640,6 @@ ifeq ($(uname_S),Darwin)
endif
NO_STRLCPY = YesPlease
NO_MEMMEM = YesPlease
- COMPAT_CFLAGS += -Icompat/regex
- COMPAT_OBJS += compat/regex/regex.o
endif
ifeq ($(uname_S),SunOS)
NEEDS_SOCKET = YesPlease
@@ -692,8 +690,6 @@ ifeq ($(uname_S),FreeBSD)
BASIC_LDFLAGS += -L/usr/local/lib
DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
THREADED_DELTA_SEARCH = YesPlease
- COMPAT_CFLAGS += -Icompat/regex
- COMPAT_OBJS += compat/regex/regex.o
endif
ifeq ($(uname_S),OpenBSD)
NO_STRCASESTR = YesPlease
@@ -720,8 +716,6 @@ ifeq ($(uname_S),AIX)
INTERNAL_QSORT = UnfortunatelyYes
NEEDS_LIBICONV=YesPlease
BASIC_CFLAGS += -D_LARGE_FILES
- COMPAT_CFLAGS += -Icompat/regex
- COMPAT_OBJS += compat/regex/regex.o
endif
ifeq ($(uname_S),GNU)
# GNU/Hurd
^ permalink raw reply related
* Re: [PATCH 3/4] diff: introduce diff.<driver>.binary
From: Jeff King @ 2008-10-24 2:46 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Johannes Sixt, Matthieu Moy
In-Reply-To: <20081005214336.GC21925@coredump.intra.peff.net>
On Sun, Oct 05, 2008 at 05:43:36PM -0400, Jeff King wrote:
> However, there is at least one conflicting situation: there
> is no way to say "use the regular rules to determine whether
> this file is binary, but if we do diff it textually, use
> this funcname pattern." That is, currently setting diff=foo
> indicates that the file is definitely text.
Hrm. I don't know what crack I was smoking when I wrote this (and then
argued about it for weeks afterward). It is actually the _opposite_
situation. That is, once you have said "diff=foo", there is no way to
say "btw, foo files are _definitely_ text."
See, this is the old code:
> -static void diff_filespec_check_attr(struct diff_filespec *one)
> +void diff_filespec_load_driver(struct diff_filespec *one)
> {
> - struct userdiff_driver *drv;
> - int check_from_data = 0;
> -
> - if (one->checked_attr)
> - return;
> -
> - drv = userdiff_find_by_path(one->path);
> - one->is_binary = 0;
> -
> - /* binaryness */
> - if (drv == USERDIFF_ATTR_TRUE)
> - ;
> - else if (drv == USERDIFF_ATTR_FALSE)
> - one->is_binary = 1;
> - else
> - check_from_data = 1;
> -
> - if (check_from_data) {
> - if (!one->data && DIFF_FILE_VALID(one))
> - diff_populate_filespec(one, 0);
> -
> - if (one->data)
> - one->is_binary = buffer_is_binary(one->data, one->size);
> - }
> + if (!one->driver)
> + one->driver = userdiff_find_by_path(one->path);
> + if (!one->driver)
> + one->driver = userdiff_find_by_name("default");
> }
You can clearly see that we use check_from_data as long as the value is
not true or false. Meaning if it is unspecified _or_ if it has a string
value (actually, this text is hacked up by my previous patch, but you
can look at maint:diff.c and see that it is similar).
And this makes sense. Otherwise, plumbing like "git diff-tree" would
get mightily confused by external diff commands. For example, consider
if you had "foo diff=bar" in your attributes file, and defined
"diff.bar.command". That external diff would be used for git-diff, but
_not_ for diff-tree. But it would be stupid for diff-tree to look at the
attribute and say "oh, we have a diff attr, it must be text."
So the patch is right to keep the binary value to "-1" except for the
few cases where it has been specified explicitly. I found this out when
I tried to "fix" it to the old behavior tonight and discovered lots of
breakage.
And this also means that diff.*.binary really _does_ do something
useful. If you have, for example, a ".foo" file that looks like binary,
but really isn't, _and_ you want to set a custom hunk header for it,
previously you were out of luck. You could do one or the other. Now you
can do:
git config diff.foo.xfuncname whatever
git config diff.foo.binary false
and get the desired effect.
As for the fallout from this with regards to our discussion last week,
there were two relevant points:
- Junio suggested that anytime we use funcname, we always want text
anyway. I think that is reasonable, but it has never been the case up
until now (in fact, you were stuck with the _opposite_ until now, so
my series at least makes it possible to say "always text", though
you have to do it manually). So I will leave it for now, and
if people feel strongly, my series provides a sane basis for a patch
that does this automatically.
- Johannes complained about having to set "diff.foo.binary = false"
when we have set "diff.foo.textconv". I agree that having to set it
is cumbersome, but what's worse is that it is wrong. You are saying
"this file is not binary" which is only _sometimes_ true. That is, it
is only true if we are in a command which actually performs the text
conversion. Plumbing sees _just_ the binary half, which is outright
wrong (and which became painfully obvious once I wrote some tests).
The solution is that textconv'd data should always be treated as
text, and that takes some refactoring of my patches. I will post a
series dealing with this in a minute.
Hopefully that explanation made sense. This turned out to be a lot
trickier than I thought (in combination with my apparent crack habit),
and I just spent several hours trying to figure out all of the niggling
details. But I realize the rest of you haven't thought about it for at
least a week. :)
-Peff
^ permalink raw reply
* [PATCH 1/5] diff: add missing static declaration
From: Jeff King @ 2008-10-24 2:48 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Johannes Sixt, Matthieu Moy
In-Reply-To: <20081024024631.GA20365@coredump.intra.peff.net>
This function isn't used outside of diff.c; the 'static' was
simply overlooked in the original writing.
Signed-off-by: Jeff King <peff@peff.net>
---
Just a cleanup.
diff.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/diff.c b/diff.c
index e368fef..d1fd594 100644
--- a/diff.c
+++ b/diff.c
@@ -1282,7 +1282,7 @@ static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two)
emit_binary_diff_body(file, two, one);
}
-void diff_filespec_load_driver(struct diff_filespec *one)
+static void diff_filespec_load_driver(struct diff_filespec *one)
{
if (!one->driver)
one->driver = userdiff_find_by_path(one->path);
--
1.6.0.3.518.gdb328.dirty
^ permalink raw reply related
* [PATCH 2/5] add userdiff textconv tests
From: Jeff King @ 2008-10-24 2:50 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Johannes Sixt, Matthieu Moy
In-Reply-To: <20081024024631.GA20365@coredump.intra.peff.net>
These tests provide a basic sanity check that textconv'd
files work. The tests try to describe how this configuration
_should_ work; thus some of the tests are marked to expect
failure.
In particular, we fail to actually textconv anything because
the 'diff.foo.binary' config option is not set, which will
be fixed in the next patch.
Signed-off-by: Jeff King <peff@peff.net>
---
Note that this just tests actual patch generation. Diffstat, etc, are
left for later, as is discussed in the next patch.
t/t4030-diff-textconv.sh | 80 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 80 insertions(+), 0 deletions(-)
create mode 100755 t/t4030-diff-textconv.sh
diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh
new file mode 100755
index 0000000..d0d7691
--- /dev/null
+++ b/t/t4030-diff-textconv.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+test_description='diff.*.textconv tests'
+. ./test-lib.sh
+
+find_diff() {
+ sed '1,/^index /d' | sed '/^-- $/,$d'
+}
+
+cat >expect.binary <<'EOF'
+Binary files a/file and b/file differ
+EOF
+
+cat >expect.text <<'EOF'
+--- a/file
++++ b/file
+@@ -1 +1,2 @@
+ 0
++1
+EOF
+
+cat >hexdump <<'EOF'
+#!/bin/sh
+perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' "$1"
+EOF
+chmod +x hexdump
+
+test_expect_success 'setup binary file with history' '
+ printf "\\0\\n" >file &&
+ git add file &&
+ git commit -m one &&
+ printf "\\1\\n" >>file &&
+ git add file &&
+ git commit -m two
+'
+
+test_expect_success 'file is considered binary by porcelain' '
+ git diff HEAD^ HEAD >diff &&
+ find_diff <diff >actual &&
+ test_cmp expect.binary actual
+'
+
+test_expect_success 'file is considered binary by plumbing' '
+ git diff-tree -p HEAD^ HEAD >diff &&
+ find_diff <diff >actual &&
+ test_cmp expect.binary actual
+'
+
+test_expect_success 'setup textconv filter' '
+ echo file diff=foo >.gitattributes &&
+ git config diff.foo.textconv "$PWD"/hexdump
+'
+
+test_expect_failure 'diff produces text' '
+ git diff HEAD^ HEAD >diff &&
+ find_diff <diff >actual &&
+ test_cmp expect.text actual
+'
+
+test_expect_success 'diff-tree produces binary' '
+ git diff-tree -p HEAD^ HEAD >diff &&
+ find_diff <diff >actual &&
+ test_cmp expect.binary actual
+'
+
+test_expect_failure 'log produces text' '
+ git log -1 -p >log &&
+ find_diff <log >actual &&
+ test_cmp expect.text actual
+'
+
+# actually passes, but only because textconv is broken
+# and its failure mode happens to be the same as success
+test_expect_failure 'format-patch produces binary' '
+ git format-patch --no-binary --stdout HEAD^ >patch &&
+ find_diff <patch >actual &&
+ test_cmp expect.binary actual
+'
+
+test_done
--
1.6.0.3.518.gdb328.dirty
^ permalink raw reply related
* Re: [PATCH stgit] revised patch for importing series from tarball
From: Clark Williams @ 2008-10-24 2:53 UTC (permalink / raw)
To: Karl Hasselström; +Cc: Git Mailing List
In-Reply-To: <20081024011708.GA20369@diana.vm.bytemark.co.uk>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On Fri, 24 Oct 2008 03:17:08 +0200
Karl Hasselström <kha@treskal.com> wrote:
> On 2008-10-19 14:16:13 -0500, Clark Williams wrote:
>
> > I added a simple test to the test harness for import as well.
>
> Great!
>
> > Oh and I added '*.elc' to the .gitignore file. May not be that many
> > folks using emacs with stgit, but hey, I am! :)
>
> Me too (but I have *.elc in my personal ignore file, so I never saw
> it). However, this is an unrelated change, and we don't need to ignore
> .elc files at the top level, only in contrib. I've taken the liberty
> of splitting this out to a separate commit and forging your signature
> on it -- let me know if that's OK and I'll push it out.
>
Yes, that's fine.
> > patch to allow importing a series from a tar archive
>
> I turned this into a complete sentence, and removed "patch" since it's
> entirely redundant.
>
> > --- a/.gitignore
> > +++ b/.gitignore
> > @@ -6,3 +6,4 @@ patches-*
> > release.sh
> > setup.cfg.rpm
> > snapshot.sh
> > +*.elc
>
> As I said, I put this in a separate patch, and put that ignore pattern
> in contrib.gitignore instead.
>
Thanks
> > make_option('--commemail',
> > - help = 'use COMMEMAIL as the committer e-mail')
> > + help = 'use COMMEMAIL as the committer e-mail'),
> > ] + make_sign_options()
>
> This is just a noise change _and_ didn't apply, so I dropped it.
>
Agreed.
Thanks for the update.
Clark
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.9 (GNU/Linux)
iEYEARECAAYFAkkBOJ0ACgkQqA4JVb61b9e6SgCeOZ0YrB/uSrvSqgLkxnLjiQp2
AWwAoKIeIQUOWEghEOWBgODnTojA2o5q
=B9dz
-----END PGP SIGNATURE-----
^ permalink raw reply
* [PATCH 3/5] refactor userdiff textconv code
From: Jeff King @ 2008-10-24 2:53 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Johannes Sixt, Matthieu Moy
In-Reply-To: <20081024024631.GA20365@coredump.intra.peff.net>
The initial implementation inserted itself at the level of
fill_mmfile. That is, the diff_filespec always contained the
actual file data, but we set up the xdl mmfile with the fake
data. This had several disadvantages:
- fill_mmfile gets called from several places, but we don't
necessarily want all of them to see the textconv'd
version (e.g., whitespace checking should probably
consider it binary)
- the filespec itself carried no information about
binaryness. If we have textconv'd it, then it is no
longer binary and we need to mark that somehow (which
lets us avoid having to manually set the filetype to
non-binary in the diff driver config)
This patch moves the text conversion to a
diff_filespec_textconv function; this function can be called
to insert the text-converted contents of the file into the
filespec. These contents will survive through any
diff_populate_filespec calls, meaning that it is safe to
pass the resulting filespec to other diff functions which
will look at the content.
We now turn on the text conversion only for actual diffs,
not diffstats or whitespace checking. We may eventually want
it for other formats, too, but we will be conservative for
now.
Signed-off-by: Jeff King <peff@peff.net>
---
I think this is much cleaner. I have a nagging worry that a
text-converted filespec might live longer than I expect. Maybe it would
make sense to free the filespec data after the diff has been generated
(and then further populate_filespec calls would just restore the
original data).
Also, I'm happy to hear comments on which functionality should get
text-converted. I think I like seeing the --stat report the binary
changes.
diff.c | 51 +++++++++++++++++++++++++++++++++++----------
diffcore.h | 1 +
t/t4030-diff-textconv.sh | 6 +---
3 files changed, 42 insertions(+), 16 deletions(-)
diff --git a/diff.c b/diff.c
index d1fd594..89bd2ff 100644
--- a/diff.c
+++ b/diff.c
@@ -284,6 +284,36 @@ static void emit_rewrite_diff(const char *name_a,
copy_file_with_prefix(o->file, '+', two->data, two->size, new, reset);
}
+static int diff_filespec_textconv(struct diff_filespec *one)
+{
+ size_t size;
+ char *buf;
+
+ if (one->data_is_textconv)
+ return 0;
+
+ if (!DIFF_FILE_VALID(one))
+ return 0;
+
+ diff_filespec_load_driver(one);
+ if (!one->driver->textconv)
+ return 0;
+
+ if (diff_populate_filespec(one, 0) < 0)
+ return -1;
+
+ buf = run_textconv(one->driver->textconv, one, &size);
+ if (!buf)
+ return -1;
+
+ diff_free_filespec_blob(one);
+ one->data = buf;
+ one->size = size;
+ one->should_free = 1;
+ one->data_is_textconv = 1;
+ return 0;
+}
+
static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
{
if (!DIFF_FILE_VALID(one)) {
@@ -294,18 +324,8 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
else if (diff_populate_filespec(one, 0))
return -1;
- diff_filespec_load_driver(one);
- if (one->driver->textconv) {
- size_t size;
- mf->ptr = run_textconv(one->driver->textconv, one, &size);
- if (!mf->ptr)
- return -1;
- mf->size = size;
- }
- else {
- mf->ptr = one->data;
- mf->size = one->size;
- }
+ mf->ptr = one->data;
+ mf->size = one->size;
return 0;
}
@@ -1292,6 +1312,8 @@ static void diff_filespec_load_driver(struct diff_filespec *one)
int diff_filespec_is_binary(struct diff_filespec *one)
{
+ if (one->data_is_textconv)
+ return 0;
if (one->is_binary == -1) {
diff_filespec_load_driver(one);
if (one->driver->binary != -1)
@@ -1387,6 +1409,10 @@ static void builtin_diff(const char *name_a,
}
}
+ if (diff_filespec_textconv(one) < 0 ||
+ diff_filespec_textconv(two) < 0)
+ die("unable to read files to diff");
+
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
@@ -1778,6 +1804,7 @@ void diff_free_filespec_blob(struct diff_filespec *s)
s->should_free = s->should_munmap = 0;
s->data = NULL;
}
+ s->data_is_textconv = 0;
}
void diff_free_filespec_data(struct diff_filespec *s)
diff --git a/diffcore.h b/diffcore.h
index 713cca7..33aebc2 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -42,6 +42,7 @@ struct diff_filespec {
#define DIFF_FILE_VALID(spec) (((spec)->mode) != 0)
unsigned should_free : 1; /* data should be free()'ed */
unsigned should_munmap : 1; /* data should be munmap()'ed */
+ unsigned data_is_textconv : 1;
struct userdiff_driver *driver;
/* data should be considered "binary"; -1 means "don't know yet" */
diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh
index d0d7691..28d3640 100755
--- a/t/t4030-diff-textconv.sh
+++ b/t/t4030-diff-textconv.sh
@@ -51,7 +51,7 @@ test_expect_success 'setup textconv filter' '
git config diff.foo.textconv "$PWD"/hexdump
'
-test_expect_failure 'diff produces text' '
+test_expect_success 'diff produces text' '
git diff HEAD^ HEAD >diff &&
find_diff <diff >actual &&
test_cmp expect.text actual
@@ -63,14 +63,12 @@ test_expect_success 'diff-tree produces binary' '
test_cmp expect.binary actual
'
-test_expect_failure 'log produces text' '
+test_expect_success 'log produces text' '
git log -1 -p >log &&
find_diff <log >actual &&
test_cmp expect.text actual
'
-# actually passes, but only because textconv is broken
-# and its failure mode happens to be the same as success
test_expect_failure 'format-patch produces binary' '
git format-patch --no-binary --stdout HEAD^ >patch &&
find_diff <patch >actual &&
--
1.6.0.3.518.gdb328.dirty
^ permalink raw reply related
* [PATCH 4/5] userdiff: require explicitly allowing textconv
From: Jeff King @ 2008-10-24 2:55 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Johannes Sixt, Matthieu Moy
In-Reply-To: <20081024024631.GA20365@coredump.intra.peff.net>
Diffs that have been produced with textconv almost certainly
cannot be applied, so we want to be careful not to generate
them in things like format-patch.
This introduces a new diff options, ALLOW_TEXTCONV, which
controls this behavior. It is off by default, but is
explicitly turned on for the "log" family of commands, as
well as the "diff" porcelain.
Because both text conversion and external diffing are
controlled by these diff options, we can get rid of the
"plumbing versus porcelain" distinction when reading the
config. This was an attempt to control the same thing, but
suffered from being too coarse-grained.
Signed-off-by: Jeff King <peff@peff.net>
---
This gives us a nice run-time knob to tweak. There was a lot of
discussion last time around about exactly when text conversion should
happen. The more I think about it, I'm wondering if perhaps this should
be disabled by default, and require "git log --textconv" to activate.
Johannes, you had mentioned some heuristics. Maybe you would like to
take a stab at implementing them on top of this?
builtin-diff.c | 1 +
builtin-log.c | 1 +
diff.c | 24 ++++++++++--------------
diff.h | 1 +
t/t4030-diff-textconv.sh | 2 +-
userdiff.c | 10 +---------
userdiff.h | 3 +--
7 files changed, 16 insertions(+), 26 deletions(-)
diff --git a/builtin-diff.c b/builtin-diff.c
index 9c8c295..2de5834 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -300,6 +300,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
}
DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
+ DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
/*
* If the user asked for our exit code then don't start a
diff --git a/builtin-log.c b/builtin-log.c
index a0944f7..75d698f 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -59,6 +59,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
} else
die("unrecognized argument: %s", arg);
}
+ DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
}
/*
diff --git a/diff.c b/diff.c
index 89bd2ff..52feba7 100644
--- a/diff.c
+++ b/diff.c
@@ -93,12 +93,6 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "diff.external"))
return git_config_string(&external_diff_cmd_cfg, var, value);
- switch (userdiff_config_porcelain(var, value)) {
- case 0: break;
- case -1: return -1;
- default: return 0;
- }
-
return git_diff_basic_config(var, value, cb);
}
@@ -109,6 +103,12 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
return 0;
}
+ switch (userdiff_config(var, value)) {
+ case 0: break;
+ case -1: return -1;
+ default: return 0;
+ }
+
if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
int slot = parse_diff_color_slot(var, 11);
if (!value)
@@ -123,12 +123,6 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
return 0;
}
- switch (userdiff_config_basic(var, value)) {
- case 0: break;
- case -1: return -1;
- default: return 0;
- }
-
return git_color_default_config(var, value, cb);
}
@@ -1409,9 +1403,11 @@ static void builtin_diff(const char *name_a,
}
}
- if (diff_filespec_textconv(one) < 0 ||
- diff_filespec_textconv(two) < 0)
+ if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
+ if (diff_filespec_textconv(one) < 0 ||
+ diff_filespec_textconv(two) < 0)
die("unable to read files to diff");
+ }
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
diff --git a/diff.h b/diff.h
index a49d865..42582ed 100644
--- a/diff.h
+++ b/diff.h
@@ -65,6 +65,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
#define DIFF_OPT_IGNORE_SUBMODULES (1 << 18)
#define DIFF_OPT_DIRSTAT_CUMULATIVE (1 << 19)
#define DIFF_OPT_DIRSTAT_BY_FILE (1 << 20)
+#define DIFF_OPT_ALLOW_TEXTCONV (1 << 21)
#define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag)
#define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag)
#define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag)
diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh
index 28d3640..e456746 100755
--- a/t/t4030-diff-textconv.sh
+++ b/t/t4030-diff-textconv.sh
@@ -69,7 +69,7 @@ test_expect_success 'log produces text' '
test_cmp expect.text actual
'
-test_expect_failure 'format-patch produces binary' '
+test_expect_success 'format-patch produces binary' '
git format-patch --no-binary --stdout HEAD^ >patch &&
find_diff <patch >actual &&
test_cmp expect.binary actual
diff --git a/userdiff.c b/userdiff.c
index d95257a..3681062 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -120,7 +120,7 @@ static int parse_tristate(int *b, const char *k, const char *v)
return 1;
}
-int userdiff_config_basic(const char *k, const char *v)
+int userdiff_config(const char *k, const char *v)
{
struct userdiff_driver *drv;
@@ -130,14 +130,6 @@ int userdiff_config_basic(const char *k, const char *v)
return parse_funcname(&drv->funcname, k, v, REG_EXTENDED);
if ((drv = parse_driver(k, v, "binary")))
return parse_tristate(&drv->binary, k, v);
-
- return 0;
-}
-
-int userdiff_config_porcelain(const char *k, const char *v)
-{
- struct userdiff_driver *drv;
-
if ((drv = parse_driver(k, v, "command")))
return parse_string(&drv->external, k, v);
if ((drv = parse_driver(k, v, "textconv")))
diff --git a/userdiff.h b/userdiff.h
index f29c18f..ba29457 100644
--- a/userdiff.h
+++ b/userdiff.h
@@ -14,8 +14,7 @@ struct userdiff_driver {
const char *textconv;
};
-int userdiff_config_basic(const char *k, const char *v);
-int userdiff_config_porcelain(const char *k, const char *v);
+int userdiff_config(const char *k, const char *v);
struct userdiff_driver *userdiff_find_by_name(const char *name);
struct userdiff_driver *userdiff_find_by_path(const char *path);
--
1.6.0.3.518.gdb328.dirty
^ permalink raw reply related
* [PATCH 5/5] document the diff driver textconv feature
From: Jeff King @ 2008-10-24 2:56 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Johannes Sixt, Matthieu Moy
In-Reply-To: <20081024024631.GA20365@coredump.intra.peff.net>
This patch also changes the term "custom diff driver" to
"external diff driver"; now that there are more facets of a
"custom driver" than just external diffing, it makes sense
to refer to the configuration of "diff.foo.*" as the "foo
diff driver", with "diff.foo.command" as the "external
driver for foo".
Signed-off-by: Jeff King <peff@peff.net>
---
Documentation/gitattributes.txt | 66 +++++++++++++++++++++++++++++++--------
1 files changed, 53 insertions(+), 13 deletions(-)
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 2694559..314e2d3 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -213,10 +213,12 @@ with `crlf`, and then `ident` and fed to `filter`.
Generating diff text
~~~~~~~~~~~~~~~~~~~~
-The attribute `diff` affects if 'git-diff' generates textual
-patch for the path or just says `Binary files differ`. It also
-can affect what line is shown on the hunk header `@@ -k,l +n,m @@`
-line.
+The attribute `diff` affects how 'git' generates diffs for particular
+files. It can tell git whether to generate a textual patch for the path
+or to treat the path as a binary file. It can also affect what line is
+shown on the hunk header `@@ -k,l +n,m @@` line, tell git to use an
+external command to generate the diff, or ask git to convert binary
+files to a text format before generating the diff.
Set::
@@ -227,7 +229,8 @@ Set::
Unset::
A path to which the `diff` attribute is unset will
- generate `Binary files differ`.
+ generate `Binary files differ` (or a binary patch, if
+ binary patches are enabled).
Unspecified::
@@ -238,21 +241,21 @@ Unspecified::
String::
- Diff is shown using the specified custom diff driver.
- The driver program is given its input using the same
- calling convention as used for GIT_EXTERNAL_DIFF
- program. This name is also used for custom hunk header
- selection.
+ Diff is shown using the specified diff driver. Each driver may
+ specify one or more options, as described in the following
+ section. The options for the diff driver "foo" are defined
+ by the configuration variables in the "diff.foo" section of the
+ git config file.
-Defining a custom diff driver
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Defining an external diff driver
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The definition of a diff driver is done in `gitconfig`, not
`gitattributes` file, so strictly speaking this manual page is a
wrong place to talk about it. However...
-To define a custom diff driver `jcdiff`, add a section to your
+To define an external diff driver `jcdiff`, add a section to your
`$GIT_DIR/config` file (or `$HOME/.gitconfig` file) like this:
----------------------------------------------------------------
@@ -328,6 +331,43 @@ patterns are available:
- `tex` suitable for source code for LaTeX documents.
+Performing text diffs of binary files
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Sometimes it is desirable to see the diff of a text-converted
+version of some binary files. For example, a word processor
+document can be converted to an ASCII text representation, and
+the diff of the text shown. Even though this conversion loses
+some information, the resulting diff is useful for human
+viewing (but cannot be applied directly).
+
+The `textconv` config option is used to define a program for
+performing such a conversion. The program should take a single
+argument, the name of a file to convert, and produce the
+resulting text on stdout.
+
+For example, to show the diff of the exif information of a
+file instead of the binary information (assuming you have the
+exif tool installed):
+
+------------------------
+[diff "jpg"]
+ textconv = exif
+------------------------
+
+NOTE: The text conversion is generally a one-way conversion;
+in this example, we lose the actual image contents and focus
+just on the text data. This means that diffs generated by
+textconv are _not_ suitable for applying. For this reason,
+only `git diff` and the `git log` family of commands (i.e.,
+log, whatchanged, show) will perform text conversion. `git
+format-patch` will never generate this output. If you want to
+send somebody a text-converted diff of a binary file (e.g.,
+because it quickly conveys the changes you have made), you
+should generate it separately and send it as a comment _in
+addition to_ the usual binary diff that you might send.
+
+
Performing a three-way merge
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--
1.6.0.3.518.gdb328.dirty
^ permalink raw reply related
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