Here's a first cut at git source support. It handles neither tags nor
conflicts.
New patches:
[git source support
Brendan Cully <[EMAIL PROTECTED]>**20051117184454
This adds support for git sources. It does not provide tags or detect
conflicts in the working directory.
] {
hunk ./README 45
-.. [#] ArX, Codeville and Git systems may be used only as the `target`
+.. [#] ArX and Codeville systems may be used only as the `target`
hunk ./README 269
-c) ArX, Baazar-NG, Codeville and Git are (currently) only
- supported as *target*; Bazaar and Tla only as *source*.
+c) ArX and Codeville are (currently) only supported as *target*;
+ Bazaar and Tla only as *source*.
hunk ./vcpx/git.py 5
+# Brendan Cully <[EMAIL PROTECTED]>
hunk ./vcpx/git.py 16
-from target import SyncronizableTargetWorkingDir, TargetInitializationFailure
+from source import UpdatableSourceWorkingDir, GetUpstreamChangesetsFailure
hunk ./vcpx/git.py 18
+from target import SyncronizableTargetWorkingDir, TargetInitializationFailure
+
+class GitWorkingDir(UpdatableSourceWorkingDir, SyncronizableTargetWorkingDir):
+ ## UpdatableSourceWorkingDir
+ def _checkoutUpstreamRevision(self, revision):
+ """ git clone """
+ from os import rename, rmdir
+ from os.path import join
+
+ # Right now we clone the entire repository and just check out to the
+ # current rev because it makes revision parsing easier. We can't
+ # easily check out arbitrary revisions anyway, but we could probably
+ # handle HEAD (master) as a special case...
+ # git clone won't checkout into an existing directory
+ target = join(self.basedir, '.gittmp')
+ # might want -s if we can determine that the path is local. Then again,
+ # that makes it a little unsafe to do git write actions here
+ self._tryCommand(['clone', '-n', self.repository.repository, target],
+ ChangesetApplicationFailure, False)
+
+ rename(join(target, '.git'), join(self.basedir, '.git'))
+ rmdir(target)
+
+ rev = self._getRev(revision)
+ if rev != revision:
+ self.log.info('Checking out revision %s (%s)' % (rev, revision))
+ else:
+ self.log.info('Checking out revision ' + rev)
+ self._tryCommand(['reset', '--hard', rev],
ChangesetApplicationFailure, False)
+
+ return self._changesetForRevision(rev)
+
+ def _getUpstreamChangesets(self, since):
+ self._tryCommand(['fetch'], GetUpstreamChangesetsFailure, False)
+
+ revs = self._tryCommand(['rev-list', '^' + since, 'origin'],
+ GetUpstreamChangesetsFailure)[:-1]
+ revs.reverse()
+ for rev in revs:
+ self.log.info('Updating to revision ' + rev)
+ yield self._changesetForRevision(rev)
+
+ def _applyChangeset(self, changeset):
+ from changes import ChangesetEntry
+ from os import remove
+ from os.path import join
+
+ self._tryCommand(['read-tree', '-m', changeset.revision],
+ ChangesetApplicationFailure, False)
+ # Delete removed files by hand
+ for entry in [entry for entry in changeset.entries
+ if entry.action_kind == ChangesetEntry.DELETED]:
+ remove(join(self.basedir, entry.name))
+
+ self._tryCommand(['checkout-index', '-f', '-u', '-a'],
+ ChangesetApplicationFailure, False)
+ # Somewhat cosmetic: point HEAD to current revision. Nothing should
+ # really rely on this, but if something goes wrong this will give
+ # an indication of how far along tailor got...
+ head = file(join(join(self.basedir, '.git'), 'HEAD'), 'w')
+ head.write(changeset.revision + '\n')
+ head.close()
+
+ # Does not handle conflicts
+ return None
+
+ def _changesetForRevision(self, revision):
+ from changes import Changeset, ChangesetEntry
+ from datetime import datetime
+
+ action_map = {'A': ChangesetEntry.ADDED, 'D': ChangesetEntry.DELETED,
+ 'M': ChangesetEntry.UPDATED, 'R': ChangesetEntry.RENAMED}
+
+ # find parent
+ lines = self._tryCommand(['rev-list', '--pretty=raw', '--max-count=1',
revision],
+ GetUpstreamChangesetsFailure)
+ parents = []
+ user = Changeset.ANONYMOUS_USER
+ loglines = []
+ date = None
+ for line in lines:
+ if line.startswith('parent'):
+ parents.append(line.split(' ').pop())
+ if line.startswith('author'):
+ author_fields = line.split(' ')[1:]
+ tz = int(author_fields.pop())
+ dt = int(author_fields.pop())
+ user = ' '.join(author_fields)
+ tzsecs = abs(tz)
+ tzsecs = (tz / 100 * 60 + tz % 100) * 60
+ if tz < 0:
+ tzsecs = -tzsecs
+ date = datetime.utcfromtimestamp(dt + tzsecs)
+ if line.startswith(' '):
+ loglines.append(line.lstrip(' '))
+
+ message = '\n'.join(loglines)
+ entries = []
+ cmd = ['diff-tree', '--root', '-M', '--name-status']
+ # haven't thought about merges yet...
+ if parents:
+ cmd.append(parents[0])
+ cmd.append(revision)
+ files = self._tryCommand(cmd, GetUpstreamChangesetsFailure)[:-1]
+ if not parents:
+ # git lets us know what it's diffing against if we omit parent
+ files.pop(0)
+ for line in files:
+ fields = line.split('\t')
+ state = fields.pop(0)
+ name = fields.pop()
+ e = ChangesetEntry(name)
+ e.action_kind = action_map[state[0]]
+ if e.action_kind == ChangesetEntry.RENAMED:
+ e.old_name = fields.pop()
+
+ entries.append(e)
+
+ return Changeset(revision, date, user, message, entries)
+
+ def _getRev(self, revision):
+ """ Return the git object corresponding to the symbolic revision """
+ if revision == 'INITIAL':
+ return self._tryCommand(['rev-list', 'HEAD'],
GetUpstreamChangesetsFailure)[-2]
+
+ return self._tryCommand('rev-parse', '--verify', revision,
GetUpstreamChangesetsFailure)[0]
hunk ./vcpx/git.py 145
-class GitWorkingDir(SyncronizableTargetWorkingDir):
+ def _tryCommand(self, cmd, exception=Exception, pipe=True):
+ c = ExternalCommand(command = self.repository.command(*cmd), cwd =
self.basedir)
+ if pipe:
+ output = c.execute(stdout=PIPE)[0]
+ else:
+ c.execute()
+ if c.exit_status:
+ raise exception(str(c) + ' failed')
+ if pipe:
+ return output.read().split('\n')
}
Context:
[Use UTC0 not UTC to force a neutral timezone
[EMAIL PROTECTED]
[Add tailor-created files to Monotone's list of ignored files
"Neil Conway <[EMAIL PROTECTED]>"**20051116194227
This could be done by installing a new ignore_file Lua hook, or adding
entries to the .mt-ignore file (which is read by the default ignore_file
hook). I chose the latter, as it is simpler. It breaks if you install
a custom ignore_file hook that doesn't read .mt-ignore, but if you do
that then presumably you know what you're doing...
]
[give -b flag for cvs trunk
[EMAIL PROTECTED]
[Create the info dir that contains the ignore file if it does not exist
[EMAIL PROTECTED]
[Don't apply duplicate tags to mercurial targets
Brendan Cully <[EMAIL PROTECTED]>**20051116052626
I just noticed that a CVS source will replay all of the tags that apply
to HEAD on every run. This teaches mercurial to filter out equivalent tags
(by checking whether they already exist in the commit history up to the last
non-tag commit).
]
[Add tag support to mercurial target
Brendan Cully <[EMAIL PROTECTED]>**20051115100534]
[Add tag support to git target
Brendan Cully <[EMAIL PROTECTED]>**20051115075709]
[Sanitize CVS tag names
Brendan Cully <[EMAIL PROTECTED]>**20051115073722
CVS tag names are more restrictive than those of other repositories. This patch
downcodes them where necessary.
]
[Fix mercurial source deletion detection
Brendan Cully <[EMAIL PROTECTED]>**20051115070747
The original mercurial delete logic would find any unchanged file in a
changeset to be deleted. This horrible bug was mitigated by the fact that
I had forgotten to actually add DELETED changeset entries to the changeset.
]
[Pick up tags from mercurial sources
Brendan Cully <[EMAIL PROTECTED]>**20051115054848
Tailor appears to only be able to tag the current revision, so this patch
silently drops any retroactive tags. It might be worth expanding the tag
object with the notion of author, tag date and files tagged (the first two
are readily available but not passed on in _tag()).
]
[Fix bzr incremental update
Brendan Cully <[EMAIL PROTECTED]>**20051114060007
Incremental update in bzr isn't working: when tailor restarts it has no
handle on the bzr branch object, and when it does a merge it doesn't
make sure that it already has the changesets it needs. This patch
introduces a getRepo method that gets a branch handle if there isn't one,
and calls bzrlib.fetch.greedy_fetch to pull in all changesets before
attempting to merge them.
]
[Fix a last-second BPB converting revision names.
Brendan Cully <[EMAIL PROTECTED]>**20051114030337]
[Mercurial source support via hglib
Brendan Cully <[EMAIL PROTECTED]>**20051114022602]
[git commit fixes (date, # at end of commit message)
Brendan Cully <[EMAIL PROTECTED]>**20051114022122
As of at least git 0.99.9e, git refuses to parse timestamps with
microseconds such as str(datetime) may emit. This patch uses strftime
to force the format to something simpler.
It also manually duplicates GIT_AUTHOR_* to GIT_COMMITTER_* so that
cg-log produces output most similar to the source log.
git-commit expects the commit message to end with a newline, so this
patch adds one if it isn't already there.
]
[Ensure str() doesn't get handed a unicode changeset
Brendan Cully <[EMAIL PROTECTED]>**20051114021851
I don't do a lot of python, so I may be misdiagnosing this bug, but I
get a UnicodeEncodeError at Tailorizer.applyable on the debug line
when tailorizing a source with non-ascii log messages on a UTF-8
terminal. It appears that str() expects a __str__ function it calls to
return a plain (not unicode) string. Could possibly be an OS X issue.
The particular test was tailorizing the bzr repository from bzr to git
or hglib, with changeset messages apparently in macroman, on a UTF-8
terminal. This problem is made more annoying by the fact that bzr no
longer seems to be able to resume (_b undefined in the upgrade path).
]
[Ignore the backup copy of the state file
[EMAIL PROTECTED]
[Fix cut&paste error
[EMAIL PROTECTED]
[TAG Version 0.9.19 (retag)
[EMAIL PROTECTED]
Patch bundle hash:
1780574a0c5904907df21872170ca59bf7441391
_______________________________________________
Tailor mailing list
[email protected]
http://lists.zooko.com/mailman/listinfo/tailor