* [StGit PATCH 03/10] Upgrade older stacks to newest version
From: Karl Hasselström @ 2007-11-25 20:51 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git, David Kågedal
In-Reply-To: <20071125203717.7823.70046.stgit@yoghurt>
This is of course needed by the new infrastructure as well. So break
it out into its own file, where it can be used by both new and old
infrastructure. This has the added benefit of making it easy to see
that the upgrade code doesn't depend on anything it shouldn't.
Signed-off-by: Karl Hasselström <kha@treskal.com>
---
stgit/lib/git.py | 7 +++
stgit/lib/stack.py | 3 +
stgit/lib/stackupgrade.py | 96 +++++++++++++++++++++++++++++++++++++++++++
stgit/stack.py | 100 +++------------------------------------------
4 files changed, 112 insertions(+), 94 deletions(-)
create mode 100644 stgit/lib/stackupgrade.py
diff --git a/stgit/lib/git.py b/stgit/lib/git.py
index 120ea35..c4011f9 100644
--- a/stgit/lib/git.py
+++ b/stgit/lib/git.py
@@ -160,6 +160,13 @@ class Refs(object):
if self.__refs == None:
self.__cache_refs()
return self.__repository.get_commit(self.__refs[ref])
+ def exists(self, ref):
+ try:
+ self.get(ref)
+ except KeyError:
+ return False
+ else:
+ return True
def set(self, ref, commit, msg):
if self.__refs == None:
self.__cache_refs()
diff --git a/stgit/lib/stack.py b/stgit/lib/stack.py
index 5a34592..8fc8b08 100644
--- a/stgit/lib/stack.py
+++ b/stgit/lib/stack.py
@@ -1,6 +1,6 @@
import os.path
from stgit import exception, utils
-from stgit.lib import git
+from stgit.lib import git, stackupgrade
class Patch(object):
def __init__(self, stack, name):
@@ -128,6 +128,7 @@ class Stack(object):
raise exception.StgException('%s: no such branch' % name)
self.__patchorder = PatchOrder(self)
self.__patches = Patches(self)
+ stackupgrade.update_to_current_format_version(repository, name)
name = property(lambda self: self.__name)
repository = property(lambda self: self.__repository)
patchorder = property(lambda self: self.__patchorder)
diff --git a/stgit/lib/stackupgrade.py b/stgit/lib/stackupgrade.py
new file mode 100644
index 0000000..00bfdf0
--- /dev/null
+++ b/stgit/lib/stackupgrade.py
@@ -0,0 +1,96 @@
+import os.path
+from stgit import utils
+from stgit.out import out
+from stgit.config import config
+
+# The current StGit metadata format version.
+FORMAT_VERSION = 2
+
+def format_version_key(branch):
+ return 'branch.%s.stgit.stackformatversion' % branch
+
+def update_to_current_format_version(repository, branch):
+ """Update a potentially older StGit directory structure to the latest
+ version. Note: This function should depend as little as possible
+ on external functions that may change during a format version
+ bump, since it must remain able to process older formats."""
+
+ branch_dir = os.path.join(repository.directory, 'patches', branch)
+ key = format_version_key(branch)
+ old_key = 'branch.%s.stgitformatversion' % branch
+ def get_format_version():
+ """Return the integer format version number, or None if the
+ branch doesn't have any StGit metadata at all, of any version."""
+ fv = config.get(key)
+ ofv = config.get(old_key)
+ if fv:
+ # Great, there's an explicitly recorded format version
+ # number, which means that the branch is initialized and
+ # of that exact version.
+ return int(fv)
+ elif ofv:
+ # Old name for the version info: upgrade it.
+ config.set(key, ofv)
+ config.unset(old_key)
+ return int(ofv)
+ elif os.path.isdir(os.path.join(branch_dir, 'patches')):
+ # There's a .git/patches/<branch>/patches dirctory, which
+ # means this is an initialized version 1 branch.
+ return 1
+ elif os.path.isdir(branch_dir):
+ # There's a .git/patches/<branch> directory, which means
+ # this is an initialized version 0 branch.
+ return 0
+ else:
+ # 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(key, '%d' % v)
+ def mkdir(d):
+ if not os.path.isdir(d):
+ os.makedirs(d)
+ def rm(f):
+ if os.path.exists(f):
+ os.remove(f)
+ def rm_ref(ref):
+ if repository.refs.exists(ref):
+ repository.refs.delete(ref)
+
+ # Update 0 -> 1.
+ if get_format_version() == 0:
+ mkdir(os.path.join(branch_dir, 'trash'))
+ patch_dir = os.path.join(branch_dir, 'patches')
+ mkdir(patch_dir)
+ refs_base = 'refs/patches/%s' % branch
+ for patch in (file(os.path.join(branch_dir, 'unapplied')).readlines()
+ + file(os.path.join(branch_dir, 'applied')).readlines()):
+ patch = patch.strip()
+ os.rename(os.path.join(branch_dir, patch),
+ os.path.join(patch_dir, patch))
+ topfield = os.path.join(patch_dir, patch, 'top')
+ if os.path.isfile(topfield):
+ top = utils.read_string(topfield, False)
+ else:
+ top = None
+ if top:
+ repository.refs.set(refs_base + '/' + patch,
+ repository.get_commit(top), 'StGit upgrade')
+ set_format_version(1)
+
+ # Update 1 -> 2.
+ if get_format_version() == 1:
+ desc_file = os.path.join(branch_dir, 'description')
+ if os.path.isfile(desc_file):
+ desc = utils.read_string(desc_file)
+ if desc:
+ config.set('branch.%s.description' % branch, desc)
+ rm(desc_file)
+ rm(os.path.join(branch_dir, 'current'))
+ rm_ref('refs/bases/%s' % branch)
+ set_format_version(2)
+
+ # Make sure we're at the latest version.
+ if not get_format_version() in [None, FORMAT_VERSION]:
+ raise StackException('Branch %s is at format version %d, expected %d'
+ % (branch, get_format_version(), FORMAT_VERSION))
diff --git a/stgit/stack.py b/stgit/stack.py
index f93d842..29e92c9 100644
--- a/stgit/stack.py
+++ b/stgit/stack.py
@@ -28,7 +28,7 @@ from stgit.run import *
from stgit import git, basedir, templates
from stgit.config import config
from shutil import copyfile
-
+from stgit.lib import git as libgit, stackupgrade
# stack exception class
class StackException(StgException):
@@ -279,9 +279,6 @@ class Patch(StgitObject):
self._set_field('log', value)
self.__update_log_ref(value)
-# The current StGIT metadata format version.
-FORMAT_VERSION = 2
-
class PatchSet(StgitObject):
def __init__(self, name = None):
try:
@@ -349,7 +346,8 @@ class PatchSet(StgitObject):
def is_initialised(self):
"""Checks if series is already initialised
"""
- return bool(config.get(self.format_version_key()))
+ return config.get(stackupgrade.format_version_key(self.get_name())
+ ) != None
def shortlog(patches):
@@ -368,7 +366,8 @@ class Series(PatchSet):
# Update the branch to the latest format version if it is
# initialized, but don't touch it if it isn't.
- self.update_to_current_format_version()
+ stackupgrade.update_to_current_format_version(
+ libgit.Repository.default(), self.get_name())
self.__refs_base = 'refs/patches/%s' % self.get_name()
@@ -382,92 +381,6 @@ class Series(PatchSet):
# trash directory
self.__trash_dir = os.path.join(self._dir(), 'trash')
- def format_version_key(self):
- return 'branch.%s.stgit.stackformatversion' % self.get_name()
-
- def update_to_current_format_version(self):
- """Update a potentially older StGIT directory structure to the
- latest version. Note: This function should depend as little as
- possible on external functions that may change during a format
- version bump, since it must remain able to process older formats."""
-
- branch_dir = os.path.join(self._basedir(), 'patches', self.get_name())
- def get_format_version():
- """Return the integer format version number, or None if the
- branch doesn't have any StGIT metadata at all, of any version."""
- fv = config.get(self.format_version_key())
- ofv = config.get('branch.%s.stgitformatversion' % self.get_name())
- if fv:
- # Great, there's an explicitly recorded format version
- # number, which means that the branch is initialized and
- # of that exact version.
- return int(fv)
- elif ofv:
- # Old name for the version info, upgrade it
- config.set(self.format_version_key(), ofv)
- config.unset('branch.%s.stgitformatversion' % self.get_name())
- return int(ofv)
- elif os.path.isdir(os.path.join(branch_dir, 'patches')):
- # There's a .git/patches/<branch>/patches dirctory, which
- # means this is an initialized version 1 branch.
- return 1
- elif os.path.isdir(branch_dir):
- # There's a .git/patches/<branch> directory, which means
- # this is an initialized version 0 branch.
- return 0
- else:
- # 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' % (self.get_name(), v))
- config.set(self.format_version_key(), '%d' % v)
- def mkdir(d):
- if not os.path.isdir(d):
- os.makedirs(d)
- def rm(f):
- if os.path.exists(f):
- os.remove(f)
- def rm_ref(ref):
- if git.ref_exists(ref):
- git.delete_ref(ref)
-
- # Update 0 -> 1.
- if get_format_version() == 0:
- mkdir(os.path.join(branch_dir, 'trash'))
- patch_dir = os.path.join(branch_dir, 'patches')
- mkdir(patch_dir)
- refs_base = 'refs/patches/%s' % self.get_name()
- for patch in (file(os.path.join(branch_dir, 'unapplied')).readlines()
- + file(os.path.join(branch_dir, 'applied')).readlines()):
- patch = patch.strip()
- os.rename(os.path.join(branch_dir, patch),
- os.path.join(patch_dir, patch))
- topfield = os.path.join(patch_dir, patch, 'top')
- if os.path.isfile(topfield):
- top = read_string(topfield, False)
- else:
- top = None
- if top:
- git.set_ref(refs_base + '/' + patch, top)
- set_format_version(1)
-
- # Update 1 -> 2.
- if get_format_version() == 1:
- desc_file = os.path.join(branch_dir, 'description')
- if os.path.isfile(desc_file):
- desc = read_string(desc_file)
- if desc:
- config.set('branch.%s.description' % self.get_name(), desc)
- rm(desc_file)
- rm(os.path.join(branch_dir, 'current'))
- rm_ref('refs/bases/%s' % self.get_name())
- set_format_version(2)
-
- # Make sure we're at the latest version.
- if not get_format_version() in [None, FORMAT_VERSION]:
- raise StackException('Branch %s is at format version %d, expected %d'
- % (self.get_name(), get_format_version(), FORMAT_VERSION))
-
def __patch_name_valid(self, name):
"""Raise an exception if the patch name is not valid.
"""
@@ -620,7 +533,8 @@ class Series(PatchSet):
self.create_empty_field('applied')
self.create_empty_field('unapplied')
- config.set(self.format_version_key(), str(FORMAT_VERSION))
+ config.set(stackupgrade.format_version_key(self.get_name()),
+ str(stackupgrade.FORMAT_VERSION))
def rename(self, to_name):
"""Renames a series
^ permalink raw reply related
* [StGit PATCH 04/10] Let "stg clean" use the new infrastructure
From: Karl Hasselström @ 2007-11-25 20:51 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git, David Kågedal
In-Reply-To: <20071125203717.7823.70046.stgit@yoghurt>
Signed-off-by: Karl Hasselström <kha@treskal.com>
---
stgit/commands/clean.py | 68 ++++++++++++++++++++++++----------------------
stgit/commands/common.py | 10 ++++++-
2 files changed, 44 insertions(+), 34 deletions(-)
diff --git a/stgit/commands/clean.py b/stgit/commands/clean.py
index c703418..bbea253 100644
--- a/stgit/commands/clean.py
+++ b/stgit/commands/clean.py
@@ -15,14 +15,10 @@ 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 optparse import make_option
from stgit.out import *
-from stgit import stack, git
-
+from stgit.commands import common
+from stgit.lib import transaction
help = 'delete the empty patches in the series'
usage = """%prog [options]
@@ -31,7 +27,7 @@ Delete the empty patches in the whole series or only those applied or
unapplied. A patch is considered empty if the two commit objects
representing its boundaries refer to the same tree object."""
-directory = DirectoryGotoToplevel()
+directory = common.DirectoryHasRepositoryLib()
options = [make_option('-a', '--applied',
help = 'delete the empty applied patches',
action = 'store_true'),
@@ -40,18 +36,35 @@ options = [make_option('-a', '--applied',
action = 'store_true')]
-def __delete_empty(patches, applied):
- """Delete the empty patches
- """
- for p in patches:
- if crt_series.empty_patch(p):
- out.start('Deleting patch "%s"' % p)
- if applied and crt_series.patch_applied(p):
- crt_series.pop_patch(p)
- crt_series.delete_patch(p)
- out.done()
- elif applied and crt_series.patch_unapplied(p):
- crt_series.push_patch(p)
+def _clean(stack, clean_applied, clean_unapplied):
+ def deleting(pn):
+ out.info('Deleting empty patch %s' % pn)
+ trans = transaction.StackTransaction(stack, 'clean')
+ if clean_unapplied:
+ trans.unapplied = []
+ for pn in stack.patchorder.unapplied:
+ p = stack.patches.get(pn)
+ if p.is_empty():
+ trans.patches[pn] = None
+ deleting(pn)
+ else:
+ trans.unapplied.append(pn)
+ if clean_applied:
+ trans.applied = []
+ parent = stack.base
+ for pn in stack.patchorder.applied:
+ p = stack.patches.get(pn)
+ if p.is_empty():
+ trans.patches[pn] = None
+ deleting(pn)
+ else:
+ if parent != p.commit.data.parent:
+ parent = trans.patches[pn] = stack.repository.commit(
+ p.commit.data.set_parent(parent))
+ else:
+ parent = p.commit
+ trans.applied.append(pn)
+ trans.run()
def func(parser, options, args):
"""Delete the empty patches in the series
@@ -59,19 +72,8 @@ def func(parser, options, args):
if len(args) != 0:
parser.error('incorrect number of arguments')
- check_local_changes()
- check_conflicts()
- check_head_top_equal(crt_series)
-
if not (options.applied or options.unapplied):
options.applied = options.unapplied = True
- if options.applied:
- applied = crt_series.get_applied()
- __delete_empty(applied, True)
-
- if options.unapplied:
- unapplied = crt_series.get_unapplied()
- __delete_empty(unapplied, False)
-
- print_crt_patch(crt_series)
+ _clean(directory.repository.current_stack,
+ options.applied, options.unapplied)
diff --git a/stgit/commands/common.py b/stgit/commands/common.py
index 36202dd..6271572 100644
--- a/stgit/commands/common.py
+++ b/stgit/commands/common.py
@@ -27,7 +27,7 @@ from stgit.out import *
from stgit.run import *
from stgit import stack, git, basedir
from stgit.config import config, file_extensions
-
+from stgit.lib import stack as libstack
# Command exception class
class CmdException(StgException):
@@ -537,3 +537,11 @@ class DirectoryGotoToplevel(DirectoryInWorktree):
def setup(self):
DirectoryInWorktree.setup(self)
self.cd_to_topdir()
+
+class DirectoryHasRepositoryLib(_Directory):
+ """For commands that use the new infrastructure in stgit.lib.*."""
+ def __init__(self):
+ self.needs_current_series = False
+ def setup(self):
+ # This will throw an exception if we don't have a repository.
+ self.repository = libstack.Repository.default()
^ permalink raw reply related
* [StGit PATCH 06/10] Let "stg applied" and "stg unapplied" use the new infrastructure
From: Karl Hasselström @ 2007-11-25 20:51 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git, David Kågedal
In-Reply-To: <20071125203717.7823.70046.stgit@yoghurt>
This is a trivial change since these commands are so simple, but
because these are the commands used by t4000-upgrade, we now test that
the new infrastructure can upgrade old stacks.
Signed-off-by: Karl Hasselström <kha@treskal.com>
---
stgit/commands/applied.py | 27 +++++++++++++--------------
stgit/commands/unapplied.py | 23 +++++++++++------------
2 files changed, 24 insertions(+), 26 deletions(-)
diff --git a/stgit/commands/applied.py b/stgit/commands/applied.py
index 45d0926..522425b 100644
--- a/stgit/commands/applied.py
+++ b/stgit/commands/applied.py
@@ -16,25 +16,21 @@ 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 optparse import make_option
from stgit.out import *
-from stgit import stack, git
+from stgit.commands import common
help = 'print the applied patches'
usage = """%prog [options]
-List the patches from the series which were already pushed onto the
-stack. They are listed in the order in which they were pushed, the
+List the patches from the series which have already been pushed onto
+the stack. They are listed in the order in which they were pushed, the
last one being the current (topmost) patch."""
-directory = DirectoryHasRepository()
+directory = common.DirectoryHasRepositoryLib()
options = [make_option('-b', '--branch',
- help = 'use BRANCH instead of the default one'),
+ help = 'use BRANCH instead of the default branch'),
make_option('-c', '--count',
help = 'print the number of applied patches',
action = 'store_true')]
@@ -46,10 +42,13 @@ def func(parser, options, args):
if len(args) != 0:
parser.error('incorrect number of arguments')
- applied = crt_series.get_applied()
+ if options.branch:
+ s = directory.repository.get_stack(options.branch)
+ else:
+ s = directory.repository.current_stack
if options.count:
- out.stdout(len(applied))
+ out.stdout(len(s.patchorder.applied))
else:
- for p in applied:
- out.stdout(p)
+ for pn in s.patchorder.applied:
+ out.stdout(pn)
diff --git a/stgit/commands/unapplied.py b/stgit/commands/unapplied.py
index d5bb43e..7702207 100644
--- a/stgit/commands/unapplied.py
+++ b/stgit/commands/unapplied.py
@@ -16,13 +16,9 @@ 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 optparse import make_option
from stgit.out import *
-from stgit import stack, git
+from stgit.commands import common
help = 'print the unapplied patches'
@@ -31,9 +27,9 @@ usage = """%prog [options]
List the patches from the series which are not pushed onto the stack.
They are listed in the reverse order in which they were popped."""
-directory = DirectoryHasRepository()
+directory = common.DirectoryHasRepositoryLib()
options = [make_option('-b', '--branch',
- help = 'use BRANCH instead of the default one'),
+ help = 'use BRANCH instead of the default branch'),
make_option('-c', '--count',
help = 'print the number of unapplied patches',
action = 'store_true')]
@@ -45,10 +41,13 @@ def func(parser, options, args):
if len(args) != 0:
parser.error('incorrect number of arguments')
- unapplied = crt_series.get_unapplied()
+ if options.branch:
+ s = directory.repository.get_stack(options.branch)
+ else:
+ s = directory.repository.current_stack
if options.count:
- out.stdout(len(unapplied))
+ out.stdout(len(s.patchorder.unapplied))
else:
- for p in unapplied:
- out.stdout(p)
+ for pn in s.patchorder.unapplied:
+ out.stdout(pn)
^ permalink raw reply related
* [StGit PATCH 07/10] Teach the new infrastructure about the index and worktree
From: Karl Hasselström @ 2007-11-25 20:51 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git, David Kågedal
In-Reply-To: <20071125203717.7823.70046.stgit@yoghurt>
And use the new powers to make "stg coalesce" able to handle arbitrary
patches, not just consecutive applied patches.
Signed-off-by: Karl Hasselström <kha@treskal.com>
---
stgit/commands/clean.py | 4 +
stgit/commands/coalesce.py | 93 ++++++++++++++++---------
stgit/lib/git.py | 123 ++++++++++++++++++++++++++++++++++
stgit/lib/stack.py | 7 +-
stgit/lib/transaction.py | 161 ++++++++++++++++++++++++++++++++++++++------
5 files changed, 326 insertions(+), 62 deletions(-)
diff --git a/stgit/commands/clean.py b/stgit/commands/clean.py
index bbea253..e2d1678 100644
--- a/stgit/commands/clean.py
+++ b/stgit/commands/clean.py
@@ -44,7 +44,7 @@ def _clean(stack, clean_applied, clean_unapplied):
trans.unapplied = []
for pn in stack.patchorder.unapplied:
p = stack.patches.get(pn)
- if p.is_empty():
+ if p.commit.data.is_empty():
trans.patches[pn] = None
deleting(pn)
else:
@@ -54,7 +54,7 @@ def _clean(stack, clean_applied, clean_unapplied):
parent = stack.base
for pn in stack.patchorder.applied:
p = stack.patches.get(pn)
- if p.is_empty():
+ if p.commit.data.is_empty():
trans.patches[pn] = None
deleting(pn)
else:
diff --git a/stgit/commands/coalesce.py b/stgit/commands/coalesce.py
index c4c1cf8..2b121a9 100644
--- a/stgit/commands/coalesce.py
+++ b/stgit/commands/coalesce.py
@@ -35,50 +35,75 @@ options = [make_option('-n', '--name', help = 'name of coalesced patch'),
make_option('-m', '--message',
help = 'commit message of coalesced patch')]
-def _coalesce(stack, name, msg, patches):
- applied = stack.patchorder.applied
+def _coalesce_patches(trans, patches, msg):
+ cd = trans.patches[patches[0]].data
+ cd = git.Commitdata(tree = cd.tree, parents = cd.parents)
+ for pn in patches[1:]:
+ c = trans.patches[pn]
+ tree = trans.stack.repository.simple_merge(
+ base = c.data.parent.data.tree,
+ ours = cd.tree, theirs = c.data.tree)
+ if not tree:
+ return None
+ cd = cd.set_tree(tree)
+ if msg == None:
+ msg = '\n\n'.join('%s\n\n%s' % (pn.ljust(70, '-'),
+ trans.patches[pn].data.message)
+ for pn in patches)
+ msg = utils.edit_string(msg, '.stgit-coalesce.txt').strip()
+ cd = cd.set_message(msg)
- # Make sure the patches are consecutive.
- applied_ix = dict((applied[i], i) for i in xrange(len(applied)))
- ixes = list(sorted(applied_ix[p] for p in patches))
- i0, i1 = ixes[0], ixes[-1]
- if i1 - i0 + 1 != len(patches):
- raise common.CmdException('The patches must be consecutive')
+ return cd
- # Make a commit for the coalesced patch.
+def _coalesce(stack, iw, name, msg, patches):
+
+ # If a name was supplied on the command line, make sure it's OK.
def bad_name(pn):
return pn not in patches and stack.patches.exists(pn)
+ def get_name(cd):
+ return name or utils.make_patch_name(cd.message, bad_name)
if name and bad_name(name):
raise common.CmdException('Patch name "%s" already taken')
- ps = [stack.patches.get(pn) for pn in applied[i0:i1+1]]
- if msg == None:
- msg = '\n\n'.join('%s\n\n%s' % (p.name.ljust(70, '-'),
- p.commit.data.message)
- for p in ps)
- msg = utils.edit_string(msg, '.stgit-coalesce.txt').strip()
- if not name:
- name = utils.make_patch_name(msg, bad_name)
- cd = git.Commitdata(tree = ps[-1].commit.data.tree,
- parents = ps[0].commit.data.parents, message = msg)
- # Rewrite refs.
+ def make_coalesced_patch(trans, new_commit_data):
+ name = get_name(new_commit_data)
+ trans.patches[name] = stack.repository.commit(new_commit_data)
+ trans.unapplied.insert(0, name)
+
trans = transaction.StackTransaction(stack, 'stg coalesce')
- for pn in applied[i0:i1+1]:
- trans.patches[pn] = None
- parent = trans.patches[name] = stack.repository.commit(cd)
- trans.applied = applied[:i0]
- trans.applied.append(name)
- for pn in applied[i1+1:]:
- p = stack.patches.get(pn)
- parent = trans.patches[pn] = stack.repository.commit(
- p.commit.data.set_parent(parent))
- trans.applied.append(pn)
- trans.run()
+ push_new_patch = bool(set(patches) & set(trans.applied))
+ new_commit_data = _coalesce_patches(trans, patches, msg)
+ try:
+ if new_commit_data:
+ # We were able to construct the coalesced commit
+ # automatically. So just delete its constituent patches.
+ to_push = trans.delete_patches(lambda pn: pn in patches)
+ make_coalesced_patch(trans, new_commit_data)
+ else:
+ # Automatic construction failed. So push the patches
+ # consecutively, so that a second construction attempt is
+ # guaranteed to work.
+ to_push = trans.pop_patches(lambda pn: pn in patches)
+ for pn in patches:
+ trans.push_patch(pn, iw)
+ new_commit_data = _coalesce_patches(trans, patches, msg)
+ make_coalesced_patch(trans, new_commit_data)
+ assert not trans.delete_patches(lambda pn: pn in patches)
+
+ # Push the new patch if necessary, and any unrelated patches we've
+ # had to pop out of the way.
+ if push_new_patch:
+ trans.push_patch(get_name(new_commit_data), iw)
+ for pn in to_push:
+ trans.push_patch(pn, iw)
+ except transaction.TransactionHalted:
+ pass
+ trans.run(iw)
def func(parser, options, args):
stack = directory.repository.current_stack
- applied = set(stack.patchorder.applied)
- patches = set(common.parse_patches(args, list(stack.patchorder.applied)))
+ patches = common.parse_patches(args, list(stack.patchorder.applied))
if len(patches) < 2:
raise common.CmdException('Need at least two patches')
- _coalesce(stack, options.name, options.message, patches)
+ _coalesce(stack, stack.repository.default_iw(),
+ options.name, options.message, patches)
diff --git a/stgit/lib/git.py b/stgit/lib/git.py
index c4011f9..ab4a376 100644
--- a/stgit/lib/git.py
+++ b/stgit/lib/git.py
@@ -95,6 +95,8 @@ class Commitdata(Repr):
return type(self)(committer = committer, defaults = self)
def set_message(self, message):
return type(self)(message = message, defaults = self)
+ def is_empty(self):
+ return self.tree == self.parent.data.tree
def __str__(self):
if self.tree == None:
tree = None
@@ -218,6 +220,21 @@ class Repository(RunWithEnv):
).output_one_line())
except run.RunException:
raise RepositoryException('Cannot find git repository')
+ def default_index(self):
+ return Index(self, (os.environ.get('GIT_INDEX_FILE', None)
+ or os.path.join(self.__git_dir, 'index')))
+ def temp_index(self):
+ return Index(self, self.__git_dir)
+ def default_worktree(self):
+ path = os.environ.get('GIT_WORK_TREE', None)
+ if not path:
+ o = run.Run('git', 'rev-parse', '--show-cdup').output_lines()
+ o = o or ['.']
+ assert len(o) == 1
+ path = o[0]
+ return Worktree(path)
+ def default_iw(self):
+ return IndexAndWorktree(self.default_index(), self.default_worktree())
directory = property(lambda self: self.__git_dir)
refs = property(lambda self: self.__refs)
def cat_object(self, sha1):
@@ -258,3 +275,109 @@ class Repository(RunWithEnv):
raise DetachedHeadException()
def set_head(self, ref, msg):
self.run(['git', 'symbolic-ref', '-m', msg, 'HEAD', ref]).no_output()
+ def simple_merge(self, base, ours, theirs):
+ """Given three trees, tries to do an in-index merge in a temporary
+ index with a temporary index. Returns the result tree, or None if
+ the merge failed (due to conflicts)."""
+ assert isinstance(base, Tree)
+ assert isinstance(ours, Tree)
+ assert isinstance(theirs, Tree)
+
+ # Take care of the really trivial cases.
+ if base == ours:
+ return theirs
+ if base == theirs:
+ return ours
+ if ours == theirs:
+ return ours
+
+ index = self.temp_index()
+ try:
+ index.merge(base, ours, theirs)
+ try:
+ return index.write_tree()
+ except MergeException:
+ return None
+ finally:
+ index.delete()
+
+class MergeException(exception.StgException):
+ pass
+
+class Index(RunWithEnv):
+ def __init__(self, repository, filename):
+ self.__repository = repository
+ if os.path.isdir(filename):
+ # Create a temp index in the given directory.
+ self.__filename = os.path.join(
+ filename, 'index.temp-%d-%x' % (os.getpid(), id(self)))
+ self.delete()
+ else:
+ self.__filename = filename
+ env = property(lambda self: utils.add_dict(
+ self.__repository.env, { 'GIT_INDEX_FILE': self.__filename }))
+ def read_tree(self, tree):
+ self.run(['git', 'read-tree', tree.sha1]).no_output()
+ def write_tree(self):
+ try:
+ return self.__repository.get_tree(
+ self.run(['git', 'write-tree']).discard_stderr(
+ ).output_one_line())
+ except run.RunException:
+ raise MergeException('Conflicting merge')
+ def is_clean(self):
+ try:
+ self.run(['git', 'update-index', '--refresh']).discard_output()
+ except run.RunException:
+ return False
+ else:
+ return True
+ def merge(self, base, ours, theirs):
+ """In-index merge, no worktree involved."""
+ self.run(['git', 'read-tree', '-m', '-i', '--aggressive',
+ base.sha1, ours.sha1, theirs.sha1]).no_output()
+ def delete(self):
+ if os.path.isfile(self.__filename):
+ os.remove(self.__filename)
+
+class Worktree(object):
+ def __init__(self, directory):
+ self.__directory = directory
+ env = property(lambda self: { 'GIT_WORK_TREE': self.__directory })
+
+class CheckoutException(exception.StgException):
+ pass
+
+class IndexAndWorktree(RunWithEnv):
+ def __init__(self, index, worktree):
+ self.__index = index
+ self.__worktree = worktree
+ index = property(lambda self: self.__index)
+ env = property(lambda self: utils.add_dict(self.__index.env,
+ self.__worktree.env))
+ def checkout(self, old_tree, new_tree):
+ # TODO: Optionally do a 3-way instead of doing nothing when we
+ # have a problem. Or maybe we should stash changes in a patch?
+ assert isinstance(old_tree, Tree)
+ assert isinstance(new_tree, Tree)
+ try:
+ self.run(['git', 'read-tree', '-u', '-m',
+ '--exclude-per-directory=.gitignore',
+ old_tree.sha1, new_tree.sha1]
+ ).discard_output()
+ except run.RunException:
+ raise CheckoutException('Index/workdir dirty')
+ def merge(self, base, ours, theirs):
+ assert isinstance(base, Tree)
+ assert isinstance(ours, Tree)
+ assert isinstance(theirs, Tree)
+ try:
+ self.run(['git', 'merge-recursive', base.sha1, '--', ours.sha1,
+ theirs.sha1]).discard_output()
+ except run.RunException, e:
+ raise MergeException('Index/worktree dirty')
+ def changed_files(self):
+ return self.run(['git', 'diff-files', '--name-only']).output_lines()
+ def update_index(self, files):
+ self.run(['git', 'update-index', '--remove', '-z', '--stdin']
+ ).input_nulterm(files).discard_output()
diff --git a/stgit/lib/stack.py b/stgit/lib/stack.py
index 8fc8b08..b4512b1 100644
--- a/stgit/lib/stack.py
+++ b/stgit/lib/stack.py
@@ -64,9 +64,6 @@ class Patch(object):
self.__stack.repository.refs.delete(self.__ref)
def is_applied(self):
return self.name in self.__stack.patchorder.applied
- def is_empty(self):
- c = self.commit
- return c.data.tree == c.data.parent.data.tree
class PatchOrder(object):
"""Keeps track of patch order, and which patches are applied.
@@ -150,6 +147,10 @@ class Stack(object):
).commit.data.parent
else:
return self.head
+ def head_top_equal(self):
+ if not self.patchorder.applied:
+ return True
+ return self.head == self.patches.get(self.patchorder.applied[-1]).commit
class Repository(git.Repository):
def __init__(self, *args, **kwargs):
diff --git a/stgit/lib/transaction.py b/stgit/lib/transaction.py
index 991e64e..c9d355d 100644
--- a/stgit/lib/transaction.py
+++ b/stgit/lib/transaction.py
@@ -1,10 +1,14 @@
from stgit import exception
from stgit.out import *
+from stgit.lib import git
class TransactionException(exception.StgException):
pass
-def print_current_patch(old_applied, new_applied):
+class TransactionHalted(TransactionException):
+ pass
+
+def _print_current_patch(old_applied, new_applied):
def now_at(pn):
out.info('Now at patch "%s"' % pn)
if not old_applied and not new_applied:
@@ -18,22 +22,47 @@ def print_current_patch(old_applied, new_applied):
else:
now_at(new_applied[-1])
+class _TransPatchMap(dict):
+ def __init__(self, stack):
+ dict.__init__(self)
+ self.__stack = stack
+ def __getitem__(self, pn):
+ try:
+ return dict.__getitem__(self, pn)
+ except KeyError:
+ return self.__stack.patches.get(pn).commit
+
class StackTransaction(object):
def __init__(self, stack, msg):
self.__stack = stack
self.__msg = msg
- self.__patches = {}
+ self.__patches = _TransPatchMap(stack)
self.__applied = list(self.__stack.patchorder.applied)
self.__unapplied = list(self.__stack.patchorder.unapplied)
- def __set_patches(self, val):
- self.__patches = dict(val)
- patches = property(lambda self: self.__patches, __set_patches)
+ self.__error = None
+ self.__current_tree = self.__stack.head.data.tree
+ stack = property(lambda self: self.__stack)
+ patches = property(lambda self: self.__patches)
def __set_applied(self, val):
self.__applied = list(val)
applied = property(lambda self: self.__applied, __set_applied)
def __set_unapplied(self, val):
self.__unapplied = list(val)
unapplied = property(lambda self: self.__unapplied, __set_unapplied)
+ def __checkout(self, tree, iw):
+ if not self.__stack.head_top_equal():
+ out.error('HEAD and top are not the same.',
+ 'This can happen if you modify a branch with git.',
+ 'The "repair" command can fix this situation.')
+ self.__abort()
+ if self.__current_tree != tree:
+ assert iw != None
+ iw.checkout(self.__current_tree, tree)
+ self.__current_tree = tree
+ @staticmethod
+ def __abort():
+ raise TransactionException(
+ 'Command aborted (all changes rolled back)')
def __check_consistency(self):
remaining = set(self.__applied + self.__unapplied)
for pn, commit in self.__patches.iteritems():
@@ -41,29 +70,29 @@ class StackTransaction(object):
assert self.__stack.patches.exists(pn)
else:
assert pn in remaining
- def run(self):
- self.__check_consistency()
-
- # Get new head commit.
+ @property
+ def __head(self):
if self.__applied:
- top_patch = self.__applied[-1]
- try:
- new_head = self.__patches[top_patch]
- except KeyError:
- new_head = self.__stack.patches.get(top_patch).commit
+ return self.__patches[self.__applied[-1]]
else:
- new_head = self.__stack.base
+ return self.__stack.base
+ def run(self, iw = None):
+ self.__check_consistency()
+ new_head = self.__head
# Set branch head.
- if new_head == self.__stack.head:
- pass # same commit: OK
- elif new_head.data.tree == self.__stack.head.data.tree:
- pass # same tree: OK
- else:
- # We can't handle this case yet.
- raise TransactionException('Error: HEAD tree changed')
+ try:
+ self.__checkout(new_head.data.tree, iw)
+ except git.CheckoutException:
+ # We have to abort the transaction. The only state we need
+ # to restore is index+worktree.
+ self.__checkout(self.__stack.head.data.tree, iw)
+ self.__abort()
self.__stack.set_head(new_head, self.__msg)
+ if self.__error:
+ out.error(self.__error)
+
# Write patches.
for pn, commit in self.__patches.iteritems():
if self.__stack.patches.exists(pn):
@@ -74,6 +103,92 @@ class StackTransaction(object):
p.set_commit(commit, self.__msg)
else:
self.__stack.patches.new(pn, commit, self.__msg)
- print_current_patch(self.__stack.patchorder.applied, self.__applied)
+ _print_current_patch(self.__stack.patchorder.applied, self.__applied)
self.__stack.patchorder.applied = self.__applied
self.__stack.patchorder.unapplied = self.__unapplied
+
+ def __halt(self, msg):
+ self.__error = msg
+ raise TransactionHalted(msg)
+
+ @staticmethod
+ def __print_popped(popped):
+ if len(popped) == 0:
+ pass
+ elif len(popped) == 1:
+ out.info('Popped %s' % popped[0])
+ else:
+ out.info('Popped %s -- %s' % (popped[-1], popped[0]))
+
+ def pop_patches(self, p):
+ """Pop all patches pn for which p(pn) is true. Return the list of
+ other patches that had to be popped to accomplish this."""
+ popped = []
+ for i in xrange(len(self.applied)):
+ if p(self.applied[i]):
+ popped = self.applied[i:]
+ del self.applied[i:]
+ break
+ popped1 = [pn for pn in popped if not p(pn)]
+ popped2 = [pn for pn in popped if p(pn)]
+ self.unapplied = popped1 + popped2 + self.unapplied
+ self.__print_popped(popped)
+ return popped1
+
+ def delete_patches(self, p):
+ """Delete all patches pn for which p(pn) is true. Return the list of
+ other patches that had to be popped to accomplish this."""
+ popped = []
+ all_patches = self.applied + self.unapplied
+ for i in xrange(len(self.applied)):
+ if p(self.applied[i]):
+ popped = self.applied[i:]
+ del self.applied[i:]
+ break
+ popped = [pn for pn in popped if not p(pn)]
+ self.unapplied = popped + [pn for pn in self.unapplied if not p(pn)]
+ self.__print_popped(popped)
+ for pn in all_patches:
+ if p(pn):
+ s = ['', ' (empty)'][self.patches[pn].data.is_empty()]
+ self.patches[pn] = None
+ out.info('Deleted %s%s' % (pn, s))
+ return popped
+
+ def push_patch(self, pn, iw = None):
+ """Attempt to push the named patch. If this results in conflicts,
+ halts the transaction. If index+worktree are given, spill any
+ conflicts to them."""
+ i = self.unapplied.index(pn)
+ cd = self.patches[pn].data
+ s = ['', ' (empty)'][cd.is_empty()]
+ oldparent = cd.parent
+ cd = cd.set_parent(self.__head)
+ base = oldparent.data.tree
+ ours = cd.parent.data.tree
+ theirs = cd.tree
+ tree = self.__stack.repository.simple_merge(base, ours, theirs)
+ merge_conflict = False
+ if not tree:
+ if iw == None:
+ self.__halt('%s does not apply cleanly' % pn)
+ try:
+ self.__checkout(ours, iw)
+ except git.CheckoutException:
+ self.__halt('Index/worktree dirty')
+ try:
+ iw.merge(base, ours, theirs)
+ tree = iw.index.write_tree()
+ self.__current_tree = tree
+ s = ' (modified)'
+ except git.MergeException:
+ tree = ours
+ merge_conflict = True
+ s = ' (conflict)'
+ cd = cd.set_tree(tree)
+ self.patches[pn] = self.__stack.repository.commit(cd)
+ del self.unapplied[i]
+ self.applied.append(pn)
+ out.info('Pushed %s%s' % (pn, s))
+ if merge_conflict:
+ self.__halt('Merge conflict')
^ permalink raw reply related
* [StGit PATCH 08/10] Let "stg clean" use the new transaction primitives
From: Karl Hasselström @ 2007-11-25 20:51 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git, David Kågedal
In-Reply-To: <20071125203717.7823.70046.stgit@yoghurt>
Signed-off-by: Karl Hasselström <kha@treskal.com>
---
stgit/commands/clean.py | 33 +++++++--------------------------
1 files changed, 7 insertions(+), 26 deletions(-)
diff --git a/stgit/commands/clean.py b/stgit/commands/clean.py
index e2d1678..cfcc004 100644
--- a/stgit/commands/clean.py
+++ b/stgit/commands/clean.py
@@ -37,33 +37,14 @@ options = [make_option('-a', '--applied',
def _clean(stack, clean_applied, clean_unapplied):
- def deleting(pn):
- out.info('Deleting empty patch %s' % pn)
trans = transaction.StackTransaction(stack, 'clean')
- if clean_unapplied:
- trans.unapplied = []
- for pn in stack.patchorder.unapplied:
- p = stack.patches.get(pn)
- if p.commit.data.is_empty():
- trans.patches[pn] = None
- deleting(pn)
- else:
- trans.unapplied.append(pn)
- if clean_applied:
- trans.applied = []
- parent = stack.base
- for pn in stack.patchorder.applied:
- p = stack.patches.get(pn)
- if p.commit.data.is_empty():
- trans.patches[pn] = None
- deleting(pn)
- else:
- if parent != p.commit.data.parent:
- parent = trans.patches[pn] = stack.repository.commit(
- p.commit.data.set_parent(parent))
- else:
- parent = p.commit
- trans.applied.append(pn)
+ def del_patch(pn):
+ if pn in stack.patchorder.applied:
+ return clean_applied and trans.patches[pn].data.is_empty()
+ elif pn in stack.patchorder.unapplied:
+ return clean_unapplied and trans.patches[pn].data.is_empty()
+ for pn in trans.delete_patches(del_patch):
+ trans.push_patch(pn)
trans.run()
def func(parser, options, args):
^ permalink raw reply related
* [StGit PATCH 05/10] Add "stg coalesce"
From: Karl Hasselström @ 2007-11-25 20:51 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git, David Kågedal
In-Reply-To: <20071125203717.7823.70046.stgit@yoghurt>
It coalesces two or more consecutive applied patches, with no need to
touch index/worktree, and no possibiliy of conflicts.
Future improvements could relax the "consecutive" and "applied"
restrictions, by building a new chain of commits just like "stg push"
will do once it's been converted to the new infrastructure.
Signed-off-by: Karl Hasselström <kha@treskal.com>
---
contrib/stgit-completion.bash | 2 +
stgit/commands/coalesce.py | 84 +++++++++++++++++++++++++++++++++++++++++
stgit/main.py | 2 +
stgit/utils.py | 11 +++++
t/t2600-coalesce.sh | 31 +++++++++++++++
5 files changed, 130 insertions(+), 0 deletions(-)
create mode 100644 stgit/commands/coalesce.py
create mode 100755 t/t2600-coalesce.sh
diff --git a/contrib/stgit-completion.bash b/contrib/stgit-completion.bash
index b3b23d4..b02eb64 100644
--- a/contrib/stgit-completion.bash
+++ b/contrib/stgit-completion.bash
@@ -18,6 +18,7 @@ _stg_commands="
diff
clean
clone
+ coalesce
commit
cp
edit
@@ -238,6 +239,7 @@ _stg ()
# repository commands
id) _stg_patches $command _all_patches ;;
# stack commands
+ coalesce) _stg_patches $command _applied_patches ;;
float) _stg_patches $command _all_patches ;;
goto) _stg_patches $command _all_other_patches ;;
hide) _stg_patches $command _unapplied_patches ;;
diff --git a/stgit/commands/coalesce.py b/stgit/commands/coalesce.py
new file mode 100644
index 0000000..c4c1cf8
--- /dev/null
+++ b/stgit/commands/coalesce.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+
+__copyright__ = """
+Copyright (C) 2007, 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
+"""
+
+from optparse import make_option
+from stgit.out import *
+from stgit import utils
+from stgit.commands import common
+from stgit.lib import git, transaction
+
+help = 'coalesce two or more patches into one'
+usage = """%prog [options] <patches>
+
+Coalesce two or more patches, creating one big patch that contains all
+their changes. The patches must all be applied, and must be
+consecutive."""
+
+directory = common.DirectoryHasRepositoryLib()
+options = [make_option('-n', '--name', help = 'name of coalesced patch'),
+ make_option('-m', '--message',
+ help = 'commit message of coalesced patch')]
+
+def _coalesce(stack, name, msg, patches):
+ applied = stack.patchorder.applied
+
+ # Make sure the patches are consecutive.
+ applied_ix = dict((applied[i], i) for i in xrange(len(applied)))
+ ixes = list(sorted(applied_ix[p] for p in patches))
+ i0, i1 = ixes[0], ixes[-1]
+ if i1 - i0 + 1 != len(patches):
+ raise common.CmdException('The patches must be consecutive')
+
+ # Make a commit for the coalesced patch.
+ def bad_name(pn):
+ return pn not in patches and stack.patches.exists(pn)
+ if name and bad_name(name):
+ raise common.CmdException('Patch name "%s" already taken')
+ ps = [stack.patches.get(pn) for pn in applied[i0:i1+1]]
+ if msg == None:
+ msg = '\n\n'.join('%s\n\n%s' % (p.name.ljust(70, '-'),
+ p.commit.data.message)
+ for p in ps)
+ msg = utils.edit_string(msg, '.stgit-coalesce.txt').strip()
+ if not name:
+ name = utils.make_patch_name(msg, bad_name)
+ cd = git.Commitdata(tree = ps[-1].commit.data.tree,
+ parents = ps[0].commit.data.parents, message = msg)
+
+ # Rewrite refs.
+ trans = transaction.StackTransaction(stack, 'stg coalesce')
+ for pn in applied[i0:i1+1]:
+ trans.patches[pn] = None
+ parent = trans.patches[name] = stack.repository.commit(cd)
+ trans.applied = applied[:i0]
+ trans.applied.append(name)
+ for pn in applied[i1+1:]:
+ p = stack.patches.get(pn)
+ parent = trans.patches[pn] = stack.repository.commit(
+ p.commit.data.set_parent(parent))
+ trans.applied.append(pn)
+ trans.run()
+
+def func(parser, options, args):
+ stack = directory.repository.current_stack
+ applied = set(stack.patchorder.applied)
+ patches = set(common.parse_patches(args, list(stack.patchorder.applied)))
+ if len(patches) < 2:
+ raise common.CmdException('Need at least two patches')
+ _coalesce(stack, options.name, options.message, patches)
diff --git a/stgit/main.py b/stgit/main.py
index deaac91..384803b 100644
--- a/stgit/main.py
+++ b/stgit/main.py
@@ -64,6 +64,7 @@ commands = Commands({
'diff': 'diff',
'clean': 'clean',
'clone': 'clone',
+ 'coalesce': 'coalesce',
'commit': 'commit',
'edit': 'edit',
'export': 'export',
@@ -108,6 +109,7 @@ stackcommands = (
'applied',
'branch',
'clean',
+ 'coalesce',
'commit',
'float',
'goto',
diff --git a/stgit/utils.py b/stgit/utils.py
index b3f6232..688276c 100644
--- a/stgit/utils.py
+++ b/stgit/utils.py
@@ -189,6 +189,17 @@ def call_editor(filename):
raise EditorException, 'editor failed, exit code: %d' % err
out.done()
+def edit_string(s, filename):
+ f = file(filename, 'w')
+ f.write(s)
+ f.close()
+ call_editor(filename)
+ f = file(filename)
+ s = f.read()
+ f.close()
+ os.remove(filename)
+ return s
+
def patch_name_from_msg(msg):
"""Return a string to be used as a patch name. This is generated
from the top line of the string passed as argument."""
diff --git a/t/t2600-coalesce.sh b/t/t2600-coalesce.sh
new file mode 100755
index 0000000..f13a309
--- /dev/null
+++ b/t/t2600-coalesce.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='Run "stg coalesce"'
+
+. ./test-lib.sh
+
+test_expect_success 'Initialize StGit stack' '
+ stg init &&
+ for i in 0 1 2 3; do
+ stg new p$i -m "foo $i" &&
+ echo "foo $i" >> foo.txt &&
+ git add foo.txt &&
+ stg refresh
+ done
+'
+
+test_expect_success 'Coalesce some patches' '
+ [ "$(echo $(stg applied))" = "p0 p1 p2 p3" ] &&
+ [ "$(echo $(stg unapplied))" = "" ] &&
+ stg coalesce --name=q0 --message="wee woo" p1 p2 &&
+ [ "$(echo $(stg applied))" = "p0 q0 p3" ] &&
+ [ "$(echo $(stg unapplied))" = "" ]
+'
+
+test_expect_success 'Coalesce at stack top' '
+ stg coalesce --name=q1 --message="wee woo wham" q0 p3 &&
+ [ "$(echo $(stg applied))" = "p0 q1" ] &&
+ [ "$(echo $(stg unapplied))" = "" ]
+'
+
+test_done
^ permalink raw reply related
* [StGit PATCH 09/10] Let "stg goto" use the new infrastructure
From: Karl Hasselström @ 2007-11-25 20:51 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git, David Kågedal
In-Reply-To: <20071125203717.7823.70046.stgit@yoghurt>
In the process, it loses the --keep option, since the new
infrastructure always keeps local changes (and aborts cleanly if they
are in the way).
Signed-off-by: Karl Hasselström <kha@treskal.com>
---
stgit/commands/clean.py | 2 +-
stgit/commands/goto.py | 52 ++++++++++++++++-------------------------------
2 files changed, 19 insertions(+), 35 deletions(-)
diff --git a/stgit/commands/clean.py b/stgit/commands/clean.py
index cfcc004..55ab858 100644
--- a/stgit/commands/clean.py
+++ b/stgit/commands/clean.py
@@ -37,7 +37,7 @@ options = [make_option('-a', '--applied',
def _clean(stack, clean_applied, clean_unapplied):
- trans = transaction.StackTransaction(stack, 'clean')
+ trans = transaction.StackTransaction(stack, 'stg clean')
def del_patch(pn):
if pn in stack.patchorder.applied:
return clean_applied and trans.patches[pn].data.is_empty()
diff --git a/stgit/commands/goto.py b/stgit/commands/goto.py
index 84b840b..3ea69dd 100644
--- a/stgit/commands/goto.py
+++ b/stgit/commands/goto.py
@@ -15,13 +15,9 @@ 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
-
+from stgit.commands import common
+from stgit.lib import transaction
help = 'push or pop patches to the given one'
usage = """%prog [options] <name>
@@ -30,38 +26,26 @@ Push/pop patches to/from the stack until the one given on the command
line becomes current. There is no '--undo' option for 'goto'. Use the
'push --undo' command for this."""
-directory = DirectoryGotoToplevel()
-options = [make_option('-k', '--keep',
- help = 'keep the local changes when popping patches',
- action = 'store_true')]
-
+directory = common.DirectoryHasRepositoryLib()
+options = []
def func(parser, options, args):
- """Pushes the given patch or all onto the series
- """
if len(args) != 1:
parser.error('incorrect number of arguments')
-
- check_conflicts()
- check_head_top_equal(crt_series)
-
- if not options.keep:
- check_local_changes()
-
- applied = crt_series.get_applied()
- unapplied = crt_series.get_unapplied()
patch = args[0]
- if patch in applied:
- applied.reverse()
- patches = applied[:applied.index(patch)]
- pop_patches(crt_series, patches, options.keep)
- elif patch in unapplied:
- if options.keep:
- raise CmdException, 'Cannot use --keep with patch pushing'
- patches = unapplied[:unapplied.index(patch)+1]
- push_patches(crt_series, patches)
+ stack = directory.repository.current_stack
+ iw = stack.repository.default_iw()
+ trans = transaction.StackTransaction(stack, 'stg goto')
+ if patch in trans.applied:
+ to_pop = set(trans.applied[trans.applied.index(patch)+1:])
+ assert not trans.pop_patches(lambda pn: pn in to_pop)
+ elif patch in trans.unapplied:
+ try:
+ for pn in trans.unapplied[:trans.unapplied.index(patch)+1]:
+ trans.push_patch(pn, iw)
+ except transaction.TransactionHalted:
+ pass
else:
- raise CmdException, 'Patch "%s" does not exist' % patch
-
- print_crt_patch(crt_series)
+ raise CmdException('Patch "%s" does not exist' % patch)
+ trans.run(iw)
^ permalink raw reply related
* [StGit PATCH 10/10] Convert "stg uncommit" to the new infrastructure
From: Karl Hasselström @ 2007-11-25 20:51 UTC (permalink / raw)
To: Catalin Marinas; +Cc: git, David Kågedal
In-Reply-To: <20071125203717.7823.70046.stgit@yoghurt>
Signed-off-by: Karl Hasselström <kha@treskal.com>
---
stgit/commands/uncommit.py | 79 +++++++++++++++++++-------------------------
1 files changed, 34 insertions(+), 45 deletions(-)
diff --git a/stgit/commands/uncommit.py b/stgit/commands/uncommit.py
index ba3448f..8422952 100644
--- a/stgit/commands/uncommit.py
+++ b/stgit/commands/uncommit.py
@@ -17,13 +17,11 @@ 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 optparse import make_option
+from stgit.commands import common
+from stgit.lib import transaction
from stgit.out import *
-from stgit import stack, git
+from stgit import utils
help = 'turn regular GIT commits into StGIT patches'
usage = """%prog [<patchnames>] | -n NUM [<prefix>]] | -t <committish> [-x]
@@ -48,7 +46,7 @@ given commit should be uncommitted.
Only commits with exactly one parent can be uncommitted; in other
words, you can't uncommit a merge."""
-directory = DirectoryGotoToplevel()
+directory = common.DirectoryHasRepositoryLib()
options = [make_option('-n', '--number', type = 'int',
help = 'uncommit the specified number of commits'),
make_option('-t', '--to',
@@ -60,19 +58,18 @@ options = [make_option('-n', '--number', type = 'int',
def func(parser, options, args):
"""Uncommit a number of patches.
"""
+ stack = directory.repository.current_stack
if options.to:
if options.number:
parser.error('cannot give both --to and --number')
if len(args) != 0:
parser.error('cannot specify patch name with --to')
patch_nr = patchnames = None
- to_commit = git_id(crt_series, options.to)
+ to_commit = stack.repository.rev_parse(options.to)
elif options.number:
if options.number <= 0:
parser.error('invalid value passed to --number')
-
patch_nr = options.number
-
if len(args) == 0:
patchnames = None
elif len(args) == 1:
@@ -88,53 +85,45 @@ def func(parser, options, args):
patchnames = args
patch_nr = len(patchnames)
- if crt_series.get_protected():
- raise CmdException, \
- 'This branch is protected. Uncommit is not permitted'
-
- def get_commit(commit_id):
- commit = git.Commit(commit_id)
- try:
- parent, = commit.get_parents()
- except ValueError:
- raise CmdException('Commit %s does not have exactly one parent'
- % commit_id)
- return (commit, commit_id, parent)
-
commits = []
- next_commit = crt_series.get_base()
+ next_commit = stack.base
if 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
+ commits.append(next_commit)
+ next_commit = next_commit.data.parent
else:
if options.exclusive:
out.start('Uncommitting to %s (exclusive)' % to_commit)
else:
out.start('Uncommitting to %s' % to_commit)
while True:
- commit, commit_id, parent = get_commit(next_commit)
- if commit_id == to_commit:
+ if next_commit == to_commit:
if not options.exclusive:
- commits.append((commit, commit_id, parent))
+ commits.append(next_commit)
break
- commits.append((commit, commit_id, parent))
- next_commit = parent
+ commits.append(next_commit)
+ next_commit = next_commit.data.parent
patch_nr = len(commits)
- for (commit, commit_id, parent), patchname in \
- zip(commits, patchnames or [None for i in xrange(len(commits))]):
- author_name, author_email, author_date = \
- name_email_date(commit.get_author())
- crt_series.new_patch(patchname,
- can_edit = False, before_existing = True,
- commit = False,
- top = commit_id, bottom = parent,
- message = commit.get_log(),
- author_name = author_name,
- author_email = author_email,
- author_date = author_date)
-
+ taken_names = set(stack.patchorder.applied + stack.patchorder.unapplied)
+ if patchnames:
+ for pn in patchnames:
+ if pn in taken_names:
+ raise common.CmdException('Patch name "%s" already taken' % pn)
+ taken_names.add(pn)
+ else:
+ patchnames = []
+ for c in reversed(commits):
+ pn = utils.make_patch_name(c.data.message,
+ lambda pn: pn in taken_names)
+ patchnames.append(pn)
+ taken_names.add(pn)
+ patchnames.reverse()
+
+ trans = transaction.StackTransaction(stack, 'stg uncommit')
+ for commit, pn in zip(commits, patchnames):
+ trans.patches[pn] = commit
+ trans.applied = list(reversed(patchnames)) + trans.applied
+ trans.run()
out.done()
^ permalink raw reply related
* Re: What's cooking in git.git (topics)
From: J. Bruce Fields @ 2007-11-25 20:53 UTC (permalink / raw)
To: Jakub Narebski; +Cc: git
In-Reply-To: <ficmbr$s87$1@ger.gmane.org>
On Sun, Nov 25, 2007 at 09:36:14PM +0100, Jakub Narebski wrote:
> Junio C Hamano wrote:
>
> > [Actively cooking]
> >
> > * jc/spht (Sat Nov 24 11:57:41 2007 -0800) 6 commits
> > + core.whitespace: documentation updates.
> > + builtin-apply: teach whitespace_rules
> > + builtin-apply: rename "whitespace" variables and fix styles
> > + core.whitespace: add test for diff whitespace error highlighting
> > + git-diff: complain about >=8 consecutive spaces in initial indent
> > + War on whitespace: first, a bit of retreat.
> >
> > Now apply also knows about the customizable definition of what
> > whitespace breakages are, and I was reasonably happy. But Bruce kicked
> > it back from "scheduled to merge" to "still cooking" status, reminding
> > that we would want to have this not a tree-wide configuration but
> > per-path attribute. And I agree with him.
>
> Currently apply.whitespace is per repository - would this be changed
> as well,
There's a difference between the choice of preferred whitespace style
and the choice of action to take when encountering "bad" whitespace.
The former is (I think) obviously a property of the project (or perhaps
of individual paths within the project). The latter may depend on what
you're doing with it at any given moment--for example, if I'm applying
patches to submit, I generally want to fix whitespace, but if I'm just
examining someone else's patches temporarily then I might want to import
them quickly without fixing up everything.
So, no, I don't think there should be a .gitattribute equivalent to
apply.whitespace.
--b.
> i.e. would it be moved to gitattributes together with custom
> diff drivers (or at least custom funcnames), custom merge drivers,
> making it per-project (if put under version control) and per-path?
>
>
> By the way, i18n.commitEncoding is per repository, and used to affect
> repository; not so with the "encoding" header in commit object.
>
> --
> Jakub Narebski
> Warsaw, Poland
> ShadeHawk on #git
>
>
> -
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [RFC] best way to show diff of commit
From: Pierre Habouzit @ 2007-11-25 21:18 UTC (permalink / raw)
To: Git ML
[-- Attachment #1: Type: text/plain, Size: 1281 bytes --]
Hi there,
There is specific script I run in my vim with git, that tries to show
from the 'status' git commit shows in the buffer which list of files has
changed, and builds a diff from it quite clumsily[0].
I wonder how hard it would be for git commit to "stash" the current
commit being sent somewhere editors would be able to have a look at (an
alternate index is probably fine). Note that maybe I'm stupid and
overlooked that such a thing already exists. I'd like to have it in two
flavors: normal and amend mode. normal mode would show what the
resulting commit diff looks like, and the amend mode only shows the
incrementall diff the amend adds to the previous commit.
My question is: what do you think is the best way to do that, and
where ?
[0] the issue with this approach is that it's completely broken in
amending mode (does not shows the proper thing), and the generated
diffs aren't excellent, because as an editor plugin, it's hard to
treat renames and copies easily, so I generate really really nasty
diffs in that case too.
--
·O· Pierre Habouzit
··O madcoder@debian.org
OOO http://www.madism.org
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: [RFC] best way to show diff of commit
From: J. Bruce Fields @ 2007-11-25 21:27 UTC (permalink / raw)
To: Pierre Habouzit, Git ML
In-Reply-To: <20071125211831.GA21121@artemis.corp>
On Sun, Nov 25, 2007 at 10:18:31PM +0100, Pierre Habouzit wrote:
> There is specific script I run in my vim with git, that tries to show
> from the 'status' git commit shows in the buffer which list of files has
> changed, and builds a diff from it quite clumsily[0].
>
> I wonder how hard it would be for git commit to "stash" the current
> commit being sent somewhere editors would be able to have a look at (an
> alternate index is probably fine). Note that maybe I'm stupid and
> overlooked that such a thing already exists. I'd like to have it in two
> flavors: normal and amend mode. normal mode would show what the
> resulting commit diff looks like, and the amend mode only shows the
> incrementall diff the amend adds to the previous commit.
>
> My question is: what do you think is the best way to do that, and
> where ?
Have you checked whether "git-commit -v" already does what you want?
--b.
^ permalink raw reply
* [Resend Trivial PATCH] For the sake of correctness, fix file descriptor leak.
From: André Goddard Rosa @ 2007-11-25 21:37 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Git Mailing List
In-Reply-To: <b8bf37780711211659v4fbd5936t29d0a0a2ff84f4b@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1077 bytes --]
Hi, all!
Plug a file descriptor leak.
From 9fba346aca7470633ee46848013051248493896c Mon Sep 17 00:00:00 2001
From: Andre Goddard Rosa <andre.goddard@gmail.com>
Date: Tue, 27 Nov 2007 10:16:22 -0200
Subject: [PATCH] For the sake of correctness, fix file descriptor leak.
Signed-off-by: Andre Goddard Rosa <andre.goddard@gmail.com>
---
builtin-rerere.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/builtin-rerere.c b/builtin-rerere.c
index 7449323..31766be 100644
--- a/builtin-rerere.c
+++ b/builtin-rerere.c
@@ -275,8 +275,10 @@ static int copy_file(const char *src, const char *dest)
if (!(in = fopen(src, "r")))
return error("Could not open %s", src);
- if (!(out = fopen(dest, "w")))
+ if (!(out = fopen(dest, "w"))) {
+ fclose(in);
return error("Could not open %s", dest);
+ }
while ((count = fread(buffer, 1, sizeof(buffer), in)))
fwrite(buffer, 1, count, out);
fclose(in);
--
1.5.3.6.861.gd794-dirty
--
[]s,
André Goddard
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0002-For-the-sake-of-correctness-fix-file-descriptor-le.patch --]
[-- Type: text/x-patch; name=0002-For-the-sake-of-correctness-fix-file-descriptor-le.patch, Size: 921 bytes --]
From 9fba346aca7470633ee46848013051248493896c Mon Sep 17 00:00:00 2001
From: Andre Goddard Rosa <andre.goddard@gmail.com>
Date: Tue, 27 Nov 2007 10:16:22 -0200
Subject: [PATCH] For the sake of correctness, fix file descriptor leak.
Signed-off-by: Andre Goddard Rosa <andre.goddard@gmail.com>
---
builtin-rerere.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/builtin-rerere.c b/builtin-rerere.c
index 7449323..31766be 100644
--- a/builtin-rerere.c
+++ b/builtin-rerere.c
@@ -275,8 +275,10 @@ static int copy_file(const char *src, const char *dest)
if (!(in = fopen(src, "r")))
return error("Could not open %s", src);
- if (!(out = fopen(dest, "w")))
+ if (!(out = fopen(dest, "w"))) {
+ fclose(in);
return error("Could not open %s", dest);
+ }
while ((count = fread(buffer, 1, sizeof(buffer), in)))
fwrite(buffer, 1, count, out);
fclose(in);
--
1.5.3.6.861.gd794-dirty
^ permalink raw reply related
* [PATCH] Add 'git fast-export', the sister of 'git fast-import'
From: Johannes Schindelin @ 2007-11-25 21:37 UTC (permalink / raw)
To: git, gitster
[-- Attachment #1: Type: TEXT/PLAIN, Size: 19296 bytes --]
This program dumps (parts of) a git repository in the format that
fast-import understands.
For clarity's sake, it does not use the 'inline' method of specifying
blobs in the commits, but builds the blobs before building the commits.
Since signed tags' signatures will not necessarily be valid (think
transformations after the export, or excluding revisions, changing
the history), there are 4 modes to handle them: abort (default),
ignore, warn and strip. The latter just turns the tags into
unsigned ones.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
This should be feature complete AFAICT.
.gitignore | 1 +
Documentation/git-fast-export.txt | 83 ++++++++
Makefile | 1 +
builtin-fast-export.c | 407 +++++++++++++++++++++++++++++++++++++
builtin.h | 1 +
git.c | 1 +
t/t9301-fast-export.sh | 124 +++++++++++
7 files changed, 618 insertions(+), 0 deletions(-)
create mode 100644 Documentation/git-fast-export.txt
create mode 100755 builtin-fast-export.c
create mode 100755 t/t9301-fast-export.sh
diff --git a/.gitignore b/.gitignore
index 6564618..8694d02 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,7 @@ git-diff-files
git-diff-index
git-diff-tree
git-describe
+git-fast-export
git-fast-import
git-fetch
git-fetch--tool
diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt
new file mode 100644
index 0000000..073ff7f
--- /dev/null
+++ b/Documentation/git-fast-export.txt
@@ -0,0 +1,83 @@
+git-fast-export(1)
+==================
+
+NAME
+----
+git-fast-export - Git data exporter
+
+
+SYNOPSIS
+--------
+'git-fast-export [options]' | 'git-fast-import'
+
+DESCRIPTION
+-----------
+This program dumps the given revisions in a form suitable to be piped
+into gitlink:git-fast-import[1].
+
+You can use it as a human readable bundle replacement (see
+gitlink:git-bundle[1]), or as a kind of an interactive
+gitlink:git-filter-branch[1].
+
+
+OPTIONS
+-------
+--progress=<n>::
+ Insert 'progress' statements every <n> objects, to be shown by
+ gitlink:git-fast-import[1] during import.
+
+--signed-tags=(ignore|warn|strip|abort)::
+ Specify how to handle signed tags. Since any transformation
+ after the export can change the tag names (which can also happen
+ when excluding revisions) the signatures will not match.
++
+When asking to 'abort' (which is the default), this program will die
+when encountering a signed tag. With 'strip', the tags will be made
+unsigned, with 'ignore', they will be silently ignored (i.e. not exported)
+and with 'warn', they will be exported, but you will see a warning.
+
+
+EXAMPLES
+--------
+
+-------------------------------------------------------------------
+$ git fast-export --all | (cd /empty/repository && git fast-import)
+-------------------------------------------------------------------
+
+This will export the whole repository and import it into the existing
+empty repository. Except for reencoding commits that are not in
+UTF-8, it would be a one-to-one mirror.
+
+-----------------------------------------------------
+$ git fast-export master~5..master |
+ sed "s|refs/heads/master|refs/heads/other|" |
+ git fast-import
+-----------------------------------------------------
+
+This makes a new branch called 'other' from 'master~5..master'
+(i.e. if 'master' has linear history, it will take the last 5 commits).
+
+Note that this assumes that none of the blobs and commit messages
+referenced by that revision range contains the string
+'refs/heads/master'.
+
+
+Limitations
+-----------
+
+Since gitlink:git-fast-import[1] cannot tag trees, you will not be
+able to export the linux-2.6.git repository completely, as it contains
+a tag referencing a tree instead of a commit.
+
+
+Author
+------
+Written by Johannes E. Schindelin <johannes.schindelin@gmx.de>.
+
+Documentation
+--------------
+Documentation by Johannes E. Schindelin <johannes.schindelin@gmx.de>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Makefile b/Makefile
index 0719fb8..ebd12ed 100644
--- a/Makefile
+++ b/Makefile
@@ -337,6 +337,7 @@ BUILTIN_OBJS = \
builtin-diff-files.o \
builtin-diff-index.o \
builtin-diff-tree.o \
+ builtin-fast-export.o \
builtin-fetch.o \
builtin-fetch-pack.o \
builtin-fetch--tool.o \
diff --git a/builtin-fast-export.c b/builtin-fast-export.c
new file mode 100755
index 0000000..48d0c54
--- /dev/null
+++ b/builtin-fast-export.c
@@ -0,0 +1,407 @@
+/*
+ * "git fast-export" builtin command
+ *
+ * Copyright (C) 2007 Johannes E. Schindelin
+ */
+#include "builtin.h"
+#include "cache.h"
+#include "commit.h"
+#include "object.h"
+#include "tag.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "log-tree.h"
+#include "revision.h"
+#include "decorate.h"
+#include "path-list.h"
+#include "utf8.h"
+#include "parse-options.h"
+
+/*
+ * TODO:
+ * - tags (--signed-tags=(ignore|warn|strip|abort)
+ */
+
+static const char *fast_export_usage[] = {
+ "git-fast-export [rev-list-opts]",
+ NULL
+};
+
+static int progress;
+static enum { IGNORE, WARN, STRIP, ABORT } signed_tag_mode = ABORT;
+
+static int parse_opt_signed_tag_mode(const struct option *opt,
+ const char *arg, int unset)
+{
+ if (unset || !strcmp(arg, "abort"))
+ signed_tag_mode = ABORT;
+ else if (!strcmp(arg, "ignore"))
+ signed_tag_mode = IGNORE;
+ else if (!strcmp(arg, "warn"))
+ signed_tag_mode = WARN;
+ else if (!strcmp(arg, "strip"))
+ signed_tag_mode = STRIP;
+ else
+ return error("Unknown signed-tag mode: %s", arg);
+ return 0;
+}
+
+static struct decoration idnums;
+static uint32_t last_idnum;
+
+static int has_unshown_parent(struct commit *commit)
+{
+ struct commit_list *parent;
+
+ for (parent = commit->parents; parent; parent = parent->next)
+ if (!(parent->item->object.flags & SHOWN) &&
+ !(parent->item->object.flags & UNINTERESTING))
+ return 1;
+ return 0;
+}
+
+/* Since intptr_t is C99, we do not use it here */
+static void mark_object(struct object *object)
+{
+ last_idnum++;
+ add_decoration(&idnums, object, (void *)last_idnum);
+}
+
+static int get_object_mark(struct object *object)
+{
+ return (int)lookup_decoration(&idnums, object);
+}
+
+static void show_progress(void)
+{
+ static int counter = 0;
+ if (!progress)
+ return;
+ if ((++counter % progress) == 0)
+ printf("progress %d objects\n", counter);
+}
+
+static void handle_object(const unsigned char *sha1)
+{
+ unsigned long size;
+ enum object_type type;
+ char *buf;
+ struct object *object;
+
+ if (is_null_sha1(sha1))
+ return;
+
+ object = parse_object(sha1);
+ if (!object)
+ die ("Could not read blob %s", sha1_to_hex(sha1));
+
+ if (object->flags & SHOWN)
+ return;
+
+ buf = read_sha1_file(sha1, &type, &size);
+ if (!buf)
+ die ("Could not read blob %s", sha1_to_hex(sha1));
+
+ mark_object(object);
+
+ printf("blob\nmark :%d\ndata %lu\n", last_idnum, size);
+ if (fwrite(buf, size, 1, stdout) != 1)
+ die ("Could not write blob %s", sha1_to_hex(sha1));
+ printf("\n");
+
+ show_progress();
+
+ object->flags |= SHOWN;
+ free(buf);
+}
+
+static void show_filemodify(struct diff_queue_struct *q,
+ struct diff_options *options, void *data)
+{
+ int i;
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filespec *spec = q->queue[i]->two;
+ if (is_null_sha1(spec->sha1))
+ printf("D %s\n", spec->path);
+ else {
+ struct object *object = lookup_object(spec->sha1);
+ printf("M 0%06o :%d %s\n", spec->mode,
+ get_object_mark(object), spec->path);
+ }
+ }
+}
+
+static const char *find_encoding(const char *begin, const char *end)
+{
+ const char *needle = "\nencoding ";
+ char *bol, *eol;
+
+ bol = memmem(begin, end ? end - begin : strlen(begin),
+ needle, strlen(needle));
+ if (!bol)
+ return git_commit_encoding;
+ bol += strlen(needle);
+ eol = strchrnul(bol, '\n');
+ *eol = '\0';
+ return bol;
+}
+
+static void handle_commit(struct commit *commit, struct rev_info *rev)
+{
+ int saved_output_format = rev->diffopt.output_format;
+ const char *author, *author_end, *committer, *committer_end;
+ const char *encoding, *message;
+ char *reencoded = NULL;
+ struct commit_list *p;
+ int i;
+
+ rev->diffopt.output_format = DIFF_FORMAT_CALLBACK;
+
+ parse_commit(commit);
+ author = strstr(commit->buffer, "\nauthor ");
+ if (!author)
+ die ("Could not find author in commit %s",
+ sha1_to_hex(commit->object.sha1));
+ author++;
+ author_end = strchrnul(author, '\n');
+ committer = strstr(author_end, "\ncommitter ");
+ if (!committer)
+ die ("Could not find committer in commit %s",
+ sha1_to_hex(commit->object.sha1));
+ committer++;
+ committer_end = strchrnul(committer, '\n');
+ message = strstr(committer_end, "\n\n");
+ encoding = find_encoding(committer_end, message);
+ if (message)
+ message += 2;
+
+ if (commit->parents) {
+ parse_commit(commit->parents->item);
+ diff_tree_sha1(commit->parents->item->tree->object.sha1,
+ commit->tree->object.sha1, "", &rev->diffopt);
+ }
+ else
+ diff_root_tree_sha1(commit->tree->object.sha1,
+ "", &rev->diffopt);
+
+ for (i = 0; i < diff_queued_diff.nr; i++)
+ handle_object(diff_queued_diff.queue[i]->two->sha1);
+
+ mark_object(&commit->object);
+ if (!is_encoding_utf8(encoding))
+ reencoded = reencode_string(message, "UTF-8", encoding);
+ printf("commit %s\nmark :%d\n%.*s\n%.*s\ndata %u\n%s",
+ (const char *)commit->util, last_idnum,
+ author_end - author, author,
+ committer_end - committer, committer,
+ reencoded ? strlen(reencoded) : message ? strlen(message) : 0,
+ reencoded ? reencoded : message ? message : "");
+ if (reencoded)
+ free(reencoded);
+
+ for (i = 0, p = commit->parents; p; p = p->next) {
+ int mark = get_object_mark(&p->item->object);
+ if (!mark)
+ continue;
+ if (i == 0)
+ printf("from :%d\n", mark);
+ else if (i == 1)
+ printf("merge :%d", mark);
+ else
+ printf(" :%d", mark);
+ i++;
+ }
+ if (i > 1)
+ printf("\n");
+
+ log_tree_diff_flush(rev);
+ rev->diffopt.output_format = saved_output_format;
+
+ printf("\n");
+
+ show_progress();
+}
+
+static void handle_tail(struct object_array *commits, struct rev_info *revs)
+{
+ struct commit *commit;
+ while (commits->nr) {
+ commit = (struct commit *)commits->objects[commits->nr - 1].item;
+ if (has_unshown_parent(commit))
+ return;
+ handle_commit(commit, revs);
+ commits->nr--;
+ }
+}
+
+static void handle_tag(const char *name, struct tag *tag)
+{
+ unsigned long size;
+ enum object_type type;
+ char *buf;
+ const char *tagger, *tagger_end, *message;
+ size_t message_size = 0;
+
+ buf = read_sha1_file(tag->object.sha1, &type, &size);
+ if (!buf)
+ die ("Could not read tag %s", sha1_to_hex(tag->object.sha1));
+ message = memmem(buf, size, "\n\n", 2);
+ if (message) {
+ message += 2;
+ message_size = strlen(message);
+ }
+ tagger = memmem(buf, message ? message - buf : size, "\ntagger ", 8);
+ if (!tagger)
+ die ("No tagger for tag %s", sha1_to_hex(tag->object.sha1));
+ tagger++;
+ tagger_end = strchrnul(tagger, '\n');
+
+ /* handle signed tags */
+ if (message) {
+ const char *signature = strstr(message,
+ "\n-----BEGIN PGP SIGNATURE-----\n");
+ if (signature)
+ switch(signed_tag_mode) {
+ case ABORT:
+ die ("Encountered signed tag %s; use "
+ "--signed-tag=<mode> to handle it.",
+ sha1_to_hex(tag->object.sha1));
+ case WARN:
+ warning ("Exporting signed tag %s",
+ sha1_to_hex(tag->object.sha1));
+ /* fallthru */
+ case IGNORE:
+ break;
+ case STRIP:
+ message_size = signature + 1 - message;
+ break;
+ }
+ }
+
+ if (!prefixcmp(name, "refs/tags/"))
+ name += 10;
+ printf("tag %s\nfrom :%d\n%.*s\ndata %d\n%.*s\n",
+ name, get_object_mark(tag->tagged),
+ tagger_end - tagger, tagger,
+ message_size, message_size, message ? message : "");
+}
+
+static void get_tags_and_duplicates(struct object_array *pending,
+ struct path_list *extra_refs)
+{
+ struct commit *commit;
+ struct tag *tag;
+ int i;
+
+ for (i = 0; i < pending->nr; i++) {
+ struct object_array_entry *e = pending->objects + i;
+ unsigned char sha1[20];
+ char *full_name;
+
+ if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1)
+ continue;
+
+ switch (e->item->type) {
+ case OBJ_COMMIT:
+ commit = (struct commit *)e->item;
+ break;
+ case OBJ_TAG:
+ tag = (struct tag *)e->item;
+ while (tag && tag->object.type == OBJ_TAG) {
+ path_list_insert(full_name, extra_refs)->util
+ = tag;
+ tag = (struct tag *)tag->tagged;
+ }
+ if (!tag)
+ die ("Tag %s points nowhere?", e->name);
+ switch(tag->object.type) {
+ case OBJ_COMMIT:
+ commit = (struct commit *)tag;
+ break;
+ case OBJ_BLOB:
+ handle_object(tag->object.sha1);
+ continue;
+ }
+ break;
+ default:
+ die ("Unexpected object of type %s",
+ typename(e->item->type));
+ }
+ if (commit->util)
+ /* more than one name for the same object */
+ path_list_insert(full_name, extra_refs)->util = commit;
+ else
+ commit->util = full_name;
+ }
+}
+
+static void handle_tags_and_duplicates(struct path_list *extra_refs)
+{
+ struct commit *commit;
+ int i;
+
+ for (i = extra_refs->nr - 1; i >= 0; i--) {
+ const char *name = extra_refs->items[i].path;
+ struct object *object = extra_refs->items[i].util;
+ switch (object->type) {
+ case OBJ_TAG:
+ handle_tag(name, (struct tag *)object);
+ break;
+ case OBJ_COMMIT:
+ /* create refs pointing to already seen commits */
+ commit = (struct commit *)object;
+ printf("reset %s\nfrom :%d\n\n", name,
+ get_object_mark(&commit->object));
+ show_progress();
+ break;
+ }
+ }
+}
+
+int cmd_fast_export(int argc, const char **argv, const char *prefix)
+{
+ struct rev_info revs;
+ struct object_array commits = { 0, 0, NULL };
+ struct path_list extra_refs = { NULL, 0, 0, 0 };
+ struct commit *commit;
+ struct option options[] = {
+ OPT_INTEGER(0, "progress", &progress,
+ "show progress after <n> objects"),
+ OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, "mode",
+ "select handling of signed tags",
+ parse_opt_signed_tag_mode),
+ OPT_END()
+ };
+
+ /* we handle encodings */
+ git_config(git_default_config);
+
+ init_revisions(&revs, prefix);
+ argc = setup_revisions(argc, argv, &revs, NULL);
+ argc = parse_options(argc, argv, options, fast_export_usage, 0);
+ if (argc > 1)
+ usage_with_options (fast_export_usage, options);
+
+ get_tags_and_duplicates(&revs.pending, &extra_refs);
+
+ prepare_revision_walk(&revs);
+ revs.diffopt.format_callback = show_filemodify;
+ DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
+ while ((commit = get_revision(&revs))) {
+ if (has_unshown_parent(commit)) {
+ struct commit_list *parent = commit->parents;
+ add_object_array(&commit->object, NULL, &commits);
+ for (; parent; parent = parent->next)
+ if (!parent->item->util)
+ parent->item->util = commit->util;
+ }
+ else {
+ handle_commit(commit, &revs);
+ handle_tail(&commits, &revs);
+ }
+ }
+
+ handle_tags_and_duplicates(&extra_refs);
+
+ return 0;
+}
diff --git a/builtin.h b/builtin.h
index 3055bcc..142ab63 100644
--- a/builtin.h
+++ b/builtin.h
@@ -33,6 +33,7 @@ extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
extern int cmd_diff(int argc, const char **argv, const char *prefix);
extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_fast_export(int argc, const char **argv, const char *prefix);
extern int cmd_fetch(int argc, const char **argv, const char *prefix);
extern int cmd_fetch_pack(int argc, const char **argv, const char *prefix);
extern int cmd_fetch__tool(int argc, const char **argv, const char *prefix);
diff --git a/git.c b/git.c
index e48c2c9..db0e7c3 100644
--- a/git.c
+++ b/git.c
@@ -303,6 +303,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "diff-files", cmd_diff_files },
{ "diff-index", cmd_diff_index, RUN_SETUP },
{ "diff-tree", cmd_diff_tree, RUN_SETUP },
+ { "fast-export", cmd_fast_export, RUN_SETUP },
{ "fetch", cmd_fetch, RUN_SETUP },
{ "fetch-pack", cmd_fetch_pack, RUN_SETUP },
{ "fetch--tool", cmd_fetch__tool, RUN_SETUP },
diff --git a/t/t9301-fast-export.sh b/t/t9301-fast-export.sh
new file mode 100755
index 0000000..59f6996
--- /dev/null
+++ b/t/t9301-fast-export.sh
@@ -0,0 +1,124 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='git-fast-export'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+ echo Wohlauf > file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ echo die Luft > file &&
+ echo geht frisch > file2 &&
+ git add file file2 &&
+ test_tick &&
+ git commit -m second &&
+ echo und > file2 &&
+ test_tick &&
+ git commit -m third file2 &&
+ test_tick &&
+ git tag rein &&
+ git checkout -b wer HEAD^ &&
+ echo lange > file2
+ test_tick &&
+ git commit -m sitzt file2 &&
+ test_tick &&
+ git tag -a -m valentin muss &&
+ git merge -s ours master
+
+'
+
+test_expect_success 'fast-export | fast-import' '
+
+ MASTER=$(git rev-parse --verify master) &&
+ REIN=$(git rev-parse --verify rein) &&
+ WER=$(git rev-parse --verify wer) &&
+ MUSS=$(git rev-parse --verify muss) &&
+ mkdir new &&
+ git --git-dir=new/.git init &&
+ git fast-export --all |
+ (cd new &&
+ git fast-import &&
+ test $MASTER = $(git rev-parse --verify refs/heads/master) &&
+ test $REIN = $(git rev-parse --verify refs/tags/rein) &&
+ test $WER = $(git rev-parse --verify refs/heads/wer) &&
+ test $MUSS = $(git rev-parse --verify refs/tags/muss))
+
+'
+
+test_expect_success 'fast-export master~2..master' '
+
+ git fast-export master~2..master |
+ sed "s/master/partial/" |
+ (cd new &&
+ git fast-import &&
+ test $MASTER != $(git rev-parse --verify refs/heads/partial) &&
+ git diff master..partial &&
+ git diff master^..partial^ &&
+ ! git rev-parse partial~2)
+
+'
+
+test_expect_success 'iso-8859-1' '
+
+ git config i18n.commitencoding ISO-8859-1 &&
+ # use author and committer name in ISO-8859-1 to match it.
+ . ../t3901-8859-1.txt &&
+ test_tick &&
+ echo rosten >file &&
+ git commit -s -m den file &&
+ git fast-export wer^..wer |
+ sed "s/wer/i18n/" |
+ (cd new &&
+ git fast-import &&
+ git cat-file commit i18n | grep "Áéí óú")
+
+'
+
+cat > signed-tag-import << EOF
+tag sign-your-name
+from $(git rev-parse HEAD)
+tagger C O Mitter <committer@example.com> 1112911993 -0700
+data 210
+A message for a sign
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.5 (GNU/Linux)
+
+fakedsignaturefakedsignaturefakedsignaturefakedsignaturfakedsign
+aturefakedsignaturefake=
+=/59v
+-----END PGP SIGNATURE-----
+EOF
+
+test_expect_success 'set up faked signed tag' '
+
+ cat signed-tag-import | git fast-import
+
+'
+
+test_expect_success 'signed-tags=abort' '
+
+ ! git fast-export --signed-tags=abort sign-your-name
+
+'
+
+test_expect_success 'signed-tags=ignore' '
+
+ git fast-export --signed-tags=ignore sign-your-name > output &&
+ grep PGP output
+
+'
+
+test_expect_success 'signed-tags=strip' '
+
+ git fast-export --signed-tags=strip sign-your-name > output &&
+ ! grep PGP output
+
+'
+
+test_done
+
--
1.5.3.6.1828.gd496
^ permalink raw reply related
* [Resend Trivial PATCH] Print the real filename whose opening failed.
From: André Goddard Rosa @ 2007-11-25 21:38 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Git Mailing List
In-Reply-To: <b8bf37780711211700g48a738b1xa147d1e7000ee76@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2905 bytes --]
Hi, all!
Print the filename whose opening failed.
From 88b7bd873b97bd81e854827c9cd8f3c306d3c3af Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Andr=C3=A9=20Goddard=20Rosa?= <andre.goddard@gmail.com>
Date: Wed, 21 Nov 2007 18:59:14 -0200
Subject: [PATCH] Print the real filename whose opening failed.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Goddard Rosa <andre.goddard@gmail.com>
---
http-push.c | 4 ++--
http-walker.c | 4 ++--
server-info.c | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/http-push.c b/http-push.c
index 9314621..e675ca6 100644
--- a/http-push.c
+++ b/http-push.c
@@ -433,7 +433,7 @@ static void start_fetch_packed(struct
transfer_request *request)
packfile = fopen(request->tmpfile, "a");
if (!packfile) {
fprintf(stderr, "Unable to open local file %s for pack",
- filename);
+ request->tmpfile);
remote->can_update_info_refs = 0;
free(url);
return;
@@ -941,7 +941,7 @@ static int fetch_index(unsigned char *sha1)
indexfile = fopen(tmpfile, "a");
if (!indexfile)
return error("Unable to open local file %s for pack index",
- filename);
+ tmpfile);
slot = get_active_slot();
slot->results = &results;
diff --git a/http-walker.c b/http-walker.c
index 444aebf..a3fb596 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -405,7 +405,7 @@ static int fetch_index(struct walker *walker,
struct alt_base *repo, unsigned ch
indexfile = fopen(tmpfile, "a");
if (!indexfile)
return error("Unable to open local file %s for pack index",
- filename);
+ tmpfile);
slot = get_active_slot();
slot->results = &results;
@@ -770,7 +770,7 @@ static int fetch_pack(struct walker *walker,
struct alt_base *repo, unsigned cha
packfile = fopen(tmpfile, "a");
if (!packfile)
return error("Unable to open local file %s for pack",
- filename);
+ tmpfile);
slot = get_active_slot();
slot->results = &results;
diff --git a/server-info.c b/server-info.c
index 0d1312c..a051e49 100644
--- a/server-info.c
+++ b/server-info.c
@@ -35,7 +35,7 @@ static int update_info_refs(int force)
safe_create_leading_directories(path0);
info_ref_fp = fopen(path1, "w");
if (!info_ref_fp)
- return error("unable to update %s", path0);
+ return error("unable to update %s", path1);
for_each_ref(add_info_ref, NULL);
fclose(info_ref_fp);
adjust_shared_perm(path1);
--
1.5.3.6.861.gd794-dirty
--
[]s,
André Goddard
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0005-Print-the-real-filename-whose-opening-failed.patch --]
[-- Type: text/x-patch; name=0005-Print-the-real-filename-whose-opening-failed.patch, Size: 2430 bytes --]
From 88b7bd873b97bd81e854827c9cd8f3c306d3c3af Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Andr=C3=A9=20Goddard=20Rosa?= <andre.goddard@gmail.com>
Date: Wed, 21 Nov 2007 18:59:14 -0200
Subject: [PATCH] Print the real filename whose opening failed.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Goddard Rosa <andre.goddard@gmail.com>
---
http-push.c | 4 ++--
http-walker.c | 4 ++--
server-info.c | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/http-push.c b/http-push.c
index 9314621..e675ca6 100644
--- a/http-push.c
+++ b/http-push.c
@@ -433,7 +433,7 @@ static void start_fetch_packed(struct transfer_request *request)
packfile = fopen(request->tmpfile, "a");
if (!packfile) {
fprintf(stderr, "Unable to open local file %s for pack",
- filename);
+ request->tmpfile);
remote->can_update_info_refs = 0;
free(url);
return;
@@ -941,7 +941,7 @@ static int fetch_index(unsigned char *sha1)
indexfile = fopen(tmpfile, "a");
if (!indexfile)
return error("Unable to open local file %s for pack index",
- filename);
+ tmpfile);
slot = get_active_slot();
slot->results = &results;
diff --git a/http-walker.c b/http-walker.c
index 444aebf..a3fb596 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -405,7 +405,7 @@ static int fetch_index(struct walker *walker, struct alt_base *repo, unsigned ch
indexfile = fopen(tmpfile, "a");
if (!indexfile)
return error("Unable to open local file %s for pack index",
- filename);
+ tmpfile);
slot = get_active_slot();
slot->results = &results;
@@ -770,7 +770,7 @@ static int fetch_pack(struct walker *walker, struct alt_base *repo, unsigned cha
packfile = fopen(tmpfile, "a");
if (!packfile)
return error("Unable to open local file %s for pack",
- filename);
+ tmpfile);
slot = get_active_slot();
slot->results = &results;
diff --git a/server-info.c b/server-info.c
index 0d1312c..a051e49 100644
--- a/server-info.c
+++ b/server-info.c
@@ -35,7 +35,7 @@ static int update_info_refs(int force)
safe_create_leading_directories(path0);
info_ref_fp = fopen(path1, "w");
if (!info_ref_fp)
- return error("unable to update %s", path0);
+ return error("unable to update %s", path1);
for_each_ref(add_info_ref, NULL);
fclose(info_ref_fp);
adjust_shared_perm(path1);
--
1.5.3.6.861.gd794-dirty
^ permalink raw reply related
* [Resend PATCH] Fix segmentation fault when user doesn't have access permission to the repository.
From: André Goddard Rosa @ 2007-11-25 21:39 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Git Mailing List
In-Reply-To: <b8bf37780711221427q5dda709dt38ce1837c0e56c1f@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 5302 bytes --]
On Nov 22, 2007 2:09 PM, Alex Riesen <raa.lkml@gmail.com> wrote:
> André Goddard Rosa, Thu, Nov 22, 2007 01:59:00 +0100:
> > @@ -487,8 +490,13 @@ static int do_fetch(struct transport *transport,
> > die("Don't know how to fetch from %s", transport->url);
> >
> > /* if not appending, truncate FETCH_HEAD */
> > - if (!append)
> > - fclose(fopen(git_path("FETCH_HEAD"), "w"));
> > + if (!append) {
> > + char *filename = git_path("FETCH_HEAD");
> > + int fd = fopen(filename, "w");
>
> This should have been "FILE *fp", not "int fd".
>
Hi, Alex!
Many thanks, you're right.
I tested it here before posting but luckly (or not, as I didn't catch
this when compiling) it worked,
as a pointer have the sizeof(int) in my x86 platform. >:|
Would you please comment on the attached patch and see if it's ok?
From dbadc5213b9957fb575c6da8528e5dd7a3f1f43e Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Andr=C3=A9=20Goddard=20Rosa?= <andre.goddard@gmail.com>
Date: Thu, 22 Nov 2007 20:22:23 -0200
Subject: [PATCH] Fix segmentation fault when user doesn't have access
permission to the repository.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Goddard Rosa <andre.goddard@gmail.com>
---
builtin-fetch--tool.c | 12 ++++++++++--
builtin-fetch.c | 21 ++++++++++++++++-----
2 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c
index ed60847..7460ab7 100644
--- a/builtin-fetch--tool.c
+++ b/builtin-fetch--tool.c
@@ -511,10 +511,14 @@ int cmd_fetch__tool(int argc, const char **argv,
const char *prefix)
if (!strcmp("append-fetch-head", argv[1])) {
int result;
FILE *fp;
+ char *filename;
if (argc != 8)
return error("append-fetch-head takes 6 args");
- fp = fopen(git_path("FETCH_HEAD"), "a");
+ filename = git_path("FETCH_HEAD");
+ fp = fopen(filename, "a");
+ if (!fp)
+ return error("cannot open %s: %s\n", filename,
strerror(errno));
result = append_fetch_head(fp, argv[2], argv[3],
argv[4], argv[5],
argv[6], !!argv[7][0],
@@ -525,10 +529,14 @@ int cmd_fetch__tool(int argc, const char **argv,
const char *prefix)
if (!strcmp("native-store", argv[1])) {
int result;
FILE *fp;
+ char *filename;
if (argc != 5)
return error("fetch-native-store takes 3 args");
- fp = fopen(git_path("FETCH_HEAD"), "a");
+ filename = git_path("FETCH_HEAD");
+ fp = fopen(filename, "a");
+ if (!fp)
+ return error("cannot open %s: %s\n", filename,
strerror(errno));
result = fetch_native_store(fp, argv[2], argv[3], argv[4],
verbose, force);
fclose(fp);
diff --git a/builtin-fetch.c b/builtin-fetch.c
index be9e3ea..84c8ed4 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -255,7 +255,7 @@ static int update_local_ref(struct ref *ref,
}
}
-static void store_updated_refs(const char *url, struct ref *ref_map)
+static int store_updated_refs(const char *url, struct ref *ref_map)
{
FILE *fp;
struct commit *commit;
@@ -263,8 +263,13 @@ static void store_updated_refs(const char *url,
struct ref *ref_map)
char note[1024];
const char *what, *kind;
struct ref *rm;
+ char *filename = git_path("FETCH_HEAD");
- fp = fopen(git_path("FETCH_HEAD"), "a");
+ fp = fopen(filename, "a");
+ if (!fp) {
+ error("cannot open %s: %s\n", filename, strerror(errno));
+ return 1;
+ }
for (rm = ref_map; rm; rm = rm->next) {
struct ref *ref = NULL;
@@ -335,6 +340,7 @@ static void store_updated_refs(const char *url,
struct ref *ref_map)
}
}
fclose(fp);
+ return 0;
}
/*
@@ -404,7 +410,7 @@ static int fetch_refs(struct transport *transport,
struct ref *ref_map)
if (ret)
ret = transport_fetch_refs(transport, ref_map);
if (!ret)
- store_updated_refs(transport->url, ref_map);
+ ret |= store_updated_refs(transport->url, ref_map);
transport_unlock_pack(transport);
return ret;
}
@@ -487,8 +493,13 @@ static int do_fetch(struct transport *transport,
die("Don't know how to fetch from %s", transport->url);
/* if not appending, truncate FETCH_HEAD */
- if (!append)
- fclose(fopen(git_path("FETCH_HEAD"), "w"));
+ if (!append) {
+ char *filename = git_path("FETCH_HEAD");
+ FILE *fp = fopen(filename, "w");
+ if (!fp)
+ return error("cannot open %s: %s\n", filename,
strerror(errno));
+ fclose(fp);
+ }
ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
--
1.5.3.6.861.gd794-dirty
--
[]s,
André Goddard
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Fix-segmentation-fault-when-user-doesn-t-have-access.patch --]
[-- Type: text/x-patch; name=0001-Fix-segmentation-fault-when-user-doesn-t-have-access.patch, Size: 3642 bytes --]
From dbadc5213b9957fb575c6da8528e5dd7a3f1f43e Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Andr=C3=A9=20Goddard=20Rosa?= <andre.goddard@gmail.com>
Date: Thu, 22 Nov 2007 20:22:23 -0200
Subject: [PATCH] Fix segmentation fault when user doesn't have access
permission to the repository.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Goddard Rosa <andre.goddard@gmail.com>
---
builtin-fetch--tool.c | 12 ++++++++++--
builtin-fetch.c | 21 ++++++++++++++++-----
2 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c
index ed60847..7460ab7 100644
--- a/builtin-fetch--tool.c
+++ b/builtin-fetch--tool.c
@@ -511,10 +511,14 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
if (!strcmp("append-fetch-head", argv[1])) {
int result;
FILE *fp;
+ char *filename;
if (argc != 8)
return error("append-fetch-head takes 6 args");
- fp = fopen(git_path("FETCH_HEAD"), "a");
+ filename = git_path("FETCH_HEAD");
+ fp = fopen(filename, "a");
+ if (!fp)
+ return error("cannot open %s: %s\n", filename, strerror(errno));
result = append_fetch_head(fp, argv[2], argv[3],
argv[4], argv[5],
argv[6], !!argv[7][0],
@@ -525,10 +529,14 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
if (!strcmp("native-store", argv[1])) {
int result;
FILE *fp;
+ char *filename;
if (argc != 5)
return error("fetch-native-store takes 3 args");
- fp = fopen(git_path("FETCH_HEAD"), "a");
+ filename = git_path("FETCH_HEAD");
+ fp = fopen(filename, "a");
+ if (!fp)
+ return error("cannot open %s: %s\n", filename, strerror(errno));
result = fetch_native_store(fp, argv[2], argv[3], argv[4],
verbose, force);
fclose(fp);
diff --git a/builtin-fetch.c b/builtin-fetch.c
index be9e3ea..84c8ed4 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -255,7 +255,7 @@ static int update_local_ref(struct ref *ref,
}
}
-static void store_updated_refs(const char *url, struct ref *ref_map)
+static int store_updated_refs(const char *url, struct ref *ref_map)
{
FILE *fp;
struct commit *commit;
@@ -263,8 +263,13 @@ static void store_updated_refs(const char *url, struct ref *ref_map)
char note[1024];
const char *what, *kind;
struct ref *rm;
+ char *filename = git_path("FETCH_HEAD");
- fp = fopen(git_path("FETCH_HEAD"), "a");
+ fp = fopen(filename, "a");
+ if (!fp) {
+ error("cannot open %s: %s\n", filename, strerror(errno));
+ return 1;
+ }
for (rm = ref_map; rm; rm = rm->next) {
struct ref *ref = NULL;
@@ -335,6 +340,7 @@ static void store_updated_refs(const char *url, struct ref *ref_map)
}
}
fclose(fp);
+ return 0;
}
/*
@@ -404,7 +410,7 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)
if (ret)
ret = transport_fetch_refs(transport, ref_map);
if (!ret)
- store_updated_refs(transport->url, ref_map);
+ ret |= store_updated_refs(transport->url, ref_map);
transport_unlock_pack(transport);
return ret;
}
@@ -487,8 +493,13 @@ static int do_fetch(struct transport *transport,
die("Don't know how to fetch from %s", transport->url);
/* if not appending, truncate FETCH_HEAD */
- if (!append)
- fclose(fopen(git_path("FETCH_HEAD"), "w"));
+ if (!append) {
+ char *filename = git_path("FETCH_HEAD");
+ FILE *fp = fopen(filename, "w");
+ if (!fp)
+ return error("cannot open %s: %s\n", filename, strerror(errno));
+ fclose(fp);
+ }
ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
--
1.5.3.6.861.gd794-dirty
^ permalink raw reply related
* [Resend PATCH] Simplify the code and avoid an assignment
From: André Goddard Rosa @ 2007-11-25 21:42 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Git Mailing List
[-- Attachment #1: Type: text/plain, Size: 2247 bytes --]
Hi, all!
Simplify the code for easier understanding.
>From cd0cc6995684e2801011910735146052e5b59ccc Mon Sep 17 00:00:00 2001
From: Andre Goddard Rosa <andre.goddard@gmail.com>
Date: Tue, 27 Nov 2007 10:22:46 -0200
Subject: [PATCH] Simplify the code and avoid an assignment.
Signed-off-by: Andre Goddard Rosa <andre.goddard@gmail.com>
---
config.c | 19 ++++++++++---------
mailmap.c | 2 +-
xdiff-interface.c | 2 +-
3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/config.c b/config.c
index 56e99fc..7c9fcdd 100644
--- a/config.c
+++ b/config.c
@@ -447,15 +447,16 @@ int git_config_from_file(config_fn_t fn, const
char *filename)
int ret;
FILE *f = fopen(filename, "r");
- ret = -1;
- if (f) {
- config_file = f;
- config_file_name = filename;
- config_linenr = 1;
- ret = git_parse_file(fn);
- fclose(f);
- config_file_name = NULL;
- }
+ if (!f)
+ return -1;
+
+ config_file = f;
+ config_file_name = filename;
+ config_linenr = 1;
+ ret = git_parse_file(fn);
+ fclose(f);
+ config_file_name = NULL;
+
return ret;
}
diff --git a/mailmap.c b/mailmap.c
index 8714167..0c13ecd 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -7,7 +7,7 @@ int read_mailmap(struct path_list *map, const char
*filename, char **repo_abbrev
char buffer[1024];
FILE *f = fopen(filename, "r");
- if (f == NULL)
+ if (!f)
return 1;
while (fgets(buffer, sizeof(buffer), f) != NULL) {
char *end_of_name, *left_bracket, *right_bracket;
diff --git a/xdiff-interface.c b/xdiff-interface.c
index be866d1..9dd1f3b 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -111,7 +111,7 @@ int read_mmfile(mmfile_t *ptr, const char *filename)
if (stat(filename, &st))
return error("Could not stat %s", filename);
- if ((f = fopen(filename, "rb")) == NULL)
+ if (!(f = fopen(filename, "rb")))
return error("Could not open %s", filename);
sz = xsize_t(st.st_size);
ptr->ptr = xmalloc(sz);
--
1.5.3.6.861.gd794-dirty
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0004-Simplify-the-code-and-avoid-an-assignment.patch --]
[-- Type: text/x-patch; name=0004-Simplify-the-code-and-avoid-an-assignment.patch, Size: 1911 bytes --]
From cd0cc6995684e2801011910735146052e5b59ccc Mon Sep 17 00:00:00 2001
From: Andre Goddard Rosa <andre.goddard@gmail.com>
Date: Tue, 27 Nov 2007 10:22:46 -0200
Subject: [PATCH] Simplify the code and avoid an assignment.
Signed-off-by: Andre Goddard Rosa <andre.goddard@gmail.com>
---
config.c | 19 ++++++++++---------
mailmap.c | 2 +-
xdiff-interface.c | 2 +-
3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/config.c b/config.c
index 56e99fc..7c9fcdd 100644
--- a/config.c
+++ b/config.c
@@ -447,15 +447,16 @@ int git_config_from_file(config_fn_t fn, const char *filename)
int ret;
FILE *f = fopen(filename, "r");
- ret = -1;
- if (f) {
- config_file = f;
- config_file_name = filename;
- config_linenr = 1;
- ret = git_parse_file(fn);
- fclose(f);
- config_file_name = NULL;
- }
+ if (!f)
+ return -1;
+
+ config_file = f;
+ config_file_name = filename;
+ config_linenr = 1;
+ ret = git_parse_file(fn);
+ fclose(f);
+ config_file_name = NULL;
+
return ret;
}
diff --git a/mailmap.c b/mailmap.c
index 8714167..0c13ecd 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -7,7 +7,7 @@ int read_mailmap(struct path_list *map, const char *filename, char **repo_abbrev
char buffer[1024];
FILE *f = fopen(filename, "r");
- if (f == NULL)
+ if (!f)
return 1;
while (fgets(buffer, sizeof(buffer), f) != NULL) {
char *end_of_name, *left_bracket, *right_bracket;
diff --git a/xdiff-interface.c b/xdiff-interface.c
index be866d1..9dd1f3b 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -111,7 +111,7 @@ int read_mmfile(mmfile_t *ptr, const char *filename)
if (stat(filename, &st))
return error("Could not stat %s", filename);
- if ((f = fopen(filename, "rb")) == NULL)
+ if (!(f = fopen(filename, "rb")))
return error("Could not open %s", filename);
sz = xsize_t(st.st_size);
ptr->ptr = xmalloc(sz);
--
1.5.3.6.861.gd794-dirty
^ permalink raw reply related
* [Resend PATCH] Avoid recalculating filename string pointer.
From: André Goddard Rosa @ 2007-11-25 21:43 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Git Mailing List
In-Reply-To: <b8bf37780711211659i65a99493te3e3d5cee008ae7d@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1446 bytes --]
Hi, all!
Avoid calculating string position in 2 different places.
From b6b05d9f8d8e053df4e971cd229e03b778c4d163 Mon Sep 17 00:00:00 2001
From: Andre Goddard Rosa <andre.goddard@gmail.com>
Date: Tue, 27 Nov 2007 10:17:54 -0200
Subject: [PATCH] Avoid recalculating filename string pointer.
Signed-off-by: Andre Goddard Rosa <andre.goddard@gmail.com>
---
fast-import.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/fast-import.c b/fast-import.c
index 98c2bd5..2d262eb 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -2304,11 +2304,13 @@ int main(int argc, const char **argv)
else if (!prefixcmp(a, "--export-marks="))
mark_file = a + 15;
else if (!prefixcmp(a, "--export-pack-edges=")) {
+ char *filename = a + 20;
+
if (pack_edges)
fclose(pack_edges);
- pack_edges = fopen(a + 20, "a");
+ pack_edges = fopen(filename, "a");
if (!pack_edges)
- die("Cannot open %s: %s", a + 20,
strerror(errno));
+ die("Cannot open %s: %s", filename,
strerror(errno));
} else if (!strcmp(a, "--force"))
force_update = 1;
else if (!strcmp(a, "--quiet"))
--
1.5.3.6.861.gd794-dirty
--
[]s,
André Goddard
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0003-Avoid-recalculating-filename-string-pointer.patch --]
[-- Type: text/x-patch; name=0003-Avoid-recalculating-filename-string-pointer.patch, Size: 1077 bytes --]
From b6b05d9f8d8e053df4e971cd229e03b778c4d163 Mon Sep 17 00:00:00 2001
From: Andre Goddard Rosa <andre.goddard@gmail.com>
Date: Tue, 27 Nov 2007 10:17:54 -0200
Subject: [PATCH] Avoid recalculating filename string pointer.
Signed-off-by: Andre Goddard Rosa <andre.goddard@gmail.com>
---
fast-import.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/fast-import.c b/fast-import.c
index 98c2bd5..2d262eb 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -2304,11 +2304,13 @@ int main(int argc, const char **argv)
else if (!prefixcmp(a, "--export-marks="))
mark_file = a + 15;
else if (!prefixcmp(a, "--export-pack-edges=")) {
+ char *filename = a + 20;
+
if (pack_edges)
fclose(pack_edges);
- pack_edges = fopen(a + 20, "a");
+ pack_edges = fopen(filename, "a");
if (!pack_edges)
- die("Cannot open %s: %s", a + 20, strerror(errno));
+ die("Cannot open %s: %s", filename, strerror(errno));
} else if (!strcmp(a, "--force"))
force_update = 1;
else if (!strcmp(a, "--quiet"))
--
1.5.3.6.861.gd794-dirty
^ permalink raw reply related
* If you would write git from scratch now, what would you change?
From: Jakub Narebski @ 2007-11-25 21:48 UTC (permalink / raw)
To: git
If you would write git from scratch now, from the beginning, without
concerns for backwards compatibility, what would you change, or what
would you want to have changed?
Yes, I know, I know. "Worse is better". It is better to release early
and get feedback what is really needed, as opposed to what do you think
is needed.
I think git is a wonderful example of "evolved" software, evolving
practically from the very beginnings.
--
Jakub Narebski
Poland
^ permalink raw reply
* Re: What's cooking in git.git (topics)
From: J. Bruce Fields @ 2007-11-25 21:51 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Nicolas Pitre, Jeff King, Johannes Schindelin, git
In-Reply-To: <7vtznbqx2w.fsf@gitster.siamese.dyndns.org>
On Sat, Nov 24, 2007 at 11:09:59AM -0800, Junio C Hamano wrote:
> Nicolas Pitre <nico@cam.org> writes:
> > I think that would be better to append a single line at the end of the
> > display with a clue about what "non fast forward" means.
>
> I'd agree, but having said all the above, I am not entirely
> happy not mentioning "what next" at all.
>
> There are two equally valid "what next" after your push is
> rejected due to non-fast-forwardness.
>
> (1) You know what you are doing.
>
> - You are pushing into a "back-up" repository, not for a
> public consumption.
>
> - You are pushing a branch that are advertised to rebase and
> rewind into your own publishing repository, and other
> people interacting with the branch know about this.
>
> - You pushed a wrong head there very recently and are fairly
> confident that nobody has seen that mistake, and pushing
> the correct one to fix the mistake.
>
> In these cases, forcing the push is the right solution
> (except that the third one is dubious, because it depends
> heavily on the "fairly confident" part).
>
> (2) You were building on a stale head, and were indeed about to
> lose others' changes with a non-fast-forward push.
>
> The right solution is to rebuild what you push so that you
> will not lose others' changes. Rebuilding can take two
> different forms:
>
> - You may want to git-fetch and rebase your work on top of
> others'.
>
> - You may want to git-pull, which will merge your work with
> what others did.
>
> But of couse the above is way too long as the help text.
>
> Does the user-manual talk about this? It has a really good
> description of how to notice when a merge is not resolved
> automatically and what to do next ("Resolving a merge" section).
> Perhaps we can enhance "Pushing changes to a public repository"
> section to include "what if the push is refused" information.
There's a very brief mention of this:
"As with git-fetch, git-push will complain if this does not
result in a <<fast-forwards,fast forward>>. Normally this is a
sign of something wrong. However, if you are sure you know what
you're doing, you may force git-push to perform the update
anyway by preceding the branch name by a plus sign:
But it'd probably be better to have a separate section. That makes it
possible to say a little more, and also gets a section called "what to
do when a push fails" into the table of contents.
(Though that's a little vague--push can also fail just because you
mispell the url or something. A more precise reference to the
particular error might be better, but we'll have to agree on the error
message first....)
Anyway, here's a first draft.
--b.
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 8355cce..7544715 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1929,15 +1929,9 @@ or just
$ git push ssh://yourserver.com/~you/proj.git master
-------------------------------------------------
-As with git-fetch, git-push will complain if this does not result in
-a <<fast-forwards,fast forward>>. Normally this is a sign of
-something wrong. However, if you are sure you know what you're
-doing, you may force git-push to perform the update anyway by
-proceeding the branch name by a plus sign:
-
--------------------------------------------------
-$ git push ssh://yourserver.com/~you/proj.git +master
--------------------------------------------------
+As with git-fetch, git-push will complain if this does not result in a
+<<fast-forwards,fast forward>>; see the following section for details on
+handling this case.
Note that the target of a "push" is normally a
<<def_bare_repository,bare>> repository. You can also push to a
@@ -1965,6 +1959,55 @@ See the explanations of the remote.<name>.url, branch.<name>.remote,
and remote.<name>.push options in gitlink:git-config[1] for
details.
+[[forcing-push]]
+What to do when a push fails
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If the push does not result in a <<fast-forwards,fast forward>> of the
+remote branch, then it will fail with an error like:
+
+-------------------------------------------------
+error: remote 'refs/heads/master' is not an ancestor of
+ local 'refs/heads/master'.
+ Maybe you are not up-to-date and need to pull first?
+error: failed to push to 'ssh://yourserver.com/~you/proj.git'
+-------------------------------------------------
+
+The most likely reason for this is that you have replaced some of the
+history that you already pushed, so that your "master" is no longer a
+descendant of upstream's "master", This could happen, for example, if
+you:
+
+ - use `git reset --hard` to remove already-published commits, or
+ - use `git commit --amend` to replace already-published commits
+ (as in <<fixing-a-mistake-by-editing-history>>), or
+ - use `git rebase` to rebase any already-published commits (as
+ in <<using-git-rebase>>).
+
+If you are sure you want to replace the branch in the public repository
+by your branch, you may force git-push to perform the update anyway by
+preceding the branch name with a plus sign:
+
+-------------------------------------------------
+$ git push ssh://yourserver.com/~you/proj.git +master
+-------------------------------------------------
+
+Normally whenever a branch head in a public repository is modified, it
+is modified to point to a descendent of the commit that it pointed to
+before. By forcing a push in this situation, you break that convention.
+(See <<problems-with-rewriting-history>>).
+
+Nevertheless, this is a common practice for people that need a simple
+way to publish a work-in-progress patch series, and it is an acceptable
+compromise as long as you warn other developers that this is how you
+intend to manage the branch.
+
+It's also possible for a push to fail in this way when other people have
+the right to push to the same repository. In that case, the correct
+solution is to update your work by either a pull or a fetch followed by
+a rebase; see the <<setting-up-a-shared-repository,next section>> for
+more.
+
[[setting-up-a-shared-repository]]
Setting up a shared repository
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
^ permalink raw reply related
* Re: [PATCH 3/2] core.whitespace: documentation updates.
From: J. Bruce Fields @ 2007-11-25 21:58 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7v3auvqq09.fsf@gitster.siamese.dyndns.org>
On Sat, Nov 24, 2007 at 01:42:46PM -0800, Junio C Hamano wrote:
> "J. Bruce Fields" <bfields@fieldses.org> writes:
>
> > I'd still prefer this to be a gitattributes thing rather than a config
> > variable[1]. Last time I raised this you said something to the effect
> > of "I think you're right, let's fix that before it's merged." Would you
> > like me to work on that?
>
> Ah, I forgot about that, and you are right. Go wild.
OK, I will go wild, but... very slowly.
--b.
^ permalink raw reply
* Re: [RFC] best way to show diff of commit
From: Pierre Habouzit @ 2007-11-25 22:09 UTC (permalink / raw)
To: J. Bruce Fields; +Cc: Git ML
In-Reply-To: <20071125212748.GB23820@fieldses.org>
[-- Attachment #1: Type: text/plain, Size: 1735 bytes --]
On Sun, Nov 25, 2007 at 09:27:48PM +0000, J. Bruce Fields wrote:
> On Sun, Nov 25, 2007 at 10:18:31PM +0100, Pierre Habouzit wrote:
> > There is specific script I run in my vim with git, that tries to show
> > from the 'status' git commit shows in the buffer which list of files has
> > changed, and builds a diff from it quite clumsily[0].
> >
> > I wonder how hard it would be for git commit to "stash" the current
> > commit being sent somewhere editors would be able to have a look at (an
> > alternate index is probably fine). Note that maybe I'm stupid and
> > overlooked that such a thing already exists. I'd like to have it in two
> > flavors: normal and amend mode. normal mode would show what the
> > resulting commit diff looks like, and the amend mode only shows the
> > incrementall diff the amend adds to the previous commit.
> >
> > My question is: what do you think is the best way to do that, and
> > where ?
>
> Have you checked whether "git-commit -v" already does what you want?
Hmm it doesn't because I would have to call git commit -v each time I
commit and well I _like_ having the status better. And moreover I want
the diff to go in a separated buffer too.
I'd rather have some kind of way to have git-commit let a trail of
what he's doing if the user validate the commit, and use that from the
editor. I'm sure it would be useful to simple GUIs as well (most
advanced GUIs would like to have fine control to what gets commited and
are likely to have the command like used quite right).
--
·O· Pierre Habouzit
··O madcoder@debian.org
OOO http://www.madism.org
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: If you would write git from scratch now, what would you change?
From: Pierre Habouzit @ 2007-11-25 22:23 UTC (permalink / raw)
To: Jakub Narebski; +Cc: git
In-Reply-To: <200711252248.27904.jnareb@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1893 bytes --]
On Sun, Nov 25, 2007 at 09:48:27PM +0000, Jakub Narebski wrote:
> If you would write git from scratch now, from the beginning, without
> concerns for backwards compatibility, what would you change, or what
> would you want to have changed?
* reset/checkout/revert. The commands to wonderful things, but this UI
is a mess for the newcomer.
* pull/fetch/push: I would have had pull being what fetch is, and
added some --merge option to actually "do the obvious merge". But
pull encourage "bad" behavior from the user, and confuses newcomers
a lot.
* I would have hidden plumbing more, using a really distinguished
namespace (stupid example, there are probably better ways, but we
could have git-_rev-parse or git-plumbing-rev-parse instead of
git-rev-parse) so that it's clear to the user that those are really
internal commands, and that he doesn't need to understand them.
This is a big issue with git: the list of commands of git is the top
of the iceberg from the UI point of view. People _feel_ they are
comfortable with a tool if they get say 75% of the UI. I don't say
it's true that understanding 75% of the UI makes you a $tool expert,
but it's how people feel it. With git, 75% of the commands (and
don't get me started with the options ;P) is a _lot_. bzr is way
better at that game: there are at least as many commands, but those
are completely hidden to the user.
Of course having our guts easy to grok and find is a big advantage
for the git gurus. But for the newcomer it's a disconcerting.
There is probably more things I'd change, but those were the first UI
rumblings from me :)
--
·O· Pierre Habouzit
··O madcoder@debian.org
OOO http://www.madism.org
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: [RFC] best way to show diff of commit
From: Junio C Hamano @ 2007-11-25 22:27 UTC (permalink / raw)
To: Pierre Habouzit; +Cc: J. Bruce Fields, Git ML
In-Reply-To: <20071125220902.GB21121@artemis.corp>
Pierre Habouzit <madcoder@debian.org> writes:
> Hmm it doesn't because I would have to call git commit -v each time I
> commit and well I _like_ having the status better. And moreover I want
> the diff to go in a separated buffer too.
I've never felt it a problem while editing the log message in Emacs.
Don't enhanced vi implementations let you split the same buffer into
two allowing you to view different portions of it these days?
^ permalink raw reply
* [PATCH] Use is_absolute_path() in diff-lib.c, lockfile.c, setup.c, trace.c
From: Steffen Prohaska @ 2007-11-25 22:29 UTC (permalink / raw)
To: git; +Cc: Steffen Prohaska
Using the helper function to test for absolute paths makes porting easier.
Signed-off-by: Steffen Prohaska <prohaska@zib.de>
---
diff-lib.c | 2 +-
lockfile.c | 2 +-
setup.c | 2 +-
trace.c | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
BTW, what happend to the msysgit related patches:
[PATCH 1/3] sha1_file.c: Fix size_t related printf format warnings
[PATCH 2/3] builtin-init-db: use get_git_dir() instead of getenv()
I never received comments about them, nor do I find them on pu.
Steffen
diff --git a/diff-lib.c b/diff-lib.c
index f8e936a..d85d8f3 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -231,7 +231,7 @@ static int handle_diff_files_args(struct rev_info *revs,
static int is_outside_repo(const char *path, int nongit, const char *prefix)
{
int i;
- if (nongit || !strcmp(path, "-") || path[0] == '/')
+ if (nongit || !strcmp(path, "-") || is_absolute_path(path))
return 1;
if (prefixcmp(path, "../"))
return 0;
diff --git a/lockfile.c b/lockfile.c
index 258fb3f..f45d3ed 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -94,7 +94,7 @@ static char *resolve_symlink(char *p, size_t s)
return p;
}
- if (link[0] == '/') {
+ if (is_absolute_path(link)) {
/* absolute path simply replaces p */
if (link_len < s)
strcpy(p, link);
diff --git a/setup.c b/setup.c
index 43cd3f9..2c7b5cb 100644
--- a/setup.c
+++ b/setup.c
@@ -59,7 +59,7 @@ const char *prefix_path(const char *prefix, int len, const char *path)
const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
{
static char path[PATH_MAX];
- if (!pfx || !*pfx || arg[0] == '/')
+ if (!pfx || !*pfx || is_absolute_path(arg))
return arg;
memcpy(path, pfx, pfx_len);
strcpy(path + pfx_len, arg);
diff --git a/trace.c b/trace.c
index 0d89dbe..d3d1b6d 100644
--- a/trace.c
+++ b/trace.c
@@ -37,7 +37,7 @@ static int get_trace_fd(int *need_close)
return STDERR_FILENO;
if (strlen(trace) == 1 && isdigit(*trace))
return atoi(trace);
- if (*trace == '/') {
+ if (is_absolute_path(trace)) {
int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666);
if (fd == -1) {
fprintf(stderr,
--
1.5.3.6.878.g8c9e2
^ permalink raw reply related
* Re: [Resend Trivial PATCH] For the sake of correctness, fix file descriptor leak.
From: Junio C Hamano @ 2007-11-25 22:29 UTC (permalink / raw)
To: André Goddard Rosa; +Cc: Git Mailing List
In-Reply-To: <b8bf37780711251337q41e02304q8fd2654b1e83201@mail.gmail.com>
The codepath you are touching will exit immediately after an error
return; I do not feel much urgency to it, although I do not think the
patch would _hurt_.
Please follow the established convention for patch submission (see
patches on the list from other people as examples).
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox