--- /dev/null
+git-imap-send(1)
+================
+
+NAME
+----
+git-imap-send - Dump a mailbox from stdin into an imap folder
+
+
+SYNOPSIS
+--------
+'git-imap-send'
+
+
+DESCRIPTION
+-----------
+This command uploads a mailbox generated with git-format-patch
+into an imap drafts folder. This allows patches to be sent as
+other email is sent with mail clients that cannot read mailbox
+files directly.
+
+Typical usage is something like:
+
+git-format-patch --signoff --stdout --attach origin | git-imap-send
+
+
+CONFIGURATION
+-------------
+
+git-imap-send requires the following values in the repository
+configuration file (shown with examples):
+
+[imap]
+ Folder = "INBOX.Drafts"
+
+[imap]
+ Tunnel = "ssh -q user@server.com /usr/bin/imapd ./Maildir 2> /dev/null"
+
+[imap]
+ Host = imap.server.com
+ User = bob
+ Password = pwd
+ Port = 143
+
+
+BUGS
+----
+Doesn't handle lines starting with "From " in the message body.
+
+
+Author
+------
+Derived from isync 1.0.1 by Mike McCormack.
+
+Documentation
+--------------
+Documentation by Mike McCormack
+
+GIT
+---
+Part of the gitlink:git[7] suite
--- /dev/null
+GIT pack format
+===============
+
+= pack-*.pack file has the following format:
+
+ - The header appears at the beginning and consists of the following:
+
+ 4-byte signature
+ 4-byte version number (network byte order)
+ 4-byte number of objects contained in the pack (network byte order)
+
+ Observation: we cannot have more than 4G versions ;-) and
+ more than 4G objects in a pack.
+
+ - The header is followed by number of object entries, each of
+ which looks like this:
+
+ (undeltified representation)
+ n-byte type and length (4-bit type, (n-1)*7+4-bit length)
+ compressed data
+
+ (deltified representation)
+ n-byte type and length (4-bit type, (n-1)*7+4-bit length)
+ 20-byte base object name
+ compressed delta data
+
+ Observation: length of each object is encoded in a variable
+ length format and is not constrained to 32-bit or anything.
+
+ - The trailer records 20-byte SHA1 checksum of all of the above.
+
+= pack-*.idx file has the following format:
+
+ - The header consists of 256 4-byte network byte order
+ integers. N-th entry of this table records the number of
+ objects in the corresponding pack, the first byte of whose
+ object name are smaller than N. This is called the
+ 'first-level fan-out' table.
+
+ Observation: we would need to extend this to an array of
+ 8-byte integers to go beyond 4G objects per pack, but it is
+ not strictly necessary.
+
+ - The header is followed by sorted 28-byte entries, one entry
+ per object in the pack. Each entry is:
+
+ 4-byte network byte order integer, recording where the
+ object is stored in the packfile as the offset from the
+ beginning.
+
+ 20-byte object name.
+
+ Observation: we would definitely need to extend this to
+ 8-byte integer plus 20-byte object name to handle a packfile
+ that is larger than 4GB.
+
+ - The file is concluded with a trailer:
+
+ A copy of the 20-byte SHA1 checksum at the end of
+ corresponding packfile.
+
+ 20-byte SHA1-checksum of all of the above.
+
+Pack Idx file:
+
+ idx
+ +--------------------------------+
+ | fanout[0] = 2 |-.
+ +--------------------------------+ |
+ | fanout[1] | |
+ +--------------------------------+ |
+ | fanout[2] | |
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
+ | fanout[255] | |
+ +--------------------------------+ |
+main | offset | |
+index | object name 00XXXXXXXXXXXXXXXX | |
+table +--------------------------------+ |
+ | offset | |
+ | object name 00XXXXXXXXXXXXXXXX | |
+ +--------------------------------+ |
+ .-| offset |<+
+ | | object name 01XXXXXXXXXXXXXXXX |
+ | +--------------------------------+
+ | | offset |
+ | | object name 01XXXXXXXXXXXXXXXX |
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | | offset |
+ | | object name FFXXXXXXXXXXXXXXXX |
+ | +--------------------------------+
+trailer | | packfile checksum |
+ | +--------------------------------+
+ | | idxfile checksum |
+ | +--------------------------------+
+ .-------.
+ |
+Pack file entry: <+
+
+ packed object header:
+ 1-byte type (upper 4-bit)
+ size0 (lower 4-bit)
+ n-byte sizeN (as long as MSB is set, each 7-bit)
+ size0..sizeN form 4+7+7+..+7 bit integer, size0
+ is the most significant part.
+ packed object data:
+ If it is not DELTA, then deflated bytes (the size above
+ is the size before compression).
+ If it is DELTA, then
+ 20-byte base object name SHA1 (the size above is the
+ size of the delta data that follows).
+ delta data, deflated.
--- /dev/null
+ Concerning Git's Packing Heuristics
+ ===================================
+
+ Oh, here's a really stupid question:
+
+ Where do I go
+ to learn the details
+ of git's packing heuristics?
+
+Be careful what you ask!
+
+Followers of the git, please open the git IRC Log and turn to
+February 10, 2006.
+
+It's a rare occasion, and we are joined by the King Git Himself,
+Linus Torvalds (linus). Nathaniel Smith, (njs`), has the floor
+and seeks enlightenment. Others are present, but silent.
+
+Let's listen in!
+
+ <njs`> Oh, here's a really stupid question -- where do I go to
+ learn the details of git's packing heuristics? google avails
+ me not, reading the source didn't help a lot, and wading
+ through the whole mailing list seems less efficient than any
+ of that.
+
+It is a bold start! A plea for help combined with a simultaneous
+tri-part attack on some of the tried and true mainstays in the quest
+for enlightenment. Brash accusations of google being useless. Hubris!
+Maligning the source. Heresy! Disdain for the mailing list archives.
+Woe.
+
+ <pasky> yes, the packing-related delta stuff is somewhat
+ mysterious even for me ;)
+
+Ah! Modesty after all.
+
+ <linus> njs, I don't think the docs exist. That's something where
+ I don't think anybody else than me even really got involved.
+ Most of the rest of git others have been busy with (especially
+ Junio), but packing nobody touched after I did it.
+
+It's cryptic, yet vague. Linus in style for sure. Wise men
+interpret this as an apology. A few argue it is merely a
+statement of fact.
+
+ <njs`> I guess the next step is "read the source again", but I
+ have to build up a certain level of gumption first :-)
+
+Indeed! On both points.
+
+ <linus> The packing heuristic is actually really really simple.
+
+Bait...
+
+ <linus> But strange.
+
+And switch. That ought to do it!
+
+ <linus> Remember: git really doesn't follow files. So what it does is
+ - generate a list of all objects
+ - sort the list according to magic heuristics
+ - walk the list, using a sliding window, seeing if an object
+ can be diffed against another object in the window
+ - write out the list in recency order
+
+The traditional understatement:
+
+ <njs`> I suspect that what I'm missing is the precise definition of
+ the word "magic"
+
+The traditional insight:
+
+ <pasky> yes
+
+And Bable-like confusion flowed.
+
+ <njs`> oh, hmm, and I'm not sure what this sliding window means either
+
+ <pasky> iirc, it appeared to me to be just the sha1 of the object
+ when reading the code casually ...
+
+ ... which simply doesn't sound as a very good heuristics, though ;)
+
+ <njs`> .....and recency order. okay, I think it's clear I didn't
+ even realize how much I wasn't realizing :-)
+
+Ah, grasshopper! And thus the enlightenment begins anew.
+
+ <linus> The "magic" is actually in theory totally arbitrary.
+ ANY order will give you a working pack, but no, it's not
+ ordered by SHA1.
+
+ Before talking about the ordering for the sliding delta
+ window, let's talk about the recency order. That's more
+ important in one way.
+
+ <njs`> Right, but if all you want is a working way to pack things
+ together, you could just use cat and save yourself some
+ trouble...
+
+Waaait for it....
+
+ <linus> The recency ordering (which is basically: put objects
+ _physically_ into the pack in the order that they are
+ "reachable" from the head) is important.
+
+ <njs`> okay
+
+ <linus> It's important because that's the thing that gives packs
+ good locality. It keeps the objects close to the head (whether
+ they are old or new, but they are _reachable_ from the head)
+ at the head of the pack. So packs actually have absolutely
+ _wonderful_ IO patterns.
+
+Read that again, because it is important.
+
+ <linus> But recency ordering is totally useless for deciding how
+ to actually generate the deltas, so the delta ordering is
+ something else.
+
+ The delta ordering is (wait for it):
+ - first sort by the "basename" of the object, as defined by
+ the name the object was _first_ reached through when
+ generating the object list
+ - within the same basename, sort by size of the object
+ - but always sort different types separately (commits first).
+
+ That's not exactly it, but it's very close.
+
+ <njs`> The "_first_ reached" thing is not too important, just you
+ need some way to break ties since the same objects may be
+ reachable many ways, yes?
+
+And as if to clarify:
+
+ <linus> The point is that it's all really just any random
+ heuristic, and the ordering is totally unimportant for
+ correctness, but it helps a lot if the heuristic gives
+ "clumping" for things that are likely to delta well against
+ each other.
+
+It is an important point, so secretly, I did my own research and have
+included my results below. To be fair, it has changed some over time.
+And through the magic of Revisionistic History, I draw upon this entry
+from The Git IRC Logs on my father's birthday, March 1:
+
+ <gitster> The quote from the above linus should be rewritten a
+ bit (wait for it):
+ - first sort by type. Different objects never delta with
+ each other.
+ - then sort by filename/dirname. hash of the basename
+ occupies the top BITS_PER_INT-DIR_BITS bits, and bottom
+ DIR_BITS are for the hash of leading path elements.
+ - then if we are doing "thin" pack, the objects we are _not_
+ going to pack but we know about are sorted earlier than
+ other objects.
+ - and finally sort by size, larger to smaller.
+
+In one swell-foop, clarification and obscurification! Nonetheless,
+authoritative. Cryptic, yet concise. It even solicits notions of
+quotes from The Source Code. Clearly, more study is needed.
+
+ <gitster> That's the sort order. What this means is:
+ - we do not delta different object types.
+ - we prefer to delta the objects with the same full path, but
+ allow files with the same name from different directories.
+ - we always prefer to delta against objects we are not going
+ to send, if there are some.
+ - we prefer to delta against larger objects, so that we have
+ lots of removals.
+
+ The penultimate rule is for "thin" packs. It is used when
+ the other side is known to have such objects.
+
+There it is again. "Thin" packs. I'm thinking to myself, "What
+is a 'thin' pack?" So I ask:
+
+ <jdl> What is a "thin" pack?
+
+ <gitster> Use of --objects-edge to rev-list as the upstream of
+ pack-objects. The pack transfer protocol negotiates that.
+
+Woo hoo! Cleared that _right_ up!
+
+ <gitster> There are two directions - push and fetch.
+
+There! Did you see it? It is not '"push" and "pull"'! How often the
+confusion has started here. So casually mentioned, too!
+
+ <gitster> For push, git-send-pack invokes git-receive-pack on the
+ other end. The receive-pack says "I have up to these commits".
+ send-pack looks at them, and computes what are missing from
+ the other end. So "thin" could be the default there.
+
+ In the other direction, fetch, git-fetch-pack and
+ git-clone-pack invokes git-upload-pack on the other end
+ (via ssh or by talking to the daemon).
+
+ There are two cases: fetch-pack with -k and clone-pack is one,
+ fetch-pack without -k is the other. clone-pack and fetch-pack
+ with -k will keep the downloaded packfile without expanded, so
+ we do not use thin pack transfer. Otherwise, the generated
+ pack will have delta without base object in the same pack.
+
+ But fetch-pack without -k will explode the received pack into
+ individual objects, so we automatically ask upload-pack to
+ give us a thin pack if upload-pack supports it.
+
+OK then.
+
+Uh.
+
+Let's return to the previous conversation still in progress.
+
+ <njs`> and "basename" means something like "the tail of end of
+ path of file objects and dir objects, as per basename(3), and
+ we just declare all commit and tag objects to have the same
+ basename" or something?
+
+Luckily, that too is a point that gitster clarified for us!
+
+If I might add, the trick is to make files that _might_ be similar be
+located close to each other in the hash buckets based on their file
+names. It used to be that "foo/Makefile", "bar/baz/quux/Makefile" and
+"Makefile" all landed in the same bucket due to their common basename,
+"Makefile". However, now they land in "close" buckets.
+
+The algorithm allows not just for the _same_ bucket, but for _close_
+buckets to be considered delta candidates. The rationale is
+essentially that files, like Makefiles, often have very similar
+content no matter what directory they live in.
+
+ <linus> I played around with different delta algorithms, and with
+ making the "delta window" bigger, but having too big of a
+ sliding window makes it very expensive to generate the pack:
+ you need to compare every object with a _ton_ of other objects.
+
+ There are a number of other trivial heuristics too, which
+ basically boil down to "don't bother even trying to delta this
+ pair" if we can tell before-hand that the delta isn't worth it
+ (due to size differences, where we can take a previous delta
+ result into account to decide that "ok, no point in trying
+ that one, it will be worse").
+
+ End result: packing is actually very size efficient. It's
+ somewhat CPU-wasteful, but on the other hand, since you're
+ really only supposed to do it maybe once a month (and you can
+ do it during the night), nobody really seems to care.
+
+Nice Engineering Touch, there. Find when it doesn't matter, and
+proclaim it a non-issue. Good style too!
+
+ <njs`> So, just to repeat to see if I'm following, we start by
+ getting a list of the objects we want to pack, we sort it by
+ this heuristic (basically lexicographically on the tuple
+ (type, basename, size)).
+
+ Then we walk through this list, and calculate a delta of
+ each object against the last n (tunable paramater) objects,
+ and pick the smallest of these deltas.
+
+Vastly simplified, but the essence is there!
+
+ <linus> Correct.
+
+ <njs`> And then once we have picked a delta or fulltext to
+ represent each object, we re-sort by recency, and write them
+ out in that order.
+
+ <linus> Yup. Some other small details:
+
+And of course there is the "Other Shoe" Factor too.
+
+ <linus> - We limit the delta depth to another magic value (right
+ now both the window and delta depth magic values are just "10")
+
+ <njs`> Hrm, my intuition is that you'd end up with really _bad_ IO
+ patterns, because the things you want are near by, but to
+ actually reconstruct them you may have to jump all over in
+ random ways.
+
+ <linus> - When we write out a delta, and we haven't yet written
+ out the object it is a delta against, we write out the base
+ object first. And no, when we reconstruct them, we actually
+ get nice IO patterns, because:
+ - larger objects tend to be "more recent" (Linus' law: files grow)
+ - we actively try to generate deltas from a larger object to a
+ smaller one
+ - this means that the top-of-tree very seldom has deltas
+ (ie deltas in _practice_ are "backwards deltas")
+
+Again, we should reread that whole paragraph. Not just because
+Linus has slipped Linus's Law in there on us, but because it is
+important. Let's make sure we clarify some of the points here:
+
+ <njs`> So the point is just that in practice, delta order and
+ recency order match each other quite well.
+
+ <linus> Yes. There's another nice side to this (and yes, it was
+ designed that way ;):
+ - the reason we generate deltas against the larger object is
+ actually a big space saver too!
+
+ <njs`> Hmm, but your last comment (if "we haven't yet written out
+ the object it is a delta against, we write out the base object
+ first"), seems like it would make these facts mostly
+ irrelevant because even if in practice you would not have to
+ wander around much, in fact you just brute-force say that in
+ the cases where you might have to wander, don't do that :-)
+
+ <linus> Yes and no. Notice the rule: we only write out the base
+ object first if the delta against it was more recent. That
+ means that you can actually have deltas that refer to a base
+ object that is _not_ close to the delta object, but that only
+ happens when the delta is needed to generate an _old_ object.
+
+ <linus> See?
+
+Yeah, no. I missed that on the first two or three readings myself.
+
+ <linus> This keeps the front of the pack dense. The front of the
+ pack never contains data that isn't relevant to a "recent"
+ object. The size optimization comes from our use of xdelta
+ (but is true for many other delta algorithms): removing data
+ is cheaper (in size) than adding data.
+
+ When you remove data, you only need to say "copy bytes n--m".
+ In contrast, in a delta that _adds_ data, you have to say "add
+ these bytes: 'actual data goes here'"
+
+ *** njs` has quit: Read error: 104 (Connection reset by peer)
+
+ <linus> Uhhuh. I hope I didn't blow njs` mind.
+
+ *** njs` has joined channel #git
+
+ <pasky> :)
+
+The silent observers are amused. Of course.
+
+And as if njs` was expected to be omniscient:
+
+ <linus> njs - did you miss anything?
+
+OK, I'll spell it out. That's Geek Humor. If njs` was not actually
+connected for a little bit there, how would he know if missed anything
+while he was disconnected? He's a benevolent dictator with a sense of
+humor! Well noted!
+
+ <njs`> Stupid router. Or gremlins, or whatever.
+
+It's a cheap shot at Cisco. Take 'em when you can.
+
+ <njs`> Yes and no. Notice the rule: we only write out the base
+ object first if the delta against it was more recent.
+
+ I'm getting lost in all these orders, let me re-read :-)
+ So the write-out order is from most recent to least recent?
+ (Conceivably it could be the opposite way too, I'm not sure if
+ we've said) though my connection back at home is logging, so I
+ can just read what you said there :-)
+
+And for those of you paying attention, the Omniscient Trick has just
+been detailed!
+
+ <linus> Yes, we always write out most recent first
+
+For the other record:
+
+ <pasky> njs`: http://pastebin.com/547965
+
+The 'net never forgets, so that should be good until the end of time.
+
+ <njs`> And, yeah, I got the part about deeper-in-history stuff
+ having worse IO characteristics, one sort of doesn't care.
+
+ <linus> With the caveat that if the "most recent" needs an older
+ object to delta against (hey, shrinking sometimes does
+ happen), we write out the old object with the delta.
+
+ <njs`> (if only it happened more...)
+
+ <linus> Anyway, the pack-file could easily be denser still, but
+ because it's used both for streaming (the git protocol) and
+ for on-disk, it has a few pessimizations.
+
+Actually, it is a made-up word. But it is a made-up word being
+used as setup for a later optimization, which is a real word:
+
+ <linus> In particular, while the pack-file is then compressed,
+ it's compressed just one object at a time, so the actual
+ compression factor is less than it could be in theory. But it
+ means that it's all nice random-access with a simple index to
+ do "object name->location in packfile" translation.
+
+ <njs`> I'm assuming the real win for delta-ing large->small is
+ more homogenous statistics for gzip to run over?
+
+ (You have to put the bytes in one place or another, but
+ putting them in a larger blob wins on compression)
+
+ Actually, what is the compression strategy -- each delta
+ individually gzipped, the whole file gzipped, somewhere in
+ between, no compression at all, ....?
+
+ Right.
+
+Reality IRC sets in. For example:
+
+ <pasky> I'll read the rest in the morning, I really have to go
+ sleep or there's no hope whatsoever for me at the today's
+ exam... g'nite all.
+
+Heh.
+
+ <linus> pasky: g'nite
+
+ <njs`> pasky: 'luck
+
+ <linus> Right: large->small matters exactly because of compression
+ behaviour. If it was non-compressed, it probably wouldn't make
+ any difference.
+
+ <njs`> yeah
+
+ <linus> Anyway: I'm not even trying to claim that the pack-files
+ are perfect, but they do tend to have a nice balance of
+ density vs ease-of use.
+
+Gasp! OK, saved. That's a fair Engineering trade off. Close call!
+In fact, Linus reflects on some Basic Engineering Fundamentals,
+design options, etc.
+
+ <linus> More importantly, they allow git to still _conceptually_
+ never deal with deltas at all, and be a "whole object" store.
+
+ Which has some problems (we discussed bad huge-file
+ behaviour on the git lists the other day), but it does mean
+ that the basic git concepts are really really simple and
+ straightforward.
+
+ It's all been quite stable.
+
+ Which I think is very much a result of having very simple
+ basic ideas, so that there's never any confusion about what's
+ going on.
+
+ Bugs happen, but they are "simple" bugs. And bugs that
+ actually get some object store detail wrong are almost always
+ so obious that they never go anywhere.
+
+ <njs`> Yeah.
+
+Nuff said.
+
+ <linus> Anyway. I'm off for bed. It's not 6AM here, but I've got
+ three kids, and have to get up early in the morning to send
+ them off. I need my beauty sleep.
+
+ <njs`> :-)
+
+ <njs`> appreciate the infodump, I really was failing to find the
+ details on git packs :-)
+
+And now you know the rest of the story.
quote.o read-cache.o refs.o run-command.o \
server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
- fetch-clone.o revision.o pager.o tree-walk.o \
+ fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
$(DIFF_OBJS)
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
ifeq "$(curl_check)" "070908"
ifndef NO_EXPAT
- EXPAT_LIBEXPAT = -lexpat
PROGRAMS += git-http-push$X
endif
endif
+ ifndef NO_EXPAT
+ EXPAT_LIBEXPAT = -lexpat
+ endif
endif
ifndef NO_OPENSSL
http.o: http.c
$(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $<
+ifdef NO_EXPAT
+http-fetch.o: http-fetch.c
+ $(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_EXPAT $<
+endif
+
git-%$X: %.o $(GITLIBS)
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
git-http-fetch$X: fetch.o http.o http-fetch.o $(LIB_FILE)
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
- $(LIBS) $(CURL_LIBCURL)
+ $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
git-http-push$X: revision.o http.o http-push.o $(LIB_FILE)
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
#include "diff.h"
#include "diffcore.h"
#include "revision.h"
+#include "xdiff-interface.h"
#define DEBUG 0
static int num_commits = 0;
static int patch_time = 0;
-#define TEMPFILE_PATH_LEN 60
-static struct patch *get_patch(struct commit *commit, struct commit *other)
-{
+struct blame_diff_state {
+ struct xdiff_emit_state xm;
struct patch *ret;
- struct util_info *info_c = (struct util_info *)commit->object.util;
- struct util_info *info_o = (struct util_info *)other->object.util;
- char tmp_path1[TEMPFILE_PATH_LEN], tmp_path2[TEMPFILE_PATH_LEN];
- char diff_cmd[TEMPFILE_PATH_LEN*2 + 20];
- struct timeval tv_start, tv_end;
- int fd;
- FILE *fin;
- char buf[1024];
-
- ret = xmalloc(sizeof(struct patch));
- ret->chunks = NULL;
- ret->num = 0;
-
- get_blob(commit);
- get_blob(other);
+};
- gettimeofday(&tv_start, NULL);
+static void process_u0_diff(void *state_, char *line, unsigned long len)
+{
+ struct blame_diff_state *state = state_;
+ struct chunk *chunk;
- fd = git_mkstemp(tmp_path1, TEMPFILE_PATH_LEN, "git-blame-XXXXXX");
- if (fd < 0)
- die("unable to create temp-file: %s", strerror(errno));
+ if (len < 4 || line[0] != '@' || line[1] != '@')
+ return;
- if (xwrite(fd, info_c->buf, info_c->size) != info_c->size)
- die("write failed: %s", strerror(errno));
- close(fd);
+ if (DEBUG)
+ printf("chunk line: %.*s", (int)len, line);
+ state->ret->num++;
+ state->ret->chunks = xrealloc(state->ret->chunks,
+ sizeof(struct chunk) * state->ret->num);
+ chunk = &state->ret->chunks[state->ret->num - 1];
+
+ assert(!strncmp(line, "@@ -", 4));
+
+ if (parse_hunk_header(line, len,
+ &chunk->off1, &chunk->len1,
+ &chunk->off2, &chunk->len2)) {
+ state->ret->num--;
+ return;
+ }
- fd = git_mkstemp(tmp_path2, TEMPFILE_PATH_LEN, "git-blame-XXXXXX");
- if (fd < 0)
- die("unable to create temp-file: %s", strerror(errno));
+ if (chunk->len1 == 0)
+ chunk->off1++;
+ if (chunk->len2 == 0)
+ chunk->off2++;
- if (xwrite(fd, info_o->buf, info_o->size) != info_o->size)
- die("write failed: %s", strerror(errno));
- close(fd);
+ if (chunk->off1 > 0)
+ chunk->off1--;
+ if (chunk->off2 > 0)
+ chunk->off2--;
- sprintf(diff_cmd, "diff -U 0 %s %s", tmp_path1, tmp_path2);
- fin = popen(diff_cmd, "r");
- if (!fin)
- die("popen failed: %s", strerror(errno));
+ assert(chunk->off1 >= 0);
+ assert(chunk->off2 >= 0);
+}
- while (fgets(buf, sizeof(buf), fin)) {
- struct chunk *chunk;
- char *start, *sp;
+static struct patch *get_patch(struct commit *commit, struct commit *other)
+{
+ struct blame_diff_state state;
+ xpparam_t xpp;
+ xdemitconf_t xecfg;
+ mmfile_t file_c, file_o;
+ xdemitcb_t ecb;
+ struct util_info *info_c = (struct util_info *)commit->object.util;
+ struct util_info *info_o = (struct util_info *)other->object.util;
+ struct timeval tv_start, tv_end;
- if (buf[0] != '@' || buf[1] != '@')
- continue;
+ get_blob(commit);
+ file_c.ptr = info_c->buf;
+ file_c.size = info_c->size;
- if (DEBUG)
- printf("chunk line: %s", buf);
- ret->num++;
- ret->chunks = xrealloc(ret->chunks,
- sizeof(struct chunk) * ret->num);
- chunk = &ret->chunks[ret->num - 1];
-
- assert(!strncmp(buf, "@@ -", 4));
-
- start = buf + 4;
- sp = index(start, ' ');
- *sp = '\0';
- if (index(start, ',')) {
- int ret =
- sscanf(start, "%d,%d", &chunk->off1, &chunk->len1);
- assert(ret == 2);
- } else {
- int ret = sscanf(start, "%d", &chunk->off1);
- assert(ret == 1);
- chunk->len1 = 1;
- }
- *sp = ' ';
-
- start = sp + 1;
- sp = index(start, ' ');
- *sp = '\0';
- if (index(start, ',')) {
- int ret =
- sscanf(start, "%d,%d", &chunk->off2, &chunk->len2);
- assert(ret == 2);
- } else {
- int ret = sscanf(start, "%d", &chunk->off2);
- assert(ret == 1);
- chunk->len2 = 1;
- }
- *sp = ' ';
+ get_blob(other);
+ file_o.ptr = info_o->buf;
+ file_o.size = info_o->size;
- if (chunk->len1 == 0)
- chunk->off1++;
- if (chunk->len2 == 0)
- chunk->off2++;
+ gettimeofday(&tv_start, NULL);
- if (chunk->off1 > 0)
- chunk->off1--;
- if (chunk->off2 > 0)
- chunk->off2--;
+ xpp.flags = XDF_NEED_MINIMAL;
+ xecfg.ctxlen = 0;
+ xecfg.flags = 0;
+ ecb.outf = xdiff_outf;
+ ecb.priv = &state;
+ memset(&state, 0, sizeof(state));
+ state.xm.consume = process_u0_diff;
+ state.ret = xmalloc(sizeof(struct patch));
+ state.ret->chunks = NULL;
+ state.ret->num = 0;
- assert(chunk->off1 >= 0);
- assert(chunk->off2 >= 0);
- }
- pclose(fin);
- unlink(tmp_path1);
- unlink(tmp_path2);
+ xdl_diff(&file_c, &file_o, &xpp, &xecfg, &ecb);
gettimeofday(&tv_end, NULL);
patch_time += 1000000 * (tv_end.tv_sec - tv_start.tv_sec) +
tv_end.tv_usec - tv_start.tv_usec;
num_get_patch++;
- return ret;
+ return state.ret;
}
static void free_patch(struct patch *p)
static char author_buf[1024];
tmp = strstr(commit->buffer, "\nauthor ") + 8;
- len = index(tmp, '\n') - tmp;
+ len = strchr(tmp, '\n') - tmp;
ret->author = author_buf;
memcpy(ret->author, tmp, len);
return util->topo_data;
}
+static int read_ancestry(const char *graft_file,
+ unsigned char **start_sha1)
+{
+ FILE *fp = fopen(graft_file, "r");
+ char buf[1024];
+ if (!fp)
+ return -1;
+ while (fgets(buf, sizeof(buf), fp)) {
+ /* The format is just "Commit Parent1 Parent2 ...\n" */
+ int len = strlen(buf);
+ struct commit_graft *graft = read_graft_line(buf, len);
+ register_commit_graft(graft, 0);
+ if (!*start_sha1)
+ *start_sha1 = graft->sha1;
+ }
+ fclose(fp);
+ return 0;
+}
+
int main(int argc, const char **argv)
{
int i;
struct commit *initial = NULL;
- unsigned char sha1[20];
+ unsigned char sha1[20], *sha1_p = NULL;
const char *filename = NULL, *commit = NULL;
char filename_buf[256];
!strcmp(argv[i], "--compability")) {
compability = 1;
continue;
+ } else if(!strcmp(argv[i], "-S")) {
+ if (i + 1 < argc &&
+ !read_ancestry(argv[i + 1], &sha1_p)) {
+ compability = 1;
+ i++;
+ continue;
+ }
+ usage(blame_usage);
} else if(!strcmp(argv[i], "--")) {
options = 0;
continue;
if(!filename)
usage(blame_usage);
- if(!commit)
+ if (commit && sha1_p)
+ usage(blame_usage);
+ else if(!commit)
commit = "HEAD";
if(prefix)
strcpy(filename_buf, filename);
filename = filename_buf;
- if (get_sha1(commit, sha1))
- die("get_sha1 failed, commit '%s' not found", commit);
- start_commit = lookup_commit_reference(sha1);
+ if (!sha1_p) {
+ if (get_sha1(commit, sha1))
+ die("get_sha1 failed, commit '%s' not found", commit);
+ sha1_p = sha1;
+ }
+ start_commit = lookup_commit_reference(sha1_p);
get_util(start_commit)->pathname = filename;
if (fill_util_info(start_commit)) {
printf("%s not found in %s\n", filename, commit);
rev.prune_fn = simplify_commit;
rev.topo_setter = topo_setter;
rev.topo_getter = topo_getter;
+ rev.parents = 1;
rev.limited = 1;
commit_list_insert(start_commit, &rev.commits);
if(blame_contents[blame_len-1] != '\n')
putc('\n', stdout);
} else {
- char* next_buf = index(buf, '\n') + 1;
+ char* next_buf = strchr(buf, '\n') + 1;
fwrite(buf, next_buf - buf, 1, stdout);
buf = next_buf;
}
#include "diff.h"
#include "diffcore.h"
#include "quote.h"
+#include "xdiff-interface.h"
static int uninteresting(struct diff_filepair *p)
{
return blob;
}
-#define TMPPATHLEN 50
-#define MAXLINELEN 10240
-
-static void write_to_temp_file(char *tmpfile, void *blob, unsigned long size)
-{
- int fd = git_mkstemp(tmpfile, TMPPATHLEN, ".diff_XXXXXX");
- if (fd < 0)
- die("unable to create temp-file");
- if (write(fd, blob, size) != size)
- die("unable to write temp-file");
- close(fd);
-}
-
-static void write_temp_blob(char *tmpfile, const unsigned char *sha1)
-{
- unsigned long size;
- void *blob;
- blob = grab_blob(sha1, &size);
- write_to_temp_file(tmpfile, blob, size);
- free(blob);
-}
-
-static int parse_num(char **cp_p, unsigned int *num_p)
-{
- char *cp = *cp_p;
- unsigned int num = 0;
- int read_some;
-
- while ('0' <= *cp && *cp <= '9')
- num = num * 10 + *cp++ - '0';
- if (!(read_some = cp - *cp_p))
- return -1;
- *cp_p = cp;
- *num_p = num;
- return 0;
-}
-
-static int parse_hunk_header(char *line, int len,
- unsigned int *ob, unsigned int *on,
- unsigned int *nb, unsigned int *nn)
-{
- char *cp;
- cp = line + 4;
- if (parse_num(&cp, ob)) {
- bad_line:
- return error("malformed diff output: %s", line);
- }
- if (*cp == ',') {
- cp++;
- if (parse_num(&cp, on))
- goto bad_line;
- }
- else
- *on = 1;
- if (*cp++ != ' ' || *cp++ != '+')
- goto bad_line;
- if (parse_num(&cp, nb))
- goto bad_line;
- if (*cp == ',') {
- cp++;
- if (parse_num(&cp, nn))
- goto bad_line;
- }
- else
- *nn = 1;
- return -!!memcmp(cp, " @@", 3);
-}
-
-static void append_lost(struct sline *sline, int n, const char *line)
+static void append_lost(struct sline *sline, int n, const char *line, int len)
{
struct lline *lline;
- int len = strlen(line);
unsigned long this_mask = (1UL<<n);
if (line[len-1] == '\n')
len--;
sline->lost_tail = &lline->next;
}
-static void combine_diff(const unsigned char *parent, const char *ourtmp,
+struct combine_diff_state {
+ struct xdiff_emit_state xm;
+
+ unsigned int lno;
+ int ob, on, nb, nn;
+ unsigned long nmask;
+ int num_parent;
+ int n;
+ struct sline *sline;
+ struct sline *lost_bucket;
+};
+
+static void consume_line(void *state_, char *line, unsigned long len)
+{
+ struct combine_diff_state *state = state_;
+ if (5 < len && !memcmp("@@ -", line, 4)) {
+ if (parse_hunk_header(line, len,
+ &state->ob, &state->on,
+ &state->nb, &state->nn))
+ return;
+ state->lno = state->nb;
+ if (!state->nb)
+ /* @@ -1,2 +0,0 @@ to remove the
+ * first two lines...
+ */
+ state->nb = 1;
+ if (state->nn == 0)
+ /* @@ -X,Y +N,0 @@ removed Y lines
+ * that would have come *after* line N
+ * in the result. Our lost buckets hang
+ * to the line after the removed lines,
+ */
+ state->lost_bucket = &state->sline[state->nb];
+ else
+ state->lost_bucket = &state->sline[state->nb-1];
+ if (!state->sline[state->nb-1].p_lno)
+ state->sline[state->nb-1].p_lno =
+ xcalloc(state->num_parent,
+ sizeof(unsigned long));
+ state->sline[state->nb-1].p_lno[state->n] = state->ob;
+ return;
+ }
+ if (!state->lost_bucket)
+ return; /* not in any hunk yet */
+ switch (line[0]) {
+ case '-':
+ append_lost(state->lost_bucket, state->n, line+1, len-1);
+ break;
+ case '+':
+ state->sline[state->lno-1].flag |= state->nmask;
+ state->lno++;
+ break;
+ }
+}
+
+static void combine_diff(const unsigned char *parent, mmfile_t *result_file,
struct sline *sline, int cnt, int n, int num_parent)
{
- FILE *in;
- char parent_tmp[TMPPATHLEN];
- char cmd[TMPPATHLEN * 2 + 1024];
- char line[MAXLINELEN];
- unsigned int lno, ob, on, nb, nn, p_lno;
+ unsigned int p_lno, lno;
unsigned long nmask = (1UL << n);
- struct sline *lost_bucket = NULL;
+ xpparam_t xpp;
+ xdemitconf_t xecfg;
+ mmfile_t parent_file;
+ xdemitcb_t ecb;
+ struct combine_diff_state state;
+ unsigned long sz;
if (!cnt)
return; /* result deleted */
- write_temp_blob(parent_tmp, parent);
- sprintf(cmd, "diff --unified=0 -La/x -Lb/x '%s' '%s'",
- parent_tmp, ourtmp);
- in = popen(cmd, "r");
- if (!in)
- die("cannot spawn %s", cmd);
-
- lno = 1;
- while (fgets(line, sizeof(line), in) != NULL) {
- int len = strlen(line);
- if (5 < len && !memcmp("@@ -", line, 4)) {
- if (parse_hunk_header(line, len,
- &ob, &on, &nb, &nn))
- break;
- lno = nb;
- if (!nb)
- /* @@ -1,2 +0,0 @@ to remove the
- * first two lines...
- */
- nb = 1;
- if (nn == 0)
- /* @@ -X,Y +N,0 @@ removed Y lines
- * that would have come *after* line N
- * in the result. Our lost buckets hang
- * to the line after the removed lines,
- */
- lost_bucket = &sline[nb];
- else
- lost_bucket = &sline[nb-1];
- if (!sline[nb-1].p_lno)
- sline[nb-1].p_lno =
- xcalloc(num_parent,
- sizeof(unsigned long));
- sline[nb-1].p_lno[n] = ob;
- continue;
- }
- if (!lost_bucket)
- continue; /* not in any hunk yet */
- switch (line[0]) {
- case '-':
- append_lost(lost_bucket, n, line+1);
- break;
- case '+':
- sline[lno-1].flag |= nmask;
- lno++;
- break;
- }
- }
- fclose(in);
- unlink(parent_tmp);
+ parent_file.ptr = grab_blob(parent, &sz);
+ parent_file.size = sz;
+ xpp.flags = XDF_NEED_MINIMAL;
+ xecfg.ctxlen = 0;
+ xecfg.flags = 0;
+ ecb.outf = xdiff_outf;
+ ecb.priv = &state;
+ memset(&state, 0, sizeof(state));
+ state.xm.consume = consume_line;
+ state.nmask = nmask;
+ state.sline = sline;
+ state.lno = 1;
+ state.num_parent = num_parent;
+ state.n = n;
+
+ xdl_diff(&parent_file, result_file, &xpp, &xecfg, &ecb);
+ free(parent_file.ptr);
/* Assign line numbers for this parent.
*
int dense, const char *header,
struct diff_options *opt)
{
- unsigned long size, cnt, lno;
+ unsigned long result_size, cnt, lno;
char *result, *cp, *ep;
struct sline *sline; /* survived lines */
int mode_differs = 0;
int i, show_hunks, shown_header = 0;
- char ourtmp_buf[TMPPATHLEN];
- char *ourtmp = ourtmp_buf;
int working_tree_file = !memcmp(elem->sha1, null_sha1, 20);
int abbrev = opt->full_index ? 40 : DEFAULT_ABBREV;
+ mmfile_t result_file;
/* Read the result of merge first */
- if (!working_tree_file) {
- result = grab_blob(elem->sha1, &size);
- write_to_temp_file(ourtmp, result, size);
- }
+ if (!working_tree_file)
+ result = grab_blob(elem->sha1, &result_size);
else {
/* Used by diff-tree to read from the working tree */
struct stat st;
int fd;
- ourtmp = elem->path;
- if (0 <= (fd = open(ourtmp, O_RDONLY)) &&
+ if (0 <= (fd = open(elem->path, O_RDONLY)) &&
!fstat(fd, &st)) {
int len = st.st_size;
int cnt = 0;
elem->mode = canon_mode(st.st_mode);
- size = len;
+ result_size = len;
result = xmalloc(len + 1);
while (cnt < len) {
int done = xread(fd, result+cnt, len-cnt);
if (done == 0)
break;
if (done < 0)
- die("read error '%s'", ourtmp);
+ die("read error '%s'", elem->path);
cnt += done;
}
result[len] = 0;
}
else {
/* deleted file */
- size = 0;
+ result_size = 0;
elem->mode = 0;
result = xmalloc(1);
result[0] = 0;
- ourtmp = "/dev/null";
}
if (0 <= fd)
close(fd);
}
- for (cnt = 0, cp = result; cp - result < size; cp++) {
+ for (cnt = 0, cp = result; cp - result < result_size; cp++) {
if (*cp == '\n')
cnt++;
}
- if (size && result[size-1] != '\n')
+ if (result_size && result[result_size-1] != '\n')
cnt++; /* incomplete line */
sline = xcalloc(cnt+1, sizeof(*sline));
sline[lno].lost_tail = &sline[lno].lost_head;
sline[lno].flag = 0;
}
- for (lno = 0, cp = result; cp - result < size; cp++) {
+ for (lno = 0, cp = result; cp - result < result_size; cp++) {
if (*cp == '\n') {
sline[lno].len = cp - sline[lno].bol;
lno++;
sline[lno].bol = cp + 1;
}
}
- if (size && result[size-1] != '\n')
- sline[cnt-1].len = size - (sline[cnt-1].bol - result);
+ if (result_size && result[result_size-1] != '\n')
+ sline[cnt-1].len = result_size - (sline[cnt-1].bol - result);
+
+ result_file.ptr = result;
+ result_file.size = result_size;
sline[0].p_lno = xcalloc((cnt+1) * num_parent, sizeof(unsigned long));
for (lno = 0; lno < cnt; lno++)
}
}
if (i <= j)
- combine_diff(elem->parent[i].sha1, ourtmp, sline,
+ combine_diff(elem->parent[i].sha1, &result_file, sline,
cnt, i, num_parent);
if (elem->parent[i].mode != elem->mode)
mode_differs = 1;
}
dump_sline(sline, cnt, num_parent);
}
- if (ourtmp == ourtmp_buf)
- unlink(ourtmp);
free(result);
for (i = 0; i < cnt; i++) {
return date;
}
-static struct commit_graft {
- unsigned char sha1[20];
- int nr_parent;
- unsigned char parent[0][20]; /* more */
-} **commit_graft;
+static struct commit_graft **commit_graft;
static int commit_graft_alloc, commit_graft_nr;
static int commit_graft_pos(const unsigned char *sha1)
return -lo - 1;
}
-static void prepare_commit_graft(void)
+int register_commit_graft(struct commit_graft *graft, int ignore_dups)
+{
+ int pos = commit_graft_pos(graft->sha1);
+
+ if (0 <= pos) {
+ if (ignore_dups)
+ free(graft);
+ else {
+ free(commit_graft[pos]);
+ commit_graft[pos] = graft;
+ }
+ return 1;
+ }
+ pos = -pos - 1;
+ if (commit_graft_alloc <= ++commit_graft_nr) {
+ commit_graft_alloc = alloc_nr(commit_graft_alloc);
+ commit_graft = xrealloc(commit_graft,
+ sizeof(*commit_graft) *
+ commit_graft_alloc);
+ }
+ if (pos < commit_graft_nr)
+ memmove(commit_graft + pos + 1,
+ commit_graft + pos,
+ (commit_graft_nr - pos - 1) *
+ sizeof(*commit_graft));
+ commit_graft[pos] = graft;
+ return 0;
+}
+
+struct commit_graft *read_graft_line(char *buf, int len)
+{
+ /* The format is just "Commit Parent1 Parent2 ...\n" */
+ int i;
+ struct commit_graft *graft = NULL;
+
+ if (buf[len-1] == '\n')
+ buf[--len] = 0;
+ if (buf[0] == '#')
+ return 0;
+ if ((len + 1) % 41) {
+ bad_graft_data:
+ error("bad graft data: %s", buf);
+ free(graft);
+ return NULL;
+ }
+ i = (len + 1) / 41 - 1;
+ graft = xmalloc(sizeof(*graft) + 20 * i);
+ graft->nr_parent = i;
+ if (get_sha1_hex(buf, graft->sha1))
+ goto bad_graft_data;
+ for (i = 40; i < len; i += 41) {
+ if (buf[i] != ' ')
+ goto bad_graft_data;
+ if (get_sha1_hex(buf + i + 1, graft->parent[i/41]))
+ goto bad_graft_data;
+ }
+ return graft;
+}
+
+int read_graft_file(const char *graft_file)
{
- char *graft_file = get_graft_file();
FILE *fp = fopen(graft_file, "r");
char buf[1024];
- if (!fp) {
- commit_graft = (struct commit_graft **) "hack";
- return;
- }
+ if (!fp)
+ return -1;
while (fgets(buf, sizeof(buf), fp)) {
/* The format is just "Commit Parent1 Parent2 ...\n" */
int len = strlen(buf);
- int i;
- struct commit_graft *graft = NULL;
-
- if (buf[len-1] == '\n')
- buf[--len] = 0;
- if (buf[0] == '#')
- continue;
- if ((len + 1) % 41) {
- bad_graft_data:
- error("bad graft data: %s", buf);
- free(graft);
- continue;
- }
- i = (len + 1) / 41 - 1;
- graft = xmalloc(sizeof(*graft) + 20 * i);
- graft->nr_parent = i;
- if (get_sha1_hex(buf, graft->sha1))
- goto bad_graft_data;
- for (i = 40; i < len; i += 41) {
- if (buf[i] != ' ')
- goto bad_graft_data;
- if (get_sha1_hex(buf + i + 1, graft->parent[i/41]))
- goto bad_graft_data;
- }
- i = commit_graft_pos(graft->sha1);
- if (0 <= i) {
+ struct commit_graft *graft = read_graft_line(buf, len);
+ if (register_commit_graft(graft, 1))
error("duplicate graft data: %s", buf);
- free(graft);
- continue;
- }
- i = -i - 1;
- if (commit_graft_alloc <= ++commit_graft_nr) {
- commit_graft_alloc = alloc_nr(commit_graft_alloc);
- commit_graft = xrealloc(commit_graft,
- sizeof(*commit_graft) *
- commit_graft_alloc);
- }
- if (i < commit_graft_nr)
- memmove(commit_graft + i + 1,
- commit_graft + i,
- (commit_graft_nr - i - 1) *
- sizeof(*commit_graft));
- commit_graft[i] = graft;
}
fclose(fp);
+ return 0;
+}
+
+static void prepare_commit_graft(void)
+{
+ static int commit_graft_prepared;
+ char *graft_file;
+
+ if (commit_graft_prepared)
+ return;
+ graft_file = get_graft_file();
+ read_graft_file(graft_file);
+ commit_graft_prepared = 1;
}
static struct commit_graft *lookup_commit_graft(const unsigned char *sha1)
{
int pos;
- if (!commit_graft)
- prepare_commit_graft();
+ prepare_commit_graft();
pos = commit_graft_pos(sha1);
if (pos < 0)
return NULL;
void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
topo_sort_set_fn_t setter,
topo_sort_get_fn_t getter);
+
+struct commit_graft {
+ unsigned char sha1[20];
+ int nr_parent;
+ unsigned char parent[FLEX_ARRAY][20]; /* more */
+};
+
+struct commit_graft *read_graft_line(char *buf, int len);
+int register_commit_graft(struct commit_graft *, int);
+int read_graft_file(const char *graft_file);
+
#endif /* COMMIT_H */
* This must be called twice on the delta data buffer, first to get the
* expected reference buffer size, and again to get the result buffer size.
*/
-static inline unsigned long get_delta_hdr_size(const unsigned char **datap)
+static inline unsigned long get_delta_hdr_size(const unsigned char **datap,
+ const unsigned char *top)
{
const unsigned char *data = *datap;
unsigned char cmd;
cmd = *data++;
size |= (cmd & ~0x80) << i;
i += 7;
- } while (cmd & 0x80);
+ } while (cmd & 0x80 && data < top);
*datap = data;
return size;
}
}
}
-proc findcont {id} {
+proc findcont {} {
global findid treediffs parentlist
global ffileline findstartline finddidsel
global displayorder numcommits matchinglines findinprogress
incr nextupdate 100
fileevent $mdf readable {}
update
- fileevent $mdf readable [list getmergediffline $mdf $id]
+ fileevent $mdf readable [list getmergediffline $mdf $id $np]
}
}
#include "fetch.h"
#include "http.h"
+#ifndef NO_EXPAT
+#include <expat.h>
+
+/* Definitions for DAV requests */
+#define DAV_PROPFIND "PROPFIND"
+#define DAV_PROPFIND_RESP ".multistatus.response"
+#define DAV_PROPFIND_NAME ".multistatus.response.href"
+#define DAV_PROPFIND_COLLECTION ".multistatus.response.propstat.prop.resourcetype.collection"
+#define PROPFIND_ALL_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/>\n</D:propfind>"
+
+/* Definitions for processing XML DAV responses */
+#ifndef XML_STATUS_OK
+enum XML_Status {
+ XML_STATUS_OK = 1,
+ XML_STATUS_ERROR = 0
+};
+#define XML_STATUS_OK 1
+#define XML_STATUS_ERROR 0
+#endif
+
+/* Flags that control remote_ls processing */
+#define PROCESS_FILES (1u << 0)
+#define PROCESS_DIRS (1u << 1)
+#define RECURSIVE (1u << 2)
+
+/* Flags that remote_ls passes to callback functions */
+#define IS_DIR (1u << 0)
+#endif
+
#define PREV_BUF_SIZE 4096
#define RANGE_HEADER_SIZE 30
struct alt_base
{
char *base;
+ int path_len;
int got_indices;
struct packed_git *packs;
struct alt_base *next;
int http_specific;
};
+#ifndef NO_EXPAT
+struct xml_ctx
+{
+ char *name;
+ int len;
+ char *cdata;
+ void (*userFunc)(struct xml_ctx *ctx, int tag_closed);
+ void *userData;
+};
+
+struct remote_ls_ctx
+{
+ struct alt_base *repo;
+ char *path;
+ void (*userFunc)(struct remote_ls_ctx *ls);
+ void *userData;
+ int flags;
+ char *dentry_name;
+ int dentry_flags;
+ int rc;
+ struct remote_ls_ctx *parent;
+};
+#endif
+
static struct object_request *object_queue_head = NULL;
static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
int serverlen = 0;
struct alt_base *newalt;
char *target = NULL;
+ char *path;
if (data[i] == '/') {
serverlen = strchr(base + 8, '/') - base;
okay = 1;
newalt->base = target;
newalt->got_indices = 0;
newalt->packs = NULL;
+ path = strstr(target, "//");
+ if (path) {
+ path = index(path+2, '/');
+ if (path)
+ newalt->path_len = strlen(path);
+ }
+
while (tail->next != NULL)
tail = tail->next;
tail->next = newalt;
free(url);
}
+#ifndef NO_EXPAT
+static void
+xml_start_tag(void *userData, const char *name, const char **atts)
+{
+ struct xml_ctx *ctx = (struct xml_ctx *)userData;
+ const char *c = index(name, ':');
+ int new_len;
+
+ if (c == NULL)
+ c = name;
+ else
+ c++;
+
+ new_len = strlen(ctx->name) + strlen(c) + 2;
+
+ if (new_len > ctx->len) {
+ ctx->name = xrealloc(ctx->name, new_len);
+ ctx->len = new_len;
+ }
+ strcat(ctx->name, ".");
+ strcat(ctx->name, c);
+
+ if (ctx->cdata) {
+ free(ctx->cdata);
+ ctx->cdata = NULL;
+ }
+
+ ctx->userFunc(ctx, 0);
+}
+
+static void
+xml_end_tag(void *userData, const char *name)
+{
+ struct xml_ctx *ctx = (struct xml_ctx *)userData;
+ const char *c = index(name, ':');
+ char *ep;
+
+ ctx->userFunc(ctx, 1);
+
+ if (c == NULL)
+ c = name;
+ else
+ c++;
+
+ ep = ctx->name + strlen(ctx->name) - strlen(c) - 1;
+ *ep = 0;
+}
+
+static void
+xml_cdata(void *userData, const XML_Char *s, int len)
+{
+ struct xml_ctx *ctx = (struct xml_ctx *)userData;
+ if (ctx->cdata)
+ free(ctx->cdata);
+ ctx->cdata = xcalloc(len+1, 1);
+ strncpy(ctx->cdata, s, len);
+}
+
+static int remote_ls(struct alt_base *repo, const char *path, int flags,
+ void (*userFunc)(struct remote_ls_ctx *ls),
+ void *userData);
+
+static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
+{
+ struct remote_ls_ctx *ls = (struct remote_ls_ctx *)ctx->userData;
+
+ if (tag_closed) {
+ if (!strcmp(ctx->name, DAV_PROPFIND_RESP) && ls->dentry_name) {
+ if (ls->dentry_flags & IS_DIR) {
+ if (ls->flags & PROCESS_DIRS) {
+ ls->userFunc(ls);
+ }
+ if (strcmp(ls->dentry_name, ls->path) &&
+ ls->flags & RECURSIVE) {
+ ls->rc = remote_ls(ls->repo,
+ ls->dentry_name,
+ ls->flags,
+ ls->userFunc,
+ ls->userData);
+ }
+ } else if (ls->flags & PROCESS_FILES) {
+ ls->userFunc(ls);
+ }
+ } else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) {
+ ls->dentry_name = xmalloc(strlen(ctx->cdata) -
+ ls->repo->path_len + 1);
+ strcpy(ls->dentry_name, ctx->cdata + ls->repo->path_len);
+ } else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {
+ ls->dentry_flags |= IS_DIR;
+ }
+ } else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) {
+ if (ls->dentry_name) {
+ free(ls->dentry_name);
+ }
+ ls->dentry_name = NULL;
+ ls->dentry_flags = 0;
+ }
+}
+
+static int remote_ls(struct alt_base *repo, const char *path, int flags,
+ void (*userFunc)(struct remote_ls_ctx *ls),
+ void *userData)
+{
+ char *url = xmalloc(strlen(repo->base) + strlen(path) + 1);
+ struct active_request_slot *slot;
+ struct slot_results results;
+ struct buffer in_buffer;
+ struct buffer out_buffer;
+ char *in_data;
+ char *out_data;
+ XML_Parser parser = XML_ParserCreate(NULL);
+ enum XML_Status result;
+ struct curl_slist *dav_headers = NULL;
+ struct xml_ctx ctx;
+ struct remote_ls_ctx ls;
+
+ ls.flags = flags;
+ ls.repo = repo;
+ ls.path = strdup(path);
+ ls.dentry_name = NULL;
+ ls.dentry_flags = 0;
+ ls.userData = userData;
+ ls.userFunc = userFunc;
+ ls.rc = 0;
+
+ sprintf(url, "%s%s", repo->base, path);
+
+ out_buffer.size = strlen(PROPFIND_ALL_REQUEST);
+ out_data = xmalloc(out_buffer.size + 1);
+ snprintf(out_data, out_buffer.size + 1, PROPFIND_ALL_REQUEST);
+ out_buffer.posn = 0;
+ out_buffer.buffer = out_data;
+
+ in_buffer.size = 4096;
+ in_data = xmalloc(in_buffer.size);
+ in_buffer.posn = 0;
+ in_buffer.buffer = in_data;
+
+ dav_headers = curl_slist_append(dav_headers, "Depth: 1");
+ dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
+
+ slot = get_active_slot();
+ slot->results = &results;
+ curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
+ curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
+ curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+ curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
+ curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
+ curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+ curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
+ curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
+ curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+
+ if (start_active_slot(slot)) {
+ run_active_slot(slot);
+ if (results.curl_result == CURLE_OK) {
+ ctx.name = xcalloc(10, 1);
+ ctx.len = 0;
+ ctx.cdata = NULL;
+ ctx.userFunc = handle_remote_ls_ctx;
+ ctx.userData = &ls;
+ XML_SetUserData(parser, &ctx);
+ XML_SetElementHandler(parser, xml_start_tag,
+ xml_end_tag);
+ XML_SetCharacterDataHandler(parser, xml_cdata);
+ result = XML_Parse(parser, in_buffer.buffer,
+ in_buffer.posn, 1);
+ free(ctx.name);
+
+ if (result != XML_STATUS_OK) {
+ ls.rc = error("XML error: %s",
+ XML_ErrorString(
+ XML_GetErrorCode(parser)));
+ }
+ } else {
+ ls.rc = -1;
+ }
+ } else {
+ ls.rc = error("Unable to start PROPFIND request");
+ }
+
+ free(ls.path);
+ free(url);
+ free(out_data);
+ free(in_buffer.buffer);
+ curl_slist_free_all(dav_headers);
+
+ return ls.rc;
+}
+
+static void process_ls_pack(struct remote_ls_ctx *ls)
+{
+ unsigned char sha1[20];
+
+ if (strlen(ls->dentry_name) == 63 &&
+ !strncmp(ls->dentry_name, "objects/pack/pack-", 18) &&
+ !strncmp(ls->dentry_name+58, ".pack", 5)) {
+ get_sha1_hex(ls->dentry_name + 18, sha1);
+ setup_index(ls->repo, sha1);
+ }
+}
+#endif
+
static int fetch_indices(struct alt_base *repo)
{
unsigned char sha1[20];
if (get_verbosely)
fprintf(stderr, "Getting pack list for %s\n", repo->base);
+#ifndef NO_EXPAT
+ if (remote_ls(repo, "objects/pack/", PROCESS_FILES,
+ process_ls_pack, NULL) == 0)
+ return 0;
+#endif
+
url = xmalloc(strlen(repo->base) + 21);
sprintf(url, "%s/objects/info/packs", repo->base);
{
char *commit_id;
char *url;
+ char *path;
int arg = 1;
int rc = 0;
alt->got_indices = 0;
alt->packs = NULL;
alt->next = NULL;
+ path = strstr(url, "//");
+ if (path) {
+ path = index(path+2, '/');
+ if (path)
+ alt->path_len = strlen(path);
+ }
if (pull(commit_id))
rc = 1;
top = delta_buf + delta_size;
/* make sure the orig file size matches what we expect */
- size = get_delta_hdr_size(&data);
+ size = get_delta_hdr_size(&data, top);
if (size != src_size)
return NULL;
/* now the result size */
- size = get_delta_hdr_size(&data);
+ size = get_delta_hdr_size(&data, top);
dst_buf = malloc(size + 1);
if (!dst_buf)
return NULL;
if (cmd & 0x20) cp_size |= (*data++ << 8);
if (cmd & 0x40) cp_size |= (*data++ << 16);
if (cp_size == 0) cp_size = 0x10000;
+ if (cp_off + cp_size < cp_size ||
+ cp_off + cp_size > src_size ||
+ cp_size > size)
+ goto bad;
memcpy(out, src_buf + cp_off, cp_size);
out += cp_size;
- } else {
+ size -= cp_size;
+ } else if (cmd) {
+ if (cmd > size)
+ goto bad;
memcpy(out, data, cmd);
out += cmd;
data += cmd;
+ size -= cmd;
+ } else {
+ /*
+ * cmd == 0 is reserved for future encoding
+ * extensions. In the mean time we must fail when
+ * encountering them (might be data corruption).
+ */
+ goto bad;
}
}
/* sanity check */
- if (data != top || out - dst_buf != size) {
+ if (data != top || size != 0) {
+ bad:
free(dst_buf);
return NULL;
}
- *dst_size = size;
+ *dst_size = out - dst_buf;
return dst_buf;
}
" --unpacked\n"
" --header | --pretty\n"
" --abbrev=nr | --no-abbrev\n"
+" --abbrev-commit\n"
" special purpose:\n"
" --bisect"
;
static int bisect_list = 0;
static int verbose_header = 0;
static int abbrev = DEFAULT_ABBREV;
+static int abbrev_commit = 0;
static int show_timestamp = 0;
static int hdr_termination = 0;
static const char *commit_prefix = "";
fputs(commit_prefix, stdout);
if (commit->object.flags & BOUNDARY)
putchar('-');
- fputs(sha1_to_hex(commit->object.sha1), stdout);
+ if (abbrev_commit && abbrev)
+ fputs(find_unique_abbrev(commit->object.sha1, abbrev), stdout);
+ else
+ fputs(sha1_to_hex(commit->object.sha1), stdout);
if (revs.parents) {
struct commit_list *parents = commit->parents;
while (parents) {
abbrev = 0;
continue;
}
+ if (!strcmp(arg, "--abbrev")) {
+ abbrev = DEFAULT_ABBREV;
+ continue;
+ }
+ if (!strcmp(arg, "--abbrev-commit")) {
+ abbrev_commit = 1;
+ continue;
+ }
if (!strncmp(arg, "--abbrev=", 9)) {
abbrev = strtoul(arg + 9, NULL, 10);
if (abbrev && abbrev < MINIMUM_ABBREV)
* the result size.
*/
data = delta_head;
- get_delta_hdr_size(&data); /* ignore base size */
+
+ /* ignore base size */
+ get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
/* Read the result size */
- result_size = get_delta_hdr_size(&data);
+ result_size = get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
*sizep = result_size;
}
return 0;
--- /dev/null
+#include "cache.h"
+#include "xdiff-interface.h"
+
+static int parse_num(char **cp_p, int *num_p)
+{
+ char *cp = *cp_p;
+ int num = 0;
+ int read_some;
+
+ while ('0' <= *cp && *cp <= '9')
+ num = num * 10 + *cp++ - '0';
+ if (!(read_some = cp - *cp_p))
+ return -1;
+ *cp_p = cp;
+ *num_p = num;
+ return 0;
+}
+
+int parse_hunk_header(char *line, int len,
+ int *ob, int *on,
+ int *nb, int *nn)
+{
+ char *cp;
+ cp = line + 4;
+ if (parse_num(&cp, ob)) {
+ bad_line:
+ return error("malformed diff output: %s", line);
+ }
+ if (*cp == ',') {
+ cp++;
+ if (parse_num(&cp, on))
+ goto bad_line;
+ }
+ else
+ *on = 1;
+ if (*cp++ != ' ' || *cp++ != '+')
+ goto bad_line;
+ if (parse_num(&cp, nb))
+ goto bad_line;
+ if (*cp == ',') {
+ cp++;
+ if (parse_num(&cp, nn))
+ goto bad_line;
+ }
+ else
+ *nn = 1;
+ return -!!memcmp(cp, " @@", 3);
+}
+
+static void consume_one(void *priv_, char *s, unsigned long size)
+{
+ struct xdiff_emit_state *priv = priv_;
+ char *ep;
+ while (size) {
+ unsigned long this_size;
+ ep = memchr(s, '\n', size);
+ this_size = (ep == NULL) ? size : (ep - s + 1);
+ priv->consume(priv, s, this_size);
+ size -= this_size;
+ s += this_size;
+ }
+}
+
+int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+{
+ struct xdiff_emit_state *priv = priv_;
+ int i;
+
+ for (i = 0; i < nbuf; i++) {
+ if (mb[i].ptr[mb[i].size-1] != '\n') {
+ /* Incomplete line */
+ priv->remainder = realloc(priv->remainder,
+ priv->remainder_size +
+ mb[i].size);
+ memcpy(priv->remainder + priv->remainder_size,
+ mb[i].ptr, mb[i].size);
+ priv->remainder_size += mb[i].size;
+ continue;
+ }
+
+ /* we have a complete line */
+ if (!priv->remainder) {
+ consume_one(priv, mb[i].ptr, mb[i].size);
+ continue;
+ }
+ priv->remainder = realloc(priv->remainder,
+ priv->remainder_size +
+ mb[i].size);
+ memcpy(priv->remainder + priv->remainder_size,
+ mb[i].ptr, mb[i].size);
+ consume_one(priv, priv->remainder,
+ priv->remainder_size + mb[i].size);
+ free(priv->remainder);
+ priv->remainder = NULL;
+ priv->remainder_size = 0;
+ }
+ if (priv->remainder) {
+ consume_one(priv, priv->remainder, priv->remainder_size);
+ free(priv->remainder);
+ priv->remainder = NULL;
+ priv->remainder_size = 0;
+ }
+ return 0;
+}
--- /dev/null
+#ifndef XDIFF_INTERFACE_H
+#define XDIFF_INTERFACE_H
+
+#include "xdiff/xdiff.h"
+
+struct xdiff_emit_state;
+
+typedef void (*xdiff_emit_consume_fn)(void *, char *, unsigned long);
+
+struct xdiff_emit_state {
+ xdiff_emit_consume_fn consume;
+ char *remainder;
+ unsigned long remainder_size;
+};
+
+int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf);
+int parse_hunk_header(char *line, int len,
+ int *ob, int *on,
+ int *nb, int *nn);
+
+#endif