Git development
 help / color / mirror / Atom feed
* [PATCH 2/2] QP-encode email body
From: Karl Hasselström @ 2006-10-22 12:49 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20061022124551.14051.25145.stgit@localhost>

From: Karl Hasselström <kha@treskal.com>

Some mail servers dislike the 8bit transfer encoding, so use
quoted-printable instead.

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

 stgit/commands/mail.py |   16 +++++++++-------
 1 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/stgit/commands/mail.py b/stgit/commands/mail.py
index b661308..885d5e9 100644
--- a/stgit/commands/mail.py
+++ b/stgit/commands/mail.py
@@ -15,7 +15,7 @@ along with this program; if not, write t
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 """
 
-import sys, os, re, time, datetime, smtplib, email.Header, email.Utils
+import sys, os, re, time, datetime, quopri, smtplib, email.Header, email.Utils
 from optparse import OptionParser, make_option
 
 from stgit.commands.common import *
@@ -253,7 +253,7 @@ def __build_extra_headers():
     """Build extra headers like content-type etc.
     """
     headers  = 'Content-Type: text/plain; charset=utf-8; format=fixed\n'
-    headers += 'Content-Transfer-Encoding: 8bit\n'
+    headers += 'Content-Transfer-Encoding: quoted-printable\n'
     headers += 'User-Agent: StGIT/%s\n' % version.version
 
     return headers
@@ -425,9 +425,9 @@ def encode_header(s, enc):
     else:
         return s
 
-def encode_headers(msg, enc):
-    """rfc2047-encode the headers of msg, assuming it is encoded in
-    enc."""
+def encode_message(msg, enc):
+    """rfc2047-encode the headers of msg, and quoted-printable-encode
+    the body. msg is assumed to be encoded in enc."""
     in_header = True
     lines = []
     for line in msg.splitlines(True):
@@ -436,6 +436,8 @@ def encode_headers(msg, enc):
                 line = encode_header(line, enc)
             else:
                 in_header = False
+        else:
+            line = quopri.encodestring(line)
         lines.append(line)
     return ''.join(lines)
 
@@ -497,7 +499,7 @@ def func(parser, options, args):
                 raise CmdException, 'No cover message template file found'
 
         msg_id = email.Utils.make_msgid('stgit')
-        msg = encode_headers(__build_cover(tmpl, total_nr, msg_id, options),
+        msg = encode_message(__build_cover(tmpl, total_nr, msg_id, options),
                              'UTF-8')
         from_addr, to_addr_list = __parse_addresses(msg)
 
@@ -524,7 +526,7 @@ def func(parser, options, args):
 
     for (p, patch_nr) in zip(patches, range(1, len(patches) + 1)):
         msg_id = email.Utils.make_msgid('stgit')
-        msg = encode_headers(__build_message(tmpl, p, patch_nr, total_nr,
+        msg = encode_message(__build_message(tmpl, p, patch_nr, total_nr,
                                              msg_id, ref_id, options), 'UTF-8')
         from_addr, to_addr_list = __parse_addresses(msg)
 

^ permalink raw reply related

* [PATCH 1/2] Regression test for "stg delete"
From: Karl Hasselström @ 2006-10-22 12:58 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20061022125337.15194.64831.stgit@localhost>

From: Karl Hasselström <kha@treskal.com>

NOTE: The subtest that attempts to delete a patch in another branch
currently fails, because there's a bug in "delete".

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

 t/t1600-delete.sh |  109 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 109 insertions(+), 0 deletions(-)

diff --git a/t/t1600-delete.sh b/t/t1600-delete.sh
new file mode 100644
index 0000000..e22e624
--- /dev/null
+++ b/t/t1600-delete.sh
@@ -0,0 +1,109 @@
+#!/bin/sh
+# Copyright (c) 2006 Karl Hasselström
+test_description='Test the delete command.'
+. ./test-lib.sh
+
+test_expect_success \
+    'Initialize the StGIT repository' \
+    'stg init'
+
+test_expect_success \
+    'Create a patch' \
+    '
+    stg new foo -m foo &&
+    echo foo > foo.txt &&
+    stg add foo.txt &&
+    stg refresh
+    '
+
+test_expect_success \
+    'Try to delete a non-existing patch' \
+    '
+    [ $(stg applied | wc -l) -eq 1 ] &&
+    ! stg delete bar &&
+    [ $(stg applied | wc -l) -eq 1 ]
+    '
+
+test_expect_success \
+    'Try to delete the topmost patch while dirty' \
+    '
+    echo dirty >> foo.txt &&
+    [ $(stg applied | wc -l) -eq 1 ] &&
+    ! stg delete foo &&
+    [ $(stg applied | wc -l) -eq 1 ] &&
+    git reset --hard
+    '
+
+test_expect_success \
+    'Delete the topmost patch' \
+    '
+    [ $(stg applied | wc -l) -eq 1 ] &&
+    stg delete foo &&
+    [ $(stg applied | wc -l) -eq 0 ]
+    '
+
+test_expect_success \
+    'Create an unapplied patch' \
+    '
+    stg new foo -m foo &&
+    echo foo > foo.txt &&
+    stg add foo.txt &&
+    stg refresh &&
+    stg pop
+    '
+
+test_expect_success \
+    'Delete an unapplied patch' \
+    '
+    [ $(stg unapplied | wc -l) -eq 1 ] &&
+    stg delete foo &&
+    [ $(stg unapplied | wc -l) -eq 0 ]
+    '
+
+test_expect_success \
+    'Create two patches' \
+    '
+    stg new foo -m foo &&
+    echo foo > foo.txt &&
+    stg add foo.txt &&
+    stg refresh &&
+    stg new bar -m bar &&
+    echo bar > bar.txt &&
+    stg add bar.txt &&
+    stg refresh
+    '
+
+test_expect_success \
+    'Try to delete a non-topmost applied patch' \
+    '
+    [ $(stg applied | wc -l) -eq 2 ] &&
+    ! stg delete foo &&
+    [ $(stg applied | wc -l) -eq 2 ]
+    '
+
+test_expect_success \
+    'Create another branch, and put one patch in each branch' \
+    '
+    stg branch --create br &&
+    stg new baz -m baz &&
+    echo baz > baz.txt &&
+    stg add baz.txt &&
+    stg refresh &&
+    stg branch master &&
+    stg new baz -m baz &&
+    echo baz > baz.txt &&
+    stg add baz.txt &&
+    stg refresh
+    '
+
+test_expect_success \
+    'Delete a patch in another branch' \
+    '
+    [ $(stg applied | wc -l) -eq 3 ] &&
+    [ $(stg applied -b br | wc -l) -eq 1 ] &&
+    stg delete -b br baz &&
+    [ $(stg applied | wc -l) -eq 3 ] &&
+    [ $(stg applied -b br | wc -l) -eq 0 ]
+    '
+
+test_done

^ permalink raw reply related

* [PATCH 0/2] Teach "stg delete" to delete several patches at once
From: Karl Hasselström @ 2006-10-22 12:53 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git

The first patch introduces a test for "stg delete", since there wasn't
already one, and it's much less stressful to modify stuff if you have
an automatic test that can tell you if you broke stuff.

As it turns out, this test finds an existing bug: it's not possible to
delete a patch in another branch if there's an identically named
branch in the current branch, and both patches are topmost in their
respective applied stacks.

The second patch teaches "delete" how to delete multiple patches in
one go.

-- 
Karl Hasselström, kha@treskal.com
      www.treskal.com/kalle

^ permalink raw reply

* [PATCH 2/2] Allow deletion of several patches at once
From: Karl Hasselström @ 2006-10-22 12:58 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20061022125337.15194.64831.stgit@localhost>

From: Karl Hasselström <kha@treskal.com>



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

 stgit/commands/delete.py |   50 ++++++++++++++++-----
 t/t1600-delete-one.sh    |  109 ++++++++++++++++++++++++++++++++++++++++++++++
 t/t1600-delete.sh        |  109 ----------------------------------------------
 t/t1601-delete-many.sh   |   45 +++++++++++++++++++
 4 files changed, 193 insertions(+), 120 deletions(-)

diff --git a/stgit/commands/delete.py b/stgit/commands/delete.py
index c97d8ed..5617ca1 100644
--- a/stgit/commands/delete.py
+++ b/stgit/commands/delete.py
@@ -24,13 +24,16 @@ from stgit.utils import *
 from stgit import stack, git
 
 
-help = 'remove the topmost or any unapplied patch'
-usage = """%prog [options] <patch>
+help = 'delete patches'
+usage = """%prog [options] <patch> [ <patch> [...] ]
 
-Delete the patch passed as argument. The patch to be deleted can only
-be part of the unapplied list or be the topmost one, in the latter
-case the command also popping it from the stack. Note that the
-'delete' operation is irreversible."""
+Delete the patches passed as arguments. If an applied patch is to be
+deleted, all other patches applied on top of it must be deleted too;
+and they must be explicitly specified, since this command will not try
+to delete a patch unless you explicitly ask it to. If any applied
+patches are deleted, they are popped from the stack.
+
+Note that the 'delete' operation is irreversible."""
 
 options = [make_option('-b', '--branch',
                        help = 'use BRANCH instead of the default one')]
@@ -38,16 +41,41 @@ options = [make_option('-b', '--branch',
 def func(parser, options, args):
     """Deletes a patch
     """
-    if len(args) != 1:
-        parser.error('incorrect number of arguments')
+    if not args:
+        parser.error('no patches to delete')
+
+    applied = {}
+    unapplied = {}
+    dontexist = {}
+    for patch in args:
+        if patch in crt_series.get_unapplied():
+            unapplied[patch] = None
+        elif patch in crt_series.get_applied():
+            applied[patch] = None
+        else:
+            dontexist[patch] = None
 
-    if args[0] == crt_series.get_current():
+    while crt_series.get_current() in applied:
+        patch = crt_series.get_current()
         check_local_changes()
         check_conflicts()
         check_head_top_equal()
+        crt_series.delete_patch(patch)
+        del applied[patch]
+        print 'Patch "%s" successfully deleted' % patch
+
+    for patch in unapplied.iterkeys():
+        crt_series.delete_patch(patch)
+        print 'Patch "%s" successfully deleted' % patch
+
+    if dontexist:
+        print 'Error: no such patch: %s' % ', '.join(dontexist.iterkeys())
+    if applied:
+        print 'Error: failed to delete %s' % ', '.join(applied.iterkeys())
 
-    crt_series.delete_patch(args[0])
-    print 'Patch "%s" successfully deleted' % args[0]
+    failed = len(dontexist) + len(applied)
+    if failed:
+        raise CmdException, 'Failed to delete %d patches' % failed
 
     if not options.branch:
         print_crt_patch()
diff --git a/t/t1600-delete-one.sh b/t/t1600-delete-one.sh
new file mode 100644
index 0000000..df03d79
--- /dev/null
+++ b/t/t1600-delete-one.sh
@@ -0,0 +1,109 @@
+#!/bin/sh
+# Copyright (c) 2006 Karl Hasselström
+test_description='Test the delete command (deleting one patch at a time).'
+. ./test-lib.sh
+
+test_expect_success \
+    'Initialize the StGIT repository' \
+    'stg init'
+
+test_expect_success \
+    'Create a patch' \
+    '
+    stg new foo -m foo &&
+    echo foo > foo.txt &&
+    stg add foo.txt &&
+    stg refresh
+    '
+
+test_expect_success \
+    'Try to delete a non-existing patch' \
+    '
+    [ $(stg applied | wc -l) -eq 1 ] &&
+    ! stg delete bar &&
+    [ $(stg applied | wc -l) -eq 1 ]
+    '
+
+test_expect_success \
+    'Try to delete the topmost patch while dirty' \
+    '
+    echo dirty >> foo.txt &&
+    [ $(stg applied | wc -l) -eq 1 ] &&
+    ! stg delete foo &&
+    [ $(stg applied | wc -l) -eq 1 ] &&
+    git reset --hard
+    '
+
+test_expect_success \
+    'Delete the topmost patch' \
+    '
+    [ $(stg applied | wc -l) -eq 1 ] &&
+    stg delete foo &&
+    [ $(stg applied | wc -l) -eq 0 ]
+    '
+
+test_expect_success \
+    'Create an unapplied patch' \
+    '
+    stg new foo -m foo &&
+    echo foo > foo.txt &&
+    stg add foo.txt &&
+    stg refresh &&
+    stg pop
+    '
+
+test_expect_success \
+    'Delete an unapplied patch' \
+    '
+    [ $(stg unapplied | wc -l) -eq 1 ] &&
+    stg delete foo &&
+    [ $(stg unapplied | wc -l) -eq 0 ]
+    '
+
+test_expect_success \
+    'Create two patches' \
+    '
+    stg new foo -m foo &&
+    echo foo > foo.txt &&
+    stg add foo.txt &&
+    stg refresh &&
+    stg new bar -m bar &&
+    echo bar > bar.txt &&
+    stg add bar.txt &&
+    stg refresh
+    '
+
+test_expect_success \
+    'Try to delete a non-topmost applied patch' \
+    '
+    [ $(stg applied | wc -l) -eq 2 ] &&
+    ! stg delete foo &&
+    [ $(stg applied | wc -l) -eq 2 ]
+    '
+
+test_expect_success \
+    'Create another branch, and put one patch in each branch' \
+    '
+    stg branch --create br &&
+    stg new baz -m baz &&
+    echo baz > baz.txt &&
+    stg add baz.txt &&
+    stg refresh &&
+    stg branch master &&
+    stg new baz -m baz &&
+    echo baz > baz.txt &&
+    stg add baz.txt &&
+    stg refresh
+    '
+
+test_expect_success \
+    'Delete a patch in another branch' \
+    '
+    [ $(stg applied | wc -l) -eq 3 ] &&
+    [ $(stg applied -b br | wc -l) -eq 1 ] &&
+    stg delete -b br baz &&
+    [ $(stg applied | wc -l) -eq 3 ] &&
+    [ $(stg applied -b br | wc -l) -eq 0 ]
+    '
+
+test_done
diff --git a/t/t1600-delete.sh b/t/t1600-delete.sh
deleted file mode 100644
index e22e624..0000000
--- a/t/t1600-delete.sh
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/bin/sh
-# Copyright (c) 2006 Karl Hasselström
-test_description='Test the delete command.'
-. ./test-lib.sh
-
-test_expect_success \
-    'Initialize the StGIT repository' \
-    'stg init'
-
-test_expect_success \
-    'Create a patch' \
-    '
-    stg new foo -m foo &&
-    echo foo > foo.txt &&
-    stg add foo.txt &&
-    stg refresh
-    '
-
-test_expect_success \
-    'Try to delete a non-existing patch' \
-    '
-    [ $(stg applied | wc -l) -eq 1 ] &&
-    ! stg delete bar &&
-    [ $(stg applied | wc -l) -eq 1 ]
-    '
-
-test_expect_success \
-    'Try to delete the topmost patch while dirty' \
-    '
-    echo dirty >> foo.txt &&
-    [ $(stg applied | wc -l) -eq 1 ] &&
-    ! stg delete foo &&
-    [ $(stg applied | wc -l) -eq 1 ] &&
-    git reset --hard
-    '
-
-test_expect_success \
-    'Delete the topmost patch' \
-    '
-    [ $(stg applied | wc -l) -eq 1 ] &&
-    stg delete foo &&
-    [ $(stg applied | wc -l) -eq 0 ]
-    '
-
-test_expect_success \
-    'Create an unapplied patch' \
-    '
-    stg new foo -m foo &&
-    echo foo > foo.txt &&
-    stg add foo.txt &&
-    stg refresh &&
-    stg pop
-    '
-
-test_expect_success \
-    'Delete an unapplied patch' \
-    '
-    [ $(stg unapplied | wc -l) -eq 1 ] &&
-    stg delete foo &&
-    [ $(stg unapplied | wc -l) -eq 0 ]
-    '
-
-test_expect_success \
-    'Create two patches' \
-    '
-    stg new foo -m foo &&
-    echo foo > foo.txt &&
-    stg add foo.txt &&
-    stg refresh &&
-    stg new bar -m bar &&
-    echo bar > bar.txt &&
-    stg add bar.txt &&
-    stg refresh
-    '
-
-test_expect_success \
-    'Try to delete a non-topmost applied patch' \
-    '
-    [ $(stg applied | wc -l) -eq 2 ] &&
-    ! stg delete foo &&
-    [ $(stg applied | wc -l) -eq 2 ]
-    '
-
-test_expect_success \
-    'Create another branch, and put one patch in each branch' \
-    '
-    stg branch --create br &&
-    stg new baz -m baz &&
-    echo baz > baz.txt &&
-    stg add baz.txt &&
-    stg refresh &&
-    stg branch master &&
-    stg new baz -m baz &&
-    echo baz > baz.txt &&
-    stg add baz.txt &&
-    stg refresh
-    '
-
-test_expect_success \
-    'Delete a patch in another branch' \
-    '
-    [ $(stg applied | wc -l) -eq 3 ] &&
-    [ $(stg applied -b br | wc -l) -eq 1 ] &&
-    stg delete -b br baz &&
-    [ $(stg applied | wc -l) -eq 3 ] &&
-    [ $(stg applied -b br | wc -l) -eq 0 ]
-    '
-
-test_done
diff --git a/t/t1601-delete-many.sh b/t/t1601-delete-many.sh
new file mode 100644
index 0000000..f217804
--- /dev/null
+++ b/t/t1601-delete-many.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+# Copyright (c) 2006 Karl Hasselström
+test_description='Test the delete command (deleting many patches at once).'
+. ./test-lib.sh
+
+test_expect_success \
+    'Initialize the StGIT repository' \
+    'stg init'
+
+test_expect_success \
+    'Create five applied and five unapplied patches' \
+    '
+    stg new foo0 -m foo0 &&
+    echo foo0 > foo.txt &&
+    stg add foo.txt &&
+    stg refresh &&
+    for i in 1 2 3 4 5 6 7 8 9; do
+        stg new foo$i -m foo$i &&
+        echo foo$i >> foo.txt &&
+        stg refresh;
+    done &&
+    stg pop -n 5
+    '
+
+test_expect_success \
+    'Delete some patches' \
+    '
+    [ $(stg applied | wc -l) -eq 5 ] &&
+    [ $(stg unapplied | wc -l) -eq 5 ] &&
+    stg delete foo7 foo6 foo3 foo4 &&
+    [ $(stg applied | wc -l) -eq 3 ] &&
+    [ $(stg unapplied | wc -l) -eq 3 ]
+    '
+
+test_expect_success \
+    'Delete some more patches, some of which fail' \
+    '
+    [ $(stg applied | wc -l) -eq 3 ] &&
+    [ $(stg unapplied | wc -l) -eq 3 ] &&
+    ! stg delete foo7 foo8 foo2 foo0 &&
+    [ $(stg applied | wc -l) -eq 2 ] &&
+    [ $(stg unapplied | wc -l) -eq 2 ]
+    '
+
+test_done

^ permalink raw reply related

* Re: [PATCH 2/2] Allow deletion of several patches at once
From: Karl Hasselström @ 2006-10-22 13:01 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20061022125816.15194.3341.stgit@localhost>

On 2006-10-22 14:58:16 +0200, Karl Hasselström wrote:

>  t/t1600-delete-one.sh    |  109 ++++++++++++++++++++++++++++++++++++++++++++++
>  t/t1600-delete.sh        |  109 ----------------------------------------------

By the way, this is why I complained about StGIT's lack of rename
support earlier. This is a file move with maybe three of 109 lines
changed, but you won't be able to see that without importing the patch
and examining it with the git tools. :-(

-- 
Karl Hasselström, kha@treskal.com
      www.treskal.com/kalle

^ permalink raw reply

* Re: VCS comparison table
From: Matthew D. Fuller @ 2006-10-22 13:03 UTC (permalink / raw)
  To: Sean; +Cc: bazaar-ng, git
In-Reply-To: <20061022074422.50dcbee6.seanlkml@sympatico.ca>

On Sun, Oct 22, 2006 at 07:44:22AM -0400 I heard the voice of
Sean, and lo! it spake thus:
> 
> Bzr revnos (dotted or otherwise) can not be guaranteed to be stable
> in a truly distributed system.

Perhaps the difference is that we're making a [fine] distinction
between "useful in a truely distributed system" and "useful when
WORKING in a truely distributed system".  cworth's point back up a few
posts is good; nearly all of my use of revnos is in direct interaction
with the tool, where the revnos just came from looking at the history.
And of those uses that aren't in that class, nearly all of THOSE are
very transient.  Non-local (in time or space) stability in either of
those cases is a total non-concern.


-- 
Matthew Fuller     (MF4839)   |  fullermd@over-yonder.net
Systems/Network Administrator |  http://www.over-yonder.net/~fullermd/
           On the Internet, nobody can hear you scream.

^ permalink raw reply

* [PATCH 0/3] Fix a few rough spots in StGIT's help command
From: Karl Hasselström @ 2006-10-22 13:02 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git

This series fixes a few irritating things in StGIT's online help. I
post them as three independent patches since these are somewhat
subjective improvements; feel free to drop the ones you don't like.

-- 
Karl Hasselström, kha@treskal.com
      www.treskal.com/kalle

^ permalink raw reply

* [PATCH 1/3] Let "stg help" be like "stg --help"
From: Karl Hasselström @ 2006-10-22 13:05 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20061022130214.16449.41482.stgit@localhost>

From: Karl Hasselström <kha@treskal.com>

"stg --help" prints the list of commands, but "stg help" just prints
the usage message for the help command. This may be useful in theory,
but the distinction is probably lost on 95% of all users (and the
remaining 5% probably don't need to see the usage message for the help
command anyway, since they know how it works). So just make both
commands output the helpful command list.

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

 stgit/main.py |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)

diff --git a/stgit/main.py b/stgit/main.py
index de35ca8..66128a3 100644
--- a/stgit/main.py
+++ b/stgit/main.py
@@ -226,8 +226,7 @@ def main():
                                   option_list = command.options)
             parser.print_help()
         else:
-            print 'usage: %s help <command>' % prog
-
+            print_help()
         sys.exit(0)
     if cmd in ['-v', '--version', 'version']:
         print 'Stacked GIT %s' % version

^ permalink raw reply related

* [PATCH 3/3] Disregard extraneous arguments when providing help
From: Karl Hasselström @ 2006-10-22 13:05 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20061022130214.16449.41482.stgit@localhost>

From: Karl Hasselström <kha@treskal.com>

"stg --help pop" prints the help text for pop, but "stg --help pop
foo" just prints the list of commands. That's silly, so fix it by
simply ignoring the extra arguments.

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

 stgit/main.py |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/stgit/main.py b/stgit/main.py
index 02d1eb1..9fa0afc 100644
--- a/stgit/main.py
+++ b/stgit/main.py
@@ -206,7 +206,7 @@ def main():
     cmd = sys.argv[1]
 
     if cmd in ['-h', '--help']:
-        if len(sys.argv) == 3 and sys.argv[2] in commands:
+        if len(sys.argv) >= 3 and sys.argv[2] in commands:
             cmd = sys.argv[2]
             sys.argv[2] = '--help'
         else:

^ permalink raw reply related

* [PATCH 2/3] When no command was given, print usage message
From: Karl Hasselström @ 2006-10-22 13:05 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20061022130214.16449.41482.stgit@localhost>

From: Karl Hasselström <kha@treskal.com>

It's just silly to say "Unknown command" when the user didn't give a
command. Better to tell her to use a command.

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

 stgit/main.py |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/stgit/main.py b/stgit/main.py
index 66128a3..02d1eb1 100644
--- a/stgit/main.py
+++ b/stgit/main.py
@@ -198,7 +198,7 @@ def main():
     prog = os.path.basename(sys.argv[0])
 
     if len(sys.argv) < 2:
-        print >> sys.stderr, 'Unknown command'
+        print >> sys.stderr, 'usage: %s <command>' % prog
         print >> sys.stderr, \
               '  Try "%s --help" for a list of supported commands' % prog
         sys.exit(1)

^ permalink raw reply related

* [PATCH 2/2] Regression test for "stg assimilate"
From: Karl Hasselström @ 2006-10-22 13:08 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20061022130559.17015.51385.stgit@localhost>

From: Karl Hasselström <kha@treskal.com>



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

 t/t1301-assimilate.sh |   86 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 86 insertions(+), 0 deletions(-)

diff --git a/t/t1301-assimilate.sh b/t/t1301-assimilate.sh
new file mode 100755
index 0000000..26b263c
--- /dev/null
+++ b/t/t1301-assimilate.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+# Copyright (c) 2006 Karl Hasselström
+test_description='Test the assimilate command.'
+. ./test-lib.sh
+
+test_expect_success \
+    'Assimilate in a non-initialized repository' \
+    'stg assimilate'
+
+test_expect_success \
+    'Initialize the StGIT repository' \
+    'stg init'
+
+test_expect_success \
+    'Assimilate in a repository without patches' \
+    'stg assimilate'
+
+test_expect_success \
+    'Create a patch' \
+    '
+    stg new foo -m foo &&
+    echo foo > foo.txt &&
+    stg add foo.txt &&
+    stg refresh
+    '
+
+test_expect_success \
+    'Assimilate when there is nothing to do' \
+    'stg assimilate'
+
+test_expect_success \
+    'Create a GIT commit' \
+    '
+    echo bar > bar.txt &&
+    git add bar.txt &&
+    git commit -a -m bar
+    '
+
+test_expect_success \
+    'Assimilate one GIT commit' \
+    '
+    [ $(stg applied | wc -l) -eq 1 ] &&
+    stg assimilate &&
+    [ $(stg applied | wc -l) -eq 2 ]
+    '
+
+test_expect_success \
+    'Create three more GIT commits' \
+    '
+    echo one > numbers.txt &&
+    git add numbers.txt &&
+    git commit -a -m one &&
+    echo two >> numbers.txt &&
+    git commit -a -m two &&
+    echo three >> numbers.txt &&
+    git commit -a -m three
+    '
+
+test_expect_success \
+    'Assimilate three GIT commits' \
+    '
+    [ $(stg applied | wc -l) -eq 2 ] &&
+    stg assimilate &&
+    [ $(stg applied | wc -l) -eq 5 ]
+    '
+
+test_expect_success \
+    'Create a mege commit' \
+    '
+    git checkout -b br master^^ &&
+    echo woof > woof.txt &&
+    git add woof.txt &&
+    git commit -a -m woof &&
+    git checkout master &&
+    git pull . br
+    '
+
+test_expect_success \
+    'Try (and fail) to assimilate the merge commit' \
+    '
+    [ $(stg applied | wc -l) -eq 5 ] &&
+    ! stg assimilate &&
+    [ $(stg applied | wc -l) -eq 5 ]
+    '
+
+test_done

^ permalink raw reply related

* [PATCH 0/2] Resistance is futile; you _will_ be assimilated ...
From: Karl Hasselström @ 2006-10-22 13:05 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git

A new StGIT command that assimilates any GIT commits made on top of
your StGIT patches, and a test to go with it.

-- 
Karl Hasselström, kha@treskal.com
      www.treskal.com/kalle

^ permalink raw reply

* [PATCH 1/2] New stg command: assimilate
From: Karl Hasselström @ 2006-10-22 13:08 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20061022130559.17015.51385.stgit@localhost>

From: Karl Hasselström <kha@treskal.com>

Introduce an "assimilate" command, with no options. It takes any GIT
commits committed on top of your StGIT patch stack and converts them
into StGIT patches.

Also change the error message when an StGIT command can't do its job
because there are GIT commits on top of the stack. Instead of
recommending "refresh -f", which is a destructive operation, recommend
"assimilate", which is not.

NOTE: "assimilate" currently refuses to work its magic if it
encounters a merge commit. This is reasonable, since merge commits
can't (yet) be represented as StGIT patches. However, it would be
possible (and well-defined) to replace the merge commit with a regular
commit on the branch with the same end result (tree object),
discarding all the parents that aren't on our branch. But this would
take a substantial amount of code, and is of dubious value, so for now
"assimilate" just cries bloody murder if it finds a merge.

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

 stgit/commands/assimilate.py |   96 ++++++++++++++++++++++++++++++++++++++++++
 stgit/commands/common.py     |    8 ++--
 stgit/git.py                 |    3 +
 stgit/main.py                |    3 +
 stgit/stack.py               |   28 +++++++++---
 5 files changed, 126 insertions(+), 12 deletions(-)

diff --git a/stgit/commands/assimilate.py b/stgit/commands/assimilate.py
new file mode 100644
index 0000000..2c0ec56
--- /dev/null
+++ b/stgit/commands/assimilate.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+
+__copyright__ = """
+Copyright (C) 2006, Karl Hasselström <kha@treskal.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+
+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
+"""
+
+import sys, os
+from optparse import OptionParser, make_option
+
+from stgit.commands.common import *
+from stgit.utils import *
+from stgit import stack, git
+
+help = 'StGIT-ify any GIT commits made on top of your StGIT stack'
+usage = """%prog
+
+If you have made GIT commits on top of your stack of StGIT patches,
+many StGIT commands will refuse to work. This command converts any
+such commits to StGIT patches, preserving their contents.
+
+Only GIT commits with exactly one parent can be assimilated; in other
+words, if you have committed a merge on top of your stack, this
+command cannot help you."""
+
+options = []
+
+def func(parser, options, args):
+    """Assimilate a number of patches.
+    """
+
+    def nothing_to_do():
+        print 'No commits to assimilate'
+
+    top_patch = crt_series.get_current_patch()
+    if not top_patch:
+        return nothing_to_do()
+
+    victims = []
+    victim = git.get_commit(git.get_head())
+    while victim.get_id_hash() != top_patch.get_top():
+        victims.append(victim)
+        parents = victim.get_parents()
+        if not parents:
+            raise CmdException, 'Commit %s has no parents, aborting' % victim
+        elif len(parents) > 1:
+            raise CmdException, 'Commit %s is a merge, aborting' % victim
+        victim = git.get_commit(parents[0])
+
+    if not victims:
+        return nothing_to_do()
+
+    if crt_series.get_protected():
+        raise CmdException(
+            'This branch is protected. Modification is not permitted')
+
+    patch2name = {}
+    name2patch = {}
+    def name_taken(name):
+        return patchname in name2patch or crt_series.patch_exists(patchname)
+    for victim in victims:
+        patchname = make_patch_name(victim.get_log())
+        if not patchname:
+            patchname = 'patch'
+        if name_taken(patchname):
+            suffix = 0
+            while name_taken('%s-%d' % (patchname, suffix)):
+                suffix += 1
+            patchname = '%s-%d' % (patchname, suffix)
+        patch2name[victim] = patchname
+        name2patch[patchname] = victim
+
+    for victim in reversed(victims):
+        print ('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(
+            patch2name[victim],
+            can_edit = False, before_existing = False, refresh = False,
+            top = victim.get_id_hash(), bottom = victim.get_parent(),
+            message = victim.get_log(),
+            author_name = aname, author_email = amail, author_date = adate,
+            committer_name = cname, committer_email = cmail)
diff --git a/stgit/commands/common.py b/stgit/commands/common.py
index bf8481e..1ea6025 100644
--- a/stgit/commands/common.py
+++ b/stgit/commands/common.py
@@ -113,10 +113,10 @@ def check_local_changes():
 
 def check_head_top_equal():
     if not crt_series.head_top_equal():
-        raise CmdException, \
-              'HEAD and top are not the same. You probably committed\n' \
-              '  changes to the tree outside of StGIT. If you know what you\n' \
-              '  are doing, use the "refresh -f" command'
+        raise CmdException(
+            'HEAD and top are not the same. You probably committed\n'
+            '  changes to the tree outside of StGIT. To bring them\n'
+            '  into StGIT, use the "assimilate" command')
 
 def check_conflicts():
     if os.path.exists(os.path.join(basedir.get(), 'conflicts')):
diff --git a/stgit/git.py b/stgit/git.py
index 43bdc7e..42b0d12 100644
--- a/stgit/git.py
+++ b/stgit/git.py
@@ -79,6 +79,9 @@ class Commit:
     def get_log(self):
         return self.__log
 
+    def __str__(self):
+        return self.get_id_hash()
+
 # dictionary of Commit objects, used to avoid multiple calls to git
 __commits = dict()
 
diff --git a/stgit/main.py b/stgit/main.py
index e9cc6cd..de35ca8 100644
--- a/stgit/main.py
+++ b/stgit/main.py
@@ -30,6 +30,7 @@ from stgit.commands.common import *
 # The commands
 import stgit.commands.add
 import stgit.commands.applied
+import stgit.commands.assimilate
 import stgit.commands.branch
 import stgit.commands.delete
 import stgit.commands.diff
@@ -70,6 +71,7 @@ #
 commands = {
     'add':      stgit.commands.add,
     'applied':  stgit.commands.applied,
+    'assimilate': stgit.commands.assimilate,
     'branch':   stgit.commands.branch,
     'delete':   stgit.commands.delete,
     'diff':     stgit.commands.diff,
@@ -113,6 +115,7 @@ repocommands = (
     )
 stackcommands = (
     'applied',
+    'assimilate',
     'clean',
     'commit',
     'float',
diff --git a/stgit/stack.py b/stgit/stack.py
index 93a3d4e..e50f189 100644
--- a/stgit/stack.py
+++ b/stgit/stack.py
@@ -350,9 +350,17 @@ class Series:
         """
         return Patch(name, self.__patch_dir, self.__refs_dir)
 
+    def get_current_patch(self):
+        """Return a Patch object representing the topmost patch, or
+        None if there is no such patch."""
+        crt = self.get_current()
+        if not crt:
+            return None
+        return Patch(crt, self.__patch_dir, self.__refs_dir)
+
     def get_current(self):
-        """Return a Patch object representing the topmost patch
-        """
+        """Return the name of the topmost patch, or None if there is
+        no such patch."""
         if os.path.isfile(self.__current_file):
             name = read_string(self.__current_file)
         else:
@@ -414,6 +422,11 @@ class Series:
         """
         return name in self.get_unapplied()
 
+    def patch_exists(self, name):
+        """Return true if there is a patch with the given name, false
+        otherwise."""
+        return self.__patch_applied(name) or self.__patch_applied(name)
+
     def __begin_stack_check(self):
         """Save the current HEAD into .git/refs/heads/base if the stack
         is empty
@@ -433,12 +446,11 @@ class Series:
     def head_top_equal(self):
         """Return true if the head and the top are the same
         """
-        crt = self.get_current()
+        crt = self.get_current_patch()
         if not crt:
             # we don't care, no patches applied
             return True
-        return git.get_head() == Patch(crt, self.__patch_dir,
-                                       self.__refs_dir).get_top()
+        return git.get_head() == crt.get_top()
 
     def is_initialised(self):
         """Checks if series is already initialised
@@ -688,7 +700,7 @@ class Series:
                   top = None, bottom = None,
                   author_name = None, author_email = None, author_date = None,
                   committer_name = None, committer_email = None,
-                  before_existing = False):
+                  before_existing = False, refresh = True):
         """Creates a new patch
         """
         if self.__patch_applied(name) or self.__patch_unapplied(name):
@@ -741,8 +753,8 @@ class Series:
         else:
             append_string(self.__applied_file, patch.get_name())
             self.__set_current(name)
-
-            self.refresh_patch(cache_update = False, log = 'new')
+            if refresh:
+                self.refresh_patch(cache_update = False, log = 'new')
 
     def delete_patch(self, name):
         """Deletes a patch

^ permalink raw reply related

* Re: VCS comparison table
From: Jakub Narebski @ 2006-10-22 13:23 UTC (permalink / raw)
  To: Erik Bågfors
  Cc: Jan Hudec, bazaar-ng, Linus Torvalds, Carl Worth,
	Andreas Ericsson, git
In-Reply-To: <845b6e870610220256u39d3d06wefd4f71851670812@mail.gmail.com>

Erik Bågfors wrote:
> Jakub Narębski wrote:

>> For example git encourages using many short and longer-lived feature
>> branches; I don't see bzr encouraging this workflow.
> 
> Why not? I think it really does.  And due to the fact that merges are
> merges and will show up as such, I think it's very suitable for
> feature branches.

I think I haven't properly explained what "feature branch" means.
"Feature branch" is short (or medium) lived branch, created for
development of one isolated feature. When feature is in stable
stage, we merge feature branch and forget about it. We are not
interested in the fact that given feature was developed on given
branch. BTW. for example in published git.git repository are
only available in the form of "digest" 'pu' (proposed updates)
branch.

I guess what you are talking about are long lived "development
branches" (like git.git 'maint', 'master', 'next' and 'pu' branches),
or perhaps long lived another user's clone of given git repository.

Git considers having clones of given repository totally equivalent,
and having fast-forward property more important than remembering
"which branch (which clone) has this commit came from" or at least
"this commit is from this (current) branch-clone".

You have graphical history viewers (bzr has it's own: bzr-gtk),
committer and author info, and reflog if enabled if you really,
really need this kind of information. 
 
> In fact, in the bzr development of bzr itself.  All commits are done
> in feature branches and then merged into bzr.dev (the main "trunk" of
> bzr) when they are considered stable.
> 
> Consider the following
> bzr branch mainline featureA
Which if I remember correctly (at least by default) needs and generates
new working tree.

> cd featureA
> hack hack; bzr commit -m 'f1'; hack hack bzr commit -m f2; etc
> No I want to merge in mainline again
> bzr merge ../mainline; bzr commit -m merge
> hack hack; bzr commit -m f3; hack hack bzr commit -m f4; etc

As it clarified during this long discussion, bzr "branches" are
something between git branches and one-branch [local] clones.
Can you for example create branch starting from an arbitrary revision,
not only tip of branch?

The above sequence of operations can be done in (at least) two different
ways in git.

Less used:
 $ cd /somewhere/else
 $ git clone -l -s <mainrepo>/.git featureA
 $ cd featureA
 $ hack; hack; git commit -a -m "f1"; hack; hack; git commit -a -m "f2"; etc   
 $ cd <mainrepo>
 $ git pull /somewhere/else/featureA/.git
 (this does commit and merge)

But more common used is:
 $ git branch featureA mainline
 $ git checkout featureA
 $ hack; hack; git commit -a -m "f1"; hack; hack; git commit -a -m "f2"; etc
 $ git checkout mainline
 $ git pull . featureA
 (although this would fast-forward in this example)

> right now, I would have something line this in the branch log
> -----------------------------------------------------------------
> committer: Erik Bågfors <erik@bagfors.nu>
> branch nick: featureA
> message:
>    f4
> -----------------------------------------------------------------
> committer: Erik Bågfors <erik@bagfors.nu>
> branch nick: featureA
> message:
>    f3
> ----------------------------------------------------------------
> committer: Erik Bågfors <erik@bagfors.nu>
> branch nick: featureA
> message:
>    merge
>       -----------------------------------------------------------------
>       committer: Foo Bar <foo@bar.com>
>       branch nick: mainline
>       message:
>          something done in mainline
>       -----------------------------------------------------------------
>       committer: Foo Bar <foo@bar.com>
>       branch nick: mainline
>       message:
>          something else done in mainline
The automatic merge message takes care of this, if we enable
merge.summary config option. For example:

commit 2c8a02263c13c6e1891e9e338eb40a4286b613e5
Merge: 2492932... 87b787a...
Author: Jakub Narebski <jnareb@gmail.com>
Date:   Sat Oct 21 13:23:19 2006 +0200

    Merge branch 'master' of git://git.kernel.org/pub/scm/git/git
    
    * 'master' of git://git.kernel.org/pub/scm/git/git:
      git-clone: define die() and use it.
      Fix typo in show-index.c
      pager: default to LESS=FRS


Another example, this time of "octopus" merge.

commit ff49fae6a547e5c70117970e01c53b64d983cd10
Merge: 7ad4ee7... 75f9007... 14eab2b... 0b35995... eee4609...
Author: Junio C Hamano <junkio@cox.net>
Date:   Fri Oct 20 18:56:14 2006 -0700

    Merge branches 'jc/diff', 'jc/diff-apply-patch', 'jc/read-tree' and 'pb/web' into pu
    
    * jc/diff:
      para walk wip
      para-walk: walk n trees, index and working tree in parallel
    
    * jc/diff-apply-patch:
      git-diff/git-apply: make diff output a bit friendlier to GNU patch (part 2)
    
    * jc/read-tree:
      merge: loosen overcautious "working file will be lost" check.
    
    * pb/web:
      gitweb: Show project README if available

That said we couldn't do that in abovementioned example
as it is simple case of fast-forward. We have above messages
for "true merges" of two _diverging_ lines of development,
and we could use similar format for "git log". In practice
we rather use history viewers: gitk, qgit, tig, git-show-branch.

For example:
$ git show-branch origin next
! [origin] git-clone: define die() and use it.
 ! [next] Merge branch 'master' into next
--
 - [next] Merge branch 'master' into next
++ [origin] git-clone: define die() and use it.

> If I understand it correctly, in git, you don't really know what has
> been committed as part of this branch/repo, and what has been
> committed in another branch/repo (this is my understanding from
> reading this thread, I might be wrong, feel free to correct me again
> :) )

You can browse reflog to get to know which changes were commited
as part of this repo, and which came from other repo (other clone
of this repo).
-- 
Jakub Narebski
Poland

^ permalink raw reply

* Re: VCS comparison table
From: Sean @ 2006-10-22 13:28 UTC (permalink / raw)
  To: Matthew D. Fuller; +Cc: bazaar-ng, git
In-Reply-To: <20061022130322.GS75501@over-yonder.net>

On Sun, 22 Oct 2006 08:03:22 -0500
"Matthew D. Fuller" <fullermd@over-yonder.net> wrote:

> Perhaps the difference is that we're making a [fine] distinction
> between "useful in a truely distributed system" and "useful when
> WORKING in a truely distributed system".  cworth's point back up a few
> posts is good; nearly all of my use of revnos is in direct interaction
> with the tool, where the revnos just came from looking at the history.
> And of those uses that aren't in that class, nearly all of THOSE are
> very transient.  Non-local (in time or space) stability in either of
> those cases is a total non-concern.

Sure, but if they're just a local feature then why propagate them with
the distributed data?  If they're meant only to be used locally,
they can be guaranteed to be stable by never replicating
them, with obvious benefits for the local user.  However bzr makes the
(IMO) mistake of including them in the data that is distributed 
between repos.  This suggests bzr team just doesn't care about the
distributed models where this will not help and will quite possibly
lead to frustration and confusion.  And yes, I know that you
haven't seen those situations yourself yet.  Obviously, it's the
Bzr teams trade-off to make, but if an avid user like yourself thinks
of revno's as local, perhaps they've made the wrong choice.

Sean

^ permalink raw reply

* Re: VCS comparison table
From: Sean @ 2006-10-22 13:28 UTC (permalink / raw)
  To: Matthew D. Fuller; +Cc: bazaar-ng, git
In-Reply-To: <20061022130322.GS75501@over-yonder.net>

On Sun, 22 Oct 2006 08:03:22 -0500
"Matthew D. Fuller" <fullermd@over-yonder.net> wrote:

> Perhaps the difference is that we're making a [fine] distinction
> between "useful in a truely distributed system" and "useful when
> WORKING in a truely distributed system".  cworth's point back up a few
> posts is good; nearly all of my use of revnos is in direct interaction
> with the tool, where the revnos just came from looking at the history.
> And of those uses that aren't in that class, nearly all of THOSE are
> very transient.  Non-local (in time or space) stability in either of
> those cases is a total non-concern.

Sure, but if they're just a local feature then why propagate them with
the distributed data?  If they're meant only to be used locally,
they can be guaranteed to be stable by never replicating
them, with obvious benefits for the local user.  However bzr makes the
(IMO) mistake of including them in the data that is distributed 
between repos.  This suggests bzr team just doesn't care about the
distributed models where this will not help and will quite possibly
lead to frustration and confusion.  And yes, I know that you
haven't seen those situations yourself yet.  Obviously, it's the
Bzr teams trade-off to make, but if an avid user like yourself thinks
of revno's as local, perhaps they've made the wrong choice.

Sean

^ permalink raw reply

* Re: VCS comparison table
From: Matthew D. Fuller @ 2006-10-22 13:33 UTC (permalink / raw)
  To: Sean; +Cc: bazaar-ng, git
In-Reply-To: <20061022092845.233deb43.seanlkml@sympatico.ca>

On Sun, Oct 22, 2006 at 09:28:45AM -0400 I heard the voice of
Sean, and lo! it spake thus:
> 
> Sure, but if they're just a local feature then why propagate them
> with the distributed data?

Because they're 'local' to a given "branch"; see my message to cworth
a little while ago for expansion of the rather particular meaning of
the word used here.  If somebody takes a clone of my _branch_, it's
the same "branch", so the numbers will be the same (and that's
desired).


-- 
Matthew Fuller     (MF4839)   |  fullermd@over-yonder.net
Systems/Network Administrator |  http://www.over-yonder.net/~fullermd/
           On the Internet, nobody can hear you scream.

^ permalink raw reply

* Re: VCS comparison table
From: Sean @ 2006-10-22 13:40 UTC (permalink / raw)
  To: Matthew D. Fuller; +Cc: bazaar-ng, git
In-Reply-To: <20061022133336.GT75501@over-yonder.net>

On Sun, 22 Oct 2006 08:33:36 -0500
"Matthew D. Fuller" <fullermd@over-yonder.net> wrote:

> Because they're 'local' to a given "branch"; see my message to cworth
> a little while ago for expansion of the rather particular meaning of
> the word used here.  If somebody takes a clone of my _branch_, it's
> the same "branch", so the numbers will be the same (and that's
> desired).

The fact is that once you start distributing them to other repositories
you CAN NOT GUARANTEE their stability.  Those number may already be
used by _HIS_ branch and when he tries to get _YOUR_ branch.. there
is a conflict.  AND THERE IS NOTHING YOU CAN DO TO FIX THAT.  It's
a fundamental flaw with distributing revnos.  The reason you likely
haven't seen a problem so far is that the bzr world seems to favor
the use of a central server that has the effect of more or less
synchronizing branch numbers to most of the nodes in the system.
However, that's only one model.  So while you may not have seen a
problem yourself, there are _inherent_ limitations of the system
you've embraced.

But it seems like nobody on the bzr team cares or wants to hear about
it, so let's just move on.

Cheers,
Sean

^ permalink raw reply

* Re: VCS comparison table
From: Sean @ 2006-10-22 13:40 UTC (permalink / raw)
  To: Matthew D. Fuller; +Cc: bazaar-ng, git
In-Reply-To: <20061022133336.GT75501@over-yonder.net>

On Sun, 22 Oct 2006 08:33:36 -0500
"Matthew D. Fuller" <fullermd@over-yonder.net> wrote:

> Because they're 'local' to a given "branch"; see my message to cworth
> a little while ago for expansion of the rather particular meaning of
> the word used here.  If somebody takes a clone of my _branch_, it's
> the same "branch", so the numbers will be the same (and that's
> desired).

The fact is that once you start distributing them to other repositories
you CAN NOT GUARANTEE their stability.  Those number may already be
used by _HIS_ branch and when he tries to get _YOUR_ branch.. there
is a conflict.  AND THERE IS NOTHING YOU CAN DO TO FIX THAT.  It's
a fundamental flaw with distributing revnos.  The reason you likely
haven't seen a problem so far is that the bzr world seems to favor
the use of a central server that has the effect of more or less
synchronizing branch numbers to most of the nodes in the system.
However, that's only one model.  So while you may not have seen a
problem yourself, there are _inherent_ limitations of the system
you've embraced.

But it seems like nobody on the bzr team cares or wants to hear about
it, so let's just move on.

Cheers,
Sean

^ permalink raw reply

* Re: VCS comparison table
From: Jakub Narebski @ 2006-10-22 13:51 UTC (permalink / raw)
  To: git; +Cc: bazaar-ng
In-Reply-To: <20061022124635.GR75501@over-yonder.net>

Matthew D. Fuller wrote:

>   It can also be useful in looking at cases where you don't
>   necessarily have the tool.  Compare putting CVS's rcsid tags in
>   strings in the source.  static const char *rcsid = "$Id"; and the
>   like.  Then you can use 'ident' on the compiled binaries to see the
>   revs of files in them.  If somebody says "foo.c has a bug in 1.34,
>   fixed in 1.37", I can without any VCS interaction just look at the
>   compiled binary and tell whether I'm prior to the bug, have the bug,
>   or after the fix.  If the binary is known to be compiled from a
>   particular branch, a tree-wide revno tells me that too.  A revid
>   (even one containing a date) won't tell me that; I'll have to find
>   the tool and a copy of the tree and find out if my rev contains that
>   other rev.

We use signed tags for tagging official releases (e.g. v1.4.0 tag),
and we use "git describe" output to be embedded during build time
in resulting binary. For example my current output of git-describe
on my clone of git repository is:

 $ git describe 
 v1.4.3.1-g2c8a022

Git project does this, gitweb does this, Linux kernel does this.
This is quite coarse grained, i.e. you know ahich released version
it is after, but you need git tools (or access to git tools via
gitweb) to check if it is after or before the fix.

Of course that is when you run GIT version of tool...
-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git

^ permalink raw reply

* Re: VCS comparison table
From: Matthew D. Fuller @ 2006-10-22 13:57 UTC (permalink / raw)
  To: Sean; +Cc: bazaar-ng, git
In-Reply-To: <20061022094041.77c06cc7.seanlkml@sympatico.ca>

On Sun, Oct 22, 2006 at 09:40:41AM -0400 I heard the voice of
Sean, and lo! it spake thus:
> 
> The fact is that once you start distributing them to other
> repositories you CAN NOT GUARANTEE their stability.

Terminology.  When those revisions get distributed to other BRANCHES,
their stability is forfeit.  We know.  We don't care.  We only care
about the numbers on ONE BRANCH.


> Those number may already be used by _HIS_ branch and when he tries
> to get _YOUR_ branch.. there is a conflict.

Terminology again.  When he has his branch and gets my branches, he
has two branches, mine and his, side by side, and the numbers in his
'my' branch still correspond to the numbers in my 'my' branch.  When
he merges the REVISIONS from my branch into his, my numbers have no
meaning on his side (there's not a 'conflict' because numbers don't
get copied, they get derived).


> So while you may not have seen a problem yourself,

You keep insisting that there's a PROBLEM here.  You're right, I don't
see one.  I KNOW the numbers only refer to a branch, I KNOW that when
you're talking about a different branch the numbers are meaningless,
and I'm perfectly fine with that because referring to revisions on *A*
branch is exactly what I USE the numbers for.

There doesn't have to be a 'central' branch, nor is there any wish for
such to be.  Any given revno only refers to *A* branch, it doesn't
have to be central to a darn thing.  HEAD in git only has meaning in
the context of *A* branch (and even 'worse', only refers to that
branch at a specific time[0]), but you'll keep on using it every day
anyway I wager.



[0] See again particular term of art "branch".


-- 
Matthew Fuller     (MF4839)   |  fullermd@over-yonder.net
Systems/Network Administrator |  http://www.over-yonder.net/~fullermd/
           On the Internet, nobody can hear you scream.

^ permalink raw reply

* Re: VCS comparison table
From: Sean @ 2006-10-22 14:24 UTC (permalink / raw)
  To: Matthew D. Fuller; +Cc: bazaar-ng, git
In-Reply-To: <20061022135702.GU75501@over-yonder.net>

On Sun, 22 Oct 2006 08:57:02 -0500
"Matthew D. Fuller" <fullermd@over-yonder.net> wrote:

> You keep insisting that there's a PROBLEM here.  You're right, I don't
> see one.  I KNOW the numbers only refer to a branch, I KNOW that when
> you're talking about a different branch the numbers are meaningless,
> and I'm perfectly fine with that because referring to revisions on *A*
> branch is exactly what I USE the numbers for.

Light goes on.  Okay.  So a bzr "branch" is only ever editable on a 
single machine.  So there is no distributed development on top of a 
bzr "branch".  Everyone else just has read-only copies of it.  In this
way you ensure that there is never a conflict of the revno's.  I'm not
sure of the ramifications of this but at least I get where you're coming
from now.

Sean

^ permalink raw reply

* Re: VCS comparison table
From: Sean @ 2006-10-22 14:24 UTC (permalink / raw)
  To: Matthew D. Fuller; +Cc: bazaar-ng, git
In-Reply-To: <20061022135702.GU75501@over-yonder.net>

On Sun, 22 Oct 2006 08:57:02 -0500
"Matthew D. Fuller" <fullermd@over-yonder.net> wrote:

> You keep insisting that there's a PROBLEM here.  You're right, I don't
> see one.  I KNOW the numbers only refer to a branch, I KNOW that when
> you're talking about a different branch the numbers are meaningless,
> and I'm perfectly fine with that because referring to revisions on *A*
> branch is exactly what I USE the numbers for.

Light goes on.  Okay.  So a bzr "branch" is only ever editable on a 
single machine.  So there is no distributed development on top of a 
bzr "branch".  Everyone else just has read-only copies of it.  In this
way you ensure that there is never a conflict of the revno's.  I'm not
sure of the ramifications of this but at least I get where you're coming
from now.

Sean

^ permalink raw reply

* Re: VCS comparison table
From: Carl Worth @ 2006-10-22 14:25 UTC (permalink / raw)
  To: Erik Bågfors
  Cc: Jakub Narebski, Jan Hudec, bazaar-ng, Linus Torvalds,
	Andreas Ericsson, git
In-Reply-To: <845b6e870610220256u39d3d06wefd4f71851670812@mail.gmail.com>

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

At Sun, 22 Oct 2006 11:56:32 +0200, "=?ISO-8859-1?Q?Erik_B=E5gfors?=" wrote:
> Consider the following
> bzr branch mainline featureA
> cd featureA
> hack hack; bzr commit -m 'f1'; hack hack bzr commit -m f2; etc
> No I want to merge in mainline again
> bzr merge ../mainline; bzr commit -m merge
> hack hack; bzr commit -m f3; hack hack bzr commit -m f4; etc

Thanks for sharing this example. I think when we look at concrete
things that the tools actually let you do, we have a better
conversation. Plus, this example highlights some very interesting
differences between the tools.

So here is a complete sequence of git commands to construct the
scenario (even the extra hacking in mainline):

	mkdir gittest; cd gittest
	git init-db
	touch mainline; git add mainline; git commit -m "Initial commit of mainline"
	git checkout -b featureA
	touch f1; git add f1; git commit -m f1
	touch f2; git add f2; git commit -m f2
	git checkout -b mainline master
	touch sd; git add sd; git commit -m "something done in mainline";
	touch se; git add se; git commit -m "something else done in mainline";
	git checkout featureA
	git pull . mainline
	touch f3; git add f3; git commit -m f3
	touch f4; git add f4; git commit -m f4

For reference, here's the same with bzr:

	mkdir bzrtest; cd bzrtest
	bzr init-repo . --trees
	bzr init mainline; cd mainline
	touch mainline; bzr add mainline; bzr commit -m "Initial commit of mainline"
	cd ..; bzr branch mainline featureA; cd featureA
	touch f1; bzr add f1; bzr commit -m f1
	touch f2; bzr add f2; bzr commit -m f2
	cd ../mainline/
	touch sd; bzr add sd; bzr commit -m "something done in mainline"
	touch se; bzr add se; bzr commit -m "something else done in mainline"
	cd ../featureA
	bzr merge ../mainline/; bzr commit -m "merge"
	touch f3; bzr add f3; bzr commit -m f3
	touch f4; bzr add f4; bzr commit -m f4

[As has recently been pointed out, the tools really are more the same
than different, and I think the above illustrates that.]

> right now, I would have something line this in the branch log

OK. So here is a difference in the tools. With git, you don't get the
indentation for the "non-mainline" commits. This is because git
doesn't recognize any branch in the DAG to be more significant than
any other. Instead, git provides a flat, and (heuristically)
time-sorted view of the commits. (It's heuristic in that git just uses
the time stamps in the commit objects---but it doesn't actually care
if these are totally "wrong"---git knows that there is no global
clock.)

That said, git does store an order for the parent edges of each
commit, and this order is assigned deterministically by the commands
that create merge commits. So someone could use git carefully, (which
it seems people are doing with bzr), to preserve "mainline as first
parent" and someone could write a modified git-log that would do
indentation.

But even without any of that manual care for creating a "mainline",
git already provides a very easy way to see the "mainline" view
anyway. See below.

> In this view,I can easily see what was part of this feature branch,
> because the commits that belongs to the feature branch are not
> indented, and they have a "branch nick" of "featureA".  I can also
> easily see what comes from other branches.

Ah, I hadn't realized that bzr commits stored an "originating branch"
inside them. Git commits definitely do not have anything like
that. And as I said above, there's no indentation in git-log, so the
commits from separate branches are "mixed up". But see below.

> I can also run bzr log with --line or --short which shows you only the
> commits made in this branch and not the once that are merged in.  So
> with --line I would get something line
> Erik Bågfors 2006-10-19 f4
> Erik Bågfors 2006-10-19 f3
> Erik Bågfors 2006-10-19 merge
> Erik Bågfors 2006-10-19 f2
> Erik Bågfors 2006-10-19 f1
>
> Which will give me a good view of what has been done in this feature
> branch only.

Thank you. You've provided a concrete example of something to do,
("see commits that belong to a feature branch"), that is really very
practical and useful. And bzr achieves this ability by adopting a
"mainline is special" treatment in bzr. This special treatment
influences or directly causes many of the things in bzr that we've
been discussing:

 * mainline commits get special treatment from revision numbers
   (in old days, they're the only commits to have revision
   numbers---more recently they're the only commits to get non-dotted
   revision numbers)

 * bzr adds empty merge commits instead of fast-forwarding since it
   needs a new "mainline" commit

 * users have to be careful about merge direction to avoid
   accidentally going the "wrong" way

 * users are discouraged from using the "give me their DAG" pull
   command since it would scramble their local view of what "mainline"
   is.

I've been arguing that all of these impacts are dubious. But I can
understand that a bzr user hearing arguments against them might fear
that they would lose the ability to be able to see a view of commits
that "belong" to a particular branch.

But git provides that view perfectly well, and it's what git users
work with all the time. It doesn't require any special treatment of
one commit parent vs. another, nor storage of "originating branch" in
the commit, nor the user taking any care whatsoever about which
direction merges are performed, (nor "who" does the merge).

And as a bonus, the command-line for this view is really simple:

	git log mainline..featureA

This gives a log view just "bzr log --line" in that in only includes
f1, f2, the merge commit, f3, and f4. You can even drop the merge if
it's uninteresting:

	git log --no-merges mainline..featureA

The mainline..featureA syntax literally just means:

	the set of commits that are reachable by featureA
	and excluding the set of commits reachable by mainline

It's an extraordinarily powerful thing to say, and its exactly what
you want here. And it's more than a "show mainline" thing, since
theses sets of commits can consist of arbitrarily complex DAG
subsets. This syntax is just a really useful way to slice up the DAG.

And this syntax is almost universally accepted by git commands. so you
can visualize a chunk of the DAG with:

	gitk mainline..featureA

Or export it as patches with:

	git format-patch mainline..featureA

I haven't been able to find something similar in bzr yet. Does it
exist?

> If I understand it correctly, in git, you don't really know what has
> been committed as part of this branch/repo, and what has been
> committed in another branch/repo (this is my understanding from
> reading this thread, I might be wrong, feel free to correct me again
> :) )

You're correct that git doesn't _store_ any sort of "branch ownership"
in the commit object. But this is a huge feature. It avoids a lot of
the things in bzr that look so bizarre to people coming from git.

-Carl

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

^ permalink raw reply

* Re: VCS comparison table
From: Erik Bågfors @ 2006-10-22 14:11 UTC (permalink / raw)
  To: Jakub Narebski
  Cc: bazaar-ng, Linus Torvalds, Andreas Ericsson, Carl Worth,
	Jan Hudec, git
In-Reply-To: <200610221524.00408.jnareb@gmail.com>

On 10/22/06, Jakub Narebski <jnareb@gmail.com> wrote:
> Erik Bågfors wrote:
> > Jakub Narębski wrote:
>
> >> For example git encourages using many short and longer-lived feature
> >> branches; I don't see bzr encouraging this workflow.
> >
> > Why not? I think it really does.  And due to the fact that merges are
> > merges and will show up as such, I think it's very suitable for
> > feature branches.
>
> I think I haven't properly explained what "feature branch" means.
> "Feature branch" is short (or medium) lived branch, created for
> development of one isolated feature. When feature is in stable
> stage, we merge feature branch and forget about it. We are not
> interested in the fact that given feature was developed on given
> branch. BTW. for example in published git.git repository are
> only available in the form of "digest" 'pu' (proposed updates)
> branch.


That's what I'm talking about too.
For example, in my bzr bzr-repo I have
bzr.init-repo-tree/
bzr.aliases/
bzr.dev/

and others...
In bzr.aliases for example, I built the support for defining aliases
in the bzr config file. That was a unique feature that didn't exist in
any other branch.  The branch survived about 17 days before it was
merged into bzr.dev.  During that time, I merge in another branch
twice.  The branch I merged at this time was NOT bzr.dev, but rather
another branch, from one of the main developers.  The reason I merged
his branch was that I needed a bugfix (or two? :) ) that he had done,
but that wasn't approved in bzr.dev yet.

After a time, his branch was merged into bzr.dev, shortly thereafter,
so was my branch.

After my branch was merged, I forgot about it.  I still have it laying
around on my computer because it really doesn't take up any extra
space (since it's in a shared repository), but I really have forgotten
about it.

This is typically how all features in bzr are created.
Short/medium/long-lived feature branches.

/Erik

^ 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