Git development
 help / color / mirror / Atom feed
* Re: Commits gone AWOL, but not reported by git-fsck --unreachable
From: Anand Kumria @ 2007-05-19 15:45 UTC (permalink / raw)
  To: git
In-Reply-To: <20070519121154.GA10268@coredump.intra.peff.net>

On Sat, 19 May 2007 08:11:54 -0400, Jeff King wrote:

> On Sat, May 19, 2007 at 05:09:33AM -0700, Robin H. Johnson wrote:
> 
>> Ahh, they do show up there (and I learnt a new git command now, thanks)
>> - the ones that I can remember the Subject lines for anyway.
>> 
>> Any hints for the future on finding stuff that is in the reflog but not
>> in the tree? (Remembering every subject line isn't feasible).
> 
> Try 'git-fsck --unreachable --no-reflogs'. :)

What version? git 1.5.1.4 doesn't seem to have that.

Anand

^ permalink raw reply

* Re: [PATCH (amend)] cvsserver: Add test cases for config file handling
From: Frank Lichtenheld @ 2007-05-19 14:05 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <20070514125915.GB5272@planck.djpig.de>

On Mon, May 14, 2007 at 02:59:16PM +0200, Frank Lichtenheld wrote:
> On Sun, May 13, 2007 at 01:04:15PM -0700, Junio C Hamano wrote:
> > Frank Lichtenheld <frank@lichtenheld.de> writes:
> > 
> > > Add a few test cases for the config file parsing
> > > done by git-cvsserver.
> > >
> > > Signed-off-by: Frank Lichtenheld <frank@lichtenheld.de>
> > > ---
> > >  t/t9420-git-cvsserver-config.sh |  109 +++++++++++++++++++++++++++++++++++++++
> > >  1 files changed, 109 insertions(+), 0 deletions(-)
> > >  create mode 100755 t/t9420-git-cvsserver-config.sh
> > 
> > Do we really need a separate test script that does quite similar setup?
> 
> Right now, probably not.
> 
> But I certainly don't intend to do all the tests in one big file which
> will become rather large over time. If you're concerned with code
> duplication, maybe I should move the code to a separate file and source
> it from there?

Still waiting on a comment here...

Gruesse,
-- 
Frank Lichtenheld <frank@lichtenheld.de>
www: http://www.djpig.de/

^ permalink raw reply

* sbuild (or whatever) integration
From: Florian Weimer @ 2007-05-19 13:17 UTC (permalink / raw)
  To: git

I'd like to build something that integrates a Debian autobuilder
(sbuilder probably) with GIT.  The intent is to get rid of a
designated integrator (our team is of a size where this seems a
reasonable thing to do[1]) and ensure predictable builds, independent
of what developers happen to have installed on their local
workstations.

Basically, what I want to do is this:

  1. Perform a checkout from a specific branch.

  2. Synthesize a new version number that is bigger than the previous
     one.  (This needs to keep a bit of extra-repository state.  At
     this stage, it's probably a good idea to ensure that the previous
     commit built on this branch is an ancestor of the current HEAD.)

  3. Generate a fake Debian changelog entry giving the HEAD commit and
     the branch name, using the generated version number.

  4. Build the whole thing in a controlled environment.

  5. Store the build log for later review.

  6. If the build was successful, transfer the packages to some
     repository (based on the branch that was used).

The GIT repository in step one is a shared one, with an update hook
that that ensures fast-forward merges, as in:

<http://www.kernel.org/pub/software/scm/git/docs/howto/update-hook-example.txt>

The idea is that this means that features are monotonic along a
branch.  Ideally, I would like to have something which can assign
meaningful version numbers across two branches, but this is of course
impossible to automatically.  I hope to work with a single release
branch anyway, and use at most two (one for testing, one for
production).

Does this sound reasonable?  Has anybody built something similar?

[1] This is for internal development, and due to various constraints,
    we haven't got developer-specific integration testing
    environments.  That's why we need to linearize before actual
    deployment.

^ permalink raw reply

* [PATCH] Teach 'git-apply --whitespace=strip' to remove empty lines at end of file
From: Marco Costalba @ 2007-05-19 13:12 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
---
  builtin-apply.c |    4 ++++
  1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/builtin-apply.c b/builtin-apply.c
index 0399743..f17f838 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -1738,6 +1738,10 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
  		newsize--;
  	}

+	if (new_whitespace == strip_whitespace)
+		while (newsize > 1 && !strncmp(new + newsize - 2, "\n\n", 2))
+			newsize--;
+
  	oldlines = old;
  	newlines = new;
  	leading = frag->leading;
-- 
1.5.2.rc3.87.g404fd-dirty

^ permalink raw reply related

* Re: [PATCH 07/16] git-read-tree: take --submodules option
From: Sven Verdoolaege @ 2007-05-19 13:05 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Alex Riesen, git
In-Reply-To: <7vd50x1n0r.fsf@assigned-by-dhcp.cox.net>

On Fri, May 18, 2007 at 08:59:48PM -0700, Junio C Hamano wrote:
> Now, suppose "git checkout" needs to recurse into one
> subdirectory that is to have a subproject.  There are three
> cases:
> 
>  (1) There is no git repository yet (the plumbing layer already
>      makes sure there is a directory, but does not do anything
>      else).
> 
>  (2) There already is a git repository there, which is the
>      correct repository (perhaps determined by .gitmodules and
>      .git/config in the superproject, or presense of the commit
>      that is recorded in the superproject's index).
> 
>  (3) There is a git repository but it is not the correct one.
> 
> We've discussed in the other thread about what to do in case
> (1) to some degree.
> 
> For case (2), I think what should happen there is an equivalent
> of this:
> 
> 	$ commit=$(git-rev-parse :subproject)
>         $ cd subproject
> 	$ git-rev-parse --verify $commit || git fetch || barf
>         $ git checkout $commit

Does everyone agree that we should fetch (possibly after asking
for confirmation from the use) _during_ the checkout ?
I now only fetch submodules during a fetch of the supermodule
(actually, in my current patch set, I only fetch a submodule
the first time I see it, but that's a bug), but if there is
a consensus on this, I can switch to fetching during checkout.

As to the key to use to lookup the URL in the config, right
now I simply use the directory name where it is attached
(which seems like a useful default to me).
I'm not all that convinced that we should store a default URL
in history, so AFAICS, the only thing we need to store is a
mapping between directory names and subproject names.
It has been suggested to do that in .gitattributes.
Is that OK for everyone, or do we really need a separate .gitmodules ?

skimo

^ permalink raw reply

* Re: [3/4] What's not in 1.5.2 (new topics)
From: Sven Verdoolaege @ 2007-05-19 12:50 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: Andy Parkins, git, Josef Weidendorfer, Junio C Hamano,
	Nicolas Pitre
In-Reply-To: <20070518110804.GD4708@mellanox.co.il>

On Fri, May 18, 2007 at 02:08:04PM +0300, Michael S. Tsirkin wrote:
> How about an ability for git-daemon to get commands with git-config?

You mean something like dump-config ?

skimo

^ permalink raw reply

* Re: [PATCH 12/16] builtin-fetch--tool: extend "native-store" for use in cloning
From: Sven Verdoolaege @ 2007-05-19 12:17 UTC (permalink / raw)
  To: Alex Riesen; +Cc: git, Junio C Hamano
In-Reply-To: <20070518225234.GI10475@steel.home>

On Sat, May 19, 2007 at 12:52:34AM +0200, Alex Riesen wrote:
> This code produces warning about possible uninitialized used of
> single_force and not_for_merge. I used the patch below, but didn't
> look into what the "all" does.

Sorry for being sloppy.  It is assumed that "all" is only used
for cloning and then the other two flags don't really matter,
but I should've set some defaults.
Yours look fine, thanks.

A fixed-up version is available in the "submodules" branch
of http://www.liacs.nl/~sverdool/git.git or
www.liacs.nl/~sverdool/gitweb.cgi?p=git.git;a=shortlog;h=submodules

skimo

^ permalink raw reply

* Re: Commits gone AWOL, but not reported by git-fsck --unreachable
From: Jeff King @ 2007-05-19 12:11 UTC (permalink / raw)
  To: Robin H. Johnson; +Cc: Git Mailing List
In-Reply-To: <20070519120933.GW17511@curie-int.orbis-terrarum.net>

On Sat, May 19, 2007 at 05:09:33AM -0700, Robin H. Johnson wrote:

> Ahh, they do show up there (and I learnt a new git command now, thanks)
> - the ones that I can remember the Subject lines for anyway.
> 
> Any hints for the future on finding stuff that is in the reflog but not
> in the tree? (Remembering every subject line isn't feasible).

Try 'git-fsck --unreachable --no-reflogs'. :)

-Peff

^ permalink raw reply

* Re: Commits gone AWOL, but not reported by git-fsck --unreachable
From: Robin H. Johnson @ 2007-05-19 12:09 UTC (permalink / raw)
  To: Jeff King, Git Mailing List
In-Reply-To: <20070519115245.GA10035@coredump.intra.peff.net>

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

On Sat, May 19, 2007 at 07:52:45AM -0400, Jeff King wrote:
> On Sat, May 19, 2007 at 03:30:11AM -0700, Robin H. Johnson wrote:
> > Problem:
> > --------
> > A bunch of my commits are NOT present in git-log, git-rev-list, or any
> > tool that provides the history of my branches.
> > 
> > If I hack around and recover the commitid, I can pass it to git-show,
> > and the commit does still exist in the data.
> > 
> > git-fsck --unreachable does not list the commit in question either, but
> > does list other blobs and trees.
> Are you sure it isn't in your reflog? Try 'git reflog show' or 'git log
> -g'.
Ahh, they do show up there (and I learnt a new git command now, thanks)
- the ones that I can remember the Subject lines for anyway.

Any hints for the future on finding stuff that is in the reflog but not
in the tree? (Remembering every subject line isn't feasible).

-- 
Robin Hugh Johnson
Gentoo Linux Developer & Council Member
E-Mail     : robbat2@gentoo.org
GnuPG FP   : 11AC BA4F 4778 E3F6 E4ED  F38E B27B 944E 3488 4E85

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

^ permalink raw reply

* Re: [PATCH] git-svn: avoid crashing svnserve when creating new directories
From: Matthieu Moy @ 2007-05-19 12:02 UTC (permalink / raw)
  To: Eric Wong; +Cc: Junio C Hamano, git
In-Reply-To: <20070519105800.GA22230@muzzle>

Eric Wong <normalperson@yhbt.net> writes:

> I have a modified version of the test I posted earlier based on your
> test.  However, I'm not comfortable binding to any port (even without
> ssh) on a users machine or potentially leaving a daemon running if the
> test is interrupted.

How about keeping the pid somewhere, and using "trap" to kill the svn
server process on interruption?

-- 
Matthieu

^ permalink raw reply

* [StGIT PATCH] Refactor message printing
From: Karl Hasselström @ 2007-05-19 11:54 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git

Feed all those little progress and status messages through a central
place where such things as newlines, indentation, and suppressing
output when not on a TTY can be taken care of once and for all.

This patch takes care of almost, but not quite, all output. The most
notable leftover is the printing of help messages.

Signed-off-by: Karl Hasselström <kha@treskal.com>
---

 stgit/commands/applied.py    |    4 +-
 stgit/commands/assimilate.py |    6 +--
 stgit/commands/branch.py     |   55 +++++++++++++--------------
 stgit/commands/clean.py      |    7 +--
 stgit/commands/commit.py     |    5 +-
 stgit/commands/common.py     |   48 ++++++++++--------------
 stgit/commands/delete.py     |    2 -
 stgit/commands/diff.py       |    2 -
 stgit/commands/export.py     |    8 ++--
 stgit/commands/files.py      |    6 +--
 stgit/commands/fold.py       |    7 +--
 stgit/commands/hide.py       |    2 -
 stgit/commands/id.py         |    2 -
 stgit/commands/imprt.py      |   10 +----
 stgit/commands/log.py        |    2 -
 stgit/commands/mail.py       |   14 +++----
 stgit/commands/patches.py    |    2 -
 stgit/commands/pick.py       |   31 +++++++--------
 stgit/commands/pull.py       |    4 +-
 stgit/commands/push.py       |    7 +--
 stgit/commands/refresh.py    |   14 +++----
 stgit/commands/rename.py     |    5 +-
 stgit/commands/series.py     |    8 ++--
 stgit/commands/sync.py       |   14 ++-----
 stgit/commands/top.py        |    2 -
 stgit/commands/unapplied.py  |    4 +-
 stgit/commands/uncommit.py   |    8 +---
 stgit/commands/unhide.py     |    2 -
 stgit/git.py                 |   18 ++++-----
 stgit/gitmergeonefile.py     |   45 ++++++++++------------
 stgit/main.py                |   15 +++----
 stgit/stack.py               |   36 +++++++++---------
 stgit/utils.py               |   85 +++++++++++++++++++++++++++++++++++++++++-
 33 files changed, 259 insertions(+), 221 deletions(-)

diff --git a/stgit/commands/applied.py b/stgit/commands/applied.py
index f131d62..0925de0 100644
--- a/stgit/commands/applied.py
+++ b/stgit/commands/applied.py
@@ -47,7 +47,7 @@ def func(parser, options, args):
     applied = crt_series.get_applied()
 
     if options.count:
-        print len(applied)
+        out.stdout(len(applied))
     else:
         for p in applied:
-            print p
+            out.stdout(p)
diff --git a/stgit/commands/assimilate.py b/stgit/commands/assimilate.py
index a8b3bfe..e5ebb55 100644
--- a/stgit/commands/assimilate.py
+++ b/stgit/commands/assimilate.py
@@ -42,7 +42,7 @@ def func(parser, options, args):
     """
 
     def nothing_to_do():
-        print 'No commits to assimilate'
+        out.info('No commits to assimilate')
 
     top_patch = crt_series.get_current_patch()
     if not top_patch:
@@ -79,8 +79,8 @@ def func(parser, options, args):
 
     victims.reverse()
     for victim in victims:
-        print ('Creating patch "%s" from commit %s'
-               % (patch2name[victim], victim))
+        out.info('Creating patch "%s" from commit %s'
+                 % (patch2name[victim], victim))
         aname, amail, adate = name_email_date(victim.get_author())
         cname, cmail, cdate = name_email_date(victim.get_committer())
         crt_series.new_patch(
diff --git a/stgit/commands/branch.py b/stgit/commands/branch.py
index 5e7b0df..07bdca8 100644
--- a/stgit/commands/branch.py
+++ b/stgit/commands/branch.py
@@ -83,8 +83,8 @@ def __print_branch(branch_name, length):
         current = '>'
     if branch.get_protected():
         protected = 'p'
-    print current + ' ' + initialized + protected + '\t' + \
-          branch_name.ljust(length) + '  | ' + branch.get_description()
+    out.stdout(current + ' ' + initialized + protected + '\t'
+               + branch_name.ljust(length) + '  | ' + branch.get_description())
 
 def __delete_branch(doomed_name, force = False):
     doomed = stack.Series(doomed_name)
@@ -92,8 +92,7 @@ def __delete_branch(doomed_name, force = False):
     if doomed.get_protected():
         raise CmdException, 'This branch is protected. Delete is not permitted'
 
-    print 'Deleting branch "%s"...' % doomed_name,
-    sys.stdout.flush()
+    out.start('Deleting branch "%s"' % doomed_name)
 
     if __is_current_branch(doomed_name):
         check_local_changes()
@@ -108,7 +107,7 @@ def __delete_branch(doomed_name, force = False):
     if doomed_name != 'master':
         git.delete_branch(doomed_name)
 
-    print 'done'
+    out.done()
 
 def func(parser, options, args):
 
@@ -127,17 +126,20 @@ def func(parser, options, args):
                 if git.rev_parse(args[1]) == git.rev_parse('refs/heads/' + args[1]):
                     # we are for sure referring to a branch
                     parentbranch = 'refs/heads/' + args[1]
-                    print 'Recording "%s" as parent branch.' % parentbranch
+                    out.info('Recording "%s" as parent branch' % parentbranch)
                 elif git.rev_parse(args[1]) and re.search('/', args[1]):
                     # FIXME: should the test be more strict ?
                     parentbranch = args[1]
                 else:
                     # Note: this includes refs to StGIT patches
-                    print 'Don\'t know how to determine parent branch from "%s".' % args[1]
+                    out.info('Don\'t know how to determine parent branch'
+                             ' from "%s"' % args[1])
                     parentbranch = None
             except git.GitException:
-                # should use a more specific exception to catch only non-git refs ?
-                print 'Don\'t know how to determine parent branch from "%s".' % args[1]
+                # should use a more specific exception to catch only
+                # non-git refs ?
+                out.info('Don\'t know how to determine parent branch'
+                         ' from "%s"' % args[1])
                 parentbranch = None
 
             tree_id = git_id(args[1])
@@ -148,9 +150,10 @@ def func(parser, options, args):
         if parentbranch:
             parentremote = git.identify_remote(parentbranch)
             if parentremote:
-                print 'Using "%s" remote to pull parent from.' % parentremote
+                out.info('Using remote "%s" to pull parent from'
+                         % parentremote)
             else:
-                print 'Recording as a local branch.'
+                out.info('Recording as a local branch')
         else:
             # no known parent branch, can't guess the remote
             parentremote = None
@@ -159,7 +162,7 @@ def func(parser, options, args):
                                    parent_remote = parentremote,
                                    parent_branch = parentbranch)
 
-        print 'Branch "%s" created.' % args[0]
+        out.info('Branch "%s" created' % args[0])
         return
 
     elif options.clone:
@@ -176,10 +179,9 @@ def func(parser, options, args):
         check_conflicts()
         check_head_top_equal()
 
-        print 'Cloning current branch to "%s"...' % clone,
-        sys.stdout.flush()
+        out.start('Cloning current branch to "%s"' % clone)
         crt_series.clone(clone)
-        print 'done'
+        out.done()
 
         return
 
@@ -202,12 +204,12 @@ def func(parser, options, args):
         branches.sort()
 
         if branches:
-            print 'Available branches:'
+            out.info('Available branches:')
             max_len = max([len(i) for i in branches])
             for i in branches:
                 __print_branch(i, max_len)
         else:
-            print 'No branches'
+            out.info('No branches')
         return
 
     elif options.protect:
@@ -224,10 +226,9 @@ def func(parser, options, args):
             raise CmdException, 'Branch "%s" is not controlled by StGIT' \
                   % branch_name
 
-        print 'Protecting branch "%s"...' % branch_name,
-        sys.stdout.flush()
+        out.start('Protecting branch "%s"' % branch_name)
         branch.protect()
-        print 'done'
+        out.done()
 
         return
 
@@ -241,7 +242,7 @@ def func(parser, options, args):
 
         stack.Series(args[0]).rename(args[1])
 
-        print 'Renamed branch "%s" as "%s".' % (args[0], args[1])
+        out.info('Renamed branch "%s" to "%s"' % (args[0], args[1]))
 
         return
 
@@ -259,10 +260,9 @@ def func(parser, options, args):
             raise CmdException, 'Branch "%s" is not controlled by StGIT' \
                   % branch_name
 
-        print 'Unprotecting branch "%s"...' % branch_name,
-        sys.stdout.flush()
+        out.info('Unprotecting branch "%s"' % branch_name)
         branch.unprotect()
-        print 'done'
+        out.done()
 
         return
 
@@ -294,12 +294,9 @@ def func(parser, options, args):
         check_conflicts()
         check_head_top_equal()
 
-        print 'Switching to branch "%s"...' % args[0],
-        sys.stdout.flush()
-
+        out.start('Switching to branch "%s"' % args[0])
         git.switch_branch(args[0])
-
-        print 'done'
+        out.done()
         return
 
     # default action: print the current branch
diff --git a/stgit/commands/clean.py b/stgit/commands/clean.py
index 8f5c606..7b57526 100644
--- a/stgit/commands/clean.py
+++ b/stgit/commands/clean.py
@@ -43,14 +43,11 @@ def __delete_empty(patches, applied):
     """
     for p in patches:
         if crt_series.empty_patch(p):
-            print 'Deleting patch "%s"...' % p,
-            sys.stdout.flush()
-
+            out.start('Deleting patch "%s"' % p)
             if applied and crt_series.patch_applied(p):
                 crt_series.pop_patch(p)
             crt_series.delete_patch(p)
-
-            print 'done'
+            out.done()
         elif applied and crt_series.patch_unapplied(p):
             crt_series.push_patch(p)
 
diff --git a/stgit/commands/commit.py b/stgit/commands/commit.py
index 8af6651..2b8d7ce 100644
--- a/stgit/commands/commit.py
+++ b/stgit/commands/commit.py
@@ -54,8 +54,7 @@ def func(parser, options, args):
 
     crt_head = git.get_head()
 
-    print 'Committing %d patches...' % len(applied),
-    sys.stdout.flush()
+    out.start('Committing %d patches' % len(applied))
 
     crt_series.pop_patch(applied[0])
     git.switch(crt_head)
@@ -63,4 +62,4 @@ def func(parser, options, args):
     for patch in applied:
         crt_series.delete_patch(patch)
 
-    print 'done'
+    out.done()
diff --git a/stgit/commands/common.py b/stgit/commands/common.py
index 28026da..22c78ae 100644
--- a/stgit/commands/common.py
+++ b/stgit/commands/common.py
@@ -131,9 +131,9 @@ def print_crt_patch(branch = None):
         patch = stack.Series(branch).get_current()
 
     if patch:
-        print 'Now at patch "%s"' % patch
+        out.info('Now at patch "%s"' % patch)
     else:
-        print 'No patches applied'
+        out.info('No patches applied')
 
 def resolved(filename, reset = None):
     if reset:
@@ -163,58 +163,53 @@ def push_patches(patches, check_merged = False):
     """
     forwarded = crt_series.forward_patches(patches)
     if forwarded > 1:
-        print 'Fast-forwarded patches "%s" - "%s"' % (patches[0],
-                                                      patches[forwarded - 1])
+        out.info('Fast-forwarded patches "%s" - "%s"'
+                 % (patches[0], patches[forwarded - 1]))
     elif forwarded == 1:
-        print 'Fast-forwarded patch "%s"' % patches[0]
+        out.info('Fast-forwarded patch "%s"' % patches[0])
 
     names = patches[forwarded:]
 
     # check for patches merged upstream
     if names and check_merged:
-        print 'Checking for patches merged upstream...',
-        sys.stdout.flush()
+        out.start('Checking for patches merged upstream')
 
         merged = crt_series.merged_patches(names)
 
-        print 'done (%d found)' % len(merged)
+        out.done('%d found' % len(merged))
     else:
         merged = []
 
     for p in names:
-        print 'Pushing patch "%s"...' % p,
-        sys.stdout.flush()
+        out.start('Pushing patch "%s"' % p)
 
         if p in merged:
             crt_series.push_patch(p, empty = True)
-            print 'done (merged upstream)'
+            out.done('merged upstream')
         else:
             modified = crt_series.push_patch(p)
 
             if crt_series.empty_patch(p):
-                print 'done (empty patch)'
+                out.done('empty patch')
             elif modified:
-                print 'done (modified)'
+                out.done('modified')
             else:
-                print 'done'
+                out.done()
 
 def pop_patches(patches, keep = False):
     """Pop the patches in the list from the stack. It is assumed that
     the patches are listed in the stack reverse order.
     """
     if len(patches) == 0:
-        print 'nothing to push/pop'
+        out.info('Nothing to push/pop')
     else:
         p = patches[-1]
         if len(patches) == 1:
-            print 'Popping patch "%s"...' % p,
+            out.start('Popping patch "%s"' % p)
         else:
-            print 'Popping "%s" - "%s" patches...' % (patches[0], p),
-        sys.stdout.flush()
-
+            out.start('Popping patches "%s" - "%s"' % (patches[0], p))
         crt_series.pop_patch(p, keep)
-
-        print 'done'
+        out.done()
 
 def parse_patches(patch_args, patch_list, boundary = 0, ordered = False):
     """Parse patch_args list for patch names in patch_list and return
@@ -334,19 +329,18 @@ def prepare_rebase(real_rebase, force=None):
     # pop all patches
     applied = crt_series.get_applied()
     if len(applied) > 0:
-        print 'Popping all applied patches...',
-        sys.stdout.flush()
+        out.start('Popping all applied patches')
         crt_series.pop_patch(applied[0])
-        print 'done'
+        out.done()
     return applied
 
 def rebase(target):
     if target == git.get_head():
-        print 'Already at "%s", no need for rebasing.' % target
+        out.info('Already at "%s", no need for rebasing.' % target)
         return
-    
-    print 'Rebasing to "%s"...' % target
+    out.start('Rebasing to "%s"' % target)
     git.reset(tree_id = git_id(target))
+    out.done()
 
 def post_rebase(applied, nopush, merged):
     # memorize that we rebased to here
diff --git a/stgit/commands/delete.py b/stgit/commands/delete.py
index e1a70c9..a9e2744 100644
--- a/stgit/commands/delete.py
+++ b/stgit/commands/delete.py
@@ -74,7 +74,7 @@ def func(parser, options, args):
     # delete the patches
     for patch in applied + patches:
         crt_series.delete_patch(patch)
-        print 'Patch "%s" successfully deleted' % patch
+        out.info('Patch "%s" successfully deleted' % patch)
 
     if not options.branch:
         print_crt_patch()
diff --git a/stgit/commands/diff.py b/stgit/commands/diff.py
index d3e1190..f56cbeb 100644
--- a/stgit/commands/diff.py
+++ b/stgit/commands/diff.py
@@ -80,7 +80,7 @@ def func(parser, options, args):
         rev2 = None
 
     if options.stat:
-        print git.diffstat(args, git_id(rev1), git_id(rev2))
+        out.stdout_raw(git.diffstat(args, git_id(rev1), git_id(rev2)) + '\n')
     else:
         diff_str = git.diff(args, git_id(rev1), git_id(rev2),
                             binary = options.binary)
diff --git a/stgit/commands/export.py b/stgit/commands/export.py
index 20d8f67..cafcbe3 100644
--- a/stgit/commands/export.py
+++ b/stgit/commands/export.py
@@ -79,8 +79,8 @@ def func(parser, options, args):
         dirname = 'patches-%s' % crt_series.get_branch()
 
     if not options.branch and git.local_changes():
-        print 'Warning: local changes in the tree. ' \
-              'You might want to commit them first'
+        out.warn('Local changes in the tree;'
+                 ' you might want to commit them first')
 
     if not options.stdout:
         if not os.path.isdir(dirname):
@@ -166,9 +166,9 @@ def func(parser, options, args):
             f = open(pfile, 'w+')
 
         if options.stdout and num > 1:
-            print '-------------------------------------------------------------------------------'
+            print '-'*79
             print patch.get_name()
-            print '-------------------------------------------------------------------------------'
+            print '-'*79
 
         # write description
         f.write(descr)
diff --git a/stgit/commands/files.py b/stgit/commands/files.py
index b33bd2a..59893d8 100644
--- a/stgit/commands/files.py
+++ b/stgit/commands/files.py
@@ -57,8 +57,8 @@ def func(parser, options, args):
     rev2 = git_id('%s//top' % patch)
 
     if options.stat:
-        print git.diffstat(rev1 = rev1, rev2 = rev2)
+        out.stdout_raw(git.diffstat(rev1 = rev1, rev2 = rev2) + '\n')
     elif options.bare:
-        print git.barefiles(rev1, rev2)
+        out.stdout_raw(git.barefiles(rev1, rev2) + '\n')
     else:
-        print git.files(rev1, rev2)
+        out.stdout_raw(git.files(rev1, rev2) + '\n')
diff --git a/stgit/commands/fold.py b/stgit/commands/fold.py
index e87ded2..297dfbf 100644
--- a/stgit/commands/fold.py
+++ b/stgit/commands/fold.py
@@ -61,12 +61,11 @@ def func(parser, options, args):
 
     if filename:
         if os.path.exists(filename):
-            print 'Folding patch "%s"...' % filename,
+            out.start('Folding patch "%s"' % filename)
         else:
             raise CmdException, 'No such file: %s' % filename
     else:
-        print 'Folding patch from stdin...',
-    sys.stdout.flush()
+        out.start('Folding patch from stdin')
 
     if options.threeway:
         crt_patch = crt_series.get_patch(current)
@@ -77,4 +76,4 @@ def func(parser, options, args):
     else:
         git.apply_patch(filename = filename)
 
-    print 'done'
+    out.done()
diff --git a/stgit/commands/hide.py b/stgit/commands/hide.py
index 9ef948d..3cb08e8 100644
--- a/stgit/commands/hide.py
+++ b/stgit/commands/hide.py
@@ -46,4 +46,4 @@ def func(parser, options, args):
 
     for patch in patches:
         crt_series.hide_patch(patch)
-        print 'Patch "%s" hidden' % patch
+        out.info('Patch "%s" hidden' % patch)
diff --git a/stgit/commands/id.py b/stgit/commands/id.py
index 4226adf..8c717e8 100644
--- a/stgit/commands/id.py
+++ b/stgit/commands/id.py
@@ -46,4 +46,4 @@ def func(parser, options, args):
     else:
         parser.error('incorrect number of arguments')
 
-    print git_id(id_str)
+    out.stdout(git_id(id_str))
diff --git a/stgit/commands/imprt.py b/stgit/commands/imprt.py
index 6fcdc62..0089a8b 100644
--- a/stgit/commands/imprt.py
+++ b/stgit/commands/imprt.py
@@ -255,7 +255,7 @@ def __create_patch(filename, message, author_name, author_email,
         raise CmdException, 'No diff found inside the patch'
 
     if options.ignore and patch in crt_series.get_applied():
-        print 'Ignoring already applied patch "%s"' % patch
+        out.info('Ignoring already applied patch "%s"' % patch)
         return
     if options.replace and patch in crt_series.get_unapplied():
         crt_series.delete_patch(patch)
@@ -289,18 +289,14 @@ def __create_patch(filename, message, author_name, author_email,
                          committer_name = committer_name,
                          committer_email = committer_email)
 
-    print 'Importing patch "%s"...' % patch,
-    sys.stdout.flush()
-
+    out.start('Importing patch "%s"' % patch)
     if options.base:
         git.apply_patch(diff = diff, base = git_id(options.base))
     else:
         git.apply_patch(diff = diff)
-
     crt_series.refresh_patch(edit = options.edit,
                              show_patch = options.showpatch)
-
-    print 'done'    
+    out.done()
 
 def __import_file(filename, options, patch = None):
     """Import a patch from a file or standard input
diff --git a/stgit/commands/log.py b/stgit/commands/log.py
index a21789e..e3e17f9 100644
--- a/stgit/commands/log.py
+++ b/stgit/commands/log.py
@@ -70,7 +70,7 @@ def show_log(log, show_patch):
             secs, tz = author_date.split()
             date = '%s %s' % (time.ctime(int(secs)), tz)
 
-            print descr, date
+            out.stdout('%s %s' % (descr, date))
 
         parent = commit.get_parent()
         if parent:
diff --git a/stgit/commands/mail.py b/stgit/commands/mail.py
index 2fcaa5f..1ca6ba2 100644
--- a/stgit/commands/mail.py
+++ b/stgit/commands/mail.py
@@ -500,13 +500,12 @@ def func(parser, options, args):
             ref_id = msg_id
 
         if options.mbox:
-            print msg_string
+            out.stdout_raw(msg_string + '\n')
         else:
-            print 'Sending the cover message...',
-            sys.stdout.flush()
+            out.start('Sending the cover message')
             __send_message(smtpserver, from_addr, to_addr_list, msg_string,
                            sleep, smtpuser, smtppassword)
-            print 'done'
+            out.done()
 
     # send the patches
     if options.template:
@@ -529,10 +528,9 @@ def func(parser, options, args):
             ref_id = msg_id
 
         if options.mbox:
-            print msg_string
+            out.stdout_raw(msg_string + '\n')
         else:
-            print 'Sending patch "%s"...' % p,
-            sys.stdout.flush()
+            out.start('Sending patch "%s"' % p)
             __send_message(smtpserver, from_addr, to_addr_list, msg_string,
                            sleep, smtpuser, smtppassword)
-            print 'done'
+            out.done()
diff --git a/stgit/commands/patches.py b/stgit/commands/patches.py
index dcfbd98..a8fb008 100644
--- a/stgit/commands/patches.py
+++ b/stgit/commands/patches.py
@@ -75,7 +75,7 @@ def func(parser, options, args):
                                   git.diff(args, patch.get_bottom(),
                                            patch.get_top()))
             else:
-                print patch.get_name()
+                out.stdout(patch.get_name())
 
     if options.diff:
         pager(diff_output)
diff --git a/stgit/commands/pick.py b/stgit/commands/pick.py
index ea0756b..a47b201 100644
--- a/stgit/commands/pick.py
+++ b/stgit/commands/pick.py
@@ -91,33 +91,30 @@ def func(parser, options, args):
         top = parent
 
     if options.fold:
-        print 'Folding commit %s...' % commit_id,
-        sys.stdout.flush()
+        out.start('Folding commit %s' % commit_id)
 
         # try a direct git-apply first
         if not git.apply_diff(bottom, top):
             git.merge(bottom, git.get_head(), top, recursive = True)
 
-        print 'done'
+        out.done()
     elif options.update:
         rev1 = git_id('//bottom')
         rev2 = git_id('//top')
         files = git.barefiles(rev1, rev2).split('\n')
 
-        print 'Updating with commit %s...' % commit_id,
-        sys.stdout.flush()
+        out.start('Updating with commit %s' % commit_id)
 
         if not git.apply_diff(bottom, top, files = files):
             raise CmdException, 'Patch updating failed'
 
-        print 'done'
+        out.done()
     else:
         message = commit.get_log()
         author_name, author_email, author_date = \
                      name_email_date(commit.get_author())
 
-        print 'Importing commit %s...' % commit_id,
-        sys.stdout.flush()
+        out.start('Importing commit %s' % commit_id)
 
         newpatch = crt_series.new_patch(patchname, message = message, can_edit = False,
                                         unapplied = True, bottom = bottom, top = top,
@@ -137,23 +134,23 @@ def func(parser, options, args):
                 refseries = crt_series
             patch = refseries.get_patch(refpatchname)
             if patch.get_log():
-                print"log was %s" % newpatch.get_log()
-                print "setting log to %s\n" %  patch.get_log()
+                out.info("Log was %s" % newpatch.get_log())
+                out.info("Setting log to %s\n" %  patch.get_log())
                 newpatch.set_log(patch.get_log())
-                print"log is now %s" % newpatch.get_log()
+                out.info("Log is now %s" % newpatch.get_log())
             else:
-                print "no log for %s\n" % patchname
- 
+                out.info("No log for %s\n" % patchname)
+
         if not options.unapplied:
             modified = crt_series.push_patch(patchname)
         else:
             modified = False
 
         if crt_series.empty_patch(patchname):
-            print 'done (empty patch)'
+            out.done('empty patch')
         elif modified:
-            print 'done (modified)'
+            out.done('modified')
         else:
-            print 'done'
-        
+            out.done()
+
     print_crt_patch()
diff --git a/stgit/commands/pull.py b/stgit/commands/pull.py
index f551b98..beaa7b5 100644
--- a/stgit/commands/pull.py
+++ b/stgit/commands/pull.py
@@ -90,10 +90,10 @@ def func(parser, options, args):
 
     # pull the remote changes
     if policy == 'pull':
-        print 'Pulling from "%s"...' % repository
+        out.info('Pulling from "%s"' % repository)
         git.pull(repository)
     elif policy == 'fetch-rebase':
-        print 'Fetching from "%s"...' % repository
+        out.info('Fetching from "%s"' % repository)
         git.fetch(repository)
         rebase(git.fetch_head())
     elif policy == 'rebase':
diff --git a/stgit/commands/push.py b/stgit/commands/push.py
index a636ad2..17b32f6 100644
--- a/stgit/commands/push.py
+++ b/stgit/commands/push.py
@@ -64,13 +64,12 @@ def func(parser, options, args):
         if not patch:
             raise CmdException, 'No patch to undo'
 
-        print 'Undoing the "%s" push...' % patch,
-        sys.stdout.flush()
+        out.start('Undoing push of "%s"' % patch)
         resolved_all()
         if crt_series.undo_push():
-            print 'done'
+            out.done()
         else:
-            print 'done (patch unchanged)'
+            out.done('patch unchanged')
         print_crt_patch()
 
         return
diff --git a/stgit/commands/refresh.py b/stgit/commands/refresh.py
index 4cf09ae..77dcbda 100644
--- a/stgit/commands/refresh.py
+++ b/stgit/commands/refresh.py
@@ -98,10 +98,9 @@ def func(parser, options, args):
         check_head_top_equal()
 
     if options.undo:
-        print 'Undoing the "%s" refresh...' % patch,
-        sys.stdout.flush()
+        out.start('Undoing the refresh of "%s"' % patch)
         crt_series.undo_refresh()
-        print 'done'
+        out.done()
         return
 
     if options.author:
@@ -128,8 +127,7 @@ def func(parser, options, args):
             between = applied[:applied.index(patch):-1]
             pop_patches(between, keep = True)
 
-        print 'Refreshing patch "%s"...' % patch,
-        sys.stdout.flush()
+        out.start('Refreshing patch "%s"' % patch)
 
         if autoresolved == 'yes':
             resolved_all()
@@ -145,12 +143,12 @@ def func(parser, options, args):
                                  backup = True, sign_str = sign_str)
 
         if crt_series.empty_patch(patch):
-            print 'done (empty patch)'
+            out.done('empty patch')
         else:
-            print 'done'
+            out.done()
 
         if options.patch:
             between.reverse()
             push_patches(between)
     else:
-        print 'Patch "%s" is already up to date' % patch
+        out.info('Patch "%s" is already up to date' % patch)
diff --git a/stgit/commands/rename.py b/stgit/commands/rename.py
index ca799c3..d6c53be 100644
--- a/stgit/commands/rename.py
+++ b/stgit/commands/rename.py
@@ -38,7 +38,6 @@ def func(parser, options, args):
     if len(args) != 2:
         parser.error('incorrect number of arguments')
 
-    print 'Renaming patch "%s" -> "%s"...' % (args[0], args[1]),
-    sys.stdout.flush()
+    out.start('Renaming patch "%s" to "%s"' % (args[0], args[1]))
     crt_series.rename_patch(args[0], args[1])
-    print 'done'
+    out.done()
diff --git a/stgit/commands/series.py b/stgit/commands/series.py
index 7777201..b699902 100644
--- a/stgit/commands/series.py
+++ b/stgit/commands/series.py
@@ -102,11 +102,11 @@ def __print_patch(patch, hidden, branch_str, prefix, empty_prefix, length,
         patch_str = patch_str.ljust(length)
 
     if options.description:
-        print prefix + patch_str + ' | ' + __get_description(patch)
+        out.stdout(prefix + patch_str + ' | ' + __get_description(patch))
     elif options.author:
-        print prefix + patch_str + ' | ' + __get_author(patch)
+        out.stdout(prefix + patch_str + ' | ' + __get_author(patch))
     else:
-        print prefix + patch_str
+        out.stdout(prefix + patch_str)
 
 def func(parser, options, args):
     """Show the patch series
@@ -157,7 +157,7 @@ def func(parser, options, args):
     patches = applied + unapplied
 
     if options.count:
-        print len(patches)
+        out.stdout(len(patches))
         return
 
     if not patches:
diff --git a/stgit/commands/sync.py b/stgit/commands/sync.py
index db52bcd..8359061 100644
--- a/stgit/commands/sync.py
+++ b/stgit/commands/sync.py
@@ -74,13 +74,10 @@ def func(parser, options, args):
                   '--undo cannot be specified with --branch or --series'
         __check_all()
 
-        print 'Undoing the "%s" sync...' % crt_series.get_current(),
-        sys.stdout.flush()
-
+        out.start('Undoing the sync of "%s"' % crt_series.get_current())
         crt_series.undo_refresh()
         git.reset()
-
-        print 'done'
+        out.done()
         return
 
     if options.branch:
@@ -147,8 +144,7 @@ def func(parser, options, args):
             del popped[:idx]
 
         # the actual sync
-        print 'Synchronising "%s"...' % p,
-        sys.stdout.flush()
+        out.start('Synchronising "%s"' % p)
 
         patch = crt_series.get_patch(p)
         bottom = patch.get_bottom()
@@ -167,9 +163,9 @@ def func(parser, options, args):
             # backup information was already reset above
             crt_series.refresh_patch(cache_update = False, backup = False,
                                      log = 'sync')
-            print 'done (updated)'
+            out.done('updated')
         else:
-            print 'done'
+            out.done()
 
     # push the remaining patches
     if popped:
diff --git a/stgit/commands/top.py b/stgit/commands/top.py
index f36a0b0..7cc92ca 100644
--- a/stgit/commands/top.py
+++ b/stgit/commands/top.py
@@ -41,6 +41,6 @@ def func(parser, options, args):
 
     name = crt_series.get_current()
     if name:
-        print name
+        out.stdout(name)
     else:
         raise CmdException, 'No patches applied'
diff --git a/stgit/commands/unapplied.py b/stgit/commands/unapplied.py
index cbac052..0d330a1 100644
--- a/stgit/commands/unapplied.py
+++ b/stgit/commands/unapplied.py
@@ -46,7 +46,7 @@ def func(parser, options, args):
     unapplied = crt_series.get_unapplied()
 
     if options.count:
-        print len(unapplied)
+        out.stdout(len(unapplied))
     else:
         for p in unapplied:
-            print p
+            out.stdout(p)
diff --git a/stgit/commands/uncommit.py b/stgit/commands/uncommit.py
index d5f64da..f611d29 100644
--- a/stgit/commands/uncommit.py
+++ b/stgit/commands/uncommit.py
@@ -99,13 +99,13 @@ def func(parser, options, args):
     commits = []
     next_commit = crt_series.get_base()
     if patch_nr:
-        print 'Uncommitting %d patches...' % patch_nr,
+        out.start('Uncommitting %d patches' % patch_nr)
         for i in xrange(patch_nr):
             commit, commit_id, parent = get_commit(next_commit)
             commits.append((commit, commit_id, parent))
             next_commit = parent
     else:
-        print 'Uncommitting to %s...' % to_commit
+        out.start('Uncommitting to %s' % to_commit)
         while True:
             commit, commit_id, parent = get_commit(next_commit)
             commits.append((commit, commit_id, parent))
@@ -114,7 +114,6 @@ def func(parser, options, args):
             next_commit = parent
         patch_nr = len(commits)
 
-    sys.stdout.flush()
     for (commit, commit_id, parent), patchname in \
         zip(commits, patchnames or [None for i in xrange(len(commits))]):
         author_name, author_email, author_date = \
@@ -127,5 +126,4 @@ def func(parser, options, args):
                              author_email = author_email,
                              author_date = author_date)
 
-
-    print 'done'
+    out.done()
diff --git a/stgit/commands/unhide.py b/stgit/commands/unhide.py
index f2db05f..74e4743 100644
--- a/stgit/commands/unhide.py
+++ b/stgit/commands/unhide.py
@@ -46,4 +46,4 @@ def func(parser, options, args):
 
     for patch in patches:
         crt_series.unhide_patch(patch)
-        print 'Patch "%s" unhidden' % patch
+        out.info('Patch "%s" unhidden' % patch)
diff --git a/stgit/git.py b/stgit/git.py
index 86630ce..845c712 100644
--- a/stgit/git.py
+++ b/stgit/git.py
@@ -220,9 +220,8 @@ def __tree_status(files = None, tree_id = 'HEAD', unknown = False,
                   noexclude = True, verbose = False):
     """Returns a list of pairs - [status, filename]
     """
-    if verbose and sys.stdout.isatty():
-        print 'Checking for changes in the working directory...',
-        sys.stdout.flush()
+    if verbose:
+        out.start('Checking for changes in the working directory')
 
     refresh_index()
 
@@ -260,8 +259,8 @@ def __tree_status(files = None, tree_id = 'HEAD', unknown = False,
         if fs[1] not in conflicts:
             cache_files.append(fs)
 
-    if verbose and sys.stdout.isatty():
-        print 'done'
+    if verbose:
+        out.done()
 
     return cache_files
 
@@ -449,8 +448,7 @@ def __copy_single(source, target, target2=''):
         for f in [f.strip() for f in realfiles]:
             m = prefix_regexp.match(f)
             if not m:
-                print '"%s" does not match "%s"' % (f, re_string)
-                assert(m)
+                raise Exception, '"%s" does not match "%s"' % (f, re_string)
             newname = target+target2+'/'+m.group(1)
             if not os.path.exists(os.path.dirname(newname)):
                 os.makedirs(os.path.dirname(newname))
@@ -767,9 +765,9 @@ def status(files = None, modified = False, new = False, deleted = False,
         if files and not fs[1] in files:
             continue
         if all:
-            print '%s %s' % (fs[0], fs[1])
+            out.stdout('%s %s' % (fs[0], fs[1]))
         else:
-            print '%s' % fs[1]
+            out.stdout('%s' % fs[1])
 
 def diff(files = None, rev1 = 'HEAD', rev2 = None, out_fd = None,
          binary = False):
@@ -958,7 +956,7 @@ def apply_patch(filename = None, diff = None, base = None,
             f = file('.stgit-failed.patch', 'w+')
             f.write(diff)
             f.close()
-            print >> sys.stderr, 'Diff written to the .stgit-failed.patch file'
+            out.warn('Diff written to the .stgit-failed.patch file')
 
         raise
 
diff --git a/stgit/gitmergeonefile.py b/stgit/gitmergeonefile.py
index 6f4c1d7..303e5f7 100644
--- a/stgit/gitmergeonefile.py
+++ b/stgit/gitmergeonefile.py
@@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 import sys, os
 from stgit import basedir
 from stgit.config import config, file_extensions, ConfigOption
-from stgit.utils import append_string
+from stgit.utils import append_string, out
 
 
 class GitMergeException(Exception):
@@ -133,8 +133,8 @@ def interactive_merge(filename):
 
     mtime = os.path.getmtime(filename)
 
-    print 'Trying the interractive %s merge' % \
-          (three_way and 'three-way' or 'two-way')
+    out.info('Trying the interactive %s merge'
+             % (three_way and 'three-way' or 'two-way'))
 
     err = os.system(imerger % files_dict)
     if err != 0:
@@ -165,16 +165,15 @@ def merge(orig_hash, file1_hash, file2_hash,
             if file1_hash == file2_hash:
                 if os.system('git-update-index --cacheinfo %s %s %s'
                              % (file1_mode, file1_hash, path)) != 0:
-                    print >> sys.stderr, 'Error: git-update-index failed'
+                    out.error('git-update-index failed')
                     __conflict(path)
                     return 1
                 if os.system('git-checkout-index -u -f -- %s' % path):
-                    print >> sys.stderr, 'Error: git-checkout-index failed'
+                    out.error('git-checkout-index failed')
                     __conflict(path)
                     return 1
                 if file1_mode != file2_mode:
-                    print >> sys.stderr, \
-                          'Error: File added in both, permissions conflict'
+                    out.error('File added in both, permissions conflict')
                     __conflict(path)
                     return 1
             # 3-way merge
@@ -189,9 +188,8 @@ def merge(orig_hash, file1_hash, file2_hash,
                     __remove_files(orig_hash, file1_hash, file2_hash)
                     return 0
                 else:
-                    print >> sys.stderr, \
-                          'Error: three-way merge tool failed for file "%s"' \
-                          % path
+                    out.error('Three-way merge tool failed for file "%s"'
+                              % path)
                     # reset the cache to the first branch
                     os.system('git-update-index --cacheinfo %s %s %s'
                               % (file1_mode, file1_hash, path))
@@ -201,7 +199,7 @@ def merge(orig_hash, file1_hash, file2_hash,
                             interactive_merge(path)
                         except GitMergeException, ex:
                             # interactive merge failed
-                            print >> sys.stderr, str(ex)
+                            out.error(ex)
                             if str(keeporig) != 'yes':
                                 __remove_files(orig_hash, file1_hash,
                                                file2_hash)
@@ -260,23 +258,21 @@ def merge(orig_hash, file1_hash, file2_hash,
             if file1_hash == file2_hash:
                 if os.system('git-update-index --add --cacheinfo %s %s %s'
                              % (file1_mode, file1_hash, path)) != 0:
-                    print >> sys.stderr, 'Error: git-update-index failed'
+                    out.error('git-update-index failed')
                     __conflict(path)
                     return 1
                 if os.system('git-checkout-index -u -f -- %s' % path):
-                    print >> sys.stderr, 'Error: git-checkout-index failed'
+                    out.error('git-checkout-index failed')
                     __conflict(path)
                     return 1
                 if file1_mode != file2_mode:
-                    print >> sys.stderr, \
-                          'Error: File "s" added in both, ' \
-                          'permissions conflict' % path
+                    out.error('File "s" added in both, permissions conflict'
+                              % path)
                     __conflict(path)
                     return 1
             # files added in both but different
             else:
-                print >> sys.stderr, \
-                      'Error: File "%s" added in branches but different' % path
+                out.error('File "%s" added in branches but different' % path)
                 # reset the cache to the first branch
                 os.system('git-update-index --cacheinfo %s %s %s'
                           % (file1_mode, file1_hash, path))
@@ -286,7 +282,7 @@ def merge(orig_hash, file1_hash, file2_hash,
                         interactive_merge(path)
                     except GitMergeException, ex:
                         # interactive merge failed
-                        print >> sys.stderr, str(ex)
+                        out.error(ex)
                         if str(keeporig) != 'yes':
                             __remove_files(orig_hash, file1_hash,
                                            file2_hash)
@@ -312,17 +308,16 @@ def merge(orig_hash, file1_hash, file2_hash,
                 obj = file2_hash
             if os.system('git-update-index --add --cacheinfo %s %s %s'
                          % (mode, obj, path)) != 0:
-                print >> sys.stderr, 'Error: git-update-index failed'
+                out.error('git-update-index failed')
                 __conflict(path)
                 return 1
             __remove_files(orig_hash, file1_hash, file2_hash)
             return os.system('git-checkout-index -u -f -- %s' % path)
 
     # Unhandled case
-    print >> sys.stderr, 'Error: Unhandled merge conflict: ' \
-          '"%s" "%s" "%s" "%s" "%s" "%s" "%s"' \
-          % (orig_hash, file1_hash, file2_hash,
-             path,
-             orig_mode, file1_mode, file2_mode)
+    out.error('Unhandled merge conflict: "%s" "%s" "%s" "%s" "%s" "%s" "%s"'
+              % (orig_hash, file1_hash, file2_hash,
+                 path,
+                 orig_mode, file1_mode, file2_mode))
     __conflict(path)
     return 1
diff --git a/stgit/main.py b/stgit/main.py
index 1a1f534..bcadbad 100644
--- a/stgit/main.py
+++ b/stgit/main.py
@@ -36,14 +36,12 @@ class Commands(dict):
         candidates = [cmd for cmd in self.keys() if cmd.startswith(key)]
 
         if not candidates:
-            print >> sys.stderr, 'Unknown command: %s' % key
-            print >> sys.stderr, '  Try "%s help" for a list of ' \
-                  'supported commands' % prog
+            out.error('Unknown command: %s' % key,
+                      'Try "%s help" for a list of supported commands' % prog)
             sys.exit(1)
         elif len(candidates) > 1:
-            print >> sys.stderr, 'Ambiguous command: %s' % key
-            print >> sys.stderr, '  Candidates are: %s' \
-                  % ', '.join(candidates)
+            out.error('Ambiguous command: %s' % key,
+                      'Candidates are: %s' % ', '.join(candidates))
             sys.exit(1)
 
         return candidates[0]
@@ -220,8 +218,7 @@ def main():
         if len(sys.argv) == 3 and not sys.argv[2] in ['-h', '--help']:
             cmd = commands.canonical_cmd(sys.argv[2])
             if not cmd in commands:
-                print >> sys.stderr, '%s help: "%s" command unknown' \
-                      % (prog, cmd)
+                out.error('%s help: "%s" command unknown' % (prog, cmd))
                 sys.exit(1)
 
             sys.argv[0] += ' %s' % cmd
@@ -267,7 +264,7 @@ def main():
     except KeyError:
         debug_level = 0
     except ValueError:
-        print >> sys.stderr, 'Invalid STGIT_DEBUG_LEVEL environment variable'
+        out.error('Invalid STGIT_DEBUG_LEVEL environment variable')
         sys.exit(1)
 
     try:
diff --git a/stgit/stack.py b/stgit/stack.py
index 223f3ee..63076af 100644
--- a/stgit/stack.py
+++ b/stgit/stack.py
@@ -307,6 +307,7 @@ def update_to_current_format_version(branch, git_dir):
             # The branch doesn't seem to be initialized at all.
             return None
     def set_format_version(v):
+        out.info('Upgraded branch %s to format version %d' % (branch, v))
         config.set(format_version_key(branch), '%d' % v)
     def mkdir(d):
         if not os.path.isdir(d):
@@ -491,10 +492,11 @@ class Series(StgitObject):
         if value:
             return value
         elif 'origin' in git.remotes_list():
-            print 'Notice: no parent remote declared for stack "%s", ' \
-                  'defaulting to "origin". Consider setting "branch.%s.remote" ' \
-                  'and "branch.%s.merge" with "git repo-config".' \
-                  % (self.__name, self.__name, self.__name)
+            out.note(('No parent remote declared for stack "%s",'
+                      ' defaulting to "origin".' % self.__name),
+                     ('Consider setting "branch.%s.remote" and'
+                      ' "branch.%s.merge" with "git repo-config".'
+                      % (self.__name, self.__name)))
             return 'origin'
         else:
             raise StackException, 'Cannot find a parent remote for "%s"' % self.__name
@@ -507,10 +509,10 @@ class Series(StgitObject):
         if value:
             return value
         elif git.rev_parse('heads/origin'):
-            print 'Notice: no parent branch declared for stack "%s", ' \
-                  'defaulting to "heads/origin". Consider setting ' \
-                  '"branch.%s.stgit.parentbranch" with "git repo-config".' \
-                  % (self.__name, self.__name)
+            out.note(('No parent branch declared for stack "%s",'
+                      ' defaulting to "heads/origin".' % self.__name),
+                     ('Consider setting "branch.%s.stgit.parentbranch"'
+                      ' with "git repo-config".' % self.__name))
             return 'heads/origin'
         else:
             raise StackException, 'Cannot find a parent branch for "%s"' % self.__name
@@ -646,10 +648,10 @@ class Series(StgitObject):
                                             author_email = patch.get_authemail(),
                                             author_date = patch.get_authdate())
             if patch.get_log():
-                print "setting log to %s" %  patch.get_log()
+                out.info('Setting log to %s' %  patch.get_log())
                 newpatch.set_log(patch.get_log())
             else:
-                print "no log for %s" % p
+                out.info('No log for %s' % p)
 
         # fast forward the cloned series to self's top
         new_series.forward_patches(applied)
@@ -697,17 +699,18 @@ class Series(StgitObject):
             if not os.listdir(self.__patch_dir):
                 os.rmdir(self.__patch_dir)
             else:
-                print 'Patch directory %s is not empty.' % self.__patch_dir
+                out.warn('Patch directory %s is not empty' % self.__patch_dir)
 
             try:
                 os.removedirs(self._dir())
             except OSError:
-                raise StackException, 'Series directory %s is not empty.' % self._dir()
+                raise StackException('Series directory %s is not empty'
+                                     % self._dir())
 
             try:
                 os.removedirs(self.__refs_dir)
             except OSError:
-                print 'Refs directory %s is not empty.' % self.__refs_dir
+                out.warn('Refs directory %s is not empty' % self.__refs_dir)
 
         # Cleanup parent informations
         # FIXME: should one day make use of git-config --section-remove,
@@ -1046,10 +1049,9 @@ class Series(StgitObject):
                 try:
                     git.merge(bottom, head, top, recursive = True)
                 except git.GitException, ex:
-                    print >> sys.stderr, \
-                          'The merge failed during "push". ' \
-                          'Use "refresh" after fixing the conflicts or ' \
-                          'revert the operation with "push --undo".'
+                    out.error('The merge failed during "push".',
+                              'Use "refresh" after fixing the conflicts or'
+                              ' revert the operation with "push --undo".')
 
         append_string(self.__applied_file, name)
 
diff --git a/stgit/utils.py b/stgit/utils.py
index fbfe748..8df9103 100644
--- a/stgit/utils.py
+++ b/stgit/utils.py
@@ -21,6 +21,86 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 """
 
+class MessagePrinter(object):
+    def __init__(self):
+        class Output(object):
+            def __init__(self, write, flush):
+                self.write = write
+                self.flush = flush
+                self.at_start_of_line = True
+                self.level = 0
+            def new_line(self):
+                """Ensure that we're at the beginning of a line."""
+                if not self.at_start_of_line:
+                    self.write('\n')
+                    self.at_start_of_line = True
+            def single_line(self, msg, print_newline = True,
+                            need_newline = True):
+                """Write a single line. Newline before and after are
+                separately configurable."""
+                if need_newline:
+                    self.new_line()
+                if self.at_start_of_line:
+                    self.write('  '*self.level)
+                self.write(msg)
+                if print_newline:
+                    self.write('\n')
+                    self.at_start_of_line = True
+                else:
+                    self.flush()
+                    self.at_start_of_line = False
+            def tagged_lines(self, tag, lines):
+                tag += ': '
+                for line in lines:
+                    self.single_line(tag + line)
+                    tag = ' '*len(tag)
+            def write_line(self, line):
+                """Write one line of text on a lines of its own, not
+                indented."""
+                self.new_line()
+                self.write('%s\n' % line)
+                self.at_start_of_line = True
+            def write_raw(self, string):
+                """Write an arbitrary string, possibly containing
+                newlines."""
+                self.new_line()
+                self.write(string)
+                self.at_start_of_line = string.endswith('\n')
+        self.__stdout = Output(sys.stdout.write, sys.stdout.flush)
+        if sys.stdout.isatty():
+            self.__out = self.__stdout
+        else:
+            self.__out = Output(lambda msg: None, lambda: None)
+    def stdout(self, line):
+        """Write a line to stdout."""
+        self.__stdout.write_line(line)
+    def stdout_raw(self, string):
+        """Write a string possibly containing newlines to stdout."""
+        self.__stdout.write_raw(string)
+    def info(self, *msgs):
+        for msg in msgs:
+            self.__out.single_line(msg)
+    def note(self, *msgs):
+        self.__out.tagged_lines('Notice', msgs)
+    def warn(self, *msgs):
+        self.__out.tagged_lines('Warning', msgs)
+    def error(self, *msgs):
+        self.__out.tagged_lines('Error', msgs)
+    def start(self, msg):
+        """Start a long-running operation."""
+        self.__out.single_line('%s ... ' % msg, print_newline = False)
+        self.__out.level += 1
+    def done(self, extramsg = None):
+        """Finish long-running operation."""
+        self.__out.level -= 1
+        if extramsg:
+            msg = 'done (%s)'
+        else:
+            msg = 'done'
+        self.__out.single_line(msg, need_newline = False)
+
+out = MessagePrinter()
+
 def mkdir_file(filename, mode):
     """Opens filename with the given mode, creating the directory it's
     in if it doesn't already exist."""
@@ -166,12 +246,11 @@ def call_editor(filename):
         editor = 'vi'
     editor += ' %s' % filename
 
-    print 'Invoking the editor: "%s"...' % editor,
-    sys.stdout.flush()
+    out.start('Invoking the editor: "%s"' % editor)
     err = os.system(editor)
     if err:
         raise EditorException, 'editor failed, exit code: %d' % err
-    print 'done'
+    out.done()
 
 def patch_name_from_msg(msg):
     """Return a string to be used as a patch name. This is generated

^ permalink raw reply related

* Re: Commits gone AWOL, but not reported by git-fsck --unreachable
From: Jeff King @ 2007-05-19 11:52 UTC (permalink / raw)
  To: Robin H. Johnson; +Cc: Git Mailing List
In-Reply-To: <20070519103011.GU17511@curie-int.orbis-terrarum.net>

On Sat, May 19, 2007 at 03:30:11AM -0700, Robin H. Johnson wrote:

> Problem:
> --------
> A bunch of my commits are NOT present in git-log, git-rev-list, or any
> tool that provides the history of my branches.
> 
> If I hack around and recover the commitid, I can pass it to git-show,
> and the commit does still exist in the data.
> 
> git-fsck --unreachable does not list the commit in question either, but
> does list other blobs and trees.

Are you sure it isn't in your reflog? Try 'git reflog show' or 'git log
-g'.

-Peff

^ permalink raw reply

* [PATCH] git-archive: recursive prefix directory creation
From: René Scharfe @ 2007-05-19 11:37 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List

Currently git-archive only adds a single directory entry for prefixes,
e.g. for --prefix=a/b/c/ only the directory a/b/c/ would be added to
the archive, not a/ nor a/b/.  While tar and unzip don't seem to have
a problem handling these missing entries, their omission was not
intended.

This patch replaces the prefix path creation code in git-archive with
calls to a new path walker similar to read_tree_recursive, named
descend_leading_dirs.  It calls a read_tree_fn_t function for each
leading directory while descending deeper into the path (e.g. first
a/, then a/b/ etc.).

Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>
---
 archive-tar.c |   11 ++---------
 archive-zip.c |   11 ++---------
 tree.c        |   45 +++++++++++++++++++++++++++++++++++++++++++++
 tree.h        |    5 +++++
 4 files changed, 54 insertions(+), 18 deletions(-)

diff --git a/archive-tar.c b/archive-tar.c
index 33e7657..7ea396d 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -308,15 +308,8 @@ int write_tar_archive(struct archiver_args *args)
 	if (args->commit_sha1)
 		write_global_extended_header(args->commit_sha1);
 
-	if (args->base && plen > 0 && args->base[plen - 1] == '/') {
-		char *base = xstrdup(args->base);
-		int baselen = strlen(base);
-
-		while (baselen > 0 && base[baselen - 1] == '/')
-			base[--baselen] = '\0';
-		write_tar_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
-		free(base);
-	}
+	descend_leading_dirs(args->tree->object.sha1, args->base, plen,
+	                     040777, 0, write_tar_entry);
 	read_tree_recursive(args->tree, args->base, plen, 0,
 			    args->pathspec, write_tar_entry);
 	write_trailer();
diff --git a/archive-zip.c b/archive-zip.c
index 3cbf6bb..2fc22ff 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -317,15 +317,8 @@ int write_zip_archive(struct archiver_args *args)
 	zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
 	verbose = args->verbose;
 
-	if (args->base && plen > 0 && args->base[plen - 1] == '/') {
-		char *base = xstrdup(args->base);
-		int baselen = strlen(base);
-
-		while (baselen > 0 && base[baselen - 1] == '/')
-			base[--baselen] = '\0';
-		write_zip_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
-		free(base);
-	}
+	descend_leading_dirs(args->tree->object.sha1, args->base, plen,
+	                     040777, 0, write_zip_entry);
 	read_tree_recursive(args->tree, args->base, plen, 0,
 			    args->pathspec, write_zip_entry);
 	write_zip_trailer(args->commit_sha1);
diff --git a/tree.c b/tree.c
index e4a39aa..5cd1f90 100644
--- a/tree.c
+++ b/tree.c
@@ -231,3 +231,48 @@ struct tree *parse_tree_indirect(const unsigned char *sha1)
 			parse_object(obj->sha1);
 	} while (1);
 }
+
+int descend_leading_dirs(const unsigned char *sha1,
+                         const char *path, size_t pathlen,
+                         unsigned int mode, int stage, read_tree_fn_t fn)
+{
+	int err;
+	size_t baselen;
+
+	if (!path)
+		return 0;
+
+	/* Discard file name part. */
+	while (pathlen > 0 && path[pathlen - 1] != '/')
+		pathlen--;
+	while (pathlen > 0 && path[pathlen - 1] == '/')
+		pathlen--;
+	if (pathlen == 0)
+		return 0;
+
+	/* Now separate base and current directory name. */
+	baselen = pathlen;
+	while (baselen > 0 && path[baselen - 1] != '/')
+		baselen--;
+
+	err = descend_leading_dirs(null_sha1, path, baselen, mode, stage, fn);
+
+	if (!err) {
+		/* We need a NUL-terminated version of the directory name. */
+		char *dirname = xstrndup(path + baselen, pathlen - baselen);
+		switch (fn(sha1, path, baselen, dirname, mode, stage)) {
+		case 0:
+			err = 1;
+			break;
+		case READ_TREE_RECURSIVE:
+			err = 0;
+			break;
+		default:
+			err = -1;
+			break;
+		}
+		free(dirname);
+	}
+
+	return err;
+}
diff --git a/tree.h b/tree.h
index dd25c53..6b0f5d9 100644
--- a/tree.h
+++ b/tree.h
@@ -30,4 +30,9 @@ extern int read_tree_recursive(struct tree *tree,
 
 extern int read_tree(struct tree *tree, int stage, const char **paths);
 
+extern int descend_leading_dirs(const unsigned char *sha1,
+                                const char *path, size_t pathlen,
+                                unsigned int mode, int stage,
+                                read_tree_fn_t fn);
+
 #endif /* TREE_H */

^ permalink raw reply related

* [PATCH] git-svn: don't minimize-url when doing an init that tracks multiple paths
From: Eric Wong @ 2007-05-19 10:59 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

I didn't have a chance to test the off-by-default minimize-url
stuff enough before, but it's quite broken for people passing
the --trunk/-T, --tags/-t, --branches/-b switches to "init" or
"clone" commands.

Additionally, follow-parent functionality seems broken when we're
not connected to the root of the repository.

Default behavior for "traditional" git-svn users who only track
one directory (without needing follow-parent) should be
reasonable, as those users started using things before
minimize-url functionality existed.

Behavior for users more used to the git-svnimport-like command
line will also benefit from a more-flexible command-line than
svnimport given the assumption they're working with
non-restrictive read permissions on the repository.

I hope to properly fix these bugs when I get a chance to in the
next week or so, but I would like to get this stopgap measure of
reverting to the old behavior as soon as possible.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
 git-svn.perl |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index 6d0cdac..233f4f2 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -485,6 +485,11 @@ sub cmd_multi_init {
 	unless (defined $_trunk || defined $_branches || defined $_tags) {
 		usage(1);
 	}
+
+	# there are currently some bugs that prevent multi-init/multi-fetch
+	# setups from working well without this.
+	$Git::SVN::_minimize_url = 1;
+
 	$_prefix = '' unless defined $_prefix;
 	if (defined $url) {
 		$url =~ s#/+$##;
-- 
Eric Wong

^ permalink raw reply related

* Re: [PATCH] git-svn: avoid crashing svnserve when creating new directories
From: Eric Wong @ 2007-05-19 10:58 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: Junio C Hamano, git
In-Reply-To: <vpqtzu9m7za.fsf@bauges.imag.fr>

Matthieu Moy <Matthieu.Moy@imag.fr> wrote:
> Eric Wong <normalperson@yhbt.net> writes:
> 
> >  	foreach (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$bat) {
> > +		next if $_ eq '';
> >  		$self->close_directory($bat->{$_}, $p);
> >  	}
> > +	$self->close_directory($bat->{''}, $p);
> 
> Works for me. Thanks a lot!
> 
> BTW, I didn't have time to write my script as a real testcase for
> git-svn, but it should probably be added to the testsuite.

I have a modified version of the test I posted earlier based on your
test.  However, I'm not comfortable binding to any port (even without
ssh) on a users machine or potentially leaving a daemon running if the
test is interrupted.

>From bfbd67e31e952489098c2a10df259fbf1e7863fb Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Sun, 13 May 2007 17:38:21 -0700
Subject: [PATCH] git-svn: test for creating new directories over svn://

As reported by Matthieu Moy, this is causing svnserve to
terminate connections (because it segfaults) segfault.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
 t/t9112-git-svn-dcommit-new-file.sh |   37 +++++++++++++++++++++++++++++++++++
 1 files changed, 37 insertions(+), 0 deletions(-)
 create mode 100755 t/t9112-git-svn-dcommit-new-file.sh

diff --git a/t/t9112-git-svn-dcommit-new-file.sh b/t/t9112-git-svn-dcommit-new-file.sh
new file mode 100755
index 0000000..b186fce
--- /dev/null
+++ b/t/t9112-git-svn-dcommit-new-file.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+#
+
+test_description='git-svn dcommit new files over svn:// test'
+
+. ./lib-git-svn.sh
+
+# standard svnserve runs on 3690, so lets not conflict with that
+SVNSERVE_PORT=${SVNSERVE_PORT-'3691'}
+
+start_svnserve () {
+	svnserve --listen-port $SVNSERVE_PORT \
+	         --root $rawsvnrepo \
+	         --listen-once \
+	         --listen-host 127.0.0.1 &
+}
+
+test_expect_success 'start tracking an empty repo' "
+	svn mkdir -m 'empty dir' $svnrepo/empty-dir &&
+	echo anon-access = write >> $rawsvnrepo/conf/svnserve.conf &&
+	start_svnserve &&
+	git svn init svn://127.0.0.1:$SVNSERVE_PORT &&
+	git svn fetch
+	"
+
+test_expect_success 'create files in new directory with dcommit' "
+	mkdir git-new-dir &&
+	echo hello > git-new-dir/world &&
+	git update-index --add git-new-dir/world &&
+	git commit -m hello &&
+	start_svnserve &&
+	git svn dcommit
+	"
+
+test_done
-- 
Eric Wong

^ permalink raw reply related

* Commits gone AWOL, but not reported by git-fsck --unreachable
From: Robin H. Johnson @ 2007-05-19 10:30 UTC (permalink / raw)
  To: Git Mailing List

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

Background:
-----------
Using git-svn to work with an external project (non-public), and the
other day I happened to run 'git-svn dcommit' right as their SVN went
down for a moment (under 5 seconds) for the snapshot-backup. I recall
swearing profusely at the time, but thought nothing of it after that,
until now, when I saw that parts of my tree weren't as they should be.

Problem:
--------
A bunch of my commits are NOT present in git-log, git-rev-list, or any
tool that provides the history of my branches.

If I hack around and recover the commitid, I can pass it to git-show,
and the commit does still exist in the data.

git-fsck --unreachable does not list the commit in question either, but
does list other blobs and trees.

Luckily, since the entire tree had ~900 commits, I was able to work some
magic and recovery my work:
1. Hack to get list of all commits
2. Exclude all commits that had git-svn-id in their description.
3. Exclude all merges.
4. Manually review all remaining commits (~70).

The following questions are mainly things that would have made my
recovery far easier.

Questions:
----------
1. Is there a plumbing tool to list EVERY single object of a given type
   (blob, tree, commit, tag) that git is aware of, regardless of it
   being reachable? (Right now I have a hack to read the pack index, and
   use find to find the non-packed object ids, then git-cat-file -t for
   the type).
2. A sane way to view an ordered set of commits, so that it should be
   possible to spot when there's a path of commits that went off and
   isn't used anymore.
3. Given a set of commits, list which are actually unique (in my tree,
   git-svn uses rebase often, so I have a lot of commits that are
   identical bar the commitid).
4. Strongly related to #4, given everything a commit, tell me what other
   identical commits exist in the tree.

-- 
Robin Hugh Johnson
Gentoo Linux Developer & Council Member
E-Mail     : robbat2@gentoo.org
GnuPG FP   : 11AC BA4F 4778 E3F6 E4ED  F38E B27B 944E 3488 4E85

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

^ permalink raw reply

* Re: [PATCH] git-svn: avoid crashing svnserve when creating new directories
From: Matthieu Moy @ 2007-05-19 10:19 UTC (permalink / raw)
  To: Eric Wong; +Cc: Junio C Hamano, git
In-Reply-To: <20070519095837.GA387@muzzle>

Eric Wong <normalperson@yhbt.net> writes:

>  	foreach (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$bat) {
> +		next if $_ eq '';
>  		$self->close_directory($bat->{$_}, $p);
>  	}
> +	$self->close_directory($bat->{''}, $p);

Works for me. Thanks a lot!

BTW, I didn't have time to write my script as a real testcase for
git-svn, but it should probably be added to the testsuite.

Thanks again,

-- 
Matthieu

^ permalink raw reply

* Re: [PATCH] Fix crlf attribute handling to match documentation
From: Andy Parkins @ 2007-05-19  9:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano
In-Reply-To: <7v646p4r50.fsf@assigned-by-dhcp.cox.net>

On Saturday 2007, May 19, Junio C Hamano wrote:

> The check you modified in the first hunk is not the above '<='
> comparison but is this:

I was talking about the second hunk there though, which I mentioned 
with "...skipped in crlf_to_worktree()...".   In which case the 
description matches.

> 	(action == CRLF_GUESS && !auto_crlf)
>
> and "core.autocrlf = input" makes "auto_crlf = -1", so when
> action is not GUESS, or even when action is GUESS, if the config
> is set to "input", the if() statement you patched in the first
> hunk should not trigger.  The above description is different from
> what the code was doing.

As I say, the description was for the second hunk.

The fault was my last paragraph, "Similarly for crlf_to_worktree(), if 
core.autocrlf is false, no", which should have said "Similarly for 
crlf_to_git()".  Sorry.

With that small change I think my description was correct.  However, it 
obviously wasn't clear - apologies.



Andy

-- 
Dr Andy Parkins, M Eng (hons), MIET
andyparkins@gmail.com

^ permalink raw reply

* [PATCH] git-svn: avoid crashing svnserve when creating new directories
From: Eric Wong @ 2007-05-19  9:58 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Matthieu Moy
In-Reply-To: <20070519061422.GA17528@muzzle>

When sorting directory names by depth (slash ("/") count) and
closing the deepest directories first (as the protocol
requires), we failed to put the root baton (with an empty string
as its key "") after top-level directories (which did not have
any slashes).

This resulted in svnserve being in a situation it couldn't
handle and caused a segmentation fault on the remote server.

This bug did not affect users of DAV and filesystem repositories.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
 git-svn.perl |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index e38811a..6d0cdac 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -2841,8 +2841,10 @@ sub close_edit {
 	my ($self) = @_;
 	my ($p,$bat) = ($self->{pool}, $self->{bat});
 	foreach (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$bat) {
+		next if $_ eq '';
 		$self->close_directory($bat->{$_}, $p);
 	}
+	$self->close_directory($bat->{''}, $p);
 	$self->SUPER::close_edit($p);
 	$p->clear;
 }
-- 
Eric Wong

^ permalink raw reply related

* Re: merge summaries
From: Steffen Prohaska @ 2007-05-19  9:35 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Junio C Hamano, Git Mailing List
In-Reply-To: <7vodki55px.fsf@assigned-by-dhcp.cox.net>


On May 18, 2007, at 8:47 PM, Junio C Hamano wrote:

>> Think about _who_ uses it for those two prominent repositories for  
>> a while.
>>
>> [ pause, ponder here ]
>>
>> In other words, "merge.summary" should be true ONLY FOR TOP-LEVEL
>> MAINTAINERS.
>>
>> Making it true for anybody else is almost always a bug. Yes, I and  
>> Junio
>> have it enabled, but pretty much nobody else should, because if  
>> they pull
>> from me or Junio in order to merge, their merge summaries would be  
>> exactly
>> the wrong way around!
>
> This does not necessarily mean that your lieutenants should not
> use merge.summary when they pull from other trees (or inside
> their own repository to merge in the topics).  They need to
> however disable it with --no-summary when they pull from you
> when they choose to merge instead of rebase to adjust to the
> updated infrastructure in your tree.

[ ... pondering ... ]

Apparently parents of merge commits are not fully equal and summaries
can help to browse the history of a branch. If summaries are enabled
"git log --first-parent" gives hints to all changes at least in
short-log form. Changes along the first-parent-path to the initial
commit are listed in full. All changes that were merged in are
listed in the summaries of the merge. This seems to be a useful feature.

But isn't this true for any branch and any merge. If someone pulls from
you or Junio and runs "git log --first-parent" why shouldn't he see
a summary of the changes that were pulled? At that point his branch's
first-parent-path to the initial commit would not touch the changes
pulled from you. So the summary could help.

What is so wrong if merge summaries of pulls from you were committed
to topic branches. Later when you pull such branches the summaries
shouldn't be a problem. A short-log of the topic branch wouldn't
contain the summaries.

Probably I do not fully understand what "the wrong way around" means.

- Steffen

^ permalink raw reply

* Re: [PATCH 07/16] git-read-tree: take --submodules option
From: Alex Riesen @ 2007-05-19  9:19 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: skimo, git
In-Reply-To: <7vd50x1n0r.fsf@assigned-by-dhcp.cox.net>

Junio C Hamano, Sat, May 19, 2007 05:59:48 +0200:
> 
>  - figure out what commit should be checked out from
>    superproject index;
> 
>  - make sure the named commit exists, or fetch to make it exist.

What if the fetch is not possible? You can't checkout? What about the
other subprojects, where the checkout succeeded? Will they be reset to
the previuos state?

To me, the fetch sounds pretty dangerous. Maybe the checkout should
be two stage: first - we check all subprojects to be checked out if it
is possible, second - either fail (default) or checkout what possible,
warn the user, leave the incomplete subprojects changed (but not
update the index with them, so that they wont be accidentally
committed).

>  - go there and check out that commit; this implies two things:
> 
>    1. if there are local changes, it will be carried along and we
>       checkout the named commit;

Shouldn't that depend on "-m" option given to git-checkout in
superproject? Sometime the user have to be sure he can checkout
everything as it were, but without breaking the local state (like what
current git-checkout without "-m" does).

>    2. the repository's HEAD becomes detached;
> 

Universally agreed upon

^ permalink raw reply

* Re: [PATCH 5/7] gitweb: Prototyp for selecting diffs in JavaScript
From: Petr Baudis @ 2007-05-19  8:27 UTC (permalink / raw)
  To: Martin Koegler; +Cc: Jakub Narebski, git
In-Reply-To: <20070519075701.GA30933@auto.tuwien.ac.at>

On Sat, May 19, 2007 at 09:57:01AM CEST, Martin Koegler wrote:
> Againgst what version/repository do you want the patch?

Whatever is comfortable for you :-) - next would be ideal for me, but
can be something else as well.

> For the new version, I intend to move the JavaScript file into the
> head tag of the html page. I could integrate your incremental blame
> link rewrite as well. If I'm doing this, we could remove the blamelink
> class hack as well as the fixBlameLinks JavaScript function.

It's not clear at all yet whether incremental blame is viable idea
because I suspect Firefox might be just too horribly slow at working
with DOM for it to make sense for any files of non-trivial size. So I
wouldn't rely on this going in just yet. :-(

-- 
				Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
Ever try. Ever fail. No matter. // Try again. Fail again. Fail better.
		-- Samuel Beckett

^ permalink raw reply

* Re: [PATCH 5/7] gitweb: Prototyp for selecting diffs in JavaScript
From: Martin Koegler @ 2007-05-19  7:57 UTC (permalink / raw)
  To: Petr Baudis; +Cc: Jakub Narebski, git
In-Reply-To: <20070518084935.GV4489@pasky.or.cz>

On Fri, May 18, 2007 at 10:49:35AM +0200, Petr Baudis wrote:
> On Sun, Apr 15, 2007 at 10:46:08PM CEST, Martin Koegler wrote:
> > ---
> > This patch is only to test the other patches. I'm working on reimplementing it in perl.
> 
>   I think this is something that I'd quite like to testdrive at
> repo.or.cz, but I'm admittelly somewhat lost in all the patches,
> resends, new versions, something got applied, etc. - if it's not too big
> a hassle for you, would you please care to resend the latest series?

Againgst what version/repository do you want the patch?

For the new version, I intend to move the JavaScript file into the
head tag of the html page. I could integrate your incremental blame
link rewrite as well. If I'm doing this, we could remove the blamelink
class hack as well as the fixBlameLinks JavaScript function.

mfg Martin Kögler

^ permalink raw reply

* [PATCH 2/2] Teach "delta" attribute to pack-objects.
From: Junio C Hamano @ 2007-05-19  7:48 UTC (permalink / raw)
  To: git
In-Reply-To: <11795608912129-git-send-email-junkio@cox.net>

This teaches pack-objects to use .gitattributes mechanism so
that the user can specify certain blobs are not worth spending
CPU cycles to attempt deltification.

The name of the attrbute is "delta", and when it is set to
false, like this:

	== .gitattributes ==
	*.jpg	-delta

they are always stored in the plain-compressed base object
representation.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-pack-objects.c |   35 ++++++++++++++++++++++++++++++++++-
 1 files changed, 34 insertions(+), 1 deletions(-)

diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 12d9685..651011c 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "cache.h"
+#include "attr.h"
 #include "object.h"
 #include "blob.h"
 #include "commit.h"
@@ -40,9 +41,10 @@ struct object_entry {
 	enum object_type in_pack_type;	/* could be delta */
 	unsigned char in_pack_header_size;
 	unsigned char preferred_base; /* we do not pack this, but is available
-				       * to be used as the base objectto delta
+				       * to be used as the base object to delta
 				       * objects against.
 				       */
+	unsigned char no_try_delta;
 };
 
 /*
@@ -737,6 +739,28 @@ static unsigned name_hash(const char *name)
 	return hash;
 }
 
+static void setup_delta_attr_check(struct git_attr_check *check)
+{
+	static struct git_attr *attr_delta;
+
+	if (!attr_delta)
+		attr_delta = git_attr("delta", 5);
+
+	check[0].attr = attr_delta;
+}
+
+static int no_try_delta(const char *path)
+{
+	struct git_attr_check check[1];
+
+	setup_delta_attr_check(check);
+	if (git_checkattr(path, ARRAY_SIZE(check), check))
+		return 0;
+	if (ATTR_FALSE(check->value))
+		return 1;
+	return 0;
+}
+
 static int add_object_entry(const unsigned char *sha1, enum object_type type,
 			    const char *name, int exclude)
 {
@@ -801,6 +825,9 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
 	if (progress)
 		display_progress(&progress_state, nr_objects);
 
+	if (name && no_try_delta(name))
+		entry->no_try_delta = 1;
+
 	return 1;
 }
 
@@ -1349,6 +1376,10 @@ static void find_deltas(struct object_entry **list, int window, int depth)
 
 		if (entry->size < 50)
 			continue;
+
+		if (entry->no_try_delta)
+			continue;
+
 		free_delta_index(n->index);
 		n->index = NULL;
 		free(n->data);
@@ -1376,6 +1407,8 @@ static void find_deltas(struct object_entry **list, int window, int depth)
 			m = array + other_idx;
 			if (!m->entry)
 				break;
+			if (m->entry->no_try_delta)
+				continue;
 			if (try_delta(n, m, max_depth) < 0)
 				break;
 		}
-- 
1.5.2.rc3.87.g404f

^ permalink raw reply related

* [PATCH 1/2] pack-objects: pass fullname down to add_object_entry()
From: Junio C Hamano @ 2007-05-19  7:48 UTC (permalink / raw)
  To: git

Instead of giving a hash for grouping, pass fullname to add_object_entry().
I want to add "do not try deltifying this object" bit to object_entry based on
the settings in .gitattributes, and hashing the name down too early would
interfere with that plan.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-pack-objects.c |   27 ++++++++++++++-------------
 1 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 5fa9813..12d9685 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -721,6 +721,9 @@ static unsigned name_hash(const char *name)
 	unsigned char c;
 	unsigned hash = 0;
 
+	if (!name)
+		return 0;
+
 	/*
 	 * This effectively just creates a sortable number from the
 	 * last sixteen non-whitespace characters. Last characters
@@ -735,12 +738,13 @@ static unsigned name_hash(const char *name)
 }
 
 static int add_object_entry(const unsigned char *sha1, enum object_type type,
-			    unsigned hash, int exclude)
+			    const char *name, int exclude)
 {
 	struct object_entry *entry;
 	struct packed_git *p, *found_pack = NULL;
 	off_t found_offset = 0;
 	int ix;
+	unsigned hash = name_hash(name);
 
 	ix = nr_objects ? locate_object_entry_hash(sha1) : -1;
 	if (ix >= 0) {
@@ -929,10 +933,9 @@ static void add_pbase_object(struct tree_desc *tree,
 		if (cmp < 0)
 			return;
 		if (name[cmplen] != '/') {
-			unsigned hash = name_hash(fullname);
 			add_object_entry(entry.sha1,
 					 S_ISDIR(entry.mode) ? OBJ_TREE : OBJ_BLOB,
-					 hash, 1);
+					 fullname, 1);
 			return;
 		}
 		if (S_ISDIR(entry.mode)) {
@@ -992,10 +995,11 @@ static int check_pbase_path(unsigned hash)
 	return 0;
 }
 
-static void add_preferred_base_object(const char *name, unsigned hash)
+static void add_preferred_base_object(const char *name)
 {
 	struct pbase_tree *it;
 	int cmplen;
+	unsigned hash = name_hash(name);
 
 	if (!num_preferred_base || check_pbase_path(hash))
 		return;
@@ -1003,7 +1007,7 @@ static void add_preferred_base_object(const char *name, unsigned hash)
 	cmplen = name_cmp_len(name);
 	for (it = pbase_tree; it; it = it->next) {
 		if (cmplen == 0) {
-			add_object_entry(it->pcache.sha1, OBJ_TREE, 0, 1);
+			add_object_entry(it->pcache.sha1, OBJ_TREE, NULL, 1);
 		}
 		else {
 			struct tree_desc tree;
@@ -1434,7 +1438,6 @@ static void read_object_list_from_stdin(void)
 {
 	char line[40 + 1 + PATH_MAX + 2];
 	unsigned char sha1[20];
-	unsigned hash;
 
 	for (;;) {
 		if (!fgets(line, sizeof(line), stdin)) {
@@ -1457,22 +1460,20 @@ static void read_object_list_from_stdin(void)
 		if (get_sha1_hex(line, sha1))
 			die("expected sha1, got garbage:\n %s", line);
 
-		hash = name_hash(line+41);
-		add_preferred_base_object(line+41, hash);
-		add_object_entry(sha1, 0, hash, 0);
+		add_preferred_base_object(line+41);
+		add_object_entry(sha1, 0, line+41, 0);
 	}
 }
 
 static void show_commit(struct commit *commit)
 {
-	add_object_entry(commit->object.sha1, OBJ_COMMIT, 0, 0);
+	add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);
 }
 
 static void show_object(struct object_array_entry *p)
 {
-	unsigned hash = name_hash(p->name);
-	add_preferred_base_object(p->name, hash);
-	add_object_entry(p->item->sha1, p->item->type, hash, 0);
+	add_preferred_base_object(p->name);
+	add_object_entry(p->item->sha1, p->item->type, p->name, 0);
 }
 
 static void show_edge(struct commit *commit)
-- 
1.5.2.rc3.87.g404f

^ permalink raw reply related


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