summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Thiel <byronimo@gmail.com>2010-11-24 23:55:51 +0100
committerSebastian Thiel <byronimo@gmail.com>2010-11-24 23:55:51 +0100
commit2c12fef1b1971ba7a50e7e5c497caf51e0f68479 (patch)
treec9179ff275c259585468985b862dd172fb4d8958
parentcf1d5bd4208514bab3e6ee523a70dff8176c8c80 (diff)
downloadgitpython-2c12fef1b1971ba7a50e7e5c497caf51e0f68479.tar.gz
Submodule: Added dry_run and progress parameter to the update method. It is copatible to the RemoteProgress and should satisfy all progress needs. Dryrun will be useful in conjunction with the progress to verify the changes to be done
-rw-r--r--doc/source/changes.rst8
-rw-r--r--objects/submodule/base.py21
-rw-r--r--objects/submodule/root.py20
-rw-r--r--remote.py121
-rw-r--r--util.py123
5 files changed, 158 insertions, 135 deletions
diff --git a/doc/source/changes.rst b/doc/source/changes.rst
index d820c8ca..74692955 100644
--- a/doc/source/changes.rst
+++ b/doc/source/changes.rst
@@ -21,15 +21,11 @@ Changelog
* ``create(...)`` method now supports the reflog, but will not raise ``GitCommandError`` anymore as it is a pure python implementation now. Instead, it raises ``OSError``.
- * **Intrusive Changes** to ``Actor`` type
-
- * the *name* field is now using unicode if ascii does not match
-
* **Intrusive Changes** to ``Repo`` type
- * ``create_head(...)`` method does not support **kwargs anymore, instead it supports a logmsg parameter
+ * ``create_head(...)`` method does not support kwargs anymore, instead it supports a logmsg parameter
-* Repo.rev_parse now supports the [ref]@{n} syntax, where n is the number of steps to look into the reference's past
+* Repo.rev_parse now supports the [ref]@{n} syntax, where *n* is the number of steps to look into the reference's past
* **BugFixes**
diff --git a/objects/submodule/base.py b/objects/submodule/base.py
index 36b48d78..85350e66 100644
--- a/objects/submodule/base.py
+++ b/objects/submodule/base.py
@@ -12,7 +12,8 @@ from StringIO import StringIO # need a dict to set bloody .name field
from git.util import (
Iterable,
join_path_native,
- to_native_path_linux
+ to_native_path_linux,
+ RemoteProgress
)
from git.config import SectionConstraint
@@ -20,6 +21,7 @@ from git.exc import (
InvalidGitRepositoryError,
NoSuchPathError
)
+
import stat
import git
@@ -29,7 +31,16 @@ import time
import shutil
-__all__ = ["Submodule"]
+__all__ = ["Submodule", "UpdateProgress"]
+
+
+class UpdateProgress(RemoteProgress):
+ """Class providing detailed progress information to the caller who should
+ derive from it and implement the ``update(...)`` message"""
+ ADD, REMOVE, UPDATE = [1 << x for x in range(RemoteProgress._num_op_codes, RemoteProgress._num_op_codes+3)]
+
+ __slots__ = tuple()
+
# IndexObject comes via util module, its a 'hacky' fix thanks to pythons import
@@ -285,7 +296,7 @@ class Submodule(util.IndexObject, Iterable, Traversable):
return sm
- def update(self, recursive=False, init=True, to_latest_revision=False):
+ def update(self, recursive=False, init=True, to_latest_revision=False, progress=None):
"""Update the repository of this submodule to point to the checkout
we point at with the binsha of this instance.
@@ -297,6 +308,7 @@ class Submodule(util.IndexObject, Iterable, Traversable):
This only works if we have a local tracking branch, which is the case
if the remote repository had a master branch, or of the 'branch' option
was specified for this submodule and the branch existed remotely
+ :param progress: UpdateProgress instance or None of no progress should be shown
:note: does nothing in bare repositories
:note: method is definitely not atomic if recurisve is True
:return: self"""
@@ -304,6 +316,9 @@ class Submodule(util.IndexObject, Iterable, Traversable):
return self
#END pass in bare mode
+ if progress is None:
+ progress = UpdateProgress()
+ #END handle progress
# ASSURE REPO IS PRESENT AND UPTODATE
#####################################
diff --git a/objects/submodule/root.py b/objects/submodule/root.py
index 753c6df4..b0dba08b 100644
--- a/objects/submodule/root.py
+++ b/objects/submodule/root.py
@@ -1,4 +1,4 @@
-from base import Submodule
+from base import Submodule, UpdateProgress
from util import (
find_first_remote_branch
)
@@ -9,7 +9,7 @@ import sys
__all__ = ["RootModule"]
-
+
class RootModule(Submodule):
"""A (virtual) Root of all submodules in the given repository. It can be used
to more easily traverse all submodules of the master repository"""
@@ -38,7 +38,8 @@ class RootModule(Submodule):
#{ Interface
- def update(self, previous_commit=None, recursive=True, force_remove=False, init=True, to_latest_revision=False):
+ def update(self, previous_commit=None, recursive=True, force_remove=False, init=True,
+ to_latest_revision=False, progress=None, dry_run=False):
"""Update the submodules of this repository to the current HEAD commit.
This method behaves smartly by determining changes of the path of a submodules
repository, next to changes to the to-be-checked-out commit or the branch to be
@@ -57,11 +58,18 @@ class RootModule(Submodule):
:param init: If we encounter a new module which would need to be initialized, then do it.
:param to_latest_revision: If True, instead of checking out the revision pointed to
by this submodule's sha, the checked out tracking branch will be merged with the
- newest remote branch fetched from the repository's origin"""
+ newest remote branch fetched from the repository's origin
+ :param progress: UpdateProgress instance or None if no progress should be sent
+ :param dry_run: if True, operations will not actually be performed. Progress messages
+ will change accordingly to indicate the WOULD DO state of the operation."""
if self.repo.bare:
raise InvalidGitRepositoryError("Cannot update submodules in bare repositories")
# END handle bare
+ if progress is None:
+ progress = UpdateProgress()
+ #END assure progress is set
+
repo = self.repo
# HANDLE COMMITS
@@ -125,7 +133,7 @@ class RootModule(Submodule):
assert nn not in [r.name for r in rmts]
smr = smm.create_remote(nn, sm.url)
- smr.fetch()
+ smr.fetch(progress=progress)
# If we have a tracking branch, it should be available
# in the new remote as well.
@@ -234,7 +242,7 @@ class RootModule(Submodule):
######################################
for sm in sms:
# update the submodule using the default method
- sm.update(recursive=False, init=init, to_latest_revision=to_latest_revision)
+ sm.update(recursive=False, init=init, to_latest_revision=to_latest_revision, progress=progress)
# update recursively depth first - question is which inconsitent
# state will be better in case it fails somewhere. Defective branch
diff --git a/remote.py b/remote.py
index 2e596ca1..69a8126b 100644
--- a/remote.py
+++ b/remote.py
@@ -7,14 +7,14 @@
# Module implementing a remote object allowing easy access to git remotes
from exc import GitCommandError
-from objects import Commit
from ConfigParser import NoOptionError
from config import SectionConstraint
from git.util import (
LazyMixin,
Iterable,
- IterableList
+ IterableList,
+ RemoteProgress
)
from refs import (
@@ -33,123 +33,6 @@ import sys
__all__ = ('RemoteProgress', 'PushInfo', 'FetchInfo', 'Remote')
-class RemoteProgress(object):
- """
- Handler providing an interface to parse progress information emitted by git-push
- and git-fetch and to dispatch callbacks allowing subclasses to react to the progress.
- """
- BEGIN, END, COUNTING, COMPRESSING, WRITING = [ 1 << x for x in range(5) ]
- STAGE_MASK = BEGIN|END
- OP_MASK = COUNTING|COMPRESSING|WRITING
-
- __slots__ = ("_cur_line", "_seen_ops")
- re_op_absolute = re.compile("(remote: )?([\w\s]+):\s+()(\d+)()(.*)")
- re_op_relative = re.compile("(remote: )?([\w\s]+):\s+(\d+)% \((\d+)/(\d+)\)(.*)")
-
- def __init__(self):
- self._seen_ops = list()
-
- def _parse_progress_line(self, line):
- """Parse progress information from the given line as retrieved by git-push
- or git-fetch
-
- :return: list(line, ...) list of lines that could not be processed"""
- # handle
- # Counting objects: 4, done.
- # Compressing objects: 50% (1/2) \rCompressing objects: 100% (2/2) \rCompressing objects: 100% (2/2), done.
- self._cur_line = line
- sub_lines = line.split('\r')
- failed_lines = list()
- for sline in sub_lines:
- # find esacpe characters and cut them away - regex will not work with
- # them as they are non-ascii. As git might expect a tty, it will send them
- last_valid_index = None
- for i,c in enumerate(reversed(sline)):
- if ord(c) < 32:
- # its a slice index
- last_valid_index = -i-1
- # END character was non-ascii
- # END for each character in sline
- if last_valid_index is not None:
- sline = sline[:last_valid_index]
- # END cut away invalid part
- sline = sline.rstrip()
-
- cur_count, max_count = None, None
- match = self.re_op_relative.match(sline)
- if match is None:
- match = self.re_op_absolute.match(sline)
-
- if not match:
- self.line_dropped(sline)
- failed_lines.append(sline)
- continue
- # END could not get match
-
- op_code = 0
- remote, op_name, percent, cur_count, max_count, message = match.groups()
-
- # get operation id
- if op_name == "Counting objects":
- op_code |= self.COUNTING
- elif op_name == "Compressing objects":
- op_code |= self.COMPRESSING
- elif op_name == "Writing objects":
- op_code |= self.WRITING
- else:
- raise ValueError("Operation name %r unknown" % op_name)
-
- # figure out stage
- if op_code not in self._seen_ops:
- self._seen_ops.append(op_code)
- op_code |= self.BEGIN
- # END begin opcode
-
- if message is None:
- message = ''
- # END message handling
-
- message = message.strip()
- done_token = ', done.'
- if message.endswith(done_token):
- op_code |= self.END
- message = message[:-len(done_token)]
- # END end message handling
-
- self.update(op_code, cur_count, max_count, message)
- # END for each sub line
- return failed_lines
-
- def line_dropped(self, line):
- """Called whenever a line could not be understood and was therefore dropped."""
- pass
-
- def update(self, op_code, cur_count, max_count=None, message=''):
- """Called whenever the progress changes
-
- :param op_code:
- Integer allowing to be compared against Operation IDs and stage IDs.
-
- Stage IDs are BEGIN and END. BEGIN will only be set once for each Operation
- ID as well as END. It may be that BEGIN and END are set at once in case only
- one progress message was emitted due to the speed of the operation.
- Between BEGIN and END, none of these flags will be set
-
- Operation IDs are all held within the OP_MASK. Only one Operation ID will
- be active per call.
- :param cur_count: Current absolute count of items
-
- :param max_count:
- The maximum count of items we expect. It may be None in case there is
- no maximum number of items or if it is (yet) unknown.
-
- :param message:
- In case of the 'WRITING' operation, it contains the amount of bytes
- transferred. It may possibly be used for other purposes as well.
-
- You may read the contents of the current line in self._cur_line"""
- pass
-
class PushInfo(object):
"""
diff --git a/util.py b/util.py
index 42719233..8c0b6697 100644
--- a/util.py
+++ b/util.py
@@ -22,7 +22,8 @@ from gitdb.util import (
__all__ = ( "stream_copy", "join_path", "to_native_path_windows", "to_native_path_linux",
"join_path_native", "Stats", "IndexFileSHA1Writer", "Iterable", "IterableList",
- "BlockingLockFile", "LockFile", 'Actor', 'get_user_id', 'assure_directory_exists')
+ "BlockingLockFile", "LockFile", 'Actor', 'get_user_id', 'assure_directory_exists',
+ 'RemoteProgress')
#{ Utility Methods
@@ -77,6 +78,7 @@ def join_path_native(a, *p):
def assure_directory_exists(path, is_file=False):
"""Assure that the directory pointed to by path exists.
+
:param is_file: If True, path is assumed to be a file and handled correctly.
Otherwise it must be a directory
:return: True if the directory was created, False if it already existed"""
@@ -103,6 +105,125 @@ def get_user_id():
#{ Classes
+class RemoteProgress(object):
+ """
+ Handler providing an interface to parse progress information emitted by git-push
+ and git-fetch and to dispatch callbacks allowing subclasses to react to the progress.
+ """
+ _num_op_codes = 5
+ BEGIN, END, COUNTING, COMPRESSING, WRITING = [1 << x for x in range(_num_op_codes)]
+ STAGE_MASK = BEGIN|END
+ OP_MASK = ~STAGE_MASK
+
+ __slots__ = ("_cur_line", "_seen_ops")
+ re_op_absolute = re.compile("(remote: )?([\w\s]+):\s+()(\d+)()(.*)")
+ re_op_relative = re.compile("(remote: )?([\w\s]+):\s+(\d+)% \((\d+)/(\d+)\)(.*)")
+
+ def __init__(self):
+ self._seen_ops = list()
+
+ def _parse_progress_line(self, line):
+ """Parse progress information from the given line as retrieved by git-push
+ or git-fetch
+
+ :return: list(line, ...) list of lines that could not be processed"""
+ # handle
+ # Counting objects: 4, done.
+ # Compressing objects: 50% (1/2) \rCompressing objects: 100% (2/2) \rCompressing objects: 100% (2/2), done.
+ self._cur_line = line
+ sub_lines = line.split('\r')
+ failed_lines = list()
+ for sline in sub_lines:
+ # find esacpe characters and cut them away - regex will not work with
+ # them as they are non-ascii. As git might expect a tty, it will send them
+ last_valid_index = None
+ for i,c in enumerate(reversed(sline)):
+ if ord(c) < 32:
+ # its a slice index
+ last_valid_index = -i-1
+ # END character was non-ascii
+ # END for each character in sline
+ if last_valid_index is not None:
+ sline = sline[:last_valid_index]
+ # END cut away invalid part
+ sline = sline.rstrip()
+
+ cur_count, max_count = None, None
+ match = self.re_op_relative.match(sline)
+ if match is None:
+ match = self.re_op_absolute.match(sline)
+
+ if not match:
+ self.line_dropped(sline)
+ failed_lines.append(sline)
+ continue
+ # END could not get match
+
+ op_code = 0
+ remote, op_name, percent, cur_count, max_count, message = match.groups()
+
+ # get operation id
+ if op_name == "Counting objects":
+ op_code |= self.COUNTING
+ elif op_name == "Compressing objects":
+ op_code |= self.COMPRESSING
+ elif op_name == "Writing objects":
+ op_code |= self.WRITING
+ else:
+ raise ValueError("Operation name %r unknown" % op_name)
+
+ # figure out stage
+ if op_code not in self._seen_ops:
+ self._seen_ops.append(op_code)
+ op_code |= self.BEGIN
+ # END begin opcode
+
+ if message is None:
+ message = ''
+ # END message handling
+
+ message = message.strip()
+ done_token = ', done.'
+ if message.endswith(done_token):
+ op_code |= self.END
+ message = message[:-len(done_token)]
+ # END end message handling
+
+ self.update(op_code, cur_count, max_count, message)
+ # END for each sub line
+ return failed_lines
+
+ def line_dropped(self, line):
+ """Called whenever a line could not be understood and was therefore dropped."""
+ pass
+
+ def update(self, op_code, cur_count, max_count=None, message=''):
+ """Called whenever the progress changes
+
+ :param op_code:
+ Integer allowing to be compared against Operation IDs and stage IDs.
+
+ Stage IDs are BEGIN and END. BEGIN will only be set once for each Operation
+ ID as well as END. It may be that BEGIN and END are set at once in case only
+ one progress message was emitted due to the speed of the operation.
+ Between BEGIN and END, none of these flags will be set
+
+ Operation IDs are all held within the OP_MASK. Only one Operation ID will
+ be active per call.
+ :param cur_count: Current absolute count of items
+
+ :param max_count:
+ The maximum count of items we expect. It may be None in case there is
+ no maximum number of items or if it is (yet) unknown.
+
+ :param message:
+ In case of the 'WRITING' operation, it contains the amount of bytes
+ transferred. It may possibly be used for other purposes as well.
+
+ You may read the contents of the current line in self._cur_line"""
+ pass
+
+
class Actor(object):
"""Actors hold information about a person acting on the repository. They
can be committers and authors or anything with a name and an email as