Git development
 help / color / mirror / Atom feed
* Re: Looking for SCM that lets me publish part of a repository
From: Sam Vilain @ 2006-03-06 20:58 UTC (permalink / raw)
  To: Matt McCutchen; +Cc: git
In-Reply-To: <1141608679.16980.12.camel@mattlaptop>

Matt McCutchen wrote:

>Dear GIT people,
>
>For the last week or so, I have been looking for a SCM system to hold
>many of my projects, some of which are available to the public and
>others of which are not.  It would be nice if I could use a single large
>private repository on my computer with each project in a separate
>folder.  Then I would like to pull some of the projects (but not all)
>into a world-readable repository on my Web site.  I have looked at
>several SCMs and have not found a way to make any of them do this, but I
>like GIT best on other grounds.  Is there a way I can coerce GIT to
>clone and pull one folder out of a repository but ignore the rest?
>  
>

SVK works like this, you get one (by default) repository in your ~ which
you then mirror published projects to, and it tracks local changes as a
branch which you can then commit back 'upstream' (or to your published
repository) with.

Jeff King also recently posted a script to extract out a part of a
repository into another one, which is a related concept.

Sam.

^ permalink raw reply

* Re: any problems with new branch of gitk?
From: Alex Riesen @ 2006-03-06 20:23 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: git
In-Reply-To: <17419.24973.402408.237865@cargo.ozlabs.ibm.com>

[-- Attachment #1: Type: text/plain, Size: 610 bytes --]

Paul Mackerras, Sun, Mar 05, 2006 23:09:17 +0100:
> I'm planning to pull the `new' branch of the gitk repository into the
> master branch, making the new version of gitk the standard version
> that will go into the git.git repository.  As far as I know the new
> version does everything the old version does.  Does anyone know of any
> problems with the new gitk that weren't in the old one?

I don't if that was intended (see the screenshot attached), but some
lines miss arrows. Click on where the arrow would be works (jump to
counterpart arrow). Never have this with the old gitk.

Tcl/Tk 8.4.9 (Gentoo).


[-- Attachment #2: no-arrows.jpg --]
[-- Type: image/jpeg, Size: 21201 bytes --]

[-- Attachment #3: no-arrows2.jpg --]
[-- Type: image/jpeg, Size: 14369 bytes --]

^ permalink raw reply

* Re: [PATCH] git-blame: Make the output human readable
From: linux @ 2006-03-06 19:33 UTC (permalink / raw)
  To: junkio; +Cc: git

Well, getting 15 characters in UTF-8 is easy (just stop before the 16th
byte for which ((b & 0xc0) != 0x80)), but what about combining characters?

You've got accents and stuff to worry about.  And the annoying fact that
Unicode defined accents as suffixes, so you have to go past the 15th
column to include all of the 

And then there's that fact that many characters are traditionally
represented as double-wide forms, even on character terminals.

See http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c for details
an an example implementation of wcwidth().

Using that, it would be something like (compiles but untested):

/*
 * Return the number of bytes from the nul-terminated utf8 string
 * that can be printed in at most max columns using a monospaced
 * font.  *actual returns the number of columns actually occupied,
 * which may be less than max.
 *
 * Output is truncated before any control characters or illegal
 * UTF-8 sequences.
 */
unsigned
fit_columns(char const *utf8, unsigned max, unsigned *actual)
{
	char const * const origin = utf8;
	unsigned width = 0;
	unsigned pos = 0;
	unsigned c;

	for (;;) {
		unsigned w;
		unsigned c = *utf8++;

		/* Part 1: Parse the next Unicode code point */
		if (c < 0x20) {
			break;	/* Control character - stop */
		} else if (c < 0x7F) {
			w = 1;	/* Standard ASCII */
		} else if (c < 0xC2 || c > 0xF4) {
			break;	/* DEL or illegal Unicode */
		} else {
			/* Multi-byte UTF-8 sequence */
			unsigned n;
			unsigned char byte = *utf8++;

			if (c < 0xE0) {
				/* 2-byte sequence: U+0080..U+07FF */
				n = 1;
				c &= 0x1F;
			} else if (c < 0xF0) {
				/* 3-byte sequence: U+0800..U+FFFF */
				if (c == 0xE0 && byte < 0xA0)
					break;	/* < /U+0800 */
				n = 2;
				c &= 0x0F;
			} else {
				/* 4-byte sequence: U+10000..U+10FFFF */
				if (byte < 0x90 ? c == 0xF0 : c == 0xF4)
					break; /* < 10000 or > 10FFFF */
				n = 3;
				c &= 0x07;
			}

			for (; n--; byte = *utf8++) {
				if (byte & 0xc0 != 0x80)
					goto done;	/* Double break */
				c = (c << 6) | (byte & 0x3f);
			}
			/* Now find the width of it */
			w = wcwidth(c);
			if (w == -1)
				break;
		}

		/* Part 2: Figure out if it will fit */
		if (width + w > max)
			break;	/* Would exceed space - stop */
		/* Part 3: It fits; update our statistics */
		width += w;
		pos = (unsigned)(utf8 - origin);
	}

done:
	if (actual)
		*actual = width;
	return pos;
}

^ permalink raw reply

* Pulling tags from git.git
From: David Ho @ 2006-03-06 18:44 UTC (permalink / raw)
  To: git

Hi,

I have been trying to pull from the git repo via rsync. 
(rsync://rsync.kernel.org/pub/scm/git/git.git)  I got all the commits
up to today but the tags since my initial cloning are missing.

I tried git pull --tags, I still only have old tags.
I switched to using the git protocol but that failed too.

git ls-remotes --tags showed, as expected, all the tags up to 1.2.4.

Is there a switch I missed?

Regards,
David

^ permalink raw reply

* Re: Pulling tags from git.git
From: David Ho @ 2006-03-06 18:54 UTC (permalink / raw)
  To: git
In-Reply-To: <4dd15d180603061044h3f70d48bk8006c15e605fdca1@mail.gmail.com>

Okay sorry, git fetch --tags did the trick.

Just out of curiosity, should git pull --tags behave similarly since
the difference is git pull does a merge after a fetch?

David

On 3/6/06, David Ho <davidkwho@gmail.com> wrote:
> Hi,
>
> I have been trying to pull from the git repo via rsync.
> (rsync://rsync.kernel.org/pub/scm/git/git.git)  I got all the commits
> up to today but the tags since my initial cloning are missing.
>
> I tried git pull --tags, I still only have old tags.
> I switched to using the git protocol but that failed too.
>
> git ls-remotes --tags showed, as expected, all the tags up to 1.2.4.
>
> Is there a switch I missed?
>
> Regards,
> David
>

^ permalink raw reply

* Re: git-status too verbose?
From: Shawn Pearce @ 2006-03-06 17:56 UTC (permalink / raw)
  To: Carl Worth; +Cc: Eric Jaffe, git
In-Reply-To: <87irqrzcs7.wl%cworth@cworth.org>

Carl Worth <cworth@cworth.org> wrote:
> On Sat, 4 Mar 2006 12:52:17 -0500, "Eric Jaffe" wrote:
> > I was wondering if anyone else thinks that git-status should be more
> > like "git-diff --name-status". That is,
> >   # A a/newfile.c
> >   # M a/oldfile.c
> 
> Something like that does seem appealing.
> 
> There are at least two issues with doing it:
> 
> 1) It might be tricky coming up with canonical single characters to be
>    used consistently within git. For example, git-ls-files currently
>    does do some single-character state indication, but it can be
>    rather confusing at times. For example:
> 
> 	State		Option	Character
> 	-----		------	---------
> 	Modified	-m	C
> 	Unmerged	-u	M
> 	Cached		-c	H
> 
>    And that looks like a permanent problem. For legacy reasons,
>    I don't think we can change either the options or the output
>    characters of git-ls-files. But perhaps we could at least
>    agree on a single, consistent mapping for all future uses.
> 
> 2) In an important sense, git-status is not verbose enough. For
>    example, given a single line such as the following:
> 
> 	modified: some-file
> 
>    This could indicate at least two different states for some-file:
> 
> 	1) Modified and updated into the index
> 
> 	2) Modified in working tree, but not updated in the index

I've played around with this idea a little bit in pg's pg-status
command but I most likely do not have all cases covered.

In general I try to show HEAD<-->index first using uppercase letters
then index<-->working directory second in lowercase letters.

Here's the critical portion of pg-status:

	git-diff-index --cached --name-status HEAD | sed -e 's/ / /'
	if test $index_only  = n
	then
	  git-diff-files --name-status | sed \
		-e 's/      / /' \
		-e 's/^D /g /' \
		-e 's/^M /m /' \
		-e '/^U /d'
	  pg--ls-others | sed 's/^/x /'
	fi

Thus far I've found it useful to behave this way and I haven't run
up against any states which didn't make immediate sense to me.
Here's the documentation I have in pg-status describing what it
can show:

Status indicators (displayed in column 1):

 A : New file has been marked for addition with pg-add.
 D : Existing file has been marked as deleted with pg-rm.
 M : Existing file has been modified (and is known to the index).
 U : File still has unmerged hunks, see pg-resolved.

 m : Existing file (maybe) has been modified (use -q to know for sure).
 g : File has been removed from directory but not marked with pg-rm.
 x : File is not known to repository and isn't being ignored.

-- 
Shawn.

^ permalink raw reply

* Re: git-status too verbose?
From: Carl Worth @ 2006-03-06 17:46 UTC (permalink / raw)
  To: Eric Jaffe; +Cc: git
In-Reply-To: <38b80e980603040952j15152a21h2c903bd011d7e905@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 2039 bytes --]

On Sat, 4 Mar 2006 12:52:17 -0500, "Eric Jaffe" wrote:
> I was wondering if anyone else thinks that git-status should be more
> like "git-diff --name-status". That is,
>   # A a/newfile.c
>   # M a/oldfile.c

Something like that does seem appealing.

There are at least two issues with doing it:

1) It might be tricky coming up with canonical single characters to be
   used consistently within git. For example, git-ls-files currently
   does do some single-character state indication, but it can be
   rather confusing at times. For example:

	State		Option	Character
	-----		------	---------
	Modified	-m	C
	Unmerged	-u	M
	Cached		-c	H

   And that looks like a permanent problem. For legacy reasons,
   I don't think we can change either the options or the output
   characters of git-ls-files. But perhaps we could at least
   agree on a single, consistent mapping for all future uses.

2) In an important sense, git-status is not verbose enough. For
   example, given a single line such as the following:

	modified: some-file

   This could indicate at least two different states for some-file:

	1) Modified and updated into the index

	2) Modified in working tree, but not updated in the index

   Currently, git-status makes this distinction only in the header
   lines for the separate chunks of its output. But, when there are a
   lot of files involved, and things start scrolling, it's sometimes
   "hard" to associate the right header with the file of interest.

   So, what I've wanted from git-status is a complete encoding of the
   file's state on the same line as the output of the filename.  Maybe
   something that uses two characters per file would work well.

   But I don't have a concrete suggestion for that---I don't think
   I've even successfully enumerated all possible file states with git
   yet...

-Carl

PS. If we do tighten up the output of git-status, I'd also vote for
making the per-chunk headers use only 1 line each instead of 2, and
also eliminating the second blank line separating each chunk.

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply

* Any news on an Eclipse plugin?
From: Shawn Pearce @ 2006-03-06 17:32 UTC (permalink / raw)
  To: git

I'm considering starting an Eclipse plugin for GIT.  So I did
some digging to see if any existing projects have anything I can
contribute to (rather than starting from scratch) but I only turned
up a thread from May 2005 started by Jon Seymour:

  http://www.gelato.unsw.edu.au/archives/git/0505/3357.html

anyone know anything newer?  If there's nothing existing worth
starting with other than the CVS or SVN plugins (as mentioned in
this thread) I'll probably start putting together a prototype and
start making it available early next week.

I'm going to carefully read the thread that I linked to above before
writing anything, so there's no need to start getting into the core
and not so core discussion again.  I'll read it over for myself. :-)

-- 
Shawn.

^ permalink raw reply

* Re: cvsimport woes
From: Rajkumar S @ 2006-03-06 16:52 UTC (permalink / raw)
  To: git
In-Reply-To: <46a038f90603060137o758ea7ch6c40652ad86a102a@mail.gmail.com>

Martin Langhoff wrote:
> On 3/6/06, Martin Langhoff <martin.langhoff@gmail.com> wrote:
>> you don't seem to be making any silly mistake. Make sure you are using
>> a recent git, and a recent cvsps. Actually you want the _latest_ cvsps
>> (2.1 I think).
> 
> Scratch this bit, naturally. I wasn't 100% paying attention. Still,
> the rest of the answer should kinda/sorta make sense.

Thanks for your kind replies.

I was also talking to Matthias Urlichs (the author of cvsimport) What he 
says is that the first version is still checked out because
the import does not do the fast-forward/merge by itself.

I am wondering how can I do the fast-forward. I had a long chat in irc 
but did not find any solution to this particular issue. I tried git 
merge, but that does not work as the command needs more arguments. (I am 
a git newbie and git concepts are still bit fuzzy for me)

I would appreciate a lot if some one can point me in the right direction.

If you are not following this thread, the problem I am talking about is 
that when I do a cvsimport for a second time (ie do a git cvsimport; 
update the cvs; do a cvsimport again) the second updates are not visible 
in the current directory, though they are present in the git database.

You can use this script to reproduce this problem.

export CVSROOT=/home/raj/cvsroot
rm -rf cvsroot/ git/ src/ /home/raj/.cvsps
mkdir cvsroot  git src
cvs init
cd src/
echo "Line one" > file.txt
echo "Line one" > file1.txt
echo "Line one" > file2.txt
cvs import -m "Imported sources" src start realstart
cd ..
rm -rf src/
cvs co src
git cvsimport -v -k -u -m -d $CVSROOT -C git/  src
cd git
git status
cd ..
cd src/
echo "Line two" >> file.txt
echo "Line two" >> file1.txt
cvs commit -m "v2.0"
cd ..
git cvsimport -v -k -u -m -d $CVSROOT -C git/  src
cd git
git status
cd ..

Now the cvs version of file.txt and git version are different.

Thanks and regards,

raj

^ permalink raw reply

* Re: [PATCH] annotate: Support annotation of files on other revisions.
From: Ryan Anderson @ 2006-03-06 15:44 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git
In-Reply-To: <Pine.LNX.4.63.0603061016320.1422@wbgn013.biozentrum.uni-wuerzburg.de>

[-- Attachment #1: Type: text/plain, Size: 390 bytes --]

Johannes Schindelin wrote:

>Hi,
>
>On Sun, 5 Mar 2006, Ryan Anderson wrote:
>
>  
>
>>+use Data::Dumper;
>>    
>>
>
>You really need this?
>  
>

I keep adding it back in when debugging things, and then promptly forget
to remove it when I cull the debugging back out.

It's a core module, so it shouldn't be a significant issue for any porting

-- 

Ryan Anderson
  sometimes Pug Majere


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 256 bytes --]

^ permalink raw reply

* PATCH: Allow format-patch to attach patches
From: Mike McCormack @ 2006-03-06 13:12 UTC (permalink / raw)
  To: git

[-- Attachment #1: Type: text/plain, Size: 280 bytes --]


The --attach patch to git-format-patch to attach patches instead of 
inlining them.  Some mailers linewrap inlined patches (eg. Mozilla).

---

  git-format-patch.sh |   52 
++++++++++++++++++++++++++++++++++++++++++++-------
  1 files changed, 45 insertions(+), 7 deletions(-)


[-- Attachment #2: b17ef6b06ebaaf9b6d1f47c6a788cffd77e2b927.diff --]
[-- Type: text/x-patch, Size: 3182 bytes --]

b17ef6b06ebaaf9b6d1f47c6a788cffd77e2b927
diff --git a/git-format-patch.sh b/git-format-patch.sh
index bbd2e55..2ebf7e8 100755
--- a/git-format-patch.sh
+++ b/git-format-patch.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Junio C Hamano
 #
 
-USAGE='[-n | -k] [-o <dir> | --stdout] [--signoff] [--check] [--diff-options] <his> [<mine>]'
+USAGE='[-n | -k] [-o <dir> | --stdout] [--signoff] [--check] [--diff-options] [--attach] <his> [<mine>]'
 LONG_USAGE='Prepare each commit with its patch since <mine> head forked from
 <his> head, one file per patch formatted to resemble UNIX mailbox
 format, for e-mail submission or use with git-am.
@@ -18,7 +18,9 @@ is ignored if --stdout is specified.
 
 When -n is specified, instead of "[PATCH] Subject", the first
 line is formatted as "[PATCH N/M] Subject", unless you have only
-one patch.'
+one patch.
+
+When --attach is specified, patches are attached, not inlined.'
 
 . git-sh-setup
 
@@ -40,6 +42,8 @@ do
     -d|--d|--da|--dat|--date|\
     -m|--m|--mb|--mbo|--mbox) # now noop
     ;;
+    --at|--att|--atta|--attac|--attach)
+    attach=t ;;
     -k|--k|--ke|--kee|--keep|--keep-|--keep-s|--keep-su|--keep-sub|\
     --keep-subj|--keep-subje|--keep-subjec|--keep-subject)
     keep_subject=t ;;
@@ -150,6 +154,11 @@ done >$series
 
 me=`git-var GIT_AUTHOR_IDENT | sed -e 's/>.*/>/'`
 headers=`git-repo-config --get format.headers`
+case "$attach" in
+"") ;;
+*)
+	mimemagic="050802040500080604070107"
+esac
 
 case "$outdir" in
 */) ;;
@@ -174,7 +183,7 @@ titleScript='
 
 process_one () {
 	perl -w -e '
-my ($keep_subject, $num, $signoff, $headers, $commsg) = @ARGV;
+my ($keep_subject, $num, $signoff, $headers, $mimemagic, $commsg) = @ARGV;
 my ($signoff_pattern, $done_header, $done_subject, $done_separator, $signoff_seen,
     $last_was_signoff);
 
@@ -229,6 +238,16 @@ while (<FH>) {
 	    print "$headers\n";
 	}
         print "Subject: $_";
+	if ($mimemagic) {
+	    print "MIME-Version: 1.0\n";
+	    print "Content-Type: multipart/mixed;\n";
+	    print " boundary=\"------------$mimemagic\"\n";
+	    print "\n";
+	    print "This is a multi-part message in MIME format.\n";
+	    print "--------------$mimemagic\n";
+	    print "Content-Type: text/plain; charset=UTF-8; format=fixed\n";
+	    print "Content-Transfer-Encoding: 8bit\n";
+	}
 	$done_subject = 1;
 	next;
     }
@@ -254,14 +273,33 @@ if (!$signoff_seen && $signoff ne "") {
 }
 print "\n---\n\n";
 close FH or die "close $commsg pipe";
-' "$keep_subject" "$num" "$signoff" "$headers" $commsg
+' "$keep_subject" "$num" "$signoff" "$headers" "$mimemagic" $commsg
 
 	git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
 	echo
+	case "$mimemagic" in
+	'');;
+	*)
+		echo "--------------$mimemagic"
+		echo "Content-Type: text/x-patch;"
+		echo " name=\"$commit.diff\""
+		echo "Content-Transfer-Encoding: 8bit"
+		echo "Content-Disposition: inline;"
+		echo " filename=\"$commit.diff\""
+		echo
+	esac
 	git-diff-tree -p $diff_opts "$commit"
-	echo "-- "
-	echo "@@GIT_VERSION@@"
-
+	case "$mimemagic" in
+	'')
+		echo "-- "
+		echo "@@GIT_VERSION@@"
+		;;
+	*)
+		echo
+		echo "--------------$mimemagic--"
+		echo
+		;;
+	esac
 	echo
 }
 


^ permalink raw reply related

* [PATCH] Allow adding arbitary lines in the mail header generated by format-patch.
From: Mike McCormack @ 2006-03-06 13:10 UTC (permalink / raw)
  To: git

[-- Attachment #1: Type: text/plain, Size: 252 bytes --]

Entries may be added to the config file as follows:

[format]
         headers = "Organization: CodeWeavers\nTo: wine-patches 
<wine-patches@winehq.org>\n"

---

  git-format-patch.sh |    8 ++++++--
  1 files changed, 6 insertions(+), 2 deletions(-)


[-- Attachment #2: cdd5602230adecce4a7dd274b4f81d52681410cb.diff --]
[-- Type: text/x-patch, Size: 1147 bytes --]

cdd5602230adecce4a7dd274b4f81d52681410cb
diff --git a/git-format-patch.sh b/git-format-patch.sh
index 2bd2639..bbd2e55 100755
--- a/git-format-patch.sh
+++ b/git-format-patch.sh
@@ -149,6 +149,7 @@ do
 done >$series
 
 me=`git-var GIT_AUTHOR_IDENT | sed -e 's/>.*/>/'`
+headers=`git-repo-config --get format.headers`
 
 case "$outdir" in
 */) ;;
@@ -173,7 +174,7 @@ titleScript='
 
 process_one () {
 	perl -w -e '
-my ($keep_subject, $num, $signoff, $commsg) = @ARGV;
+my ($keep_subject, $num, $signoff, $headers, $commsg) = @ARGV;
 my ($signoff_pattern, $done_header, $done_subject, $done_separator, $signoff_seen,
     $last_was_signoff);
 
@@ -224,6 +225,9 @@ while (<FH>) {
 	    s/^\[PATCH[^]]*\]\s*//;
 	    s/^/[PATCH$num] /;
 	}
+	if ($headers) {
+	    print "$headers\n";
+	}
         print "Subject: $_";
 	$done_subject = 1;
 	next;
@@ -250,7 +254,7 @@ if (!$signoff_seen && $signoff ne "") {
 }
 print "\n---\n\n";
 close FH or die "close $commsg pipe";
-' "$keep_subject" "$num" "$signoff" $commsg
+' "$keep_subject" "$num" "$signoff" "$headers" $commsg
 
 	git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
 	echo


^ permalink raw reply related

* [PATCH] Add git-imap-send.
From: Mike McCormack @ 2006-03-06 13:09 UTC (permalink / raw)
  To: git

[-- Attachment #1: Type: text/plain, Size: 848 bytes --]


This probably needs a bit more work, but I'll solicit comments and 
flames anyway...



git-imap-send drops a patch series generated by git-format-patch into an 
IMAP folder. This allows patch submitters to send patches through their 
own mail program.

git-imap-send uses the following values from the GIT repository 
configuration:

The target IMAP folder:

[imap]
         Folder = "INBOX.Drafts"

A command to open an ssh tunnel to the imap mail server.

[imap]
         Tunnel = "ssh -q user@imap.server.com /usr/bin/imapd ./Maildir 
2> /dev/null"

OR

[imap]
	Host = imap.server.com
	User = bob
	Password = pwd
	Port = 143

---

  .gitignore  |    1
  Makefile    |    4
  imap-send.c | 1935 
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3 files changed, 1939 insertions(+), 1 deletions(-)
  create mode 100644 imap-send.c


[-- Attachment #2: b8fbeb40b92b9b6048c9f7ad66a10d8222c58b76.diff --]
[-- Type: text/x-patch, Size: 46777 bytes --]

b8fbeb40b92b9b6048c9f7ad66a10d8222c58b76
diff --git a/.gitignore b/.gitignore
index abbc509..c567cc0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,6 +42,7 @@ git-grep
 git-hash-object
 git-http-fetch
 git-http-push
+git-imap-send
 git-index-pack
 git-init-db
 git-local-fetch
diff --git a/Makefile b/Makefile
index b6d8804..bc49f06 100644
--- a/Makefile
+++ b/Makefile
@@ -165,7 +165,7 @@ PROGRAMS = \
 	git-upload-pack$X git-verify-pack$X git-write-tree$X \
 	git-update-ref$X git-symbolic-ref$X git-check-ref-format$X \
 	git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
-	git-describe$X git-merge-tree$X git-blame$X
+	git-describe$X git-merge-tree$X git-blame$X git-imap-send$X
 
 # what 'all' will build and 'install' will install, in gitexecdir
 ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
@@ -522,6 +522,8 @@ git-ssh-upload$X: rsh.o
 git-ssh-pull$X: rsh.o fetch.o
 git-ssh-push$X: rsh.o
 
+git-imap-send$X: imap-send.o $(LIB_FILE)
+
 git-http-fetch$X: fetch.o http.o http-fetch.o $(LIB_FILE)
 	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(LIBS) $(CURL_LIBCURL)
diff --git a/imap-send.c b/imap-send.c
new file mode 100644
index 0000000..b6e5151
--- /dev/null
+++ b/imap-send.c
@@ -0,0 +1,1935 @@
+/*
+ * git-imap-send - drops patches into an imap Drafts folder
+ *
+ *  derived from isync/mbsync - mailbox synchronizer
+ * Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
+ * Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
+ * Copyright (C) 2004 Theodore Y. Ts'o <tytso@mit.edu>
+ * Copyright (C) 2006 Mike McCormack
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * As a special exception, mbsync may be linked with the OpenSSL library,
+ * despite that library's more restrictive license.
+ */
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <assert.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#if HAVE_LIBSSL
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# include <openssl/hmac.h>
+#endif
+#include <pwd.h>
+
+#include "cache.h"
+
+#define as(ar) (sizeof(ar)/sizeof(ar[0]))
+
+typedef struct store_conf {
+	char *name;
+	const char *path; /* should this be here? its interpretation is driver-specific */
+	char *map_inbox;
+	char *trash;
+	unsigned max_size; /* off_t is overkill */
+	unsigned trash_remote_new:1, trash_only_new:1;
+} store_conf_t;
+
+typedef struct string_list {
+	struct string_list *next;
+	char string[1];
+} string_list_t;
+
+typedef struct channel_conf {
+	struct channel_conf *next;
+	char *name;
+	store_conf_t *master, *slave;
+	char *master_name, *slave_name;
+	char *sync_state;
+	string_list_t *patterns;
+	int mops, sops;
+	unsigned max_messages; /* for slave only */
+} channel_conf_t;
+
+typedef struct group_conf {
+	struct group_conf *next;
+	char *name;
+	string_list_t *channels;
+} group_conf_t;
+
+/* For message->status */
+#define M_RECENT       (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */
+#define M_DEAD         (1<<1) /* expunged */
+#define M_FLAGS        (1<<2) /* flags fetched */
+#define M_PROCESSED    (1<<3) /* registered in pair */
+#define M_NOT_SYNCED   (1<<4) /* not in remote mailbox, yet */
+#define M_EXPIRED      (1<<5) /* kicked out by MaxMessages */
+
+typedef struct message {
+	struct message *next;
+	/* string_list_t *keywords; */
+	size_t size; /* zero implies "not fetched" */
+	int uid;
+	unsigned char flags, status;
+} message_t;
+
+/* For opts, both in store and driver_t->select() */
+#define OPEN_OLD        (1<<0)
+#define OPEN_NEW        (1<<1)
+#define OPEN_FLAGS      (1<<2)
+#define OPEN_SIZE       (1<<3)
+#define OPEN_CREATE     (1<<4)
+#define OPEN_EXPUNGE    (1<<5)
+#define OPEN_SETFLAGS   (1<<6)
+#define OPEN_APPEND     (1<<7)
+
+typedef struct store {
+	store_conf_t *conf; /* foreign */
+
+	/* currently open mailbox */
+	const char *name; /* foreign! maybe preset? */
+	char *path; /* own */
+	message_t *msgs; /* own */
+	int uidvalidity;
+	unsigned char opts; /* maybe preset? */
+	/* note that the following do _not_ reflect stats from msgs, but mailbox totals */
+	int count; /* # of messages */
+	int recent; /* # of recent messages - don't trust this beyond the initial read */
+} store_t;
+
+typedef struct {
+	char *data;
+	int len;
+	unsigned char flags;
+	unsigned char crlf:1;
+} msg_data_t;
+
+#define DRV_OK          0
+#define DRV_MSG_BAD     -1
+#define DRV_BOX_BAD     -2
+#define DRV_STORE_BAD   -3
+
+static int Verbose, Quiet;
+
+static void info( const char *, ... );
+static void warn( const char *, ... );
+
+static char *next_arg( char ** );
+
+static void free_generic_messages( message_t * );
+
+static int nfvasprintf( char **str, const char *fmt, va_list va );
+static int nfsnprintf( char *buf, int blen, const char *fmt, ... );
+
+
+static void arc4_init( void );
+static unsigned char arc4_getbyte( void );
+
+typedef struct imap_server_conf {
+	char *name;
+	char *tunnel;
+	char *host;
+	int port;
+	char *user;
+	char *pass;
+#if HAVE_LIBSSL
+	char *cert_file;
+	unsigned use_imaps:1;
+	unsigned require_ssl:1;
+	unsigned use_sslv2:1;
+	unsigned use_sslv3:1;
+	unsigned use_tlsv1:1;
+	unsigned require_cram:1;
+#endif
+} imap_server_conf_t;
+
+typedef struct imap_store_conf {
+	store_conf_t gen;
+	imap_server_conf_t *server;
+	unsigned use_namespace:1;
+} imap_store_conf_t;
+
+typedef struct imap_message {
+	message_t gen;
+/*	int seq; will be needed when expunges are tracked */
+} imap_message_t;
+
+#define NIL	(void*)0x1
+#define LIST	(void*)0x2
+
+typedef struct _list {
+	struct _list *next, *child;
+	char *val;
+	int len;
+} list_t;
+
+typedef struct {
+	int fd;
+#if HAVE_LIBSSL
+	SSL *ssl;
+	unsigned int use_ssl:1;
+#endif
+} Socket_t;
+
+typedef struct {
+	Socket_t sock;
+	int bytes;
+	int offset;
+	char buf[1024];
+} buffer_t;
+
+struct imap_cmd;
+#define max_in_progress 50 /* make this configurable? */
+
+typedef struct imap {
+	int uidnext; /* from SELECT responses */
+	list_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
+	string_list_t *boxes; /* LIST results */
+	message_t **msgapp; /* FETCH results */
+	unsigned caps, rcaps; /* CAPABILITY results */
+	/* command queue */
+	int nexttag, num_in_progress, literal_pending;
+	struct imap_cmd *in_progress, **in_progress_append;
+#if HAVE_LIBSSL
+	SSL_CTX *SSLContext;
+#endif
+	buffer_t buf; /* this is BIG, so put it last */
+} imap_t;
+
+typedef struct imap_store {
+	store_t gen;
+	int uidvalidity;
+	imap_t *imap;
+	const char *prefix;
+	unsigned /*currentnc:1,*/ trashnc:1;
+} imap_store_t;
+
+struct imap_cmd_cb {
+	int (*cont)( imap_store_t *ctx, struct imap_cmd *cmd, const char *prompt );
+	void (*done)( imap_store_t *ctx, struct imap_cmd *cmd, int response);
+	void *ctx;
+	char *data;
+	int dlen;
+	int uid;
+	unsigned create:1, trycreate:1;
+};
+
+struct imap_cmd {
+	struct imap_cmd *next;
+	struct imap_cmd_cb cb;
+	char *cmd;
+	int tag;
+};
+
+#define CAP(cap) (imap->caps & (1 << (cap)))
+
+enum CAPABILITY {
+	NOLOGIN = 0,
+	UIDPLUS,
+	LITERALPLUS,
+	NAMESPACE,
+#if HAVE_LIBSSL
+	CRAM,
+	STARTTLS,
+#endif
+};
+
+static const char *cap_list[] = {
+	"LOGINDISABLED",
+	"UIDPLUS",
+	"LITERAL+",
+	"NAMESPACE",
+#if HAVE_LIBSSL
+	"AUTH=CRAM-MD5",
+	"STARTTLS",
+#endif
+};
+
+#define RESP_OK    0
+#define RESP_NO    1
+#define RESP_BAD   2
+
+static int get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd );
+
+
+static const char *Flags[] = {
+	"Draft",
+	"Flagged",
+	"Answered",
+	"Seen",
+	"Deleted",
+};
+
+#if HAVE_LIBSSL
+
+/* this gets called when a certificate is to be verified */
+static int
+verify_cert( SSL *ssl )
+{
+	X509 *cert;
+	int err;
+	char buf[256];
+	int ret = -1;
+	BIO *bio;
+
+	cert = SSL_get_peer_certificate( ssl );
+	if (!cert) {
+		fprintf( stderr, "Error, no server certificate\n" );
+		return -1;
+	}
+
+	err = SSL_get_verify_result( ssl );
+	if (err == X509_V_OK)
+		return 0;
+
+	fprintf( stderr, "Error, can't verify certificate: %s (%d)\n",
+	         X509_verify_cert_error_string(err), err );
+
+	X509_NAME_oneline( X509_get_subject_name( cert ), buf, sizeof(buf) );
+	info( "\nSubject: %s\n", buf );
+	X509_NAME_oneline( X509_get_issuer_name( cert ), buf, sizeof(buf) );
+	info( "Issuer:  %s\n", buf );
+	bio = BIO_new( BIO_s_mem() );
+	ASN1_TIME_print( bio, X509_get_notBefore( cert ) );
+	memset( buf, 0, sizeof(buf) );
+	BIO_read( bio, buf, sizeof(buf) - 1 );
+	info( "Valid from: %s\n", buf );
+	ASN1_TIME_print( bio, X509_get_notAfter( cert ) );
+	memset( buf, 0, sizeof(buf) );
+	BIO_read( bio, buf, sizeof(buf) - 1 );
+	BIO_free( bio );
+	info( "      to:   %s\n", buf );
+
+	fputs( "\n*** WARNING ***  There is no way to verify this certificate.  It is\n"
+	       "                 possible that a hostile attacker has replaced the\n"
+	       "                 server certificate.  Continue at your own risk!\n"
+	       "\nAccept this certificate anyway? [no]: ",  stderr );
+	if (fgets( buf, sizeof(buf), stdin ) && (buf[0] == 'y' || buf[0] == 'Y')) {
+		ret = 0;
+		fprintf( stderr, "\n*** Fine, but don't say I didn't warn you!\n\n" );
+	}
+	return ret;
+}
+
+static int
+init_ssl_ctx( imap_store_t *ctx )
+{
+	imap_t *imap = ctx->imap;
+	imap_store_conf_t *conf = (imap_store_conf_t *)ctx->gen.conf;
+	imap_server_conf_t *srvc = conf->server;
+	SSL_METHOD *method;
+	int options = 0;
+
+	if (srvc->use_tlsv1 && !srvc->use_sslv2 && !srvc->use_sslv3)
+		method = TLSv1_client_method();
+	else
+		method = SSLv23_client_method();
+	imap->SSLContext = SSL_CTX_new( method );
+
+	if (!srvc->cert_file) {
+		fprintf( stderr, "Error, CertificateFile not defined\n" );
+		return -1;
+	} else if (access( srvc->cert_file, R_OK ))
+		warn( "*** Warning: can't read CertificateFile, so can't verify server certificates\n" );
+	else if (!SSL_CTX_load_verify_locations( imap->SSLContext, srvc->cert_file, NULL )) {
+		fprintf( stderr, "Error, SSL_CTX_load_verify_locations: %s\n",
+		         ERR_error_string( ERR_get_error(), 0 ) );
+		return -1;
+	}
+
+	if (!srvc->use_sslv2)
+		options |= SSL_OP_NO_SSLv2;
+	if (!srvc->use_sslv3)
+		options |= SSL_OP_NO_SSLv3;
+	if (!srvc->use_tlsv1)
+		options |= SSL_OP_NO_TLSv1;
+
+	SSL_CTX_set_options( imap->SSLContext, options );
+
+	/* we check the result of the verification after SSL_connect() */
+	SSL_CTX_set_verify( imap->SSLContext, SSL_VERIFY_NONE, 0 );
+	return 0;
+}
+#endif /* HAVE_LIBSSL */
+
+static void
+socket_perror( const char *func, Socket_t *sock, int ret )
+{
+#if HAVE_LIBSSL
+	int err;
+
+	if (sock->use_ssl) {
+		switch ((err = SSL_get_error( sock->ssl, ret ))) {
+		case SSL_ERROR_SYSCALL:
+		case SSL_ERROR_SSL:
+			if ((err = ERR_get_error()) == 0) {
+				if (ret == 0)
+					fprintf( stderr, "SSL_%s:got EOF\n", func );
+				else
+					fprintf( stderr, "SSL_%s:%d:%s\n", func, errno, strerror(errno) );
+			} else
+				fprintf( stderr, "SSL_%s:%d:%s\n", func, err, ERR_error_string( err, 0 ) );
+			return;
+		default:
+			fprintf( stderr, "SSL_%s:%d:unhandled SSL error\n", func, err );
+			break;
+		}
+		return;
+	}
+#else
+	(void)sock;
+#endif
+	if (ret < 0)
+		perror( func );
+	else
+		fprintf( stderr, "%s: unexpected EOF\n", func );
+}
+
+static int
+socket_read( Socket_t *sock, char *buf, int len )
+{
+	int n =
+#if HAVE_LIBSSL
+		sock->use_ssl ? SSL_read( sock->ssl, buf, len ) :
+#endif
+		read( sock->fd, buf, len );
+	if (n <= 0) {
+		socket_perror( "read", sock, n );
+		close( sock->fd );
+		sock->fd = -1;
+	}
+	return n;
+}
+
+static int
+socket_write( Socket_t *sock, char *buf, int len )
+{
+	int n =
+#if HAVE_LIBSSL
+		sock->use_ssl ? SSL_write( sock->ssl, buf, len ) :
+#endif
+		write( sock->fd, buf, len );
+	if (n != len) {
+		socket_perror( "write", sock, n );
+		close( sock->fd );
+		sock->fd = -1;
+	}
+	return n;
+}
+
+#if 0
+
+static int
+socket_pending( Socket_t *sock )
+{
+	int num = -1;
+
+	if (ioctl( sock->fd, FIONREAD, &num ) < 0)
+		return -1;
+	if (num > 0)
+		return num;
+#if HAVE_LIBSSL
+	if (sock->use_ssl)
+		return SSL_pending( sock->ssl );
+#endif
+	return 0;
+}
+
+#endif
+
+/* simple line buffering */
+static int
+buffer_gets( buffer_t * b, char **s )
+{
+	int n;
+	int start = b->offset;
+
+	*s = b->buf + start;
+
+	for (;;) {
+		/* make sure we have enough data to read the \r\n sequence */
+		if (b->offset + 1 >= b->bytes) {
+			if (start) {
+				/* shift down used bytes */
+				*s = b->buf;
+
+				assert( start <= b->bytes );
+				n = b->bytes - start;
+
+				if (n)
+					memcpy( b->buf, b->buf + start, n );
+				b->offset -= start;
+				b->bytes = n;
+				start = 0;
+			}
+
+			n = socket_read( &b->sock, b->buf + b->bytes,
+			                 sizeof(b->buf) - b->bytes );
+
+			if (n <= 0)
+				return -1;
+
+			b->bytes += n;
+		}
+
+		if (b->buf[b->offset] == '\r') {
+			assert( b->offset + 1 < b->bytes );
+			if (b->buf[b->offset + 1] == '\n') {
+				b->buf[b->offset] = 0;  /* terminate the string */
+				b->offset += 2; /* next line */
+				if (Verbose)
+					puts( *s );
+				return 0;
+			}
+		}
+
+		b->offset++;
+	}
+	/* not reached */
+}
+
+static void
+info( const char *msg, ... )
+{
+	va_list va;
+
+	if (!Quiet) {
+		va_start( va, msg );
+		vprintf( msg, va );
+		va_end( va );
+		fflush( stdout );
+	}
+}
+
+static void
+warn( const char *msg, ... )
+{
+	va_list va;
+
+	if (Quiet < 2) {
+		va_start( va, msg );
+		vfprintf( stderr, msg, va );
+		va_end( va );
+	}
+}
+
+static char *
+next_arg( char **s )
+{
+	char *ret;
+
+	if (!s || !*s)
+		return 0;
+	while (isspace( (unsigned char) **s ))
+		(*s)++;
+	if (!**s) {
+		*s = 0;
+		return 0;
+	}
+	if (**s == '"') {
+		++*s;
+		ret = *s;
+		*s = strchr( *s, '"' );
+	} else {
+		ret = *s;
+		while (**s && !isspace( (unsigned char) **s ))
+			(*s)++;
+	}
+	if (*s) {
+		if (**s)
+			*(*s)++ = 0;
+		if (!**s)
+			*s = 0;
+	}
+	return ret;
+}
+
+static void
+free_generic_messages( message_t *msgs )
+{
+	message_t *tmsg;
+
+	for (; msgs; msgs = tmsg) {
+		tmsg = msgs->next;
+		free( msgs );
+	}
+}
+
+static int
+vasprintf( char **strp, const char *fmt, va_list ap )
+{
+	int len;
+	char tmp[1024];
+
+	if ((len = vsnprintf( tmp, sizeof(tmp), fmt, ap )) < 0 || !(*strp = xmalloc( len + 1 )))
+		return -1;
+	if (len >= (int)sizeof(tmp))
+		vsprintf( *strp, fmt, ap );
+	else
+		memcpy( *strp, tmp, len + 1 );
+	return len;
+}
+
+static int
+nfsnprintf( char *buf, int blen, const char *fmt, ... )
+{
+	int ret;
+	va_list va;
+
+	va_start( va, fmt );
+	if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen)
+		die( "Fatal: buffer too small. Please report a bug.\n");
+	va_end( va );
+	return ret;
+}
+
+static int
+nfvasprintf( char **str, const char *fmt, va_list va )
+{
+	int ret = vasprintf( str, fmt, va );
+	if (ret < 0)
+		die( "Fatal: Out of memory\n");
+	return ret;
+}
+
+static struct {
+	unsigned char i, j, s[256];
+} rs;
+
+static void
+arc4_init( void )
+{
+	int i, fd;
+	unsigned char j, si, dat[128];
+
+	if ((fd = open( "/dev/urandom", O_RDONLY )) < 0 && (fd = open( "/dev/random", O_RDONLY )) < 0) {
+		fprintf( stderr, "Fatal: no random number source available.\n" );
+		exit( 3 );
+	}
+	if (read( fd, dat, 128 ) != 128) {
+		fprintf( stderr, "Fatal: cannot read random number source.\n" );
+		exit( 3 );
+	}
+	close( fd );
+
+	for (i = 0; i < 256; i++)
+		rs.s[i] = i;
+	for (i = j = 0; i < 256; i++) {
+		si = rs.s[i];
+		j += si + dat[i & 127];
+		rs.s[i] = rs.s[j];
+		rs.s[j] = si;
+	}
+	rs.i = rs.j = 0;
+
+	for (i = 0; i < 256; i++)
+		arc4_getbyte();
+}
+
+static unsigned char
+arc4_getbyte( void )
+{
+	unsigned char si, sj;
+
+	rs.i++;
+	si = rs.s[rs.i];
+	rs.j += si;
+	sj = rs.s[rs.j];
+	rs.s[rs.i] = sj;
+	rs.s[rs.j] = si;
+	return rs.s[(si + sj) & 0xff];
+}
+
+static struct imap_cmd *
+v_issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb,
+                  const char *fmt, va_list ap )
+{
+	imap_t *imap = ctx->imap;
+	struct imap_cmd *cmd;
+	int n, bufl;
+	char buf[1024];
+
+	cmd = xmalloc( sizeof(struct imap_cmd) );
+	nfvasprintf( &cmd->cmd, fmt, ap );
+	cmd->tag = ++imap->nexttag;
+
+	if (cb)
+		cmd->cb = *cb;
+	else
+		memset( &cmd->cb, 0, sizeof(cmd->cb) );
+
+	while (imap->literal_pending)
+		get_cmd_result( ctx, 0 );
+
+	bufl = nfsnprintf( buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
+	                   "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
+	                   cmd->tag, cmd->cmd, cmd->cb.dlen );
+	if (Verbose) {
+		if (imap->num_in_progress)
+			printf( "(%d in progress) ", imap->num_in_progress );
+		if (memcmp( cmd->cmd, "LOGIN", 5 ))
+			printf( ">>> %s", buf );
+		else
+			printf( ">>> %d LOGIN <user> <pass>\n", cmd->tag );
+	}
+	if (socket_write( &imap->buf.sock, buf, bufl ) != bufl) {
+		free( cmd->cmd );
+		free( cmd );
+		if (cb && cb->data)
+			free( cb->data );
+		return NULL;
+	}
+	if (cmd->cb.data) {
+		if (CAP(LITERALPLUS)) {
+			n = socket_write( &imap->buf.sock, cmd->cb.data, cmd->cb.dlen );
+			free( cmd->cb.data );
+			if (n != cmd->cb.dlen ||
+			    (n = socket_write( &imap->buf.sock, "\r\n", 2 )) != 2)
+			{
+				free( cmd->cmd );
+				free( cmd );
+				return NULL;
+			}
+			cmd->cb.data = 0;
+		} else
+			imap->literal_pending = 1;
+	} else if (cmd->cb.cont)
+		imap->literal_pending = 1;
+	cmd->next = 0;
+	*imap->in_progress_append = cmd;
+	imap->in_progress_append = &cmd->next;
+	imap->num_in_progress++;
+	return cmd;
+}
+
+static struct imap_cmd *
+issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )
+{
+	struct imap_cmd *ret;
+	va_list ap;
+
+	va_start( ap, fmt );
+	ret = v_issue_imap_cmd( ctx, cb, fmt, ap );
+	va_end( ap );
+	return ret;
+}
+
+#if 0
+
+static struct imap_cmd *
+issue_imap_cmd_w( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )
+{
+	imap_t *imap = ctx->imap;
+	struct imap_cmd *ret;
+	va_list ap;
+
+	va_start( ap, fmt );
+	ret = v_issue_imap_cmd( ctx, cb, fmt, ap );
+	va_end( ap );
+	while (imap->num_in_progress > max_in_progress ||
+	       socket_pending( &imap->buf.sock ))
+		get_cmd_result( ctx, 0 );
+	return ret;
+}
+
+#endif
+
+static int
+imap_exec( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )
+{
+	va_list ap;
+	struct imap_cmd *cmdp;
+
+	va_start( ap, fmt );
+	cmdp = v_issue_imap_cmd( ctx, cb, fmt, ap );
+	va_end( ap );
+	if (!cmdp)
+		return RESP_BAD;
+
+	return get_cmd_result( ctx, cmdp );
+}
+
+#if 0
+
+static int
+imap_exec_b( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )
+{
+	va_list ap;
+	struct imap_cmd *cmdp;
+
+	va_start( ap, fmt );
+	cmdp = v_issue_imap_cmd( ctx, cb, fmt, ap );
+	va_end( ap );
+	if (!cmdp)
+		return DRV_STORE_BAD;
+
+	switch (get_cmd_result( ctx, cmdp )) {
+	case RESP_BAD: return DRV_STORE_BAD;
+	case RESP_NO: return DRV_BOX_BAD;
+	default: return DRV_OK;
+	}
+}
+
+#endif
+
+static int
+imap_exec_m( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )
+{
+	va_list ap;
+	struct imap_cmd *cmdp;
+
+	va_start( ap, fmt );
+	cmdp = v_issue_imap_cmd( ctx, cb, fmt, ap );
+	va_end( ap );
+	if (!cmdp)
+		return DRV_STORE_BAD;
+
+	switch (get_cmd_result( ctx, cmdp )) {
+	case RESP_BAD: return DRV_STORE_BAD;
+	case RESP_NO: return DRV_MSG_BAD;
+	default: return DRV_OK;
+	}
+}
+
+/*
+static void
+drain_imap_replies( imap_t *imap )
+{
+	while (imap->num_in_progress)
+		get_cmd_result( imap, 0 );
+}
+*/
+
+static int
+is_atom( list_t *list )
+{
+	return list && list->val && list->val != NIL && list->val != LIST;
+}
+
+static int
+is_list( list_t *list )
+{
+	return list && list->val == LIST;
+}
+
+static void
+free_list( list_t *list )
+{
+	list_t *tmp;
+
+	for (; list; list = tmp) {
+		tmp = list->next;
+		if (is_list( list ))
+			free_list( list->child );
+		else if (is_atom( list ))
+			free( list->val );
+		free( list );
+	}
+}
+
+static int
+parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level )
+{
+	list_t *cur;
+	char *s = *sp, *p;
+	int n, bytes;
+
+	for (;;) {
+		while (isspace( (unsigned char)*s ))
+			s++;
+		if (level && *s == ')') {
+			s++;
+			break;
+		}
+		*curp = cur = xmalloc( sizeof(*cur) );
+		curp = &cur->next;
+		cur->val = 0; /* for clean bail */
+		if (*s == '(') {
+			/* sublist */
+			s++;
+			cur->val = LIST;
+			if (parse_imap_list_l( imap, &s, &cur->child, level + 1 ))
+				goto bail;
+		} else if (imap && *s == '{') {
+			/* literal */
+			bytes = cur->len = strtol( s + 1, &s, 10 );
+			if (*s != '}')
+				goto bail;
+
+			s = cur->val = xmalloc( cur->len );
+
+			/* dump whats left over in the input buffer */
+			n = imap->buf.bytes - imap->buf.offset;
+
+			if (n > bytes)
+				/* the entire message fit in the buffer */
+				n = bytes;
+
+			memcpy( s, imap->buf.buf + imap->buf.offset, n );
+			s += n;
+			bytes -= n;
+
+			/* mark that we used part of the buffer */
+			imap->buf.offset += n;
+
+			/* now read the rest of the message */
+			while (bytes > 0) {
+				if ((n = socket_read (&imap->buf.sock, s, bytes)) <= 0)
+					goto bail;
+				s += n;
+				bytes -= n;
+			}
+
+			if (buffer_gets( &imap->buf, &s ))
+				goto bail;
+		} else if (*s == '"') {
+			/* quoted string */
+			s++;
+			p = s;
+			for (; *s != '"'; s++)
+				if (!*s)
+					goto bail;
+			cur->len = s - p;
+			s++;
+			cur->val = xmalloc( cur->len + 1 );
+			memcpy( cur->val, p, cur->len );
+			cur->val[cur->len] = 0;
+		} else {
+			/* atom */
+			p = s;
+			for (; *s && !isspace( (unsigned char)*s ); s++)
+				if (level && *s == ')')
+					break;
+			cur->len = s - p;
+			if (cur->len == 3 && !memcmp ("NIL", p, 3))
+				cur->val = NIL;
+			else {
+				cur->val = xmalloc( cur->len + 1 );
+				memcpy( cur->val, p, cur->len );
+				cur->val[cur->len] = 0;
+			}
+		}
+
+		if (!level)
+			break;
+		if (!*s)
+			goto bail;
+	}
+	*sp = s;
+	*curp = 0;
+	return 0;
+
+  bail:
+	*curp = 0;
+	return -1;
+}
+
+static list_t *
+parse_imap_list( imap_t *imap, char **sp )
+{
+	list_t *head;
+
+	if (!parse_imap_list_l( imap, sp, &head, 0 ))
+		return head;
+	free_list( head );
+	return NULL;
+}
+
+static list_t *
+parse_list( char **sp )
+{
+	return parse_imap_list( 0, sp );
+}
+
+static int
+parse_fetch( imap_t *imap, char *cmd ) /* move this down */
+{
+	list_t *tmp, *list, *flags;
+	char *body = 0;
+	imap_message_t *cur;
+	msg_data_t *msgdata;
+	struct imap_cmd *cmdp;
+	int uid = 0, mask = 0, status = 0, size = 0;
+	unsigned i;
+
+	list = parse_imap_list( imap, &cmd );
+
+	if (!is_list( list )) {
+		fprintf( stderr, "IMAP error: bogus FETCH response\n" );
+		free_list( list );
+		return -1;
+	}
+
+	for (tmp = list->child; tmp; tmp = tmp->next) {
+		if (is_atom( tmp )) {
+			if (!strcmp( "UID", tmp->val )) {
+				tmp = tmp->next;
+				if (is_atom( tmp ))
+					uid = atoi( tmp->val );
+				else
+					fprintf( stderr, "IMAP error: unable to parse UID\n" );
+			} else if (!strcmp( "FLAGS", tmp->val )) {
+				tmp = tmp->next;
+				if (is_list( tmp )) {
+					for (flags = tmp->child; flags; flags = flags->next) {
+						if (is_atom( flags )) {
+							if (flags->val[0] == '\\') { /* ignore user-defined flags for now */
+								if (!strcmp( "Recent", flags->val + 1)) {
+									status |= M_RECENT;
+									goto flagok;
+								}
+								for (i = 0; i < as(Flags); i++)
+									if (!strcmp( Flags[i], flags->val + 1 )) {
+										mask |= 1 << i;
+										goto flagok;
+									}
+								fprintf( stderr, "IMAP warning: unknown system flag %s\n", flags->val );
+							}
+						  flagok: ;
+						} else
+							fprintf( stderr, "IMAP error: unable to parse FLAGS list\n" );
+					}
+					status |= M_FLAGS;
+				} else
+					fprintf( stderr, "IMAP error: unable to parse FLAGS\n" );
+			} else if (!strcmp( "RFC822.SIZE", tmp->val )) {
+				tmp = tmp->next;
+				if (is_atom( tmp ))
+					size = atoi( tmp->val );
+				else
+					fprintf( stderr, "IMAP error: unable to parse SIZE\n" );
+			} else if (!strcmp( "BODY[]", tmp->val )) {
+				tmp = tmp->next;
+				if (is_atom( tmp )) {
+					body = tmp->val;
+					tmp->val = 0;       /* don't free together with list */
+					size = tmp->len;
+				} else
+					fprintf( stderr, "IMAP error: unable to parse BODY[]\n" );
+			}
+		}
+	}
+
+	if (body) {
+		for (cmdp = imap->in_progress; cmdp; cmdp = cmdp->next)
+			if (cmdp->cb.uid == uid)
+				goto gotuid;
+		fprintf( stderr, "IMAP error: unexpected FETCH response (UID %d)\n", uid );
+		free_list( list );
+		return -1;
+	  gotuid:
+		msgdata = (msg_data_t *)cmdp->cb.ctx;
+		msgdata->data = body;
+		msgdata->len = size;
+		msgdata->crlf = 1;
+		if (status & M_FLAGS)
+			msgdata->flags = mask;
+	} else if (uid) { /* ignore async flag updates for now */
+		/* XXX this will need sorting for out-of-order (multiple queries) */
+		cur = xcalloc( sizeof(*cur), 1 );
+		*imap->msgapp = &cur->gen;
+		imap->msgapp = &cur->gen.next;
+		cur->gen.next = 0;
+		cur->gen.uid = uid;
+		cur->gen.flags = mask;
+		cur->gen.status = status;
+		cur->gen.size = size;
+	}
+
+	free_list( list );
+	return 0;
+}
+
+static void
+parse_capability( imap_t *imap, char *cmd )
+{
+	char *arg;
+	unsigned i;
+
+	imap->caps = 0x80000000;
+	while ((arg = next_arg( &cmd )))
+		for (i = 0; i < as(cap_list); i++)
+			if (!strcmp( cap_list[i], arg ))
+				imap->caps |= 1 << i;
+	imap->rcaps = imap->caps;
+}
+
+static int
+parse_response_code( imap_store_t *ctx, struct imap_cmd_cb *cb, char *s )
+{
+	imap_t *imap = ctx->imap;
+	char *arg, *p;
+
+	if (*s != '[')
+		return RESP_OK;		/* no response code */
+	s++;
+	if (!(p = strchr( s, ']' ))) {
+		fprintf( stderr, "IMAP error: malformed response code\n" );
+		return RESP_BAD;
+	}
+	*p++ = 0;
+	arg = next_arg( &s );
+	if (!strcmp( "UIDVALIDITY", arg )) {
+		if (!(arg = next_arg( &s )) || !(ctx->gen.uidvalidity = atoi( arg ))) {
+			fprintf( stderr, "IMAP error: malformed UIDVALIDITY status\n" );
+			return RESP_BAD;
+		}
+	} else if (!strcmp( "UIDNEXT", arg )) {
+		if (!(arg = next_arg( &s )) || !(imap->uidnext = atoi( arg ))) {
+			fprintf( stderr, "IMAP error: malformed NEXTUID status\n" );
+			return RESP_BAD;
+		}
+	} else if (!strcmp( "CAPABILITY", arg )) {
+		parse_capability( imap, s );
+	} else if (!strcmp( "ALERT", arg )) {
+		/* RFC2060 says that these messages MUST be displayed
+		 * to the user
+		 */
+		for (; isspace( (unsigned char)*p ); p++);
+		fprintf( stderr, "*** IMAP ALERT *** %s\n", p );
+	} else if (cb && cb->ctx && !strcmp( "APPENDUID", arg )) {
+		if (!(arg = next_arg( &s )) || !(ctx->gen.uidvalidity = atoi( arg )) ||
+		    !(arg = next_arg( &s )) || !(*(int *)cb->ctx = atoi( arg )))
+		{
+			fprintf( stderr, "IMAP error: malformed APPENDUID status\n" );
+			return RESP_BAD;
+		}
+	}
+	return RESP_OK;
+}
+
+static int
+get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd )
+{
+	imap_t *imap = ctx->imap;
+	struct imap_cmd *cmdp, **pcmdp, *ncmdp;
+	char *cmd, *arg, *arg1, *p;
+	int n, resp, resp2, tag;
+
+	for (;;) {
+		if (buffer_gets( &imap->buf, &cmd ))
+			return RESP_BAD;
+
+		arg = next_arg( &cmd );
+		if (*arg == '*') {
+			arg = next_arg( &cmd );
+			if (!arg) {
+				fprintf( stderr, "IMAP error: unable to parse untagged response\n" );
+				return RESP_BAD;
+			}
+
+			if (!strcmp( "NAMESPACE", arg )) {
+				imap->ns_personal = parse_list( &cmd );
+				imap->ns_other = parse_list( &cmd );
+				imap->ns_shared = parse_list( &cmd );
+			} else if (!strcmp( "OK", arg ) || !strcmp( "BAD", arg ) ||
+			           !strcmp( "NO", arg ) || !strcmp( "BYE", arg )) {
+				if ((resp = parse_response_code( ctx, 0, cmd )) != RESP_OK)
+					return resp;
+			} else if (!strcmp( "CAPABILITY", arg ))
+				parse_capability( imap, cmd );
+			else if ((arg1 = next_arg( &cmd ))) {
+				if (!strcmp( "EXISTS", arg1 ))
+					ctx->gen.count = atoi( arg );
+				else if (!strcmp( "RECENT", arg1 ))
+					ctx->gen.recent = atoi( arg );
+				else if(!strcmp ( "FETCH", arg1 )) {
+					if (parse_fetch( imap, cmd ))
+						return RESP_BAD;
+				}
+			} else {
+				fprintf( stderr, "IMAP error: unable to parse untagged response\n" );
+				return RESP_BAD;
+			}
+		} else if (!imap->in_progress) {
+			fprintf( stderr, "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "" );
+			return RESP_BAD;
+		} else if (*arg == '+') {
+			/* This can happen only with the last command underway, as
+			   it enforces a round-trip. */
+			cmdp = (struct imap_cmd *)((char *)imap->in_progress_append -
+			       offsetof(struct imap_cmd, next));
+			if (cmdp->cb.data) {
+				n = socket_write( &imap->buf.sock, cmdp->cb.data, cmdp->cb.dlen );
+				free( cmdp->cb.data );
+				cmdp->cb.data = 0;
+				if (n != (int)cmdp->cb.dlen)
+					return RESP_BAD;
+			} else if (cmdp->cb.cont) {
+				if (cmdp->cb.cont( ctx, cmdp, cmd ))
+					return RESP_BAD;
+			} else {
+				fprintf( stderr, "IMAP error: unexpected command continuation request\n" );
+				return RESP_BAD;
+			}
+			if (socket_write( &imap->buf.sock, "\r\n", 2 ) != 2)
+				return RESP_BAD;
+			if (!cmdp->cb.cont)
+				imap->literal_pending = 0;
+			if (!tcmd)
+				return DRV_OK;
+		} else {
+			tag = atoi( arg );
+			for (pcmdp = &imap->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next)
+				if (cmdp->tag == tag)
+					goto gottag;
+			fprintf( stderr, "IMAP error: unexpected tag %s\n", arg );
+			return RESP_BAD;
+		  gottag:
+			if (!(*pcmdp = cmdp->next))
+				imap->in_progress_append = pcmdp;
+			imap->num_in_progress--;
+			if (cmdp->cb.cont || cmdp->cb.data)
+				imap->literal_pending = 0;
+			arg = next_arg( &cmd );
+			if (!strcmp( "OK", arg ))
+				resp = DRV_OK;
+			else {
+				if (!strcmp( "NO", arg )) {
+					if (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp( cmd, "[TRYCREATE]", 11 ))) { /* SELECT, APPEND or UID COPY */
+						p = strchr( cmdp->cmd, '"' );
+						if (!issue_imap_cmd( ctx, 0, "CREATE \"%.*s\"", strchr( p + 1, '"' ) - p + 1, p )) {
+							resp = RESP_BAD;
+							goto normal;
+						}
+						/* not waiting here violates the spec, but a server that does not
+						   grok this nonetheless violates it too. */
+						cmdp->cb.create = 0;
+						if (!(ncmdp = issue_imap_cmd( ctx, &cmdp->cb, "%s", cmdp->cmd ))) {
+							resp = RESP_BAD;
+							goto normal;
+						}
+						free( cmdp->cmd );
+						free( cmdp );
+						if (!tcmd)
+							return 0;	/* ignored */
+						if (cmdp == tcmd)
+							tcmd = ncmdp;
+						continue;
+					}
+					resp = RESP_NO;
+				} else /*if (!strcmp( "BAD", arg ))*/
+					resp = RESP_BAD;
+				fprintf( stderr, "IMAP command '%s' returned response (%s) - %s\n",
+				         memcmp (cmdp->cmd, "LOGIN", 5) ?
+				         		cmdp->cmd : "LOGIN <user> <pass>",
+				         		arg, cmd ? cmd : "");
+			}
+			if ((resp2 = parse_response_code( ctx, &cmdp->cb, cmd )) > resp)
+				resp = resp2;
+		  normal:
+			if (cmdp->cb.done)
+				cmdp->cb.done( ctx, cmdp, resp );
+			if (cmdp->cb.data)
+				free( cmdp->cb.data );
+			free( cmdp->cmd );
+			free( cmdp );
+			if (!tcmd || tcmd == cmdp)
+				return resp;
+		}
+	}
+	/* not reached */
+}
+
+static void
+imap_close_server( imap_store_t *ictx )
+{
+	imap_t *imap = ictx->imap;
+
+	if (imap->buf.sock.fd != -1) {
+		imap_exec( ictx, 0, "LOGOUT" );
+		close( imap->buf.sock.fd );
+	}
+#ifdef HAVE_LIBSSL
+	if (imap->SSLContext)
+		SSL_CTX_free( imap->SSLContext );
+#endif
+	free_list( imap->ns_personal );
+	free_list( imap->ns_other );
+	free_list( imap->ns_shared );
+	free( imap );
+}
+
+static void
+imap_close_store( store_t *ctx )
+{
+	imap_close_server( (imap_store_t *)ctx );
+	free_generic_messages( ctx->msgs );
+	free( ctx );
+}
+
+#ifdef HAVE_LIBSSL
+static int
+start_tls( imap_store_t *ctx )
+{
+	imap_t *imap = ctx->imap;
+	int ret;
+	static int ssl_inited;
+
+	if (!ssl_inited) {
+		SSL_library_init();
+		SSL_load_error_strings();
+		ssl_inited = 1;
+	}
+
+        if (init_ssl_ctx( ctx ))
+		return 1;
+
+	imap->buf.sock.ssl = SSL_new( imap->SSLContext );
+	SSL_set_fd( imap->buf.sock.ssl, imap->buf.sock.fd );
+	if ((ret = SSL_connect( imap->buf.sock.ssl )) <= 0) {
+		socket_perror( "connect", &imap->buf.sock, ret );
+		return 1;
+	}
+
+	/* verify the server certificate */
+	if (verify_cert( imap->buf.sock.ssl ))
+		return 1;
+
+	imap->buf.sock.use_ssl = 1;
+	info( "Connection is now encrypted\n" );
+	return 0;
+}
+
+#define ENCODED_SIZE(n) (4*((n+2)/3))
+
+static char
+hexchar( unsigned int b )
+{
+	if (b < 10)
+		return '0' + b;
+	return 'a' + (b - 10);
+}
+
+/* XXX merge into do_cram_auth? */
+static char *
+cram( const char *challenge, const char *user, const char *pass )
+{
+	HMAC_CTX hmac;
+	char hash[16];
+	char hex[33];
+	int i;
+	unsigned int hashlen = sizeof(hash);
+	char buf[256];
+	int len = strlen( challenge );
+	char *response = xcalloc( 1 + len, 1 );
+	char *final;
+
+	/* response will always be smaller than challenge because we are
+	 * decoding.
+	 */
+	len = EVP_DecodeBlock( (unsigned char *)response, (unsigned char *)challenge, strlen( challenge ) );
+
+	HMAC_Init( &hmac, (unsigned char *) pass, strlen( pass ), EVP_md5() );
+	HMAC_Update( &hmac, (unsigned char *)response, strlen( response ) );
+	HMAC_Final( &hmac, (unsigned char *)hash, &hashlen );
+
+	assert( hashlen == sizeof(hash) );
+
+	free( response );
+
+	hex[32] = 0;
+	for (i = 0; i < 16; i++) {
+		hex[2 * i] = hexchar( (hash[i] >> 4) & 0xf );
+		hex[2 * i + 1] = hexchar( hash[i] & 0xf );
+	}
+
+	nfsnprintf( buf, sizeof(buf), "%s %s", user, hex );
+
+	len = strlen( buf );
+	len = ENCODED_SIZE( len ) + 1;
+	final = xmalloc( len );
+	final[len - 1] = 0;
+
+	assert( EVP_EncodeBlock( (unsigned char *)final, (unsigned char *)buf, strlen( buf ) ) == len - 1 );
+
+	return final;
+}
+
+static int
+do_cram_auth (imap_store_t *ctx, struct imap_cmd *cmdp, const char *prompt)
+{
+	imap_t *imap = ctx->imap;
+	imap_server_conf_t *srvc = ((imap_store_conf_t *)ctx->gen.conf)->server;
+	char *resp;
+	int n, l;
+
+	resp = cram( prompt, srvc->user, srvc->pass );
+
+	if (Verbose)
+		printf( ">+> %s\n", resp );
+	l = strlen( resp );
+	n = socket_write( &imap->buf.sock, resp, l );
+	free( resp );
+	if (n != l)
+		return -1;
+	cmdp->cb.cont = 0;
+	return 0;
+}
+#endif
+
+static store_t *
+imap_open_store( imap_server_conf_t *srvc )
+{
+	imap_store_t *ctx;
+	imap_t *imap;
+	char *arg, *rsp;
+	struct hostent *he;
+	struct sockaddr_in addr;
+	int s, a[2], preauth;
+#if HAVE_LIBSSL
+	int use_ssl;
+#endif
+
+	ctx = xcalloc( sizeof(*ctx), 1 );
+
+	ctx->imap = imap = xcalloc( sizeof(*imap), 1 );
+	imap->buf.sock.fd = -1;
+	imap->in_progress_append = &imap->in_progress;
+
+	/* open connection to IMAP server */
+#if HAVE_LIBSSL
+	use_ssl = 0;
+#endif
+
+	if (srvc->tunnel) {
+		info( "Starting tunnel '%s'... ", srvc->tunnel );
+
+		if (socketpair( PF_UNIX, SOCK_STREAM, 0, a )) {
+			perror( "socketpair" );
+			exit( 1 );
+		}
+
+		if (fork() == 0) {
+			if (dup2( a[0], 0 ) == -1 || dup2( a[0], 1 ) == -1)
+				_exit( 127 );
+			close( a[0] );
+			close( a[1] );
+			execl( "/bin/sh", "sh", "-c", srvc->tunnel, 0 );
+			_exit( 127 );
+		}
+
+		close (a[0]);
+
+		imap->buf.sock.fd = a[1];
+
+		info( "ok\n" );
+	} else {
+		memset( &addr, 0, sizeof(addr) );
+		addr.sin_port = htons( srvc->port );
+		addr.sin_family = AF_INET;
+
+		info( "Resolving %s... ", srvc->host );
+		he = gethostbyname( srvc->host );
+		if (!he) {
+			perror( "gethostbyname" );
+			goto bail;
+		}
+		info( "ok\n" );
+
+		addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
+
+		s = socket( PF_INET, SOCK_STREAM, 0 );
+
+		info( "Connecting to %s:%hu... ", inet_ntoa( addr.sin_addr ), ntohs( addr.sin_port ) );
+		if (connect( s, (struct sockaddr *)&addr, sizeof(addr) )) {
+			close( s );
+			perror( "connect" );
+			goto bail;
+		}
+		info( "ok\n" );
+
+		imap->buf.sock.fd = s;
+
+#if HAVE_LIBSSL
+		if (srvc->use_imaps) {
+			if (start_tls( ctx ))
+				goto bail;
+			use_ssl = 1;
+		}
+#endif
+	}
+
+	/* read the greeting string */
+	if (buffer_gets( &imap->buf, &rsp )) {
+		fprintf( stderr, "IMAP error: no greeting response\n" );
+		goto bail;
+	}
+	arg = next_arg( &rsp );
+	if (!arg || *arg != '*' || (arg = next_arg( &rsp )) == NULL) {
+		fprintf( stderr, "IMAP error: invalid greeting response\n" );
+		goto bail;
+	}
+	preauth = 0;
+	if (!strcmp( "PREAUTH", arg ))
+		preauth = 1;
+	else if (strcmp( "OK", arg ) != 0) {
+		fprintf( stderr, "IMAP error: unknown greeting response\n" );
+		goto bail;
+	}
+	parse_response_code( ctx, 0, rsp );
+	if (!imap->caps && imap_exec( ctx, 0, "CAPABILITY" ) != RESP_OK)
+		goto bail;
+
+	if (!preauth) {
+#if HAVE_LIBSSL
+		if (!srvc->use_imaps && (srvc->use_sslv2 || srvc->use_sslv3 || srvc->use_tlsv1)) {
+			/* always try to select SSL support if available */
+			if (CAP(STARTTLS)) {
+				if (imap_exec( ctx, 0, "STARTTLS" ) != RESP_OK)
+					goto bail;
+				if (start_tls( ctx ))
+					goto bail;
+				use_ssl = 1;
+
+				if (imap_exec( ctx, 0, "CAPABILITY" ) != RESP_OK)
+					goto bail;
+			} else {
+				if (srvc->require_ssl) {
+					fprintf( stderr, "IMAP error: SSL support not available\n" );
+					goto bail;
+				} else
+					warn( "IMAP warning: SSL support not available\n" );
+			}
+		}
+#endif
+
+		info ("Logging in...\n");
+		if (!srvc->user) {
+			fprintf( stderr, "Skipping server %s, no user\n", srvc->host );
+			goto bail;
+		}
+		if (!srvc->pass) {
+			char prompt[80];
+			sprintf( prompt, "Password (%s@%s): ", srvc->user, srvc->host );
+			arg = getpass( prompt );
+			if (!arg) {
+				perror( "getpass" );
+				exit( 1 );
+			}
+			if (!*arg) {
+				fprintf( stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host );
+				goto bail;
+			}
+			/*
+			 * getpass() returns a pointer to a static buffer.  make a copy
+			 * for long term storage.
+			 */
+			srvc->pass = strdup( arg );
+		}
+#if HAVE_LIBSSL
+		if (CAP(CRAM)) {
+			struct imap_cmd_cb cb;
+
+			info( "Authenticating with CRAM-MD5\n" );
+			memset( &cb, 0, sizeof(cb) );
+			cb.cont = do_cram_auth;
+			if (imap_exec( ctx, &cb, "AUTHENTICATE CRAM-MD5" ) != RESP_OK)
+				goto bail;
+		} else if (srvc->require_cram) {
+			fprintf( stderr, "IMAP error: CRAM-MD5 authentication is not supported by server\n" );
+			goto bail;
+		} else
+#endif
+		{
+			if (CAP(NOLOGIN)) {
+				fprintf( stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host );
+				goto bail;
+			}
+#if HAVE_LIBSSL
+			if (!use_ssl)
+#endif
+				warn( "*** IMAP Warning *** Password is being sent in the clear\n" );
+			if (imap_exec( ctx, 0, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass ) != RESP_OK) {
+				fprintf( stderr, "IMAP error: LOGIN failed\n" );
+				goto bail;
+			}
+		}
+	} /* !preauth */
+
+	ctx->prefix = "";
+	ctx->trashnc = 1;
+	return (store_t *)ctx;
+
+  bail:
+	imap_close_store( &ctx->gen );
+	return 0;
+}
+
+static int
+imap_make_flags( int flags, char *buf )
+{
+	const char *s;
+	unsigned i, d;
+
+	for (i = d = 0; i < as(Flags); i++)
+		if (flags & (1 << i)) {
+			buf[d++] = ' ';
+			buf[d++] = '\\';
+			for (s = Flags[i]; *s; s++)
+				buf[d++] = *s;
+		}
+	buf[0] = '(';
+	buf[d++] = ')';
+	return d;
+}
+
+#define TUIDL 8
+
+static int
+imap_store_msg( store_t *gctx, msg_data_t *data, int *uid )
+{
+	imap_store_t *ctx = (imap_store_t *)gctx;
+	imap_t *imap = ctx->imap;
+	struct imap_cmd_cb cb;
+	char *fmap, *buf;
+	const char *prefix, *box;
+	int ret, i, j, d, len, extra, nocr;
+	int start, sbreak = 0, ebreak = 0;
+	char flagstr[128], tuid[TUIDL * 2 + 1];
+
+	memset( &cb, 0, sizeof(cb) );
+
+	fmap = data->data;
+	len = data->len;
+	nocr = !data->crlf;
+	extra = 0, i = 0;
+	if (!CAP(UIDPLUS) && uid) {
+	  nloop:
+		start = i;
+		while (i < len)
+			if (fmap[i++] == '\n') {
+				extra += nocr;
+				if (i - 2 + nocr == start) {
+					sbreak = ebreak = i - 2 + nocr;
+					goto mktid;
+				}
+				if (!memcmp( fmap + start, "X-TUID: ", 8 )) {
+					extra -= (ebreak = i) - (sbreak = start) + nocr;
+					goto mktid;
+				}
+				goto nloop;
+			}
+		/* invalid message */
+		free( fmap );
+		return DRV_MSG_BAD;
+	 mktid:
+		for (j = 0; j < TUIDL; j++)
+			sprintf( tuid + j * 2, "%02x", arc4_getbyte() );
+		extra += 8 + TUIDL * 2 + 2;
+	}
+	if (nocr)
+		for (; i < len; i++)
+			if (fmap[i] == '\n')
+				extra++;
+
+	cb.dlen = len + extra;
+	buf = cb.data = xmalloc( cb.dlen );
+	i = 0;
+	if (!CAP(UIDPLUS) && uid) {
+		if (nocr) {
+			for (; i < sbreak; i++)
+				if (fmap[i] == '\n') {
+					*buf++ = '\r';
+					*buf++ = '\n';
+				} else
+					*buf++ = fmap[i];
+		} else {
+			memcpy( buf, fmap, sbreak );
+			buf += sbreak;
+		}
+		memcpy( buf, "X-TUID: ", 8 );
+		buf += 8;
+		memcpy( buf, tuid, TUIDL * 2 );
+		buf += TUIDL * 2;
+		*buf++ = '\r';
+		*buf++ = '\n';
+		i = ebreak;
+	}
+	if (nocr) {
+		for (; i < len; i++)
+			if (fmap[i] == '\n') {
+				*buf++ = '\r';
+				*buf++ = '\n';
+			} else
+				*buf++ = fmap[i];
+	} else
+		memcpy( buf, fmap + i, len - i );
+
+	free( fmap );
+
+	d = 0;
+	if (data->flags) {
+		d = imap_make_flags( data->flags, flagstr );
+		flagstr[d++] = ' ';
+	}
+	flagstr[d] = 0;
+
+	if (!uid) {
+		box = gctx->conf->trash;
+		prefix = ctx->prefix;
+		cb.create = 1;
+		if (ctx->trashnc)
+			imap->caps = imap->rcaps & ~(1 << LITERALPLUS);
+	} else {
+		box = gctx->name;
+		prefix = !strcmp( box, "INBOX" ) ? "" : ctx->prefix;
+		cb.create = (gctx->opts & OPEN_CREATE) != 0;
+		/*if (ctx->currentnc)
+			imap->caps = imap->rcaps & ~(1 << LITERALPLUS);*/
+	}
+	cb.ctx = uid;
+	ret = imap_exec_m( ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr );
+	imap->caps = imap->rcaps;
+	if (ret != DRV_OK)
+		return ret;
+	if (!uid)
+		ctx->trashnc = 0;
+	else {
+		/*ctx->currentnc = 0;*/
+		gctx->count++;
+	}
+
+	return DRV_OK;
+}
+
+
+/*
+ * mm-push-imap - push a patch into an imap folder
+ */
+
+#define CHUNKSIZE 0x1000
+
+static int
+read_message( FILE *f, msg_data_t *msg )
+{
+	int len, r;
+
+	memset( msg, 0, sizeof *msg );
+	len = CHUNKSIZE;
+	msg->data = xmalloc( len+1 );
+	msg->data[0] = 0;
+
+	while(!feof( f )) {
+		if (msg->len >= len) {
+			void *p;
+			len += CHUNKSIZE;
+			p = xrealloc(msg->data, len+1);
+			if (!p)
+				break;
+		}
+		r = fread( &msg->data[msg->len], 1, len - msg->len, f );
+		if (r <= 0)
+			break;
+		msg->len += r;
+	}
+	msg->data[msg->len] = 0;
+	return msg->len;
+}
+
+static int count_messages( msg_data_t *msg )
+{
+	int count = 0;
+	char *p = msg->data;
+
+	while (1) {
+		if (!strncmp( "From ", p, 5 )) {
+			count++;
+			p += 5;
+		}
+		p = strstr( p+5, "\nFrom ");
+		if (!p)
+			break;
+		p++;
+	}
+	return count;
+}
+
+static int
+split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs )
+{
+	char *p, *data;
+
+	memset( msg, 0, sizeof *msg );
+	if (*ofs >= all_msgs->len)
+		return 0;
+
+	data = &all_msgs->data[ *ofs ];
+	msg->len = all_msgs->len - *ofs;
+
+	if (msg->len < 5 || strncmp( data, "From ", 5 ))
+		return 0;
+
+	p = strstr( data, "\nFrom " );
+	if (p)
+		msg->len = &p[1] - data;
+
+	msg->data = xmalloc( msg->len + 1 );
+	if (!msg->data)
+		return 0;
+
+	memcpy( msg->data, data, msg->len );
+	msg->data[ msg->len ] = 0;
+
+	*ofs += msg->len;
+ 	return 1;
+}
+
+static imap_server_conf_t server =
+{
+	NULL,	/* name */
+	NULL,	/* tunnel */
+	NULL,	/* host */
+	0,	/* port */
+	NULL,	/* user */
+	NULL,	/* pass */
+#if HAVE_LIBSSL
+	NULL,	/* cert_file */
+	0,	/* use_imaps */
+	0,	/* require_ssl */
+	0,	/* use_sslv2 */
+	0,	/* use_sslv3 */
+	0,	/* use_tlsv1 */
+	0,	/* require_cram */
+#endif
+};
+
+static char *imap_folder;
+
+static int
+git_imap_config(const char *key, const char *val)
+{
+	char imap_key[] = "imap.";
+
+	if (strncmp( key, imap_key, sizeof imap_key - 1 ))
+		return 0;
+	key += sizeof imap_key - 1;
+
+	if (!strcasecmp( "Folder", key )) {
+		imap_folder = strdup( val );
+	} else if (!strcasecmp( "Host", key )) {
+#if HAVE_LIBSSL
+		if (!memcmp( "imaps:", val, 6 )) {
+			val += 6;
+			server.use_imaps = 1;
+			server.use_sslv2 = 1;
+			server.use_sslv3 = 1;
+			if (!server.port)
+				server.port = 993;
+		} else
+#endif
+		{
+			if (!memcmp( "imap:", val, 5 ))
+				val += 5;
+			if (!server.port)
+				server.port = 143;
+		}
+		if (!memcmp( "//", val, 2 ))
+			val += 2;
+		server.host = strdup( val );
+	}
+	else if (!strcasecmp( "User", key ))
+		server.user = strdup( val );
+	else if (!strcasecmp( "Pass", key ))
+		server.pass = strdup( val );
+	else if (!strcasecmp( "Port", key ))
+		server.port = git_config_int( key, val );
+	else if (!strcasecmp( "Tunnel", key ))
+		server.tunnel = strdup( val );
+#if HAVE_LIBSSL
+	else if (!strcasecmp( "CertificateFile", key ))
+		server.cert_file = expand_strdup( val );
+	else if (!strcasecmp( "RequireSSL", key ))
+		server.require_ssl = git_config_bool( key, val );
+	else if (!strcasecmp( "UseSSLv2", key ))
+		server.use_sslv2 = git_config_bool( key, val );
+	else if (!strcasecmp( "UseSSLv3", key ))
+		server.use_sslv3 = git_config_bool( key, val );
+	else if (!strcasecmp( "UseTLSv1", key ))
+		server.use_tlsv1 = git_config_bool( key, val );
+	else if (!strcasecmp( "RequireCRAM", key ))
+		server.require_cram = git_config_bool( key, val );
+#endif
+	return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+	msg_data_t all_msgs, msg;
+	store_t *ctx = 0;
+	int uid = 0;
+	int ofs = 0;
+	int r;
+	int total, n = 0;
+
+	/* init the random number generator */
+	arc4_init();
+
+	git_config( git_imap_config );
+
+	if (!imap_folder) {
+		fprintf( stderr, "no imap store specified\n" );
+		return 1;
+	}
+
+	/* read the messages */
+	if (!read_message( stdin, &all_msgs )) {
+		fprintf(stderr,"nothing to send\n");
+		return 1;
+	}
+
+	/* write it to the imap server */
+	ctx = imap_open_store( &server );
+	if (!ctx) {
+		fprintf(stderr,"failed to open store\n");
+		return 1;
+	}
+
+	total = count_messages( &all_msgs );
+	fprintf( stderr, "sending %d messages\n", total );
+	ctx->name = imap_folder;
+	while (1) {
+		fprintf( stderr, "sent (%d/%d)\r", n, total );
+		if (!split_msg( &all_msgs, &msg, &ofs ))
+			break;
+		r = imap_store_msg( ctx, &msg, &uid );
+		if (r != DRV_OK) break;
+		n++;
+	}
+	fprintf( stderr,"\n" );
+
+	imap_close_store( ctx );
+
+	return 0;
+}


^ permalink raw reply related

* Re: [PATCH] Add git-annotate(1) and git-blame(1)
From: Jonas Fonseca @ 2006-03-06 11:40 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7v3bhwcezy.fsf@assigned-by-dhcp.cox.net>

Junio C Hamano <junkio@cox.net> wrote Sun, Mar 05, 2006:
> Jonas Fonseca <fonseca@diku.dk> writes:
> 
> > Signed-off-by: Jonas Fonseca <fonseca@diku.dk>
> 
> Wonderful, but I was hoping the "deathmatch" would leave only
> one that needs to be documented and we could wait documenting
> until then ;-).

Why not let them compete on providing the best documentation as well? ;)
But ok, I can understand your point.

-- 
Jonas Fonseca

^ permalink raw reply

* Re: What's in git.git
From: Lukas Sandström @ 2006-03-06 10:29 UTC (permalink / raw)
  To: Junio C Hamano, Git Mailing List
In-Reply-To: <7v1wxg82r3.fsf@assigned-by-dhcp.cox.net>

Junio C Hamano wrote:
 
> I'd like asciidoc tweaks in "next" by Francis Daly tested by
> people who have access to different vintages of docbook-xsl by
> trying to build manpages.  Look for displayed examples, such as
> the one in git-branch documentation.  I've tried it with v1.68
> and getting far better results than before, and Francis says
> v1.69 works fine with or without the change. IOW this is a
> workaround for a problem in v1.68.
> 

I tested it with asciidoc 7.0.1 and the examples in the git-branch 
man page look better with the fix.

/Lukas

Before:
 Examples
       Start development off of a know tag


              $  git  clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6 $
              cd my2.6 $ git branch my2.6.14 v2.6.14 $ git checkout my2.6.14

               These two steps are the same as "checkout -b my2.6.14 v2.6.14".

       Delete unneeded branch


              $  git clone git://git.kernel.org/.../git.git my.git $ cd my.git
              $ git branch -D todo

               delete todo branch even if the "master" branch  does  not  have
              all commits from todo branch.

After:
   Examples
       Start development off of a know tag

              $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6
              $ cd my2.6
              $ git branch my2.6.14 v2.6.14
              $ git checkout my2.6.14

               These two steps are the same as "checkout -b my2.6.14 v2.6.14".


       Delete unneeded branch

              $ git clone git://git.kernel.org/.../git.git my.git
              $ cd my.git
              $ git branch -D todo

               delete todo branch even if the "master" branch does not have all
              commits from todo branch.

^ permalink raw reply

* Re: cvsimport woes
From: Martin Langhoff @ 2006-03-06  9:37 UTC (permalink / raw)
  To: Rajkumar S; +Cc: git
In-Reply-To: <46a038f90603060124h4ea1c3c6gaa5d8b52ed311230@mail.gmail.com>

On 3/6/06, Martin Langhoff <martin.langhoff@gmail.com> wrote:
> you don't seem to be making any silly mistake. Make sure you are using
> a recent git, and a recent cvsps. Actually you want the _latest_ cvsps
> (2.1 I think).

Scratch this bit, naturally. I wasn't 100% paying attention. Still,
the rest of the answer should kinda/sorta make sense.

sorry!

martin

^ permalink raw reply

* Re: cvsimport woes
From: Martin Langhoff @ 2006-03-06  9:24 UTC (permalink / raw)
  To: Rajkumar S; +Cc: git
In-Reply-To: <44094618.6070404@asianetindia.com>

Hi Raj,

you don't seem to be making any silly mistake. Make sure you are using
a recent git, and a recent cvsps. Actually you want the _latest_ cvsps
(2.1 I think).

A good thing to check is what cvsps is telling cvsimport.


Now, here you are not showing us your cvsimport commandline:
> cvs_direct initialized to CVSROOT /home/raj/cvsroot
> cvs rlog: Logging src
> skip patchset 1: 1141457879 before 1141457879
> skip patchset 2: 1141457879 before 1141457879

Ahhh... ok, you are doing it all very fast. Is this a script you are
running? add sleep 1 before you call cvsimport.

cheers,


martin

^ permalink raw reply

* Re: [PATCH] annotate: Support annotation of files on other revisions.
From: Johannes Schindelin @ 2006-03-06  9:24 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: gitzilla, git
In-Reply-To: <7vk6b8axz4.fsf@assigned-by-dhcp.cox.net>

Hi,

On Sun, 5 Mar 2006, Junio C Hamano wrote:

> > Is there any reason that git-{annotate,blame} can't take more than one
> > filename, ever?
> 
> I do not see it would be much useful -- the output does not
> have a sign to show file boundary.

CVS does it. Why shouldn't git, too?

Ciao,
Dscho

P.S.: The output for more than one file is separated by something like

	Annotations for <filename.txt>
	***************

^ permalink raw reply

* Re: git clone downloads objects that are in GIT_OBJECT_DIRECTORY
From: Johannes Schindelin @ 2006-03-06  9:20 UTC (permalink / raw)
  To: sean; +Cc: Shawn Pearce, junkio, git
In-Reply-To: <BAYC1-PASMTP05F90E7F1807F05A274507AEE90@CEZ.ICE>

Hi,

On Sun, 5 Mar 2006, sean wrote:

> However, it might be nice to have a command that allows you to 
> change origin information for a repo without needing to know git
> internals; maybe something like:
> 
> $ git set-origin <URL>
> 
> Or maybe better:
> 
> $ git set-remote --pull master:origin origin <URL>

FWIW, I once sent patches to make this easier by placing this information 
into the config file, but for reasons I did not understand, they were 
rejected. Sigh!

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH] annotate: Support annotation of files on other revisions.
From: Johannes Schindelin @ 2006-03-06  9:18 UTC (permalink / raw)
  To: Ryan Anderson; +Cc: git
In-Reply-To: <20060306024353.GA23001@mythryan2.michonline.com>

Hi,

On Sun, 5 Mar 2006, Ryan Anderson wrote:

> +use Data::Dumper;

You really need this?

Ciao,
Dscho

P.S.: Not to be left behind, I also weigh in into the other discussion: 
I'd like "git-{annotate,blame} [HEAD [--]] [filenames...]".

^ permalink raw reply

* Re: What's in git.git
From: Johannes Schindelin @ 2006-03-06  9:15 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7v1wxg82r3.fsf@assigned-by-dhcp.cox.net>

Hi,

On Sun, 5 Mar 2006, Junio C Hamano wrote:

> Another thing I have started in "pu" branch is to stop placing
> an object we decided to delta that is already max-depth deep
> back in the delta-base window, because such a thing only wastes
> the delta base slot.  The changed pack-objects does pick up more
> delta, but the resulting pack seems bigger and I am puzzled why.

Not that I know much about the pack format, but is it possible that the 
deltas are deflated? In that case, a possible explanation is that a better 
delta is less compressible (and taken together, this could amount to more 
bytes).

Ciao,
Dscho

^ permalink raw reply

* Re: What's in git.git
From: Martin Langhoff @ 2006-03-06  9:05 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7v1wxg82r3.fsf@assigned-by-dhcp.cox.net>

On 3/6/06, Junio C Hamano <junkio@cox.net> wrote:
> - The deathmatch between annotate/blame (Ryan Anderson, Fredrik
>   Kuivinen, me cheerleading)

Add fuel to the fire  ;-) Can git-blame take cached git-rev-list
output like annotate does with -S?


m

^ permalink raw reply

* [PATCH] blame: unbreak "diff -U 0".
From: Junio C Hamano @ 2006-03-06  8:45 UTC (permalink / raw)
  To: git; +Cc: Fredrik Kuivinen
In-Reply-To: <7v1wxg82r3.fsf@assigned-by-dhcp.cox.net>

Junio C Hamano <junkio@cox.net> writes:

> The "deathmatch" between Ryan's annotate and Fredrik's blame is
> officially on.  Currently the last test in all three branches
> fail.  Please do not get alarmed.

It turns out the breakage was caused after merging Fredrik's
improvements.  I'm double-checking the fix and will be pushing
out corrected "master", "next" and "pu" shortly.

Sorry, Fredrik.

-- >8 --
Subject: [PATCH] blame: unbreak "diff -U 0".

The commit 604c86d15bb319a1e93ba218fca48ce1c500ae52 changed the
original "diff -u0" to "diff -u -U 0" for portability.

A big mistake without proper testing.

The form "diff -u -U 0" shows the default 3-line contexts,
because -u and -U 0 contradicts with each other; "diff -U 0" (or
its longhand "diff --unified=0") is what we meant.

Signed-off-by: Junio C Hamano <junkio@cox.net>

---

 blame.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

690e307f54b4aa84349b89e8552b25e7495f1cd6
diff --git a/blame.c b/blame.c
index fca22f9..90338af 100644
--- a/blame.c
+++ b/blame.c
@@ -92,7 +92,7 @@ static struct patch *get_patch(struct co
 		die("write failed: %s", strerror(errno));
 	close(fd);
 
-	sprintf(diff_cmd, "diff -u -U 0 %s %s", tmp_path1, tmp_path2);
+	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));
@@ -234,7 +234,7 @@ static void print_patch(struct patch *p)
 	}
 }
 
-#if 0
+#if DEBUG
 /* For debugging only */
 static void print_map(struct commit *cmit, struct commit *other)
 {
-- 
1.2.4.g3bcd

^ permalink raw reply related

* Re: [PATCH] annotate: Support annotation of files on other revisions.
From: Fredrik Kuivinen @ 2006-03-06  7:49 UTC (permalink / raw)
  To: Martin Langhoff; +Cc: Ryan Anderson, Fredrik Kuivinen, git, junkio
In-Reply-To: <46a038f90603052018n76d316eclf84b6921c9595f16@mail.gmail.com>

On Mon, Mar 06, 2006 at 05:18:35PM +1300, Martin Langhoff wrote:
> On 3/6/06, Ryan Anderson <ryan@michonline.com> wrote:
> > annotate was *trying* to handle that cleanly, but failed due to a silly
> 
> Great stuff, thanks! I'll let it hit master and then I'll drop the
> messy part of req_annotate() in cvsserver.
> 
> > For annotate, the syntax I was using was:
> >         git annotate Makefile headname
> >
> > I'm not married to it, so please, send a patch to change it if you want
> > (Please fix up the test case I'm sending in this patch, as well.)
> 
> That's _perfect_. I was just making the syntax up.
> 

That syntax should work with git-blame too. If it doesn't, it's a bug.

- Fredrik

^ permalink raw reply

* What's in git.git
From: Junio C Hamano @ 2006-03-06  7:13 UTC (permalink / raw)
  To: git

The "deathmatch" between Ryan's annotate and Fredrik's blame is
officially on.  Currently the last test in all three branches
fail.  Please do not get alarmed.

I'd like asciidoc tweaks in "next" by Francis Daly tested by
people who have access to different vintages of docbook-xsl by
trying to build manpages.  Look for displayed examples, such as
the one in git-branch documentation.  I've tried it with v1.68
and getting far better results than before, and Francis says
v1.69 works fine with or without the change. IOW this is a
workaround for a problem in v1.68.

I've been tweaking on-and-off the similarity estimator in the
"next" branch.  It has become independent from the xdelta code
used for pack generation.  It may detect more renames that it
missed before, and it may miss some other renames and copies.
In general, it seems that the algorithm tends to detect slightly
more breaks than before.  I'd appreciate feedback from people
interested in this area.

Another thing I have started in "pu" branch is to stop placing
an object we decided to delta that is already max-depth deep
back in the delta-base window, because such a thing only wastes
the delta base slot.  The changed pack-objects does pick up more
delta, but the resulting pack seems bigger and I am puzzled why.

This may suggest that our criteria to delta should be tightened
a bit (the value of max_size in try_delta should be decreased).
We are better off storing a plain deflated representation instad
of generating a bad, bigger delta.  Insights?

-- -- --

* The 'master' branch has these since the last announcement.

- Documentation fixes (Dmitry V. Levin, Mark Wooding, Martin
  Langhoff, Jeff Muizelaar)

  git/Documentation: fix SYNOPSIS style bugs
  Documentation/Makefile: Some `git-*.txt' files aren't manpages.
  cvsserver: updated documentation
  cosmetics: change from 'See-Also' to 'See Also'
  documentation: add 'see also' sections to git-rm and git-add

- The deathmatch between annotate/blame (Ryan Anderson, Fredrik
  Kuivinen, me cheerleading)

  annotate: Support annotation of files on other revisions.
  git-blame: Make the output human readable
  git-blame: Use the same tests for git-blame as for git-annotate
  blame: avoid -lm by not using log().
  blame and annotate: show localtime with timezone.
  blame: avoid "diff -u0".
  annotate/blame tests updates.
  annotate-blame test: don't "source", but say "."
  annotate-blame test: add evil merge.

- Tweak rev-list (Linus Torvalds)

  get_revision(): do not dig deeper when we know we are at the end.

- Fix git-commit --amend (me)

  git-commit --amend: allow empty commit.

- Misc fixes and cleanups (Mark Wooding and me)

  gitignore: Ignore some more boring things.
  contrib/emacs/Makefile: Provide tool for byte-compiling files.
  Const tightening.

* The 'next' branch, in addition, has these.

- Fix manpage formatting (Francis Daly)

  Tweak asciidoc output to work with broken docbook-xsl

- Tweak break/rename/copy similarity estimator tweaks (me)

  diffcore-rename: similarity estimator fix.
  count-delta: no need for this anymore.
  diffcore-break: similarity estimator fix.
  diffcore-delta: make change counter to byte oriented again.

- Help pack generation tweaks (me)

  verify-pack -v: show delta-chain histogram.

- checkout-index --temp (Shawn Pearce)

  Add --temp and --stage=all options to checkout-index.

* The 'pu' branch, in addition, has these.

- WIP: pack generation tweak (me)

  [WIP] do not waste delta window with objects with already at max-depth.

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox