* Re: manpage name conflict
From: Sebastian Kuzminsky @ 2005-05-19 18:18 UTC (permalink / raw)
To: git
In-Reply-To: <Pine.LNX.4.58.0505190956330.2322@ppc970.osdl.org>
Linus Torvalds <torvalds@osdl.org> wrote:
> On Thu, 19 May 2005, Sebastian Kuzminsky wrote:
> > Anyway, here's the documentation patch:
>
> It's whitespace-corrupted, with tabs turned into spaces..
<blush>
Index: Documentation/Makefile
===================================================================
--- 75b95bec390d6728b9b1b4572056af8cee34ea7d/Documentation/Makefile (mode:100644)
+++ uncommitted/Documentation/Makefile (mode:100644)
@@ -1,6 +1,6 @@
DOC_SRC=$(wildcard git*.txt)
DOC_HTML=$(patsubst %.txt,%.html,$(DOC_SRC))
-DOC_MAN=$(patsubst %.txt,%.1,$(DOC_SRC))
+DOC_MAN=$(patsubst %.txt,%.1,$(wildcard git-*.txt)) git.7
all: $(DOC_HTML) $(DOC_MAN)
@@ -13,13 +13,15 @@
touch $@
clean:
- rm -f *.xml *.html *.1
+ rm -f *.xml *.html *.1 *.7
%.html : %.txt
asciidoc -b css-embedded -d manpage $<
-%.1 : %.xml
+%.1 %.7 : %.xml
xmlto man $<
+ # FIXME: this next line works around an output filename bug in asciidoc 6.0.3
+ [ "$@" = "git.7" ] || mv git.1 $@
%.xml : %.txt
asciidoc -b docbook -d manpage $<
Index: Documentation/git-diff-helper.txt
===================================================================
--- 75b95bec390d6728b9b1b4572056af8cee34ea7d/Documentation/git-diff-helper.txt (mode:100644)
+++ uncommitted/Documentation/git-diff-helper.txt (mode:100644)
@@ -1,5 +1,5 @@
git-diff-helper(1)
-=======================
+==================
v0.1, May 2005
NAME
Index: Documentation/git.txt
===================================================================
--- 75b95bec390d6728b9b1b4572056af8cee34ea7d/Documentation/git.txt (mode:100644)
+++ uncommitted/Documentation/git.txt (mode:100644)
@@ -1,4 +1,4 @@
-git(1)
+git(7)
======
v0.1, May 2005
--
Sebastian Kuzminsky
"Marie will know I'm headed south, so's to meet me by and by"
-Townes Van Zandt
^ permalink raw reply
* Re: [PATCH] Detect renames in diff family.
From: Joel Becker @ 2005-05-19 17:46 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.58.0505190901340.2322@ppc970.osdl.org>
On Thu, May 19, 2005 at 09:19:28AM -0700, Linus Torvalds wrote:
> In other words, let's say that we create a new architecture or a new
> filesystem, and we have tons of _new_ files, but not a lot of removed
> files. It would literally be very cool to see that the new files are based
> on contents of old files, and that it would thus potentially be very
> interesting to see a diff like
Subversion encourages exactly this with the 'svn cp' command.
Just as knowing when a file was renamed allows you to track the history
past its first appearance under the current name, 'cp' allows you to
follow the history even if the original name still exists. I have found
this useful more than once.
Now, whether you track this up front with an expensive commit or
use tools to discover the relationship at query time (ala your
why-rename-tracking-isnt-needed argument) is a different question. As
we all know, most tools ask the user to explicitly declare the
relationship at the time it happens with 'svn rename' and 'svn cp' or
the analog. But git could do the comparisons, with appropriate
heuristics, at the time someone asks.
Joel
--
Life's Little Instruction Book #335
"Every so often, push your luck."
Joel Becker
Senior Member of Technical Staff
Oracle
E-mail: joel.becker@oracle.com
Phone: (650) 506-8127
^ permalink raw reply
* Re: [PATCH] Deltification library work by Nicolas Pitre.
From: Nicolas Pitre @ 2005-05-19 17:39 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Linus Torvalds, git
In-Reply-To: <7vekc3178w.fsf@assigned-by-dhcp.cox.net>
On Thu, 19 May 2005, Junio C Hamano wrote:
> >>>>> "NP" == Nicolas Pitre <nico@cam.org> writes:
>
> NP> In fact I think the code in that file might be simplified even further
> NP> eventually, at which point there might not be much of the original code
> NP> left anymore and the license switched to GPL v2.
>
> I am afraid that kind of code transformation would not change
> the copyright issues.
Maybe you're right. Anyway it is a non issue now.
Nicolas
^ permalink raw reply
* Re: [PATCH] Detect renames in diff family.
From: Linus Torvalds @ 2005-05-19 17:31 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7v4qcz16n6.fsf@assigned-by-dhcp.cox.net>
On Thu, 19 May 2005, Junio C Hamano wrote:
>
> - I have been assuming that diff_delta uses its two input
> read-only but have not verified that myself yet.
Since test-delta uses mmap(PROT_READ), we'd get SIGSEGV if diff_delta
actually wrote to the thing. So this is a safe assumption.
Linus
^ permalink raw reply
* Re: [PATCH] Detect renames in diff family.
From: Junio C Hamano @ 2005-05-19 17:13 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
In-Reply-To: <Pine.LNX.4.58.0505190901340.2322@ppc970.osdl.org>
>>>>> "LT" == Linus Torvalds <torvalds@osdl.org> writes:
LT> I notice that you left some debugging output in there ("**score **"
LT> stuff), and I'll remove it, but it's merged and pushed out and passed my
LT> trivial tests.
Oops,... thanks. I still had some doubts about it and that's
why I said it was beta, but that is fine. My doubts are minor:
- the command line interface "-M" to read "-M" or "-M[0-9]"
(one digit); -M defaults to -M5 and give the cut-off point at
similarity score 5000, -M9 at 9000, etc.
- I was debating myself if adding something like this was a
good idea (using scale between 0 and 9 corresponding the
-M[0-9] option):
diff --git a/arch/um/kernel/sys_call_table.c b/arch/um/sys-x86_64/sys_call_table.c
*** rename similarity index 8
rename old arch/um/kernel/sys_call_table.c
rename new arch/um/sys-x86_64/sys_call_table.c
--- a/arch/um/kernel/sys_call_table.c
+++ b/arch/um/sys-x86_64/sys_call_table.c
- I have been assuming that diff_delta uses its two input
read-only but have not verified that myself yet.
- I did not check for leaks and knew I had outdated comments in
some while doing the diff core interface cleanups.
A bit of clean-up patch, which may not apply exactly if you
removed the **score** stuff is attached.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
# - HEAD: Detect renames in diff family.
# + 11: Cleanup and leak fix after rename in diff family patch.
diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -85,12 +85,10 @@ struct diff_spec {
unsigned char blob_sha1[20];
unsigned short mode; /* file mode */
unsigned sha1_valid : 1; /* if true, use blob_sha1 and trust mode;
- * however with a NULL SHA1, read them
- * from the file system.
- * if false, use the name and read mode from
+ * if false, use the name and read from
* the filesystem.
*/
- unsigned file_valid : 1; /* if false the file does not even exist */
+ unsigned file_valid : 1; /* if false the file does not exist */
};
static void builtin_diff(const char *name_a,
@@ -506,6 +504,7 @@ static void free_data(struct diff_spec_h
else if (s->flags & SHOULD_MUNMAP)
munmap(s->data, s->size);
s->flags &= ~(SHOULD_FREE|SHOULD_MUNMAP);
+ s->data = 0;
}
static void flush_remaining_diff(struct diff_spec_hold *elem,
@@ -625,9 +624,17 @@ void diff_flush(void)
/* We really want to cull the candidates list early
* with cheap tests in order to avoid doing deltas.
+ *
+ * With the current callers, we should not have already
+ * matched entries at this point, but it is nonetheless
+ * checked for sanity.
*/
for (dst = createdfile; dst; dst = dst->next) {
+ if (dst->flags & MATCHED)
+ continue;
for (src = deletedfile; src; src = src->next) {
+ if (src->flags & MATCHED)
+ continue;
if (! is_exact_match(src, dst))
continue;
flush_rename_pair(src, dst);
@@ -665,6 +672,7 @@ void diff_flush(void)
}
qsort(mx, num_create*num_delete, sizeof(*mx), score_compare);
+#if 0
for (c = 0; c < num_create * num_delete; c++) {
src = mx[c].src;
dst = mx[c].dst;
@@ -674,6 +682,7 @@ void diff_flush(void)
"**score ** %d %s %s\n",
mx[c].score, src->path, dst->path);
}
+#endif
for (c = 0; c < num_create * num_delete; c++) {
src = mx[c].src;
@@ -684,6 +693,7 @@ void diff_flush(void)
break;
flush_rename_pair(src, dst);
}
+ free(mx);
exit_path:
flush_remaining_diff(createdfile, 1);
^ permalink raw reply
* Re: [PATCH] Deltification library work by Nicolas Pitre.
From: Junio C Hamano @ 2005-05-19 16:59 UTC (permalink / raw)
To: Nicolas Pitre; +Cc: Linus Torvalds, git
In-Reply-To: <Pine.LNX.4.62.0505191104410.20274@localhost.localdomain>
>>>>> "NP" == Nicolas Pitre <nico@cam.org> writes:
NP> In fact I think the code in that file might be simplified even further
NP> eventually, at which point there might not be much of the original code
NP> left anymore and the license switched to GPL v2.
I am afraid that kind of code transformation would not change
the copyright issues.
^ permalink raw reply
* Re: manpage name conflict
From: Linus Torvalds @ 2005-05-19 16:57 UTC (permalink / raw)
To: Sebastian Kuzminsky; +Cc: git
In-Reply-To: <E1DYnpO-0003cF-I6@highlab.com>
On Thu, 19 May 2005, Sebastian Kuzminsky wrote:
>
> Anyway, here's the documentation patch:
It's whitespace-corrupted, with tabs turned into spaces..
Linus
^ permalink raw reply
* Re: manpage name conflict
From: Linus Torvalds @ 2005-05-19 16:47 UTC (permalink / raw)
To: Sebastian Kuzminsky; +Cc: git
In-Reply-To: <E1DYnpO-0003cF-I6@highlab.com>
On Thu, 19 May 2005, Sebastian Kuzminsky wrote:
>
> But what is going to be the name of the git package? Let's please
> not make it "git", because that's taken by the GNU Interactive Tools.
> How about "git-core" or "git-plumbing" or "linus-is-a-git"?
"git-core" sounds good to me. I don't mind "linus-is-a-git" either, but I
suspect it would end up confusing people if the git packages are installed
with something that starts with "linus-"
Linus
^ permalink raw reply
* Re: manpage name conflict
From: Sebastian Kuzminsky @ 2005-05-19 16:24 UTC (permalink / raw)
To: git
In-Reply-To: <20050519155804.GB4513@pasky.ji.cz>
Petr Baudis <pasky@ucw.cz> wrote:
> Does this manpage actually belong to man1? What about git(7) or
> something? It's not an actual command.
Good point.
Ok, I've appended a patch (against the top of git-pb) that moves the
git manpage to man7. It also does two other things:
* Sort of works around the asciidoc 6.0.3 bug where the manpages all
get called "git.1". It just renames them to what they should have
been called.
* Fixes a cut-n-paste bug in git-diff-helper.txt that was making
asciidoc choke.
> Not directly related to this problem, but just FYI - git isn't staying
> as part of Cogito forever, actually I think its time in Cogito
> distribution is running over soon (now that I've pushed all the interesting
> local changes to git-pb, consequently to git-linus).
>
> So you will have to either bundle it manually in the distribution
> packages, or provide a separate git package for cogito to depend on
> (when the unbundling really happens). Either way, this is git issue,
> not cogito. :-)
Right. Hm. It's no problem to have git be it's own separate package
with all the appropriate relationships (cogito Requires git, and git
suggests cogito).
But what is going to be the name of the git package? Let's please
not make it "git", because that's taken by the GNU Interactive Tools.
How about "git-core" or "git-plumbing" or "linus-is-a-git"?
;)
Anyway, here's the documentation patch:
Index: Documentation/Makefile
===================================================================
--- 75b95bec390d6728b9b1b4572056af8cee34ea7d/Documentation/Makefile (mode:100644)
+++ uncommitted/Documentation/Makefile (mode:100644)
@@ -1,6 +1,6 @@
DOC_SRC=$(wildcard git*.txt)
DOC_HTML=$(patsubst %.txt,%.html,$(DOC_SRC))
-DOC_MAN=$(patsubst %.txt,%.1,$(DOC_SRC))
+DOC_MAN=$(patsubst %.txt,%.1,$(wildcard git-*.txt)) git.7
all: $(DOC_HTML) $(DOC_MAN)
@@ -13,13 +13,15 @@
touch $@
clean:
- rm -f *.xml *.html *.1
+ rm -f *.xml *.html *.1 *.7
%.html : %.txt
asciidoc -b css-embedded -d manpage $<
-%.1 : %.xml
+%.1 %.7 : %.xml
xmlto man $<
+ # FIXME: this next line works around an output filename bug in asciidoc 6.0.3
+ [ "$@" = "git.7" ] || mv git.1 $@
%.xml : %.txt
asciidoc -b docbook -d manpage $<
Index: Documentation/git-diff-helper.txt
===================================================================
--- 75b95bec390d6728b9b1b4572056af8cee34ea7d/Documentation/git-diff-helper.txt (mode:100644)
+++ uncommitted/Documentation/git-diff-helper.txt (mode:100644)
@@ -1,5 +1,5 @@
git-diff-helper(1)
-=======================
+==================
v0.1, May 2005
NAME
Index: Documentation/git.txt
===================================================================
--- 75b95bec390d6728b9b1b4572056af8cee34ea7d/Documentation/git.txt (mode:100644)
+++ uncommitted/Documentation/git.txt (mode:100644)
@@ -1,4 +1,4 @@
-git(1)
+git(7)
======
v0.1, May 2005
--
Sebastian Kuzminsky
"Marie will know I'm headed south, so's to meet me by and by"
-Townes Van Zandt
^ permalink raw reply
* Re: [PATCH] Detect renames in diff family.
From: Linus Torvalds @ 2005-05-19 16:19 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vu0kz1p6k.fsf@assigned-by-dhcp.cox.net>
On Thu, 19 May 2005, Junio C Hamano wrote:
>
> Special request for Linus is to check if I did not screw up the
> various calls into the diff core from diff-tree. Essentially
> the idea is to start one patchset session with diff_setup() and
> close it with diff_flush() before you start another patchset
> session.
It all looks ok from a quick setup, and with this I can now do
git-whatchanged -M
in the kernel, and searching for renames I find:
diff --git a/arch/um/kernel/sys_call_table.c b/arch/um/sys-x86_64/sys_call_table.c
rename old arch/um/kernel/sys_call_table.c
rename new arch/um/sys-x86_64/sys_call_table.c
--- a/arch/um/kernel/sys_call_table.c
+++ b/arch/um/sys-x86_64/sys_call_table.c
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
* Copyright 2003 PathScale, Inc.
* Licensed under the GPL
@@ -14,6 +14,12 @@
#include "sysdep/syscalls.h"
#include "kern_util.h"
+#ifdef CONFIG_NFSD
....
which looks quite correct.
I notice that you left some debugging output in there ("**score **"
stuff), and I'll remove it, but it's merged and pushed out and passed my
trivial tests.
[ rambling mode on: ]
One thing that struck me is that there is nothing wrong with having the
same old file marked twice for a rename, or considering new files to be
copies of old files. So if we ever allow that, then "rename" may be the
wrong name for this, since the logic certainly allows the old file to
still exist (or be removed and show up multiple times in a new guise).
In other words, let's say that we create a new architecture or a new
filesystem, and we have tons of _new_ files, but not a lot of removed
files. It would literally be very cool to see that the new files are based
on contents of old files, and that it would thus potentially be very
interesting to see a diff like
diff --git a/arch/i386/kernel/irq.c b/arch/x86-64/kernel/irq.c
based-on old arch/i386/kernel/irq.c
creates new arch/x86-64/kernel/irq.c
--- arch/i386/kernel/irq.c
+++ arch/x86_64/kernel/irq.c
@@ -1,205 +1,31 @@
/*
- * linux/arch/i386/kernel/irq.c
+ * linux/arch/x86_64/kernel/irq.c
*
* Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
*
...
(the above is a made-up example, but it's at least _half-way_ valid).
I'm not suggesting you actually do this, if only because it's quite
expensive: it means that any newly added file would have to be compared
with _all_ files in the previous archive, which is just too damn
expensive. But I'd like people to kind of keep this in mind as a
possibility, because maybe wasting CPU time in a big way might actually be
acceptable in some cases, and having a separate flag to enable this kind
of thing might be interesting, no?
Linus
^ permalink raw reply
* Re: [PATCH 0/4] Pulling refs files
From: Daniel Barkalow @ 2005-05-19 16:00 UTC (permalink / raw)
To: Petr Baudis; +Cc: git, Linus Torvalds
In-Reply-To: <20050519065207.GB18281@pasky.ji.cz>
On Thu, 19 May 2005, Petr Baudis wrote:
> Dear diary, on Thu, May 19, 2005 at 05:19:01AM CEST, I got a letter
> where Daniel Barkalow <barkalow@iabervon.org> told me that...
> > 2) fetching reference files by name, and making them available to the
> > local program without writing them to disk at all.
> > 3) fetching other files by name and writing them to either the
> > corresponding filename or a provided replacement.
> >
> > I had thought that (2) could be done as a special case of (3), but I think
> > that it has to be separate, because (2) just returns the value, while
> > (3) can't just return the contents, but has to write it somewhere, since
> > it isn't constrained to be exactly 20 bytes.
>
> Huh. How would (2) be useful and why can't you just still write it e.g.
> to some user-supplied temporary file? I think that'd be still actually
> much less trouble for the scripts to handle.
(2) is what is needed if the user just requests downloading objects
starting with a reference stored remotely, and doesn't request that the
reference be written anywhere. It is also useful because the system wants
to verify that it has actually downloaded the objects successfully before
writing the reference.
Note that the scripts see a higher-level interface; these are the
operations that (e.g.) http-pull.c has to provide for pull.c, which builds
a larger operation (determine the target hash, download the objects, write
the specified ref file) out of them. It would be inconvenient for pull.c
to download to a temporary file and then read the temporary file, which
shouldn't normally be visible yet, to figure out what it's doing. It wants
to have a function that takes a string and returns a hash, getting the
value from the remote host, and it's inconvenient to deal with the disk in
the middle.
-Daniel
*This .sig left intentionally blank*
^ permalink raw reply
* Re: manpage name conflict
From: Petr Baudis @ 2005-05-19 15:58 UTC (permalink / raw)
To: Sebastian Kuzminsky; +Cc: git
In-Reply-To: <E1DYmy8-0003YB-JW@highlab.com>
Dear diary, on Thu, May 19, 2005 at 05:29:52PM CEST, I got a letter
where Sebastian Kuzminsky <seb@highlab.com> told me that...
> Hi folks, I maintain a Debian package for Cogito (it just went into "Sid"
> aka "unstable"), and I just got a bug report from a user that I'd like
> your input on.
>
>
> The problem is that Cogito wants to install a git(1) manpage, and so does
> the GNU Interactive Tools. The GNU Interactive Tools actually have a
> program called "git", so it seems only fair that they get to call their
> manpage by the same name. The GIT-as-in-Cogito git(1) manpage gives
> an overview of the GIT-as-in-Cogito core, so maybe we could install it
> as git-core(1)?
Does this manpage actually belong to man1? What about git(7) or
something? It's not an actual command.
Not directly related to this problem, but just FYI - git isn't staying
as part of Cogito forever, actually I think its time in Cogito
distribution is running over soon (now that I've pushed all the interesting
local changes to git-pb, consequently to git-linus).
So you will have to either bundle it manually in the distribution
packages, or provide a separate git package for cogito to depend on
(when the unbundling really happens). Either way, this is git issue,
not cogito. :-)
--
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: [PATCH] Deltification library work by Nicolas Pitre.
From: Davide Libenzi @ 2005-05-19 15:40 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0505190833380.2322@ppc970.osdl.org>
On Thu, 19 May 2005, Linus Torvalds wrote:
>
> [ This goes to the list because Davide can apparently receive the list
> emails, but for some reason apparently doesn't like my osdl.org
> address ]
[Just greylist timeout ;)]
> Davide,
>
> would you mind signing off on me adding the lines
>
> * This file is free software; you can redistribute it and/or
> * modify it under the terms of the GNU Lesser General Public
> * License as published by the Free Software Foundation; either
> * version 2.1 of the License, or (at your option) any later version.
> + *
> + * Use of this within git automatically means that the LGPL
> + * licensing gets turned into GPLv2 within this project.
> */
Signed-off-by: Davide Libenzi <davidel@xmailserver.org>
- Davide
^ permalink raw reply
* Re: [PATCH] Deltification library work by Nicolas Pitre.
From: Linus Torvalds @ 2005-05-19 15:36 UTC (permalink / raw)
To: Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0505190736020.2322@ppc970.osdl.org>
[ This goes to the list because Davide can apparently receive the list
emails, but for some reason apparently doesn't like my osdl.org
address ]
Davide,
would you mind signing off on me adding the lines
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
+ *
+ * Use of this within git automatically means that the LGPL
+ * licensing gets turned into GPLv2 within this project.
*/
(If you just send me an ack and your "signed-off-by" line, I'll edit
Nico's patch appropriately, and check it in with all of our sign-offs).
That way there's no question about the dual-licensing.
Linus
^ permalink raw reply
* manpage name conflict
From: Sebastian Kuzminsky @ 2005-05-19 15:29 UTC (permalink / raw)
To: git
Hi folks, I maintain a Debian package for Cogito (it just went into "Sid"
aka "unstable"), and I just got a bug report from a user that I'd like
your input on.
The problem is that Cogito wants to install a git(1) manpage, and so does
the GNU Interactive Tools. The GNU Interactive Tools actually have a
program called "git", so it seems only fair that they get to call their
manpage by the same name. The GIT-as-in-Cogito git(1) manpage gives
an overview of the GIT-as-in-Cogito core, so maybe we could install it
as git-core(1)?
What do you think?
--
Sebastian Kuzminsky
"Marie will know I'm headed south, so's to meet me by and by"
-Townes Van Zandt
^ permalink raw reply
* Re: [PATCH] Deltification library work by Nicolas Pitre.
From: Nicolas Pitre @ 2005-05-19 15:14 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.58.0505190736020.2322@ppc970.osdl.org>
On Thu, 19 May 2005, Linus Torvalds wrote:
> Sure. I'll apply this one and merge in Junio's rename on top of it, but I
> wanted to verify one thing first:
>
> > + * This file is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2.1 of the License, or (at your option) any later version.
>
> I don't know the different LGPL versions, so can somebody verify that LGPL
> 2.1 is fully compatible with GPLv2...
>
> In fact I'd prefer to have that notice in the code to make it obvious that
> the LGPL becomes the GPLv2 when linked into the rest of git.
I don't mind switching it to GPL v2 if I'm allowed to. I kept LGPL v2.1
for that file since that's the license used for xdiff where significant
portion of that file has been copied from.
In fact I think the code in that file might be simplified even further
eventually, at which point there might not be much of the original code
left anymore and the license switched to GPL v2. But in the mean time
someone else with better knowledge of GPL vs LGPL interaction is needed
to give advice.
Nicolas
^ permalink raw reply
* Re: [PATCH] Deltification library work by Nicolas Pitre.
From: Linus Torvalds @ 2005-05-19 14:38 UTC (permalink / raw)
To: Nicolas Pitre; +Cc: Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.62.0505191019180.20274@localhost.localdomain>
On Thu, 19 May 2005, Nicolas Pitre wrote:
>
> I'd prefer if the following patch was applied instead, following the
> patch separation I've done already.
Sure. I'll apply this one and merge in Junio's rename on top of it, but I
wanted to verify one thing first:
> + * This file is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
I don't know the different LGPL versions, so can somebody verify that LGPL
2.1 is fully compatible with GPLv2...
In fact I'd prefer to have that notice in the code to make it obvious that
the LGPL becomes the GPLv2 when linked into the rest of git.
Linus
^ permalink raw reply
* Re: [PATCH] Deltification library work by Nicolas Pitre.
From: Nicolas Pitre @ 2005-05-19 14:27 UTC (permalink / raw)
To: Junio C Hamano; +Cc: torvalds, git
In-Reply-To: <7vwtpv1pd4.fsf@assigned-by-dhcp.cox.net>
On Thu, 19 May 2005, Junio C Hamano wrote:
> This is stolen from the deltification patch by Nicolas Pitre.
> Although the deltification patch has not been submitted for the
> inclusion, the library part here is useful for the rename
> detection logic in the diff work I have been doing. The next
> patch will depend on this, so if Nico is OK with this one,
> please consider inclusion of this patch.
I'd prefer if the following patch was applied instead, following the
patch separation I've done already.
=====
This patch adds basic library functions to create and replay delta
information. Also included is a test-delta utility to validate the code.
Signed-off-by: Nicolas Pitre <nico@cam.org>
Index: git/diff-delta.c
===================================================================
--- /dev/null
+++ git/diff-delta.c
@@ -0,0 +1,330 @@
+/*
+ * diff-delta.c: generate a delta between two buffers
+ *
+ * Many parts of this file have been lifted from LibXDiff version 0.10.
+ * http://www.xmailserver.org/xdiff-lib.html
+ *
+ * LibXDiff was written by Davide Libenzi <davidel@xmailserver.org>
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * Many mods for GIT usage by Nicolas Pitre <nico@cam.org>, (C) 2005.
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+#include <stdlib.h>
+#include "delta.h"
+
+
+/* block size: min = 16, max = 64k, power of 2 */
+#define BLK_SIZE 16
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+#define GR_PRIME 0x9e370001
+#define HASH(v, b) (((unsigned int)(v) * GR_PRIME) >> (32 - (b)))
+
+/* largest prime smaller than 65536 */
+#define BASE 65521
+
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+#define NMAX 5552
+
+#define DO1(buf, i) { s1 += buf[i]; s2 += s1; }
+#define DO2(buf, i) DO1(buf, i); DO1(buf, i + 1);
+#define DO4(buf, i) DO2(buf, i); DO2(buf, i + 2);
+#define DO8(buf, i) DO4(buf, i); DO4(buf, i + 4);
+#define DO16(buf) DO8(buf, 0); DO8(buf, 8);
+
+static unsigned int adler32(unsigned int adler, const unsigned char *buf, int len)
+{
+ int k;
+ unsigned int s1 = adler & 0xffff;
+ unsigned int s2 = adler >> 16;
+
+ while (len > 0) {
+ k = MIN(len, NMAX);
+ len -= k;
+ while (k >= 16) {
+ DO16(buf);
+ buf += 16;
+ k -= 16;
+ }
+ if (k != 0)
+ do {
+ s1 += *buf++;
+ s2 += s1;
+ } while (--k);
+ s1 %= BASE;
+ s2 %= BASE;
+ }
+
+ return (s2 << 16) | s1;
+}
+
+static unsigned int hashbits(unsigned int size)
+{
+ unsigned int val = 1, bits = 0;
+ while (val < size && bits < 32) {
+ val <<= 1;
+ bits++;
+ }
+ return bits ? bits: 1;
+}
+
+typedef struct s_chanode {
+ struct s_chanode *next;
+ int icurr;
+} chanode_t;
+
+typedef struct s_chastore {
+ chanode_t *head, *tail;
+ int isize, nsize;
+ chanode_t *ancur;
+ chanode_t *sncur;
+ int scurr;
+} chastore_t;
+
+static void cha_init(chastore_t *cha, int isize, int icount)
+{
+ cha->head = cha->tail = NULL;
+ cha->isize = isize;
+ cha->nsize = icount * isize;
+ cha->ancur = cha->sncur = NULL;
+ cha->scurr = 0;
+}
+
+static void *cha_alloc(chastore_t *cha)
+{
+ chanode_t *ancur;
+ void *data;
+
+ ancur = cha->ancur;
+ if (!ancur || ancur->icurr == cha->nsize) {
+ ancur = malloc(sizeof(chanode_t) + cha->nsize);
+ if (!ancur)
+ return NULL;
+ ancur->icurr = 0;
+ ancur->next = NULL;
+ if (cha->tail)
+ cha->tail->next = ancur;
+ if (!cha->head)
+ cha->head = ancur;
+ cha->tail = ancur;
+ cha->ancur = ancur;
+ }
+
+ data = (void *)ancur + sizeof(chanode_t) + ancur->icurr;
+ ancur->icurr += cha->isize;
+ return data;
+}
+
+static void cha_free(chastore_t *cha)
+{
+ chanode_t *cur = cha->head;
+ while (cur) {
+ chanode_t *tmp = cur;
+ cur = cur->next;
+ free(tmp);
+ }
+}
+
+typedef struct s_bdrecord {
+ struct s_bdrecord *next;
+ unsigned int fp;
+ const unsigned char *ptr;
+} bdrecord_t;
+
+typedef struct s_bdfile {
+ const unsigned char *data, *top;
+ chastore_t cha;
+ unsigned int fphbits;
+ bdrecord_t **fphash;
+} bdfile_t;
+
+static int delta_prepare(const unsigned char *buf, int bufsize, bdfile_t *bdf)
+{
+ unsigned int fphbits;
+ int i, hsize;
+ const unsigned char *base, *data, *top;
+ bdrecord_t *brec;
+ bdrecord_t **fphash;
+
+ fphbits = hashbits(bufsize / BLK_SIZE + 1);
+ hsize = 1 << fphbits;
+ fphash = malloc(hsize * sizeof(bdrecord_t *));
+ if (!fphash)
+ return -1;
+ for (i = 0; i < hsize; i++)
+ fphash[i] = NULL;
+ cha_init(&bdf->cha, sizeof(bdrecord_t), hsize / 4 + 1);
+
+ bdf->data = data = base = buf;
+ bdf->top = top = buf + bufsize;
+ data += (bufsize / BLK_SIZE) * BLK_SIZE;
+ if (data == top)
+ data -= BLK_SIZE;
+
+ for ( ; data >= base; data -= BLK_SIZE) {
+ brec = cha_alloc(&bdf->cha);
+ if (!brec) {
+ cha_free(&bdf->cha);
+ free(fphash);
+ return -1;
+ }
+ brec->fp = adler32(0, data, MIN(BLK_SIZE, top - data));
+ brec->ptr = data;
+ i = HASH(brec->fp, fphbits);
+ brec->next = fphash[i];
+ fphash[i] = brec;
+ }
+
+ bdf->fphbits = fphbits;
+ bdf->fphash = fphash;
+
+ return 0;
+}
+
+static void delta_cleanup(bdfile_t *bdf)
+{
+ free(bdf->fphash);
+ cha_free(&bdf->cha);
+}
+
+#define COPYOP_SIZE(o, s) \
+ (!!(o & 0xff) + !!(o & 0xff00) + !!(o & 0xff0000) + !!(o & 0xff000000) + \
+ !!(s & 0xff) + !!(s & 0xff00) + 1)
+
+void *diff_delta(void *from_buf, unsigned long from_size,
+ void *to_buf, unsigned long to_size,
+ unsigned long *delta_size)
+{
+ int i, outpos, outsize, inscnt, csize, msize, moff;
+ unsigned int fp;
+ const unsigned char *data, *top, *ptr1, *ptr2;
+ unsigned char *out, *orig;
+ bdrecord_t *brec;
+ bdfile_t bdf;
+
+ if (!from_size || !to_size || delta_prepare(from_buf, from_size, &bdf))
+ return NULL;
+
+ outpos = 0;
+ outsize = 8192;
+ out = malloc(outsize);
+ if (!out) {
+ delta_cleanup(&bdf);
+ return NULL;
+ }
+
+ data = to_buf;
+ top = to_buf + to_size;
+
+ /* store reference buffer size */
+ orig = out + outpos++;
+ *orig = i = 0;
+ do {
+ if (from_size & 0xff) {
+ *orig |= (1 << i);
+ out[outpos++] = from_size;
+ }
+ i++;
+ from_size >>= 8;
+ } while (from_size);
+
+ /* store target buffer size */
+ orig = out + outpos++;
+ *orig = i = 0;
+ do {
+ if (to_size & 0xff) {
+ *orig |= (1 << i);
+ out[outpos++] = to_size;
+ }
+ i++;
+ to_size >>= 8;
+ } while (to_size);
+
+ inscnt = 0;
+ moff = 0;
+ while (data < top) {
+ msize = 0;
+ fp = adler32(0, data, MIN(top - data, BLK_SIZE));
+ i = HASH(fp, bdf.fphbits);
+ for (brec = bdf.fphash[i]; brec; brec = brec->next) {
+ if (brec->fp == fp) {
+ csize = bdf.top - brec->ptr;
+ if (csize > top - data)
+ csize = top - data;
+ for (ptr1 = brec->ptr, ptr2 = data;
+ csize && *ptr1 == *ptr2;
+ csize--, ptr1++, ptr2++);
+
+ csize = ptr1 - brec->ptr;
+ if (csize > msize) {
+ moff = brec->ptr - bdf.data;
+ msize = csize;
+ if (msize >= 0x10000) {
+ msize = 0x10000;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!msize || msize < COPYOP_SIZE(moff, msize)) {
+ if (!inscnt)
+ outpos++;
+ out[outpos++] = *data++;
+ inscnt++;
+ if (inscnt == 0x7f) {
+ out[outpos - inscnt - 1] = inscnt;
+ inscnt = 0;
+ }
+ } else {
+ if (inscnt) {
+ out[outpos - inscnt - 1] = inscnt;
+ inscnt = 0;
+ }
+
+ data += msize;
+ orig = out + outpos++;
+ i = 0x80;
+
+ if (moff & 0xff) { out[outpos++] = moff; i |= 0x01; }
+ moff >>= 8;
+ if (moff & 0xff) { out[outpos++] = moff; i |= 0x02; }
+ moff >>= 8;
+ if (moff & 0xff) { out[outpos++] = moff; i |= 0x04; }
+ moff >>= 8;
+ if (moff & 0xff) { out[outpos++] = moff; i |= 0x08; }
+
+ if (msize & 0xff) { out[outpos++] = msize; i |= 0x10; }
+ msize >>= 8;
+ if (msize & 0xff) { out[outpos++] = msize; i |= 0x20; }
+
+ *orig = i;
+ }
+
+ /* next time around the largest possible output is 1 + 4 + 3 */
+ if (outpos > outsize - 8) {
+ void *tmp = out;
+ outsize = outsize * 3 / 2;
+ out = realloc(out, outsize);
+ if (!out) {
+ free(tmp);
+ delta_cleanup(&bdf);
+ return NULL;
+ }
+ }
+ }
+
+ if (inscnt)
+ out[outpos - inscnt - 1] = inscnt;
+
+ delta_cleanup(&bdf);
+ *delta_size = outpos;
+ return out;
+}
Index: git/delta.h
===================================================================
--- /dev/null
+++ git/delta.h
@@ -0,0 +1,6 @@
+extern void *diff_delta(void *from_buf, unsigned long from_size,
+ void *to_buf, unsigned long to_size,
+ unsigned long *delta_size);
+extern void *patch_delta(void *src_buf, unsigned long src_size,
+ void *delta_buf, unsigned long delta_size,
+ unsigned long *dst_size);
Index: git/Makefile
===================================================================
--- git.orig/Makefile
+++ git/Makefile
@@ -36,9 +36,9 @@
$(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 date.o
+ tag.o date.o diff-delta.o patch-delta.o
LIB_FILE=libgit.a
-LIB_H=cache.h object.h blob.h tree.h commit.h tag.h
+LIB_H=cache.h object.h blob.h tree.h commit.h tag.h delta.h
LIB_H += strbuf.h
LIB_OBJS += strbuf.o
@@ -72,6 +72,9 @@
test-date: test-date.c date.o
$(CC) $(CFLAGS) -o $@ test-date.c date.o
+test-delta: test-delta.c diff-delta.o patch-delta.o
+ $(CC) $(CFLAGS) -o $@ $^
+
git-%: %.c $(LIB_FILE)
$(CC) $(CFLAGS) -o $@ $(filter %.c,$^) $(LIBS)
Index: git/patch-delta.c
===================================================================
--- /dev/null
+++ git/patch-delta.c
@@ -0,0 +1,88 @@
+/*
+ * patch-delta.c:
+ * recreate a buffer from a source and the delta produced by diff-delta.c
+ *
+ * (C) 2005 Nicolas Pitre <nico@cam.org>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "delta.h"
+
+void *patch_delta(void *src_buf, unsigned long src_size,
+ void *delta_buf, unsigned long delta_size,
+ unsigned long *dst_size)
+{
+ const unsigned char *data, *top;
+ unsigned char *dst_buf, *out, cmd;
+ unsigned long size;
+ int i;
+
+ /* the smallest delta size possible is 6 bytes */
+ if (delta_size < 6)
+ return NULL;
+
+ data = delta_buf;
+ top = delta_buf + delta_size;
+
+ /* make sure the orig file size matches what we expect */
+ size = i = 0;
+ cmd = *data++;
+ while (cmd) {
+ if (cmd & 1)
+ size |= *data++ << i;
+ i += 8;
+ cmd >>= 1;
+ }
+ if (size != src_size)
+ return NULL;
+
+ /* now the result size */
+ size = i = 0;
+ cmd = *data++;
+ while (cmd) {
+ if (cmd & 1)
+ size |= *data++ << i;
+ i += 8;
+ cmd >>= 1;
+ }
+ dst_buf = malloc(size);
+ if (!dst_buf)
+ return NULL;
+
+ out = dst_buf;
+ while (data < top) {
+ cmd = *data++;
+ if (cmd & 0x80) {
+ unsigned long cp_off = 0, cp_size = 0;
+ const unsigned char *buf;
+ if (cmd & 0x01) cp_off = *data++;
+ if (cmd & 0x02) cp_off |= (*data++ << 8);
+ if (cmd & 0x04) cp_off |= (*data++ << 16);
+ if (cmd & 0x08) cp_off |= (*data++ << 24);
+ if (cmd & 0x10) cp_size = *data++;
+ if (cmd & 0x20) cp_size |= (*data++ << 8);
+ if (cp_size == 0) cp_size = 0x10000;
+ buf = (cmd & 0x40) ? dst_buf : src_buf;
+ memcpy(out, buf + cp_off, cp_size);
+ out += cp_size;
+ } else {
+ memcpy(out, data, cmd);
+ out += cmd;
+ data += cmd;
+ }
+ }
+
+ /* sanity check */
+ if (data != top || out - dst_buf != size) {
+ free(dst_buf);
+ return NULL;
+ }
+
+ *dst_size = size;
+ return dst_buf;
+}
Index: git/test-delta.c
===================================================================
--- /dev/null
+++ git/test-delta.c
@@ -0,0 +1,79 @@
+/*
+ * test-delta.c: test code to exercise diff-delta.c and patch-delta.c
+ *
+ * (C) 2005 Nicolas Pitre <nico@cam.org>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include "delta.h"
+
+static const char *usage =
+ "test-delta (-d|-p) <from_file> <data_file> <out_file>";
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ struct stat st;
+ void *from_buf, *data_buf, *out_buf;
+ unsigned long from_size, data_size, out_size;
+
+ if (argc != 5 || (strcmp(argv[1], "-d") && strcmp(argv[1], "-p"))) {
+ fprintf(stderr, "Usage: %s\n", usage);
+ return 1;
+ }
+
+ fd = open(argv[2], O_RDONLY);
+ if (fd < 0 || fstat(fd, &st)) {
+ perror(argv[2]);
+ return 1;
+ }
+ from_size = st.st_size;
+ from_buf = mmap(NULL, from_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (from_buf == MAP_FAILED) {
+ perror(argv[2]);
+ return 1;
+ }
+ close(fd);
+
+ fd = open(argv[3], O_RDONLY);
+ if (fd < 0 || fstat(fd, &st)) {
+ perror(argv[3]);
+ return 1;
+ }
+ data_size = st.st_size;
+ data_buf = mmap(NULL, data_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (data_buf == MAP_FAILED) {
+ perror(argv[3]);
+ return 1;
+ }
+ close(fd);
+
+ if (argv[1][1] == 'd')
+ out_buf = diff_delta(from_buf, from_size,
+ data_buf, data_size, &out_size);
+ else
+ out_buf = patch_delta(from_buf, from_size,
+ data_buf, data_size, &out_size);
+ if (!out_buf) {
+ fprintf(stderr, "delta operation failed (returned NULL)\n");
+ return 1;
+ }
+
+ fd = open (argv[4], O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (fd < 0 || write(fd, out_buf, out_size) != out_size) {
+ perror(argv[4]);
+ return 1;
+ }
+
+ return 0;
+}
^ permalink raw reply
* Re: gitk-1.0 released
From: Ingo Molnar @ 2005-05-19 13:30 UTC (permalink / raw)
To: Paul Mackerras; +Cc: git
In-Reply-To: <20050519132411.GA29111@elte.hu>
* Ingo Molnar <mingo@elte.hu> wrote:
> - the ability to copy & paste from all the windows would be nice. (e.g.
> in the bugreport above i had to type down the "Octopus merge .." text
> instead of pasting it from gitk)
scrap this one - the patch view window allows copy & paste, and the name
of the patch is included there too.
Ingo
^ permalink raw reply
* Re: gitk-1.0 released
From: Ingo Molnar @ 2005-05-19 13:24 UTC (permalink / raw)
To: Paul Mackerras; +Cc: git
In-Reply-To: <17036.36624.911071.810357@cargo.ozlabs.ibm.com>
* Paul Mackerras <paulus@samba.org> wrote:
> I have released a new version of gitk. I got brave and called it 1.0
> and it is at:
>
> http://ozlabs.org/~paulus/gitk-1.0
very nice! Works well and it's pretty fast on a 2GHz P4.
a bugreport: when looking at the main git history, the following commit
seems to be rendered incorrectly:
211232bae64bcc60bbf5d1b5e5b2344c22ed767e
The "Octopus merge ..." text is incorrectly overlayed with a graph line.
here's a feature wishlist if you dont mind:
- the ability to copy & paste from all the windows would be nice. (e.g.
in the bugreport above i had to type down the "Octopus merge .." text
instead of pasting it from gitk)
- i guess this one is on your todo list: the history graph of a single
object (file).
- first window appearance on an uncached repository can be pretty slow
due to disk seeks - so it might make sense to display something (an
hourglass?) sooner - when i first started it i thought it hung. On
already cached repositories the window comes up immediately, and the
list of commits is updated dynamically.
(and the biggest missing feature of GIT right now is author +
last-commit annotated file viewing which could be integrated into gitk
a'ka BK's revtool: selecting a given line of the file would bring one to
that commit, etc.)
Ingo
^ permalink raw reply
* [PATCH] Declare stacked variables before the first statement.
From: Thomas Glanzmann @ 2005-05-19 13:23 UTC (permalink / raw)
To: GIT, Linus Torvalds
[PATCH] Declare stacked variables before the first statement.
Signed-off-by: Thomas Glanzmann <sithglan@stud.uni-erlangen.de>
--- a/diff.c
+++ b/diff.c
@@ -83,7 +83,7 @@
const char *name_b,
struct diff_tempfile *temp)
{
- int i, next_at;
+ int i, next_at, cmd_size;
const char *diff_cmd = "diff -L'%s%s' -L'%s%s'";
const char *diff_arg = "'%s' '%s'||:"; /* "||:" is to return 0 */
const char *input_name_sq[2];
@@ -100,7 +100,7 @@
* we use 2 spaces around diff-opts, and we need to count
* terminating NUL, so we subtract 9 here.
*/
- int cmd_size = (strlen(diff_cmd) + strlen(diff_opts) +
+ cmd_size = (strlen(diff_cmd) + strlen(diff_opts) +
strlen(diff_arg) - 9);
for (i = 0; i < 2; i++) {
input_name_sq[i] = sq_expand(temp[i].name);
^ permalink raw reply
* gitk-1.0 released
From: Paul Mackerras @ 2005-05-19 13:05 UTC (permalink / raw)
To: git
I have released a new version of gitk. I got brave and called it 1.0
and it is at:
http://ozlabs.org/~paulus/gitk-1.0
(that's the actual script itself).
Gitk is a git commit viewer with the following features:
* Clear and compact representation of the commit graph
* Displays headline, author and date of each commit in the
summary window
* Displays the full details of one commit - the comments,
list of files and colorized diff - in the details window
* Find function for searching through commits
* Displays the SHA1 ID of the selected commit and makes it
the X selection so it can be pasted into other windows
* Convenient key bindings for scanning through each commit
in turn
Gitk passes its command-line arguments to git-rev-tree to allow you to
select the range of commits to display. With no arguments it will
display all the commits starting at HEAD.
The key bindings are:
up or p: select the commit on the next line up from the current one
down or n: select the commit on the next line down from the current one
pageup: scroll the commit summary window up one page
pagedown: scroll the commit summary window down one page
delete or backspace or b: scroll the details window up one page
space: scroll the details window down one page
u: scroll the details window up 18 lines
d: scroll the details window down 18 lines
f: move to the start of the next file diff in the details window
^F: search for commits matching the search string
^G or /: select the next commit matching the search string
^R or ?: select the previous commit matching the search string
^- or ^KP-: reduce the font size
^= or ^KP+: increase the font size
^Q: quit.
Features added since the last release include:
* Better error handling.
* Gitk now remembers the layout of the window, so if you adjust the
sizes of the panes to suit, then quit and restart, it should display
the panes in the proportions you have chosen.
* Instances of the search string are now highlighted in the details
window as well as the summary window.
* The circle diameter and line thickness now scale with the font size.
* Accommodates the new git-diff-tree output format.
Paul.
^ permalink raw reply
* [PATCH] cleanup of in-code names
From: Alexey Nezhdanov @ 2005-05-19 11:17 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git, snake
Fixes all in-code names that leaved during "big name change".
Signed-off-by: Alexey Nezhdanov <snake@penza-gsm.ru>
---
Index: checkout-cache.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/checkout-cache.c (mode:100644)
+++ uncommitted/checkout-cache.c (mode:100644)
@@ -5,22 +5,22 @@
*
* Careful: order of argument flags does matter. For example,
*
- * checkout-cache -a -f file.c
+ * git-checkout-cache -a -f file.c
*
* Will first check out all files listed in the cache (but not
* overwrite any old ones), and then force-checkout "file.c" a
* second time (ie that one _will_ overwrite any old contents
* with the same filename).
*
- * Also, just doing "checkout-cache" does nothing. You probably
- * meant "checkout-cache -a". And if you want to force it, you
- * want "checkout-cache -f -a".
+ * Also, just doing "git-checkout-cache" does nothing. You probably
+ * meant "git-checkout-cache -a". And if you want to force it, you
+ * want "git-checkout-cache -f -a".
*
* Intuitiveness is not the goal here. Repeatability is. The
* reason for the "no arguments means no work" thing is that
* from scripts you are supposed to be able to do things like
*
- * find . -name '*.h' -print0 | xargs -0 checkout-cache -f --
+ * find . -name '*.h' -print0 | xargs -0 git-checkout-cache -f --
*
* which will force all existing *.h files to be replaced with
* their cached copies. If an empty command line implied "all",
@@ -122,7 +122,7 @@
if (!new || strcmp(type, "blob")) {
if (new)
free(new);
- return error("checkout-cache: unable to read sha1 file of %s (%s)",
+ return error("git-checkout-cache: unable to read sha1 file of %s (%s)",
path, sha1_to_hex(ce->sha1));
}
switch (ntohl(ce->ce_mode) & S_IFMT) {
@@ -130,14 +130,14 @@
fd = create_file(path, ntohl(ce->ce_mode));
if (fd < 0) {
free(new);
- return error("checkout-cache: unable to create file %s (%s)",
+ return error("git-checkout-cache: unable to create file %s (%s)",
path, strerror(errno));
}
wrote = write(fd, new, size);
close(fd);
free(new);
if (wrote != size)
- return error("checkout-cache: unable to write file %s", path);
+ return error("git-checkout-cache: unable to write file %s", path);
break;
case S_IFLNK:
memcpy(target, new, size);
@@ -145,14 +145,14 @@
create_directories(path);
if (symlink(target, path)) {
free(new);
- return error("checkout-cache: unable to create symlink %s (%s)",
+ return error("git-checkout-cache: unable to create symlink %s (%s)",
path, strerror(errno));
}
free(new);
break;
default:
free(new);
- return error("checkout-cache: unknown file mode for %s", path);
+ return error("git-checkout-cache: unknown file mode for %s", path);
}
return 0;
}
@@ -172,7 +172,7 @@
return 0;
if (!force) {
if (!quiet)
- fprintf(stderr, "checkout-cache: %s already exists\n", path);
+ fprintf(stderr, "git-checkout-cache: %s already exists\n", path);
return 0;
}
@@ -195,7 +195,7 @@
if (!quiet) {
pos = -pos - 1;
fprintf(stderr,
- "checkout-cache: %s is %s.\n",
+ "git-checkout-cache: %s is %s.\n",
name,
(pos < active_nr &&
!strcmp(active_cache[pos]->name, name)) ?
Index: commit-tree.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/commit-tree.c (mode:100644)
+++ uncommitted/commit-tree.c (mode:100644)
@@ -98,7 +98,7 @@
*/
#define MAXPARENT (16)
-static char *commit_tree_usage = "commit-tree <sha1> [-p <sha1>]* < changelog";
+static char *commit_tree_usage = "git-commit-tree <sha1> [-p <sha1>]* < changelog";
int main(int argc, char **argv)
{
Index: convert-cache.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/convert-cache.c (mode:100644)
+++ uncommitted/convert-cache.c (mode:100644)
@@ -304,7 +304,7 @@
struct entry *entry;
if (argc != 2 || get_sha1(argv[1], sha1))
- usage("convert-cache <sha1>");
+ usage("git-convert-cache <sha1>");
entry = convert_entry(sha1);
printf("new sha1: %s\n", sha1_to_hex(entry->new_sha1));
Index: diff-cache.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/diff-cache.c (mode:100644)
+++ uncommitted/diff-cache.c (mode:100644)
@@ -179,7 +179,7 @@
argv++;
argc--;
if (!strcmp(arg, "-r")) {
- /* We accept the -r flag just to look like diff-tree */
+ /* We accept the -r flag just to look like git-diff-tree */
continue;
}
if (!strcmp(arg, "-p")) {
Index: diff-files.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/diff-files.c (mode:100644)
+++ uncommitted/diff-files.c (mode:100644)
@@ -7,7 +7,7 @@
#include "diff.h"
static const char *diff_files_usage =
-"diff-files [-p] [-q] [-r] [-z] [paths...]";
+"git-diff-files [-p] [-q] [-r] [-z] [paths...]";
static int generate_patch = 0;
static int line_termination = '\n';
Index: diff-tree.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/diff-tree.c (mode:100644)
+++ uncommitted/diff-tree.c (mode:100644)
@@ -258,7 +258,7 @@
update_tree_entry(&tree2, &size2);
continue;
}
- die("diff-tree: internal error");
+ die("git-diff-tree: internal error");
}
return 0;
}
@@ -379,7 +379,7 @@
header = generate_header(name, sha1_to_hex(parent), buf, size);
diff_tree_sha1(parent, commit, "");
if (!header && verbose_header)
- header_prefix = "\ndiff-tree ";
+ header_prefix = "\ngit-diff-tree ";
offset += 48;
}
return 0;
@@ -408,7 +408,7 @@
}
static char *diff_tree_usage =
-"diff-tree [-p] [-r] [-z] [--stdin] [-m] [-s] [-v] <tree sha1> <tree sha1>";
+"git-diff-tree [-p] [-r] [-z] [--stdin] [-m] [-s] [-v] <tree sha1> <tree sha1>";
int main(int argc, char **argv)
{
@@ -461,7 +461,7 @@
}
if (!strcmp(arg, "-v")) {
verbose_header = 1;
- header_prefix = "diff-tree ";
+ header_prefix = "git-diff-tree ";
continue;
}
if (!strcmp(arg, "--stdin")) {
Index: fsck-cache.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/fsck-cache.c (mode:100644)
+++ uncommitted/fsck-cache.c (mode:100644)
@@ -152,7 +152,7 @@
}
if (has_full_path) {
- fprintf(stderr, "warning: fsck-cache: tree %s "
+ fprintf(stderr, "warning: git-fsck-cache: tree %s "
"has full pathnames in it\n",
sha1_to_hex(item->object.sha1));
}
@@ -385,7 +385,7 @@
continue;
}
if (*arg == '-')
- usage("fsck-cache [--tags] [[--unreachable] [--cache] <head-sha1>*]");
+ usage("git-fsck-cache [--tags] [[--unreachable] [--cache] <head-sha1>*]");
}
sha1_dir = get_object_directory();
Index: ls-files.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/ls-files.c (mode:100644)
+++ uncommitted/ls-files.c (mode:100644)
@@ -283,7 +283,7 @@
}
static const char *ls_files_usage =
- "ls-files [-z] [-t] (--[cached|deleted|others|stage|unmerged|killed])* "
+ "git-ls-files [-z] [-t] (--[cached|deleted|others|stage|unmerged|killed])* "
"[ --ignored [--exclude=<pattern>] [--exclude-from=<file>) ]";
int main(int argc, char **argv)
Index: ls-tree.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/ls-tree.c (mode:100644)
+++ uncommitted/ls-tree.c (mode:100644)
@@ -81,7 +81,7 @@
return 0;
}
-static const char *ls_tree_usage = "ls-tree [-r] [-z] <key>";
+static const char *ls_tree_usage = "git-ls-tree [-r] [-z] <key>";
int main(int argc, char **argv)
{
Index: merge-base.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/merge-base.c (mode:100644)
+++ uncommitted/merge-base.c (mode:100644)
@@ -60,7 +60,7 @@
if (argc != 3 ||
get_sha1(argv[1], rev1key) ||
get_sha1(argv[2], rev2key)) {
- usage("merge-base <commit-id> <commit-id>");
+ usage("git-merge-base <commit-id> <commit-id>");
}
rev1 = lookup_commit_reference(rev1key);
rev2 = lookup_commit_reference(rev2key);
Index: merge-cache.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/merge-cache.c (mode:100644)
+++ uncommitted/merge-cache.c (mode:100644)
@@ -39,7 +39,7 @@
int found;
if (pos >= active_nr)
- die("merge-cache: %s not in the cache", path);
+ die("git-merge-cache: %s not in the cache", path);
arguments[0] = pgm;
arguments[1] = "";
arguments[2] = "";
@@ -64,7 +64,7 @@
arguments[stage + 4] = ownbuf[stage];
} while (++pos < active_nr);
if (!found)
- die("merge-cache: %s not in the cache", path);
+ die("git-merge-cache: %s not in the cache", path);
run_program();
return found;
}
@@ -97,7 +97,7 @@
int i, force_file = 0;
if (argc < 3)
- usage("merge-cache [-o] <merge-program> (-a | <filename>*)");
+ usage("git-merge-cache [-o] <merge-program> (-a | <filename>*)");
read_cache();
@@ -118,7 +118,7 @@
merge_all();
continue;
}
- die("merge-cache: unknown option %s", arg);
+ die("git-merge-cache: unknown option %s", arg);
}
merge_file(arg);
}
Index: read-tree.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/read-tree.c (mode:100644)
+++ uncommitted/read-tree.c (mode:100644)
@@ -159,7 +159,7 @@
}
}
-static char *read_tree_usage = "read-tree (<sha> | -m <sha1> [<sha2> <sha3>])";
+static char *read_tree_usage = "git-read-tree (<sha> | -m <sha1> [<sha2> <sha3>])";
int main(int argc, char **argv)
{
Index: rev-list.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/rev-list.c (mode:100644)
+++ uncommitted/rev-list.c (mode:100644)
@@ -27,7 +27,7 @@
}
if (!commit_arg || get_sha1(commit_arg, sha1))
- usage("usage: rev-list [OPTION] commit-id\n"
+ usage("usage: git-rev-list [OPTION] commit-id\n"
" --max-count=nr\n"
" --max-age=epoch\n"
" --min-age=epoch\n");
Index: rev-tree.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/rev-tree.c (mode:100644)
+++ uncommitted/rev-tree.c (mode:100644)
@@ -64,7 +64,7 @@
}
/*
- * Usage: rev-tree [--edges] [--cache <cache-file>] <commit-id> [<commit-id2>]
+ * Usage: git-rev-tree [--edges] [--cache <cache-file>] <commit-id> [<commit-id2>]
*
* The cache-file can be quite important for big trees. This is an
* expensive operation if you have to walk the whole chain of
@@ -98,7 +98,7 @@
basemask |= 1<<nr;
}
if (nr >= MAX_COMMITS || get_sha1(arg, sha1[nr]))
- usage("rev-tree [--edges] [--cache <cache-file>] <commit-id> [<commit-id>]");
+ usage("git-rev-tree [--edges] [--cache <cache-file>] <commit-id> [<commit-id>]");
process_commit(sha1[nr]);
nr++;
}
Index: rpush.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/rpush.c (mode:100644)
+++ uncommitted/rpush.c (mode:100644)
@@ -14,7 +14,7 @@
do {
size = read(fd_in, sha1 + posn, 20 - posn);
if (size < 0) {
- perror("rpush: read ");
+ perror("git-rpush: read ");
return;
}
if (!size)
@@ -26,7 +26,7 @@
buf = map_sha1_file(sha1, &objsize);
if (!buf) {
- fprintf(stderr, "rpush: could not find %s\n",
+ fprintf(stderr, "git-rpush: could not find %s\n",
sha1_to_hex(sha1));
return;
}
@@ -35,9 +35,9 @@
size = write(fd_out, buf + posn, objsize - posn);
if (size <= 0) {
if (!size) {
- fprintf(stderr, "rpush: write closed");
+ fprintf(stderr, "git-rpush: write closed");
} else {
- perror("rpush: write ");
+ perror("git-rpush: write ");
}
return;
}
@@ -56,7 +56,7 @@
arg++;
}
if (argc < arg + 2) {
- usage("rpush [-c] [-t] [-a] commit-id url");
+ usage("git-rpush [-c] [-t] [-a] commit-id url");
return 1;
}
commit_id = argv[arg];
Index: tar-tree.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/tar-tree.c (mode:100644)
+++ uncommitted/tar-tree.c (mode:100644)
@@ -17,7 +17,7 @@
#define EXT_HEADER_PATH 1
#define EXT_HEADER_LINKPATH 2
-static const char *tar_tree_usage = "tar-tree <key> [basedir]";
+static const char *tar_tree_usage = "git-tar-tree <key> [basedir]";
static char block[BLOCKSIZE];
static unsigned long offset;
@@ -40,9 +40,9 @@
continue;
if (errno == EPIPE)
exit(0);
- die("tar-tree: %s", strerror(errno));
+ die("git-tar-tree: %s", strerror(errno));
} else if (!ret) {
- die("tar-tree: disk full?");
+ die("git-tar-tree: disk full?");
}
size -= ret;
buf += ret;
Index: unpack-file.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/unpack-file.c (mode:100644)
+++ uncommitted/unpack-file.c (mode:100644)
@@ -27,7 +27,7 @@
unsigned char sha1[20];
if (argc != 2 || get_sha1(argv[1], sha1))
- usage("unpack-file.c <sha1>");
+ usage("git-unpack-file <sha1>");
puts(create_temp_file(sha1));
return 0;
Index: update-cache.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/update-cache.c (mode:100644)
+++ uncommitted/update-cache.c (mode:100644)
@@ -10,7 +10,7 @@
* Default to not allowing changes to the list of files. The
* tool doesn't actually care, but this makes it harder to add
* files to the revision control by mistake by doing something
- * like "update-cache *" and suddenly having all the object
+ * like "git-update-cache *" and suddenly having all the object
* files be revision controlled.
*/
static int allow_add = 0, allow_remove = 0, allow_replace = 0, not_new = 0;
@@ -179,7 +179,7 @@
* file that hasn't been changed but where the stat entry is
* out of date.
*
- * For example, you'd want to do this after doing a "read-tree",
+ * For example, you'd want to do this after doing a "git-read-tree",
* to link up the stat cache details with the proper files.
*/
static struct cache_entry *refresh_entry(struct cache_entry *ce)
@@ -373,17 +373,17 @@
}
if (!strcmp(path, "--cacheinfo")) {
if (i+3 >= argc)
- die("update-cache: --cacheinfo <mode> <sha1> <path>");
+ die("git-update-cache: --cacheinfo <mode> <sha1> <path>");
if (add_cacheinfo(argv[i+1], argv[i+2], argv[i+3]))
- die("update-cache: --cacheinfo cannot add %s", argv[i+3]);
+ die("git-update-cache: --cacheinfo cannot add %s", argv[i+3]);
i += 3;
continue;
}
if (!strcmp(path, "--force-remove")) {
if (argc <= i + 1)
- die("update-cache: --force-remove <path>");
+ die("git-update-cache: --force-remove <path>");
if (remove_file_from_cache(argv[i+1]))
- die("update-cache: --force-remove cannot remove %s", argv[i+1]);
+ die("git-update-cache: --force-remove cannot remove %s", argv[i+1]);
i++;
continue;
}
Index: write-tree.c
===================================================================
--- 9adfcec0fcbf1828127b46991ffb7f6c76586b73/write-tree.c (mode:100644)
+++ uncommitted/write-tree.c (mode:100644)
@@ -89,7 +89,7 @@
unsigned char sha1[20];
if (entries < 0)
- die("write-tree: error reading cache");
+ die("git-write-tree: error reading cache");
/* Verify that the tree is merged */
funny = 0;
@@ -104,7 +104,7 @@
}
}
if (funny)
- die("write-tree: not able to write tree");
+ die("git-write-tree: not able to write tree");
/* Also verify that the cache does not have path and path/file
* at the same time. At this point we know the cache has only
@@ -131,11 +131,11 @@
}
}
if (funny)
- die("write-tree: not able to write tree");
+ die("git-write-tree: not able to write tree");
/* Ok, write it out */
if (write_tree(active_cache, entries, "", 0, sha1) != entries)
- die("write-tree: internal error");
+ die("git-write-tree: internal error");
printf("%s\n", sha1_to_hex(sha1));
return 0;
}
^ permalink raw reply
* Re: [PATCH 0/1] Diff-helper update
From: Junio C Hamano @ 2005-05-19 11:11 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git, pasky
In-Reply-To: <7vacmsnl92.fsf@assigned-by-dhcp.cox.net>
>>>>> "JCH" == Junio C Hamano <junkio@cox.net> writes:
>>>>> "LT" == Linus Torvalds <torvalds@osdl.org> writes:
LT> On Wed, 18 May 2005, Junio C Hamano wrote:
>>>
>>> I suspect doing something like this might be saner instead,
>>> assuming non raw-diffs come at the end.
LT> It won't ever trigger, since we only exit the loop once we see EOF.
JCH> I was not talking about correctness, but the readability of the
JCH> code. Breaking out from the loop to process raw-diff and
JCH> switching to straight copy would make our intent more explicit.
I was completely mistaken about what you were talking about.
Your output (helper's input) is (<non-diff material>* <raw-diff
material>*)*, so of course we should not break out of the main
loop.
^ permalink raw reply
* [PATCH] Detect renames in diff family.
From: Junio C Hamano @ 2005-05-19 10:32 UTC (permalink / raw)
To: torvalds; +Cc: git
This patch rips out the rename detection engine from diff-helper
and moves it to the diff core, and updates the internal calling
convention used by diff-tree family into the diff core. In
order to give the same option name to diff-tree family as well
as to diff-helper, I've changed the earlier diff-helper '-r'
option to '-M' (stands for Move; sorry but the natural
abbreviation 'r' for 'rename' is already taken for 'recursive').
Although I did a fair amount of test with the git-diff-tree with
existing rename commits in the core GIT repository, this should
still be considered beta (preview) release. This patch depends
on the diff-delta patch I stole from Nico. Please give it a
good beating.
Special request for Linus is to check if I did not screw up the
various calls into the diff core from diff-tree. Essentially
the idea is to start one patchset session with diff_setup() and
close it with diff_flush() before you start another patchset
session.
This implements almost everything I wanted to see in this series
of patch, except a few minor cleanups in the calling convention
into diff core, but that will be a separate cleanup patch.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
Documentation/git-diff-cache.txt | 5
Documentation/git-diff-files.txt | 5
Documentation/git-diff-helper.txt | 4
Documentation/git-diff-tree.txt | 5
diff-cache.c | 16 +
diff-files.c | 11 -
diff-helper.c | 209 ++-----------------
diff-tree.c | 25 +-
diff.c | 410 +++++++++++++++++++++++++++++++++++---
diff.h | 19 -
git-apply-patch-script | 3
t/t4001-diff-rename.sh | 60 +++++
12 files changed, 533 insertions(+), 239 deletions(-)
t/t4001-diff-rename.sh (. --> 100644)
diff --git a/Documentation/git-diff-cache.txt b/Documentation/git-diff-cache.txt
--- a/Documentation/git-diff-cache.txt
+++ b/Documentation/git-diff-cache.txt
@@ -9,7 +9,7 @@
SYNOPSIS
--------
-'git-diff-cache' [-p] [-r] [-z] [-m] [--cached] <tree-ish>
+'git-diff-cache' [-p] [-r] [-z] [-m] [-M] [--cached] <tree-ish>
DESCRIPTION
-----------
@@ -33,6 +33,9 @@
-z::
\0 line termination on output
+-M::
+ Detect renames; implies -p.
+
--cached::
do not consider the on-disk file at all
diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt
--- a/Documentation/git-diff-files.txt
+++ b/Documentation/git-diff-files.txt
@@ -9,7 +9,7 @@
SYNOPSIS
--------
-'git-diff-files' [-p] [-q] [-r] [-z] [<pattern>...]
+'git-diff-files' [-p] [-q] [-r] [-z] [-M] [<pattern>...]
DESCRIPTION
-----------
@@ -26,6 +26,9 @@
-q::
Remain silent even on nonexisting files
+-M::
+ Detect renames; implies -p.
+
-r::
This flag does not mean anything. It is there only to match
git-diff-tree. Unlike git-diff-tree, git-diff-files always looks
diff --git a/Documentation/git-diff-helper.txt b/Documentation/git-diff-helper.txt
--- a/Documentation/git-diff-helper.txt
+++ b/Documentation/git-diff-helper.txt
@@ -9,7 +9,7 @@
SYNOPSIS
--------
-'git-diff-helper' [-z] [-R] [-r]
+'git-diff-helper' [-z] [-R] [-M]
DESCRIPTION
-----------
@@ -31,7 +31,7 @@
would show a diff to bring the working file back to what
is in the <tree>.
--r::
+-M::
Detect renames.
diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt
--- a/Documentation/git-diff-tree.txt
+++ b/Documentation/git-diff-tree.txt
@@ -9,7 +9,7 @@
SYNOPSIS
--------
-'git-diff-tree' [-p] [-r] [-z] [--stdin] [-m] [-s] [-v] <tree-ish> <tree-ish> [<pattern>]\*
+'git-diff-tree' [-p] [-r] [-z] [--stdin] [-M] [-m] [-s] [-v] <tree-ish> <tree-ish> [<pattern>]\*
DESCRIPTION
-----------
@@ -33,6 +33,9 @@
generate patch (see section on generating patches). For
git-diff-tree, this flag implies '-r' as well.
+-M::
+ Detect renames; implies -p, in turn implying also '-r'.
+
-r::
recurse
diff --git a/diff-cache.c b/diff-cache.c
--- a/diff-cache.c
+++ b/diff-cache.c
@@ -5,6 +5,7 @@
static int generate_patch = 0;
static int match_nonexisting = 0;
static int line_termination = '\n';
+static int detect_rename = 0;
/* A file entry went away or appeared */
static void show_file(const char *prefix, struct cache_entry *ce, unsigned char *sha1, unsigned int mode)
@@ -165,13 +166,14 @@
}
static char *diff_cache_usage =
-"git-diff-cache [-p] [-r] [-z] [-m] [--cached] <tree sha1>";
+"git-diff-cache [-p] [-r] [-z] [-m] [-M] [--cached] <tree-ish>";
int main(int argc, char **argv)
{
unsigned char tree_sha1[20];
void *tree;
unsigned long size;
+ int ret;
read_cache();
while (argc > 2) {
@@ -186,6 +188,10 @@
generate_patch = 1;
continue;
}
+ if (!strcmp(arg, "-M")) {
+ generate_patch = detect_rename = 1;
+ continue;
+ }
if (!strcmp(arg, "-z")) {
line_termination = '\0';
continue;
@@ -204,6 +210,9 @@
if (argc != 2 || get_sha1(argv[1], tree_sha1))
usage(diff_cache_usage);
+ if (generate_patch)
+ diff_setup(detect_rename, 0, 0, 0, 0);
+
mark_merge_entries();
tree = read_object_with_reference(tree_sha1, "tree", &size, 0);
@@ -212,5 +221,8 @@
if (read_tree(tree, size, 1))
die("unable to read tree object %s", argv[1]);
- return diff_cache(active_cache, active_nr);
+ ret = diff_cache(active_cache, active_nr);
+ if (generate_patch)
+ diff_flush();
+ return ret;
}
diff --git a/diff-files.c b/diff-files.c
--- a/diff-files.c
+++ b/diff-files.c
@@ -7,10 +7,11 @@
#include "diff.h"
static const char *diff_files_usage =
-"diff-files [-p] [-q] [-r] [-z] [paths...]";
+"diff-files [-p] [-q] [-r] [-z] [-M] [paths...]";
static int generate_patch = 0;
static int line_termination = '\n';
+static int detect_rename = 0;
static int silent = 0;
static int matches_pathspec(struct cache_entry *ce, char **spec, int cnt)
@@ -79,6 +80,9 @@
; /* no-op */
else if (!strcmp(argv[1], "-z"))
line_termination = 0;
+ else if (!strcmp(argv[1], "-M")) {
+ detect_rename = generate_patch = 1;
+ }
else
usage(diff_files_usage);
argv++; argc--;
@@ -92,6 +96,9 @@
exit(1);
}
+ if (generate_patch)
+ diff_setup(detect_rename, 0, 0, 0, 0);
+
for (i = 0; i < entries; i++) {
struct stat st;
unsigned int oldmode, mode;
@@ -132,5 +139,7 @@
show_modified(oldmode, mode, ce->sha1, null_sha1,
ce->name);
}
+ if (generate_patch)
+ diff_flush();
return 0;
}
diff --git a/diff-helper.c b/diff-helper.c
--- a/diff-helper.c
+++ b/diff-helper.c
@@ -6,160 +6,23 @@
#include "strbuf.h"
#include "diff.h"
-static int matches_pathspec(const char *name, const char **spec, int cnt)
-{
- int i;
- int namelen = strlen(name);
- for (i = 0; i < cnt; i++) {
- int speclen = strlen(spec[i]);
- if (! strncmp(spec[i], name, speclen) &&
- speclen <= namelen &&
- (name[speclen] == 0 ||
- name[speclen] == '/'))
- return 1;
- }
- return 0;
-}
-
static int detect_rename = 0;
-/*
- * We do not detect circular renames. Just hold created and deleted
- * entries and later attempt to match them up. If they do not match,
- * then spit them out as deletes or creates as original.
- */
-
-static struct diff_spec_hold {
- struct diff_spec_hold *next;
- struct diff_spec_hold *matched;
- struct diff_spec old, new;
- char path[1];
-} *createdfile, *deletedfile;
-
-static void hold_spec(const char *path,
- struct diff_spec *old, struct diff_spec *new)
-{
- struct diff_spec_hold **list, *elem;
- list = (! old->file_valid) ? &createdfile : &deletedfile;
- elem = xmalloc(sizeof(*elem) + strlen(path));
- strcpy(elem->path, path);
- elem->next = *list;
- *list = elem;
- elem->old = *old;
- elem->new = *new;
- elem->matched = 0;
-}
-
-#define MINIMUM_SCORE 7000
-int estimate_similarity(struct diff_spec *one, struct diff_spec *two)
-{
- /* Return how similar they are, representing the score as an
- * integer between 0 and 10000.
- *
- * This version is very dumb and detects exact matches only.
- * Wnen Nico's delta stuff gets in, I'll use the delta
- * algorithm to estimate the similarity score in core.
- */
-
- if (one->sha1_valid && two->sha1_valid &&
- !memcmp(one->blob_sha1, two->blob_sha1, 20))
- return 10000;
- return 0;
-}
-
-static void flush_renames(const char **spec, int cnt, int reverse)
-{
- struct diff_spec_hold *rename_src, *rename_dst, *elem;
- struct diff_spec_hold *leftover = NULL;
- int score, best_score;
-
- while (createdfile) {
- rename_dst = createdfile;
- createdfile = rename_dst->next;
- best_score = MINIMUM_SCORE;
- rename_src = NULL;
- for (elem = deletedfile;
- elem;
- elem = elem->next) {
- if (elem->matched)
- continue;
- score = estimate_similarity(&elem->old,
- &rename_dst->new);
- if (best_score < score) {
- rename_src = elem;
- best_score = score;
- }
- }
- if (rename_src) {
- rename_src->matched = rename_dst;
- rename_dst->matched = rename_src;
-
- if (!cnt ||
- matches_pathspec(rename_src->path, spec, cnt) ||
- matches_pathspec(rename_dst->path, spec, cnt)) {
- if (reverse)
- run_external_diff(rename_dst->path,
- rename_src->path,
- &rename_dst->new,
- &rename_src->old);
- else
- run_external_diff(rename_src->path,
- rename_dst->path,
- &rename_src->old,
- &rename_dst->new);
- }
- }
- else {
- rename_dst->next = leftover;
- leftover = rename_dst;
- }
- }
-
- /* unmatched deletes */
- for (elem = deletedfile; elem; elem = elem->next) {
- if (elem->matched)
- continue;
- if (!cnt ||
- matches_pathspec(elem->path, spec, cnt)) {
- if (reverse)
- run_external_diff(elem->path, NULL,
- &elem->new, &elem->old);
- else
- run_external_diff(elem->path, NULL,
- &elem->old, &elem->new);
- }
- }
-
- /* unmatched creates */
- for (elem = leftover; elem; elem = elem->next) {
- if (!cnt ||
- matches_pathspec(elem->path, spec, cnt)) {
- if (reverse)
- run_external_diff(elem->path, NULL,
- &elem->new, &elem->old);
- else
- run_external_diff(elem->path, NULL,
- &elem->old, &elem->new);
- }
- }
-}
-
-static int parse_oneside_change(const char *cp, struct diff_spec *one,
- char *path)
+static int parse_oneside_change(const char *cp, int *mode,
+ unsigned char *sha1, char *path)
{
- int ch;
+ int ch, m;
- one->file_valid = one->sha1_valid = 1;
- one->mode = 0;
+ m = 0;
while ((ch = *cp) && '0' <= ch && ch <= '7') {
- one->mode = (one->mode << 3) | (ch - '0');
+ m = (m << 3) | (ch - '0');
cp++;
}
-
+ *mode = m;
if (strncmp(cp, "\tblob\t", 6))
return -1;
cp += 6;
- if (get_sha1_hex(cp, one->blob_sha1))
+ if (get_sha1_hex(cp, sha1))
return -1;
cp += 40;
if (*cp++ != '\t')
@@ -168,79 +31,63 @@
return 0;
}
-static int parse_diff_raw_output(const char *buf,
- const char **spec, int cnt, int reverse)
+static int parse_diff_raw_output(const char *buf)
{
- struct diff_spec old, new;
char path[PATH_MAX];
+ unsigned char old_sha1[20], new_sha1[20];
const char *cp = buf;
- int ch;
+ int ch, old_mode, new_mode;
switch (*cp++) {
case 'U':
- if (!cnt || matches_pathspec(cp + 1, spec, cnt))
- diff_unmerge(cp + 1);
- return 0;
+ diff_unmerge(cp + 1);
+ break;
case '+':
- old.file_valid = 0;
- parse_oneside_change(cp, &new, path);
+ parse_oneside_change(cp, &new_mode, new_sha1, path);
+ diff_addremove('+', new_mode, new_sha1, path, NULL);
break;
case '-':
- new.file_valid = 0;
- parse_oneside_change(cp, &old, path);
+ parse_oneside_change(cp, &old_mode, old_sha1, path);
+ diff_addremove('-', old_mode, old_sha1, path, NULL);
break;
case '*':
- old.file_valid = old.sha1_valid =
- new.file_valid = new.sha1_valid = 1;
- old.mode = new.mode = 0;
+ old_mode = new_mode = 0;
while ((ch = *cp) && ('0' <= ch && ch <= '7')) {
- old.mode = (old.mode << 3) | (ch - '0');
+ old_mode = (old_mode << 3) | (ch - '0');
cp++;
}
if (strncmp(cp, "->", 2))
return -1;
cp += 2;
while ((ch = *cp) && ('0' <= ch && ch <= '7')) {
- new.mode = (new.mode << 3) | (ch - '0');
+ new_mode = (new_mode << 3) | (ch - '0');
cp++;
}
if (strncmp(cp, "\tblob\t", 6))
return -1;
cp += 6;
- if (get_sha1_hex(cp, old.blob_sha1))
+ if (get_sha1_hex(cp, old_sha1))
return -1;
cp += 40;
if (strncmp(cp, "->", 2))
return -1;
cp += 2;
- if (get_sha1_hex(cp, new.blob_sha1))
+ if (get_sha1_hex(cp, new_sha1))
return -1;
cp += 40;
if (*cp++ != '\t')
return -1;
strcpy(path, cp);
+ diff_change(old_mode, new_mode, old_sha1, new_sha1, path, 0);
break;
default:
return -1;
}
-
- if (detect_rename && old.file_valid != new.file_valid) {
- /* hold these */
- hold_spec(path, &old, &new);
- return 0;
- }
-
- if (!cnt || matches_pathspec(path, spec, cnt)) {
- if (reverse)
- run_external_diff(path, NULL, &new, &old);
- else
- run_external_diff(path, NULL, &old, &new);
- }
return 0;
}
static const char *diff_helper_usage =
- "git-diff-helper [-r] [-R] [-z] paths...";
+ "git-diff-helper [-z] [-R] [-M] paths...";
int main(int ac, const char **av) {
struct strbuf sb;
@@ -254,7 +101,7 @@
reverse = 1;
else if (av[1][1] == 'z')
line_termination = 0;
- else if (av[1][1] == 'r')
+ else if (av[1][1] == 'M')
detect_rename = 1;
else
usage(diff_helper_usage);
@@ -262,18 +109,20 @@
}
/* the remaining parameters are paths patterns */
+ diff_setup(detect_rename, 0, reverse, av+1, ac-1);
+
while (1) {
int status;
read_line(&sb, stdin, line_termination);
if (sb.eof)
break;
- status = parse_diff_raw_output(sb.buf, av+1, ac-1, reverse);
+ status = parse_diff_raw_output(sb.buf);
if (status) {
- flush_renames(av+1, ac-1, reverse);
+ diff_flush();
printf("%s%c", sb.buf, line_termination);
}
}
- flush_renames(av+1, ac-1, reverse);
+ diff_flush();
return 0;
}
diff --git a/diff-tree.c b/diff-tree.c
--- a/diff-tree.c
+++ b/diff-tree.c
@@ -9,6 +9,7 @@
static int read_stdin = 0;
static int line_termination = '\n';
static int generate_patch = 0;
+static int detect_rename = 0;
static const char *header = NULL;
static const char *header_prefix = "";
@@ -281,6 +282,18 @@
return retval;
}
+static int diff_tree_sha1_top(const unsigned char *old,
+ const unsigned char *new, const char *base)
+{
+ int ret;
+ if (generate_patch)
+ diff_setup(detect_rename, 0, 0, 0, 0);
+ ret = diff_tree_sha1(old, new, base);
+ if (generate_patch)
+ diff_flush();
+ return ret;
+}
+
static int get_one_line(const char *msg, unsigned long len)
{
int ret = 0;
@@ -377,7 +390,7 @@
if (get_sha1_hex(buf + offset + 7, parent))
return -1;
header = generate_header(name, sha1_to_hex(parent), buf, size);
- diff_tree_sha1(parent, commit, "");
+ diff_tree_sha1_top(parent, commit, "");
if (!header && verbose_header)
header_prefix = "\ndiff-tree ";
offset += 48;
@@ -401,14 +414,14 @@
line[81] = 0;
sprintf(this_header, "%s (from %s)\n", line, line+41);
header = this_header;
- return diff_tree_sha1(parent, commit, "");
+ return diff_tree_sha1_top(parent, commit, "");
}
line[40] = 0;
return diff_tree_commit(commit, line);
}
static char *diff_tree_usage =
-"diff-tree [-p] [-r] [-z] [--stdin] [-m] [-s] [-v] <tree sha1> <tree sha1>";
+"diff-tree [-p] [-r] [-z] [--stdin] [-M] [-m] [-s] [-v] <tree-ish> <tree-ish>";
int main(int argc, char **argv)
{
@@ -447,6 +460,10 @@
recursive = generate_patch = 1;
continue;
}
+ if (!strcmp(arg, "-M")) {
+ detect_rename = recursive = generate_patch = 1;
+ continue;
+ }
if (!strcmp(arg, "-z")) {
line_termination = '\0';
continue;
@@ -490,7 +507,7 @@
diff_tree_commit(sha1[0], NULL);
break;
case 2:
- diff_tree_sha1(sha1[0], sha1[1], "");
+ diff_tree_sha1_top(sha1[0], sha1[1], "");
break;
}
diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -7,8 +7,10 @@
#include <limits.h>
#include "cache.h"
#include "diff.h"
+#include "delta.h"
static const char *diff_opts = "-pu";
+static unsigned char null_sha1[20] = { 0, };
static const char *external_diff(void)
{
@@ -79,6 +81,18 @@
char tmp_path[50];
} diff_temp[2];
+struct diff_spec {
+ unsigned char blob_sha1[20];
+ unsigned short mode; /* file mode */
+ unsigned sha1_valid : 1; /* if true, use blob_sha1 and trust mode;
+ * however with a NULL SHA1, read them
+ * from the file system.
+ * if false, use the name and read mode from
+ * the filesystem.
+ */
+ unsigned file_valid : 1; /* if false the file does not even exist */
+};
+
static void builtin_diff(const char *name_a,
const char *name_b,
struct diff_tempfile *temp)
@@ -160,7 +174,7 @@
struct cache_entry *ce;
struct stat st;
int pos, len;
-
+
/* We do not read the cache ourselves here, because the
* benchmark with my previous version that always reads cache
* shows that it makes things worse for diff-tree comparing
@@ -214,9 +228,6 @@
struct diff_tempfile *temp,
struct diff_spec *one)
{
- static unsigned char null_sha1[20] = { 0, };
- int use_work_tree = 0;
-
if (!one->file_valid) {
not_a_valid_file:
/* A '-' entry produces this for file-2, and
@@ -228,12 +239,8 @@
return;
}
- if (one->sha1_valid &&
- (!memcmp(one->blob_sha1, null_sha1, sizeof(null_sha1)) ||
- work_tree_matches(name, one->blob_sha1)))
- use_work_tree = 1;
-
- if (!one->sha1_valid || use_work_tree) {
+ if (!one->sha1_valid ||
+ work_tree_matches(name, one->blob_sha1)) {
struct stat st;
temp->name = name;
if (lstat(temp->name, &st) < 0) {
@@ -295,22 +302,59 @@
remove_tempfile();
}
+static int detect_rename;
+static int reverse_diff;
+static const char **pathspec;
+static int speccnt;
+static int diff_rename_minimum_score;
+
+static int matches_pathspec(const char *name)
+{
+ int i;
+ int namelen;
+
+ if (speccnt == 0)
+ return 1;
+
+ namelen = strlen(name);
+ for (i = 0; i < speccnt; i++) {
+ int speclen = strlen(pathspec[i]);
+ if (! strncmp(pathspec[i], name, speclen) &&
+ speclen <= namelen &&
+ (name[speclen] == 0 || name[speclen] == '/'))
+ return 1;
+ }
+ return 0;
+}
+
/* An external diff command takes:
*
* diff-cmd name infile1 infile1-sha1 infile1-mode \
- * infile2 infile2-sha1 infile2-mode.
+ * infile2 infile2-sha1 infile2-mode [ rename-to ]
*
*/
-void run_external_diff(const char *name,
- const char *other,
- struct diff_spec *one,
- struct diff_spec *two)
+static void run_external_diff(const char *name,
+ const char *other,
+ struct diff_spec *one,
+ struct diff_spec *two)
{
struct diff_tempfile *temp = diff_temp;
pid_t pid;
int status;
static int atexit_asked = 0;
+ if (reverse_diff) {
+ struct diff_spec *tmp_spec;
+ tmp_spec = one; one = two; two = tmp_spec;
+ if (other) {
+ const char *tmp;
+ tmp = name; name = other; other = tmp;
+ }
+ }
+
+ if (!matches_pathspec(name) && (!other || !matches_pathspec(other)))
+ return;
+
if (one && two) {
prepare_temp_file(name, &temp[0], one);
prepare_temp_file(other ? : name, &temp[1], two);
@@ -329,14 +373,23 @@
die("unable to fork");
if (!pid) {
const char *pgm = external_diff();
- /* not passing rename patch to external ones */
- if (!other && pgm) {
- if (one && two)
- execlp(pgm, pgm,
- name,
- temp[0].name, temp[0].hex, temp[0].mode,
- temp[1].name, temp[1].hex, temp[1].mode,
- NULL);
+ if (pgm) {
+ if (one && two) {
+ const char *exec_arg[9];
+ const char **arg = &exec_arg[0];
+ *arg++ = pgm;
+ *arg++ = name;
+ *arg++ = temp[0].name;
+ *arg++ = temp[0].hex;
+ *arg++ = temp[0].mode;
+ *arg++ = temp[1].name;
+ *arg++ = temp[1].hex;
+ *arg++ = temp[1].mode;
+ if (other)
+ *arg++ = other;
+ *arg = 0;
+ execvp(pgm, (char *const*) exec_arg);
+ }
else
execlp(pgm, pgm, name, NULL);
}
@@ -367,6 +420,293 @@
remove_tempfile();
}
+/*
+ * We do not detect circular renames. Just hold created and deleted
+ * entries and later attempt to match them up. If they do not match,
+ * then spit them out as deletes or creates as original.
+ */
+
+static struct diff_spec_hold {
+ struct diff_spec_hold *next;
+ struct diff_spec it;
+ unsigned long size;
+ int flags;
+#define MATCHED 1
+#define SHOULD_FREE 2
+#define SHOULD_MUNMAP 4
+ void *data;
+ char path[1];
+} *createdfile, *deletedfile;
+
+static void hold_diff(const char *name,
+ struct diff_spec *one,
+ struct diff_spec *two)
+{
+ struct diff_spec_hold **list, *elem;
+
+ if (one->file_valid && two->file_valid)
+ die("internal error");
+
+ if (!detect_rename) {
+ run_external_diff(name, NULL, one, two);
+ return;
+ }
+ elem = xmalloc(sizeof(*elem) + strlen(name));
+ strcpy(elem->path, name);
+ elem->size = 0;
+ elem->data = NULL;
+ elem->flags = 0;
+ if (one->file_valid) {
+ list = &deletedfile;
+ elem->it = *one;
+ }
+ else {
+ list = &createdfile;
+ elem->it = *two;
+ }
+ elem->next = *list;
+ *list = elem;
+}
+
+static int populate_data(struct diff_spec_hold *s)
+{
+ char type[20];
+
+ if (s->data)
+ return 0;
+ if (s->it.sha1_valid) {
+ s->data = read_sha1_file(s->it.blob_sha1, type, &s->size);
+ s->flags |= SHOULD_FREE;
+ }
+ else {
+ struct stat st;
+ int fd;
+ fd = open(s->path, O_RDONLY);
+ if (fd < 0)
+ return -1;
+ if (fstat(fd, &st)) {
+ close(fd);
+ return -1;
+ }
+ s->size = st.st_size;
+ s->data = mmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
+ close(fd);
+ if (!s->size)
+ s->data = "";
+ else
+ s->flags |= SHOULD_MUNMAP;
+ }
+ return 0;
+}
+
+static void free_data(struct diff_spec_hold *s)
+{
+ if (s->flags & SHOULD_FREE)
+ free(s->data);
+ else if (s->flags & SHOULD_MUNMAP)
+ munmap(s->data, s->size);
+ s->flags &= ~(SHOULD_FREE|SHOULD_MUNMAP);
+}
+
+static void flush_remaining_diff(struct diff_spec_hold *elem,
+ int on_created_list)
+{
+ static struct diff_spec null_file_spec;
+
+ null_file_spec.file_valid = 0;
+ for ( ; elem ; elem = elem->next) {
+ free_data(elem);
+ if (elem->flags & MATCHED)
+ continue;
+ if (on_created_list)
+ run_external_diff(elem->path, NULL,
+ &null_file_spec, &elem->it);
+ else
+ run_external_diff(elem->path, NULL,
+ &elem->it, &null_file_spec);
+ }
+}
+
+static int is_exact_match(struct diff_spec_hold *src,
+ struct diff_spec_hold *dst)
+{
+ if (src->it.sha1_valid && dst->it.sha1_valid &&
+ !memcmp(src->it.blob_sha1, dst->it.blob_sha1, 20))
+ return 1;
+ if (populate_data(src) || populate_data(dst))
+ /* this is an error but will be caught downstream */
+ return 0;
+ if (src->size == dst->size &&
+ !memcmp(src->data, dst->data, src->size))
+ return 1;
+ return 0;
+}
+
+#define MINIMUM_SCORE 5000
+int estimate_similarity(struct diff_spec_hold *src, struct diff_spec_hold *dst)
+{
+ /* src points at a deleted file and dst points at a created
+ * file. They may be quite similar, in which case we want to
+ * say src is renamed to dst.
+ *
+ * Compare them and return how similar they are, representing
+ * the score as an integer between 0 and 10000. 10000 is
+ * reserved for the case where they match exactly.
+ */
+ void *delta;
+ unsigned long delta_size;
+
+ delta_size = ((src->size < dst->size) ?
+ (dst->size - src->size) : (src->size - dst->size));
+
+ /* We would not consider rename followed by more than
+ * 20% edits; that is, delta_size must be smaller than
+ * (src->size + dst->size)/2 * 0.2, which means...
+ */
+ if ((src->size + dst->size) < delta_size * 10)
+ return 0;
+
+ delta = diff_delta(src->data, src->size,
+ dst->data, dst->size,
+ &delta_size);
+ free(delta);
+
+ /* This "delta" is really xdiff with adler32 and all the
+ * overheads but it is a quick and dirty approximation.
+ *
+ * Now we will give some score to it. Let's say 20% edit gets
+ * 5000 points and 0% edit gets 9000 points. That is, every
+ * 1/20000 edit gets 1 point penalty. The amount of penalty is:
+ *
+ * (delta_size * 2 / (src->size + dst->size)) * 20000
+ *
+ */
+ return 9000 - (40000 * delta_size / (src->size+dst->size));
+}
+
+struct diff_score {
+ struct diff_spec_hold *src;
+ struct diff_spec_hold *dst;
+ int score;
+};
+
+static int score_compare(const void *a_, const void *b_)
+{
+ const struct diff_score *a = a_, *b = b_;
+ return b->score - a->score;
+}
+
+static void flush_rename_pair(struct diff_spec_hold *src,
+ struct diff_spec_hold *dst)
+{
+ src->flags |= MATCHED;
+ dst->flags |= MATCHED;
+ free_data(src);
+ free_data(dst);
+ run_external_diff(src->path, dst->path,
+ &src->it, &dst->it);
+}
+
+static void free_held_diff(struct diff_spec_hold *list)
+{
+ struct diff_spec_hold *h;
+ for (h = list; list; list = h) {
+ h = list->next;
+ free_data(list);
+ free(list);
+ }
+}
+
+void diff_flush(void)
+{
+ int num_create, num_delete, c, d;
+ struct diff_spec_hold *elem, *src, *dst;
+ struct diff_score *mx;
+
+ /* We really want to cull the candidates list early
+ * with cheap tests in order to avoid doing deltas.
+ */
+ for (dst = createdfile; dst; dst = dst->next) {
+ for (src = deletedfile; src; src = src->next) {
+ if (! is_exact_match(src, dst))
+ continue;
+ flush_rename_pair(src, dst);
+ break;
+ }
+ }
+
+ /* Count surviving candidates */
+ for (num_create = 0, elem = createdfile; elem; elem = elem->next)
+ if (!(elem->flags & MATCHED))
+ num_create++;
+
+ for (num_delete = 0, elem = deletedfile; elem; elem = elem->next)
+ if (!(elem->flags & MATCHED))
+ num_delete++;
+
+ if (num_create == 0 || num_delete == 0)
+ goto exit_path;
+
+ mx = xmalloc(sizeof(*mx) * num_create * num_delete);
+ for (c = 0, dst = createdfile; dst; dst = dst->next) {
+ int base = c * num_delete;
+ if (dst->flags & MATCHED)
+ continue;
+ for (d = 0, src = deletedfile; src; src = src->next) {
+ struct diff_score *m = &mx[base+d];
+ if (src->flags & MATCHED)
+ continue;
+ m->src = src;
+ m->dst = dst;
+ m->score = estimate_similarity(src, dst);
+ d++;
+ }
+ c++;
+ }
+ qsort(mx, num_create*num_delete, sizeof(*mx), score_compare);
+
+ for (c = 0; c < num_create * num_delete; c++) {
+ src = mx[c].src;
+ dst = mx[c].dst;
+ if ((src->flags & MATCHED) || (dst->flags & MATCHED))
+ continue;
+ fprintf(stderr,
+ "**score ** %d %s %s\n",
+ mx[c].score, src->path, dst->path);
+ }
+
+ for (c = 0; c < num_create * num_delete; c++) {
+ src = mx[c].src;
+ dst = mx[c].dst;
+ if ((src->flags & MATCHED) || (dst->flags & MATCHED))
+ continue;
+ if (mx[c].score < diff_rename_minimum_score)
+ break;
+ flush_rename_pair(src, dst);
+ }
+
+ exit_path:
+ flush_remaining_diff(createdfile, 1);
+ flush_remaining_diff(deletedfile, 0);
+ free_held_diff(createdfile);
+ free_held_diff(deletedfile);
+ createdfile = deletedfile = NULL;
+}
+
+void diff_setup(int detect_rename_, int minimum_score_, int reverse_diff_,
+ const char **pathspec_, int speccnt_)
+{
+ free_held_diff(createdfile);
+ free_held_diff(deletedfile);
+ createdfile = deletedfile = NULL;
+
+ detect_rename = detect_rename_;
+ reverse_diff = reverse_diff_;
+ pathspec = pathspec_;
+ speccnt = speccnt_;
+ diff_rename_minimum_score = minimum_score_ ? : MINIMUM_SCORE;
+}
+
void diff_addremove(int addremove, unsigned mode,
const unsigned char *sha1,
const char *base, const char *path)
@@ -376,7 +716,8 @@
memcpy(spec[0].blob_sha1, sha1, 20);
spec[0].mode = mode;
- spec[0].sha1_valid = spec[0].file_valid = 1;
+ spec[0].sha1_valid = !!memcmp(sha1, null_sha1, 20);
+ spec[0].file_valid = 1;
spec[1].file_valid = 0;
if (addremove == '+') {
@@ -384,12 +725,12 @@
} else {
one = spec; two = one + 1;
}
-
+
if (path) {
strcpy(concatpath, base);
strcat(concatpath, path);
}
- run_external_diff(path ? concatpath : base, NULL, one, two);
+ hold_diff(path ? concatpath : base, one, two);
}
void diff_change(unsigned old_mode, unsigned new_mode,
@@ -399,17 +740,22 @@
char concatpath[PATH_MAX];
struct diff_spec spec[2];
+ if (path) {
+ strcpy(concatpath, base);
+ strcat(concatpath, path);
+ }
+
memcpy(spec[0].blob_sha1, old_sha1, 20);
spec[0].mode = old_mode;
memcpy(spec[1].blob_sha1, new_sha1, 20);
spec[1].mode = new_mode;
- spec[0].sha1_valid = spec[0].file_valid = 1;
- spec[1].sha1_valid = spec[1].file_valid = 1;
+ spec[0].sha1_valid = !!memcmp(old_sha1, null_sha1, 20);
+ spec[1].sha1_valid = !!memcmp(new_sha1, null_sha1, 20);
+ spec[1].file_valid = spec[0].file_valid = 1;
- if (path) {
- strcpy(concatpath, base);
- strcat(concatpath, path);
- }
+ /* We do not look at changed files as candidate for
+ * rename detection ever.
+ */
run_external_diff(path ? concatpath : base, NULL, &spec[0], &spec[1]);
}
diff --git a/diff.h b/diff.h
--- a/diff.h
+++ b/diff.h
@@ -17,21 +17,10 @@
extern void diff_unmerge(const char *path);
-/* These are for diff-helper */
+extern void diff_setup(int detect_rename, int minimum_score,
+ int reverse,
+ const char **spec, int cnt);
-struct diff_spec {
- unsigned char blob_sha1[20];
- unsigned short mode; /* file mode */
- unsigned sha1_valid : 1; /* if true, use blob_sha1 and trust mode;
- * however with a NULL SHA1, read them
- * from the file system.
- * if false, use the name and read mode from
- * the filesystem.
- */
- unsigned file_valid : 1; /* if false the file does not even exist */
-};
-
-extern void run_external_diff(const char *name, const char *other,
- struct diff_spec *, struct diff_spec *);
+extern void diff_flush(void);
#endif /* DIFF_H */
diff --git a/git-apply-patch-script b/git-apply-patch-script
--- a/git-apply-patch-script
+++ b/git-apply-patch-script
@@ -11,6 +11,9 @@
1)
echo >&2 "cannot handle unmerged diff on path $1."
exit 1 ;;
+8)
+ echo >&2 "cannot handle rename diff between $1 and $8 yet."
+ exit 1 ;;
esac
name="$1" tmp1="$2" hex1="$3" mode1="$4" tmp2="$5" hex2="$6" mode2="$7"
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
new file mode 100644
--- /dev/null
+++ b/t/t4001-diff-rename.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Test rename detection in diff engine.
+
+'
+. ./test-lib.sh
+
+echo >path0 'Line 1
+Line 2
+Line 3
+Line 4
+Line 5
+Line 6
+Line 7
+Line 8
+Line 9
+Line 10
+line 11
+Line 12
+Line 13
+Line 14
+Line 15
+'
+
+test_expect_success \
+ 'update-cache --add a file.' \
+ 'git-update-cache --add path0'
+
+test_expect_success \
+ 'write that tree.' \
+ 'tree=$(git-write-tree)'
+
+sed -e 's/line/Line/' <path0 >path1
+rm -f path0
+test_expect_success \
+ 'renamed and edited the file.' \
+ 'git-update-cache --add --remove path0 path1'
+
+test_expect_success \
+ 'git-diff-cache -p -M after rename and editing.' \
+ 'git-diff-cache -p -M $tree >current'
+cat >expected <<\EOF
+diff --git a/path0 b/path1
+rename old path0
+rename new path1
+--- a/path0
++++ b/path1
+@@ -8,7 +8,7 @@ Line 7
+ Line 8
+ Line 9
+ Line 10
+-line 11
++Line 11
+ Line 12
+ Line 13
+ Line 14
+EOF
------------------------------------------------
^ 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