Commits

David Wolever committed 3866fb8

Installing workon, hg-git from easy_install

  • Participants
  • Parent commits dd3fc79

Comments (0)

Files changed (50)

 .DS_Store
 .bzr
 .hg
+.git
 .pyc
 .shelf
 bazaar/locations.conf

hgrc.laptop.local

 [extensions]
 hgsubversion = 
-hgext.hg-git = ~/.mercurial/extensions/hg-git
+hggit =
 hgk =
 pbranch = ~/.mercurial/extensions/pbranch.py
 

mercurial/extensions/hg-git/.gitignore

-*.pyc

mercurial/extensions/hg-git/.hgignore

-syntax: glob
-
-*.pyc
-tests/*.err

mercurial/extensions/hg-git/COPYING

-		    GNU GENERAL PUBLIC LICENSE
-		       Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-			    Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-		    GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-			    NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-		     END OF TERMS AND CONDITIONS
-
-	    How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    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
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
-    Gnomovision version 69, Copyright (C) year  name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <signature of Ty Coon>, 1 April 1989
-  Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs.  If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Library General
-Public License instead of this License.

mercurial/extensions/hg-git/DESIGN.txt

-Hg-Git Plugin Design Notes
-==========================
-
-This plugin is designed to allow you to push to a Git server over the Git protocol and to pull from a Git based project.  All data is stored in Hg native format with a mapping table.  People collaborating in Git should not  even be able to tell that you're using Hg to collaborate on their project (except for the '--HG--' section added to commit message). 
-
-Nothing should need to be kept in the Git format - you should be able to run 'hg gclear' at any time to wipe out the Git directory and everything can be rebuilt losslessly from the existing Hg data - it is a cache only.
-
-We are using the Dulwich library, which I've modified quite a bit - I'll have to get these changes back upstream at some point.
-
-I've been adding 'TODO' comments all over the place where I only partially implemented something so I could get the entire first draft of functionality completed.  The TODO areas should be mostly edge cases (executable bits, file rename info, tags, submodules, etc).
-
-Lossless Two Way
-================
-
-We need to store data that Git records that Merc does not in a git/extra_data file.  This would be parents over two and committer information (author will be mapped to Hg committer).  This way two Hg developers can collaborate without the Git transport messing up the local commits.
-
-Each Git commit should be reproducible as a Merc ID and vice versa on any system without losing data (ie changing the SHA).
-
-Branch Translation Policy
-=========================
-
-Branches in Hg and Git are pretty different.  This is meant to provide a clear policy on how one is converted to the other.
-
-* Without Bookmarks: *
-
-If you don't have bookmarks enabled, Git simply maps your 'tip' to the 'master' branch of the repository you're pushing to, since that is the most commonly used default branch name.  Actually, pulling will map whatever the server points to as HEAD, but when pushing it will assume 'master' is your tip.
-
-	$ hg gpush origin        # will push tip to remote 'master'
-	$ hg gpush origin master # same as above
-	$ hg gpush origin --all  # same as above
-
-If the remote server has divergent branches (branches with commits not reachable from HEAD) it will basically ignore them, not convert them into Hg changesets.  It will tell you this (and why) when fetched.
-
-Conversely, on pushing, Hg named branches are ignored if they are not reachable from traversing the parents of tip. (SC: is this best?)
-
-* With Bookmarks: *
-
-If you have bookmarks enabled, it will treat your bookmarks like Git branches and will only push up references if you specify them.
-
-	hg gpush origin        # will error, you have to specify a branch
-	hg gpush origin master # pushes the master branch
-	hg gpush origin --all  # pushes all local branches
-
-If a fetch gets branches, it _will_ import them and will create bookmarks that point to them, even if they have no common ancestors with the mainline (HEAD).
-
-* Other points *
-
-If you do not specify a remote name, it will assume 'origin'.  This is helpful if you do not have bookmarks enabled as it will push tip automatically.  If you have bookmarks enabled this is not helpful because you have to specify a branch name after.
-
-Eventually, I would like to setup tracking branch refspecs much like Git - say 'this local branch pushes and pulls to/from this remote and branch', but that will be one of the last things done.
-
-Testing Hg-Git
-==============
-
-Tests are implemented in the Mercurial-standard way of small shell scripts.
-The scripts are located in the tests directory, and to run them you should
-change to that directory and then run tests/run-tests.py from the Mercurial
-sources. For example, if you have a copy of the Mercurial source code at
-/Stuff/hg-crew, you would do something like this:
-
-cd tests ; /Stuff/hg-crew/tests/run-tests.py
-
-And you should see output like this:
-.
-# Ran 1 tests, 0 skipped, 0 failed.
-
-Note that due to limitations of Dulwich at the moment, the tests need to
-start a git-daemon process. Right now, they use the default port, so will
-skip if they detect an already-running git-daemon.

mercurial/extensions/hg-git/Makefile

-PYTHON=python
-
-help:
-	@echo 'Commonly used make targets:'
-	@echo '  tests        - run all tests in the automatic test suite'
-
-all: tests
-
-tests:
-	cd tests && $(PYTHON) run-tests.py --with-hg=`which hg` $(TESTFLAGS)
-
-test-%:
-	cd tests && $(PYTHON) run-tests.py --with-hg=`which hg` $(TESTFLAGS) $@
-.PHONY: help all local build doc clean install install-bin install-doc \
-	install-home install-home-bin install-home-doc dist dist-notests tests \
-	update-pot

mercurial/extensions/hg-git/README.md

-*Warning: This plugin is not yet stabilized. Use to help me identify bugs, but it will be a few weeks before its fully stable.*
-
-*Project status as of 5/27:*  Pretty solid, but a bit slow - can losslessly convert most major scenarios and can handle projects up to several thousand commits. Submodules in Git are not handled. See TODO.txt for full list of things I'm working on.
-
-
-Hg-Git Mercurial Plugin
-=======================
-
-This is the Hg-Git plugin for Mercurial, adding the ability to push and pull to/from a Git server repository from Hg.  This means you can collaborate on Git based projects from Hg, or use a Git server as a collaboration point for a team with developers using both Git and Hg.
-
-The Hg-Git plugin can convert commits/changesets losslessly from one system to another, so you can push via an Hg repository and another Hg client can pull it and their changeset node ids will be identical - Mercurial data does not get lost in translation.  It is intended that Hg users may wish to use this to collaborate even if no Git users are involved in the project, and it may even provide some advantages if you're using Bookmarks (see below).
-
-Dependencies
-============
-
-This plugin is implemented entirely in Python - there are no Git binary dependencies, you do not need to have Git installed on your system.  The only dependencies are Mercurial and Dulwich.  The plugin is known to work on Hg versions 1.1 through 1.3 and requires at least Dulwich 0.3.2.
-
-Commands
-=========
-
-You can clone a Git repository from Hg by running `hg clone [url]`.  For example, if you were to run `hg clone git://github.com/schacon/munger.git` it would clone the repository down into the directory 'munger.git', then convert it to an Hg repository for you.
-
-	hg clone git://github.com/schacon/hg-git.git
-
-If you want to clone a github repository for later pushing (or any other repository you access via ssh), you need to convert the ssh url to a format with explicit protocol prefix (mind the switch from colon to slash after the host!):
-
-	git clone git@github.com:schacon/hg-git.git
-	hg clone git+ssh://git@github.com/schacon/hg-git.git
-
-If you are starting from an existing Hg repository, you have to setup a Git repository somewhere that you have push access to, add it as default path or default-push path in your .hg/hgrc and then run `hg push` from within your project.  For example:
-
-	cd hg-git # (an Hg repository)
-	(edit .hg/hgrc and add the target git url in the paths section)
-	hg push
-
-This will convert all your Hg data into Git objects and push them up to the Git server.
-
-Now that you have an Hg repository that can push/pull to/from a Git repository, you can fetch updates with `hg pull`.
-
-	$ hg pull
-
-That will pull down any commits that have been pushed to the server in the meantime and give you a new head that you can merge in.
-
-Hg Bookmarks Integration
-========================
-
-If you have the bookmarks extension enabled, Hg-Git will use it. It will push your bookmarks up to the Git server as branches and will pull Git branches down and set them up as bookmarks.
-
-This is actually pretty cool, since you can use this extension to transfer your Hg bookmarks via the Git protocol, rather than having to scp them, as the Hg transfer protocol does not currently support transferring bookmarks.
-
-Installing
-==========
-
-Clone this repository somewhere and make the 'extensions' section in your `~/.hgrc` file look something like this:
-
-	[extensions]
-	hgext.bookmarks =
-	hgext.hg-git = [path-to]/hg-git
-
-That will enable the Hg-Git extension for you.  The bookmarks section is not compulsory, but it makes some things a bit nicer for you.
-
-Authors
-========
-
-* Scott Chacon <schacon@gmail.com> - main development
-* Augie Fackler <durin42@gmail.com> - testing and moral support
-* Sverre Rabbelier <sverre@rabbelier.nl> - gexport, mode and i18n stuff and misc fixes
-* Dulwich Developers - most of this code depends on the awesome work they did.
- 
-Sponsorship
-===========
-
-GitHub let me (Scott) work on this full time for several days, which is why this got done at all.  If you're looking for a free Git host to push your open source Hg projects to, do try us out (http://github.com).

mercurial/extensions/hg-git/TODO.txt

-GENERAL
-==========
-* work fine with eclipse plugin or tortoise-hg
-
-MAPPING ISSUES
-==============
-* work in Git on a named branch created in Hg is forward-ported to be named branch commits in Hg and stripped back out if re-exported
-
-
-REMOTE/BRANCH STUFF
-=====================
-* explain what branch mapping policy determined when updating refs
-* error nicer if pushing to remote without push access (over git://)
-
-WEBSITE
-===========
-* more usage documentation
-* screencast
-
-
-SPEED/EFFICIENCY
-================
-* dulwich improvements
-  - don't send blobs/trees already on server
-  - thin packs
-  - packfile creation benchmarking (seems to take a while sometimes)
-  - at least provide status output
-
-MAYBE
-==========
-* submodules?
-* .gitignore, etc - try to convert? 
-  - (probably not automatically, but perhaps a generator?)

mercurial/extensions/hg-git/__init__.py

-# git.py - git server bridge
-#
-# Copyright 2008 Scott Chacon <schacon at gmail dot com>
-#   also some code (and help) borrowed from durin42  
-#
-# This software may be used and distributed according to the terms
-# of the GNU General Public License, incorporated herein by reference.
-
-'''push and pull from a Git server
-
-This extension lets you communicate (push and pull) with a Git server.
-This way you can use Git hosting for your project or collaborate with a 
-project that is in Git.  A bridger of worlds, this plugin be.
-
-'''
-
-from mercurial import commands, extensions, hg, util
-from mercurial.i18n import _
-
-from dulwich.repo import Repo
-from dulwich.errors import NotGitRepository
-
-import gitrepo, hgrepo
-from git_handler import GitHandler
-
-# support for `hg clone git://github.com/defunkt/facebox.git`
-# also hg clone git+ssh://git@github.com/schacon/simplegit.git
-hg.schemes['git'] = gitrepo
-hg.schemes['git+ssh'] = gitrepo
-
-_oldlocal = hg.schemes['file']
-
-def _local(path):
-    p = util.drop_scheme('file', path)
-    try:
-        Repo(p)
-        return gitrepo
-    except NotGitRepository:
-        return _oldlocal(path)
-
-hg.schemes['file'] = _local
-
-def reposetup(ui, repo):
-    klass = hgrepo.generate_repo_subclass(repo.__class__)
-    repo.__class__ = klass
-
-def gimport(ui, repo, remote_name=None):
-    git = GitHandler(repo, ui)
-    git.import_commits(remote_name)
-
-def gexport(ui, repo):
-    git = GitHandler(repo, ui)
-    git.export_commits()
-
-def gclear(ui, repo):
-    repo.ui.status(_("clearing out the git cache data\n"))
-    git = GitHandler(repo, ui)
-    git.clear()
-
-cmdtable = {
-  "gimport":
-        (gimport, [], _('hg gimport')),
-  "gexport":
-        (gexport, [], _('hg gexport')),
-  "gclear":
-      (gclear, [], _('Clears out the Git cached data')),
-}

mercurial/extensions/hg-git/git_handler.py

-import os, sys, math, urllib, re
-import toposort
-
-from dulwich.errors import HangupException
-from dulwich.index import commit_tree
-from dulwich.objects import Blob, Commit, Tag, Tree, parse_timezone
-from dulwich.pack import create_delta, apply_delta
-from dulwich.repo import Repo
-
-from hgext import bookmarks
-from mercurial.i18n import _
-from mercurial.node import hex, bin, nullid
-from mercurial import context, util as hgutil
-
-try:
-    from mercurial.error import RepoError
-except ImportError:
-    from mercurial.repo import RepoError
-
-
-class GitHandler(object):
-
-    def __init__(self, dest_repo, ui):
-        self.repo = dest_repo
-        self.ui = ui
-        self.mapfile = 'git-mapfile'
-        self.tagsfile = 'git-tags'
-
-        if ui.config('git', 'intree'):
-            self.gitdir = self.repo.wjoin('.git')
-        else:
-            self.gitdir = self.repo.join('git')
-
-        self.paths = ui.configitems('paths')
-
-        self.init_if_missing()
-        self.load_git()
-        self.load_map()
-        self.load_tags()
-
-    # make the git data directory
-    def init_if_missing(self):
-        if not os.path.exists(self.gitdir):
-            os.mkdir(self.gitdir)
-            Repo.init_bare(self.gitdir)
-
-    def load_git(self):
-        self.git = Repo(self.gitdir)
-
-    ## FILE LOAD AND SAVE METHODS
-
-    def map_set(self, gitsha, hgsha):
-        self._map_git[gitsha] = hgsha
-        self._map_hg[hgsha] = gitsha
-
-    def map_hg_get(self, gitsha):
-        return self._map_git.get(gitsha)
-
-    def map_git_get(self, hgsha):
-        return self._map_hg.get(hgsha)
-
-    def load_map(self):
-        self._map_git = {}
-        self._map_hg = {}
-        if os.path.exists(self.repo.join(self.mapfile)):
-            for line in self.repo.opener(self.mapfile):
-                gitsha, hgsha = line.strip().split(' ', 1)
-                self._map_git[gitsha] = hgsha
-                self._map_hg[hgsha] = gitsha
-
-    def save_map(self):
-        file = self.repo.opener(self.mapfile, 'w+', atomictemp=True)
-        for hgsha, gitsha in sorted(self._map_hg.iteritems()):
-            file.write("%s %s\n" % (gitsha, hgsha))
-        file.rename()
-
-
-    def load_tags(self):
-        self.tags = {}
-        if os.path.exists(self.repo.join(self.tagsfile)):
-            for line in self.repo.opener(self.tagsfile):
-                sha, name = line.strip().split(' ', 1)
-                self.tags[name] = sha
-
-    def save_tags(self):
-        file = self.repo.opener(self.tagsfile, 'w+', atomictemp=True)
-        for name, sha in sorted(self.tags.iteritems()):
-            if not self.repo.tagtype(name) == 'global':
-                file.write("%s %s\n" % (sha, name))
-        file.rename()
-
-    ## END FILE LOAD AND SAVE METHODS
-
-    ## COMMANDS METHODS
-
-    def import_commits(self, remote_name):
-        self.import_git_objects(remote_name)
-        self.save_map()
-
-    def fetch(self, remote, heads):
-        self.export_commits()
-        refs = self.fetch_pack(remote, heads)
-        remote_name = self.remote_name(remote)
-
-        if refs:
-            self.import_git_objects(remote_name, refs)
-            self.import_tags(refs)
-            self.update_hg_bookmarks(refs)
-            if remote_name:
-                self.update_remote_branches(remote_name, refs)
-            elif not self.paths:
-                # intial cloning
-                self.update_remote_branches('default', refs)
-        else:
-            self.ui.status(_("nothing new on the server\n"))
-
-        self.save_map()
-
-    def export_commits(self):
-        try:
-            self.export_git_objects()
-            self.export_hg_tags()
-            self.update_references()
-        finally:
-            self.save_map()
-
-    def get_refs(self, remote):
-        self.export_commits()
-        client, path = self.get_transport_and_path(remote)
-        old_refs = {}
-        new_refs = {}
-        def changed(refs):
-            old_refs.update(refs)
-            to_push = set(self.local_heads().values() + self.tags.values())
-            new_refs.update(self.get_changed_refs(refs, to_push, True))
-            # don't push anything
-            return {}
-
-        try:
-            client.send_pack(path, changed, None)
-
-            changed_refs = [ref for ref, sha in new_refs.iteritems()
-                            if sha != old_refs.get(ref)]
-            new = [bin(self.map_hg_get(new_refs[ref])) for ref in changed_refs]
-            old = dict( (bin(self.map_hg_get(old_refs[r])), 1)
-                       for r in changed_refs if r in old_refs)
-
-            return old, new
-        except HangupException:
-            raise hgutil.Abort("the remote end hung up unexpectedly")
-
-    def push(self, remote, revs, force):
-        self.export_commits()
-        changed_refs = self.upload_pack(remote, revs, force)
-        remote_name = self.remote_name(remote)
-
-        if remote_name and changed_refs:
-            for ref, sha in changed_refs.iteritems():
-                self.ui.status("    "+ remote_name + "::" + ref + " => GIT:" + sha[0:8] + "\n")
-
-            self.update_remote_branches(remote_name, changed_refs)
-
-    def clear(self):
-        mapfile = self.repo.join(self.mapfile)
-        if os.path.exists(self.gitdir):
-            for root, dirs, files in os.walk(self.gitdir, topdown=False):
-                for name in files:
-                    os.remove(os.path.join(root, name))
-                for name in dirs:
-                    os.rmdir(os.path.join(root, name))
-            os.rmdir(self.gitdir)
-        if os.path.exists(mapfile):
-            os.remove(mapfile)
-
-    ## CHANGESET CONVERSION METHODS
-
-    def export_git_objects(self):
-        self.ui.status(_("importing Hg objects into Git\n"))
-        nodes = [self.repo.lookup(n) for n in self.repo]
-        export = [node for node in nodes if not hex(node) in self._map_hg]
-        total = len(export)
-        if total:
-          magnitude = int(math.log(total, 10)) + 1
-        else:
-          magnitude = 1
-        for i, rev in enumerate(export):
-            if i%100 == 0:
-                self.ui.status(_("at: %*d/%d\n") % (magnitude, i, total))
-
-            ctx = self.repo.changectx(rev)
-            state = ctx.extra().get('hg-git', None)
-            if state == 'octopus':
-                self.ui.debug("revision %d is a part of octopus explosion\n" % ctx.rev())
-                continue
-            self.export_hg_commit(rev)
-
-    # convert this commit into git objects
-    # go through the manifest, convert all blobs/trees we don't have
-    # write the commit object (with metadata info)
-    def export_hg_commit(self, rev):
-        self.ui.note(_("converting revision %s\n") % rev)
-
-        oldenc = self.swap_out_encoding()
-
-        ctx = self.repo.changectx(rev)
-        extra = ctx.extra()
-
-        commit = Commit()
-
-        (time, timezone) = ctx.date()
-        commit.author = self.get_git_author(ctx)
-        commit.author_time = int(time)
-        commit.author_timezone = -timezone
-
-        if 'committer' in extra:
-            # fixup timezone
-            (name, timestamp, timezone) = extra['committer'].rsplit(' ', 2)
-            commit.committer = name
-            commit.commit_time = timestamp
-
-            # work around a timezone format change
-            if int(timezone) % 60 != 0: #pragma: no cover
-                timezone = parse_timezone(timezone)
-            else:
-                timezone = -int(timezone)
-            commit.commit_timezone = timezone
-        else:
-            commit.committer = commit.author
-            commit.commit_time = commit.author_time
-            commit.commit_timezone = commit.author_timezone
-
-        commit.parents = []
-        for parent in self.get_git_parents(ctx):
-            hgsha = hex(parent.node())
-            git_sha = self.map_git_get(hgsha)
-            if git_sha:
-                commit.parents.append(git_sha)
-
-        commit.message = self.get_git_message(ctx)
-
-        if 'encoding' in extra:
-            commit.encoding = extra['encoding']
-
-        tree_sha = commit_tree(self.git.object_store, self.iterblobs(ctx))
-        commit.tree = tree_sha
-
-        self.git.object_store.add_object(commit)
-        self.map_set(commit.id, ctx.hex())
-
-        self.swap_out_encoding(oldenc)
-        return commit.id
-
-    def get_git_author(self, ctx):
-        # hg authors might not have emails
-        author = ctx.user()
-
-        # check for git author pattern compliance
-        regex = re.compile('^(.*?) \<(.*?)\>(.*)$')
-        a = regex.match(author)
-
-        if a:
-            name = a.group(1)
-            email = a.group(2)
-            if len(a.group(3)) > 0:
-                name += ' ext:(' + urllib.quote(a.group(3)) + ')'
-            author = name + ' <' + email + '>'
-        else:
-            author = author + ' <none@none>'
-
-        if 'author' in ctx.extra():
-            author = apply_delta(author, ctx.extra()['author'])
-
-        return author
-
-    def get_git_parents(self, ctx):
-        def is_octopus_part(ctx):
-            return ctx.extra().get('hg-git', None) in ('octopus', 'octopus-done')
-
-        parents = []
-        if ctx.extra().get('hg-git', None) == 'octopus-done':
-            # implode octopus parents
-            part = ctx
-            while is_octopus_part(part):
-                (p1, p2) = part.parents()
-                assert not is_octopus_part(p1)
-                parents.append(p1)
-                part = p2
-            parents.append(p2)
-        else:
-            parents = ctx.parents()
-
-        return parents
-
-    def get_git_message(self, ctx):
-        extra = ctx.extra()
-
-        message = ctx.description() + "\n"
-        if 'message' in extra:
-            message = apply_delta(message, extra['message'])
-
-        # HG EXTRA INFORMATION
-        add_extras = False
-        extra_message = ''
-        if not ctx.branch() == 'default':
-            add_extras = True
-            extra_message += "branch : " + ctx.branch() + "\n"
-
-        renames = []
-        for f in ctx.files():
-            if f not in ctx.manifest():
-                continue
-            rename = ctx.filectx(f).renamed()
-            if rename:
-                renames.append((rename[0], f))
-
-        if renames:
-            add_extras = True
-            for oldfile, newfile in renames:
-                extra_message += "rename : " + oldfile + " => " + newfile + "\n"
-
-        for key, value in extra.iteritems():
-            if key in ('author', 'committer', 'encoding', 'message', 'branch', 'hg-git'):
-                continue
-            else:
-                add_extras = True
-                extra_message += "extra : " + key + " : " +  urllib.quote(value) + "\n"
-
-        if add_extras:
-            message += "\n--HG--\n" + extra_message
-
-        return message
-
-    def iterblobs(self, ctx):
-        for f in ctx:
-            fctx = ctx[f]
-            blobid = self.map_git_get(hex(fctx.filenode()))
-
-            if not blobid:
-                blob = Blob.from_string(fctx.data())
-                self.git.object_store.add_object(blob)
-                self.map_set(blob.id, hex(fctx.filenode()))
-                blobid = blob.id
-
-            if 'l' in ctx.flags(f):
-                mode = 0120000
-            elif 'x' in ctx.flags(f):
-                mode = 0100755
-            else:
-                mode = 0100644
-
-            yield f, blobid, mode
-
-    def import_git_objects(self, remote_name=None, refs=None):
-        self.ui.status(_("importing Git objects into Hg\n"))
-        # import heads and fetched tags as remote references
-        todo = []
-        done = set()
-        convert_list = {}
-
-        # get a list of all the head shas
-        if refs:
-          for head, sha in refs.iteritems():
-              # refs contains all the refs in the server, not just the ones
-              # we are pulling
-              if sha in self.git.object_store:
-                  todo.append(sha)
-        else:
-            todo = self.git.refs.values()[:]
-
-        # traverse the heads getting a list of all the unique commits
-        while todo:
-            sha = todo.pop()
-            assert isinstance(sha, str)
-            if sha in done:
-                continue
-            done.add(sha)
-            obj = self.git.get_object(sha)
-            if isinstance (obj, Commit):
-                convert_list[sha] = obj
-                todo.extend([p for p in obj.parents if p not in done])
-            if isinstance(obj, Tag):
-                (obj_type, obj_sha) = obj.get_object()
-                obj = self.git.get_object(obj_sha)
-                if isinstance (obj, Commit):
-                    convert_list[sha] = obj
-                    todo.extend([p for p in obj.parents if p not in done])
-
-        # sort the commits
-        commits = toposort.TopoSort(convert_list).items()
-
-        commits = [commit for commit in commits if not commit in self._map_git]
-        # import each of the commits, oldest first
-        total = len(commits)
-        if total:
-            magnitude = int(math.log(total, 10)) + 1
-        else:
-            magnitude = 1
-        for i, csha in enumerate(commits):
-            if i%100 == 0:
-                self.ui.status(_("at: %*d/%d\n") % (magnitude, i, total))
-            commit = convert_list[csha]
-            self.import_git_commit(commit)
-
-    def import_git_commit(self, commit):
-        self.ui.debug(_("importing: %s\n") % commit.id)
-        # TODO: Do something less coarse-grained than try/except on the
-        #        get_file call for removed files
-
-        (strip_message, hg_renames, hg_branch, extra) = self.extract_hg_metadata(commit.message)
-
-        # get a list of the changed, added, removed files
-        files = self.get_files_changed(commit)
-
-        date = (commit.author_time, -commit.author_timezone)
-        text = strip_message
-
-        origtext = text
-        try:
-            text.decode('utf-8')
-        except UnicodeDecodeError:
-            text = self.decode_guess(text, commit.encoding)
-
-        text = '\n'.join([l.rstrip() for l in text.splitlines()]).strip('\n')
-        if text + '\n' != origtext:
-            extra['message'] = create_delta(text +'\n', origtext)
-
-        author = commit.author
-
-        # convert extra data back to the end
-        if ' ext:' in commit.author:
-            regex = re.compile('^(.*?)\ ext:\((.*)\) <(.*)\>$')
-            m = regex.match(commit.author)
-            if m:
-                name = m.group(1)
-                ex = urllib.unquote(m.group(2))
-                email = m.group(3)
-                author = name + ' <' + email + '>' + ex
-
-        if ' <none@none>' in commit.author:
-            author = commit.author[:-12]
-
-        try:
-            author.decode('utf-8')
-        except UnicodeDecodeError:
-            origauthor = author
-            author = self.decode_guess(author, commit.encoding)
-            extra['author'] = create_delta(author, origauthor)
-
-        oldenc = self.swap_out_encoding()
-
-        def getfilectx(repo, memctx, f):
-            try:
-                (mode, sha, data) = self.get_file(commit, f)
-                e = self.convert_git_int_mode(mode)
-            except (TypeError, KeyError):
-                raise IOError()
-            if f in hg_renames:
-                copied_path = hg_renames[f]
-            else:
-                copied_path = None
-            return context.memfilectx(f, data, 'l' in e, 'x' in e, copied_path)
-
-        gparents = map(self.map_hg_get, commit.parents)
-        p1, p2 = (nullid, nullid)
-        octopus = False
-
-        if len(gparents) > 1:
-            # merge, possibly octopus
-            def commit_octopus(p1, p2):
-                ctx = context.memctx(self.repo, (p1, p2), text, files, getfilectx,
-                                     author, date, {'hg-git': 'octopus'})
-                return hex(self.repo.commitctx(ctx))
-
-            octopus = len(gparents) > 2
-            p2 = gparents.pop()
-            p1 = gparents.pop()
-            while len(gparents) > 0:
-                p2 = commit_octopus(p1, p2)
-                p1 = gparents.pop()
-        else:
-            if gparents:
-                p1 = gparents.pop()
-
-        files = list(set(files))
-
-        pa = None
-        if not (p2 == nullid):
-            node1 = self.repo.changectx(p1)
-            node2 = self.repo.changectx(p2)
-            pa = node1.ancestor(node2)
-
-        # if named branch, add to extra
-        if hg_branch:
-            extra['branch'] = hg_branch
-
-        # if committer is different than author, add it to extra
-        if commit.author != commit.committer \
-               or commit.author_time != commit.commit_time \
-               or commit.author_timezone != commit.commit_timezone:
-            extra['committer'] = "%s %d %d" % (commit.committer, commit.commit_time, -commit.commit_timezone)
-
-        if commit.encoding:
-            extra['encoding'] = commit.encoding
-
-        if hg_branch:
-            extra['branch'] = hg_branch
-
-        if octopus:
-            extra['hg-git'] ='octopus-done'
-
-        ctx = context.memctx(self.repo, (p1, p2), text, files, getfilectx,
-                             author, date, extra)
-
-        node = self.repo.commitctx(ctx)
-
-        self.swap_out_encoding(oldenc)
-
-        # save changeset to mapping file
-        cs = hex(node)
-        self.map_set(commit.id, cs)
-
-    ## PACK UPLOADING AND FETCHING
-
-    def upload_pack(self, remote, revs, force):
-        client, path = self.get_transport_and_path(remote)
-        def changed(refs):
-            to_push = revs or set(self.local_heads().values() + self.tags.values())
-            return self.get_changed_refs(refs, to_push, force)
-
-        genpack = self.git.object_store.generate_pack_contents
-        try:
-            self.ui.status(_("creating and sending data\n"))
-            changed_refs = client.send_pack(path, changed, genpack)
-            return changed_refs
-        except HangupException:
-            raise hgutil.Abort("the remote end hung up unexpectedly")
-
-    def get_changed_refs(self, refs, revs, force):
-        new_refs = refs.copy()
-
-        #The remote repo is empty and the local one doesn't have bookmarks/tags
-        if refs.keys()[0] == 'capabilities^{}':
-            del new_refs['capabilities^{}']
-            if not self.local_heads():
-                tip = hex(self.repo.lookup('tip'))
-                bookmarks.bookmark(self.ui, self.repo, 'master', tip)
-                bookmarks.setcurrent(self.repo, 'master')
-                new_refs['refs/heads/master'] = self.map_git_get(tip)
-
-        for rev in revs:
-            ctx = self.repo[rev]
-            heads = [t for t in ctx.tags() if t in self.local_heads()]
-            tags = [t for t in ctx.tags() if t in self.tags]
-
-            if not (heads or tags):
-                raise hgutil.Abort("revision %s cannot be pushed since"
-                                   " it doesn't have a ref" % ctx)
-
-            for r in heads + tags:
-                if r in heads:
-                    ref = 'refs/heads/'+r
-                else:
-                    ref = 'refs/tags/'+r
-
-                if ref not in refs:
-                    new_refs[ref] = self.map_git_get(ctx.hex())
-                elif new_refs[ref] in self._map_git:
-                    rctx = self.repo[self.map_hg_get(new_refs[ref])]
-                    if rctx.ancestor(ctx) == rctx or force:
-                        new_refs[ref] = self.map_git_get(ctx.hex())
-                    else:
-                        raise hgutil.Abort("pushing %s overwrites %s"
-                                           % (ref, ctx))
-                else:
-                    raise hgutil.Abort("%s changed on the server, please pull "
-                                       "and merge before pushing" % ref)
-
-        return new_refs
-
-
-    def fetch_pack(self, remote_name, heads):
-        client, path = self.get_transport_and_path(remote_name)
-        graphwalker = self.git.get_graph_walker()
-        def determine_wants(refs):
-            if heads:
-                want = []
-                for h in heads:
-                    r = [ref for ref in refs if ref.endswith('/'+h)]
-                    if not r:
-                        raise hgutil.Abort("ref %s not found on remote server")
-                    elif len(r) == 1:
-                        want.append(refs[r[0]])
-                    else:
-                        raise hgutil.Abort("ambiguous reference %s: %r"%(h, r))
-            else:
-                want = [sha for ref, sha in refs.iteritems()
-                        if not ref.endswith('^{}')]
-            return want
-        f, commit = self.git.object_store.add_pack()
-        try:
-            return client.fetch_pack(path, determine_wants, graphwalker, f.write, self.ui.status)
-        except HangupException:
-            raise hgutil.Abort("the remote end hung up unexpectedly")
-        finally:
-            commit()
-
-    ## REFERENCES HANDLING
-
-    def update_references(self):
-        heads = self.local_heads()
-
-        # Create a local Git branch name for each
-        # Mercurial bookmark.
-        for key in heads:
-            self.git.refs['refs/heads/' + key] = self.map_git_get(heads[key])
-
-    def export_hg_tags(self):
-        for tag, sha in self.repo.tags().iteritems():
-            if self.repo.tagtype(tag) in ('global', 'git'):
-                self.git.refs['refs/tags/' + tag] = self.map_git_get(hex(sha))
-                self.tags[tag] = hex(sha)
-
-    def local_heads(self):
-        try:
-            bms = bookmarks.parse(self.repo)
-            return dict([(bm, hex(bms[bm])) for bm in bms])
-        except AttributeError: #pragma: no cover
-            return {}
-
-    def import_tags(self, refs):
-        keys = refs.keys()
-        if not keys:
-            return
-        for k in keys[:]:
-            ref_name = k
-            parts = k.split('/')
-            if parts[0] == 'refs' and parts[1] == 'tags':
-                ref_name = "/".join([v for v in parts[2:]])
-                # refs contains all the refs in the server, not just
-                # the ones we are pulling
-                if refs[k] not in self.git.object_store:
-                    continue
-                if ref_name[-3:] == '^{}':
-                    ref_name = ref_name[:-3]
-                if not ref_name in self.repo.tags():
-                    obj = self.git.get_object(refs[k])
-                    sha = None
-                    if isinstance (obj, Commit): # lightweight
-                        sha = self.map_hg_get(refs[k])
-                        self.tags[ref_name] = sha
-                    elif isinstance (obj, Tag): # annotated
-                        (obj_type, obj_sha) = obj.get_object()
-                        obj = self.git.get_object(obj_sha)
-                        if isinstance (obj, Commit):
-                            sha = self.map_hg_get(obj_sha)
-                            # TODO: better handling for annotated tags
-                            self.tags[ref_name] = sha
-        self.save_tags()
-
-    def update_hg_bookmarks(self, refs):
-        try:
-            bms = bookmarks.parse(self.repo)
-            heads = dict([(ref[11:],refs[ref]) for ref in refs
-                          if ref.startswith('refs/heads/')])
-
-            for head, sha in heads.iteritems():
-                # refs contains all the refs in the server, not just
-                # the ones we are pulling
-                if sha not in self.git.object_store:
-                    continue
-                hgsha = bin(self.map_hg_get(sha))
-                if not head in bms:
-                    # new branch
-                    bms[head] = hgsha
-                else:
-                    bm = self.repo[bms[head]]
-                    if bm.ancestor(self.repo[hgsha]) == bm:
-                        # fast forward
-                        bms[head] = hgsha
-            if heads:
-                bookmarks.write(self.repo, bms)
-
-        except AttributeError:
-            self.ui.warn(_('creating bookmarks failed, do you have'
-                         ' bookmarks enabled?\n'))
-
-    def update_remote_branches(self, remote_name, refs):
-        heads = dict([(ref[11:],refs[ref]) for ref in refs
-                      if ref.startswith('refs/heads/')])
-
-        for head, sha in heads.iteritems():
-            # refs contains all the refs in the server, not just the ones
-            # we are pulling
-            if sha not in self.git.object_store:
-                continue
-            hgsha = bin(self.map_hg_get(sha))
-            tag = '%s/%s' % (remote_name, head)
-            self.repo.tag(tag, hgsha, '', True, None, None)
-
-        for ref_name in refs:
-            if ref_name.startswith('refs/heads'):
-                new_ref = 'refs/remotes/%s/%s' % (remote_name, ref_name[10:])
-                self.git.refs[new_ref] = refs[ref_name]
-            elif ref_name.startswith('refs/tags'):
-                self.git.refs[ref_name] = refs[ref_name]
-
-
-    ## UTILITY FUNCTIONS
-
-    def convert_git_int_mode(self, mode):
-        # TODO: make these into constants
-        convert = {
-         0100644: '',
-         0100755: 'x',
-         0120000: 'l'}
-        if mode in convert:
-            return convert[mode]
-        return ''
-
-    def extract_hg_metadata(self, message):
-        split = message.split("\n--HG--\n", 1)
-        renames = {}
-        extra = {}
-        branch = False
-        if len(split) == 2:
-            message, meta = split
-            lines = meta.split("\n")
-            for line in lines:
-                if line == '':
-                    continue
-
-                command, data = line.split(" : ", 1)
-
-                if command == 'rename':
-                    before, after = data.split(" => ", 1)
-                    renames[after] = before
-                if command == 'branch':
-                    branch = data
-                if command == 'extra':
-                    before, after = data.split(" : ", 1)
-                    extra[before] = urllib.unquote(after)
-        return (message, renames, branch, extra)
-
-    def get_file(self, commit, f):
-        otree = self.git.tree(commit.tree)
-        parts = f.split('/')
-        for part in parts:
-            (mode, sha) = otree[part]
-            obj = self.git.get_object(sha)
-            if isinstance (obj, Blob):
-                return (mode, sha, obj._text)
-            elif isinstance(obj, Tree):
-                otree = obj
-
-    def get_files_changed(self, commit):
-        def filenames(basetree, comptree, prefix):
-            basefiles = set()
-            changes = list()
-            csha = None
-            cmode = None
-            if basetree is not None:
-                for (bmode, bname, bsha) in basetree.entries():
-                    if bmode == 0160000: # TODO: properly handle submodules
-                        continue
-                    basefiles.add(bname)
-                    bobj = self.git.get_object(bsha)
-                    if comptree is not None:
-                        if bname in comptree:
-                            (cmode, csha) = comptree[bname]
-                        else:
-                            (cmode, csha) = (None, None)
-                    if not ((csha == bsha) and (cmode == bmode)):
-                        if isinstance (bobj, Blob):
-                            changes.append (prefix + bname)
-                        elif isinstance(bobj, Tree):
-                            ctree = None
-                            if csha:
-                                ctree = self.git.get_object(csha)
-                            changes.extend(filenames(bobj,
-                                                     ctree,
-                                                     prefix + bname + '/'))
-
-            # handle removals
-            if comptree is not None:
-                for (bmode, bname, bsha) in comptree.entries():
-                    if bmode == 0160000: # TODO: handle submodles
-                        continue
-                    if bname not in basefiles:
-                        bobj = self.git.get_object(bsha)
-                        if isinstance(bobj, Blob):
-                            changes.append(prefix + bname)
-                        elif isinstance(bobj, Tree):
-                            changes.extend(filenames(None, bobj,
-                                                     prefix + bname + '/'))
-            return changes
-
-        all_changes = list()
-        otree = self.git.tree(commit.tree)
-        if len(commit.parents) == 0:
-            all_changes = filenames(otree, None, '')
-        for parent in commit.parents:
-            pcommit = self.git.commit(parent)
-            ptree = self.git.tree(pcommit.tree)
-            all_changes.extend(filenames(otree, ptree, ''))
-
-        return all_changes
-
-    def remote_name(self, remote):
-        names = [name for name, path in self.paths if path == remote]
-        if names:
-            return names[0]
-
-    # Stolen from hgsubversion
-    def swap_out_encoding(self, new_encoding='UTF-8'):
-        try:
-            from mercurial import encoding
-            old = encoding.encoding
-            encoding.encoding = new_encoding
-        except ImportError:
-            old = hgutil._encoding
-            hgutil._encoding = new_encoding
-        return old
-
-    def decode_guess(self, string, encoding):
-        # text is not valid utf-8, try to make sense of it
-        if encoding:
-            try:
-                return string.decode(encoding).encode('utf-8')
-            except UnicodeDecodeError:
-                pass
-
-        try:
-            return string.decode('latin-1').encode('utf-8')
-        except UnicodeDecodeError:
-            return string.decode('ascii', 'replace').encode('utf-8')
-
-    def get_transport_and_path(self, uri):
-        from dulwich.client import TCPGitClient, SSHGitClient, SubprocessGitClient
-        for handler, transport in (("git://", TCPGitClient), ("git@", SSHGitClient), ("git+ssh://", SSHGitClient)):
-            if uri.startswith(handler):
-                host, path = uri[len(handler):].split("/", 1)
-                return transport(host, thin_packs=False), '/' + path
-        # if its not git or git+ssh, try a local url..
-        return SubprocessGitClient(thin_packs=False), uri

mercurial/extensions/hg-git/gitrepo.py

-from mercurial import repo, util
-from git_handler import GitHandler
-
-class gitrepo(repo.repository):
-    capabilities = ['lookup']
-    def __init__(self, ui, path, create):
-        if create: # pragma: no cover
-            raise util.Abort('Cannot create a git repository.')
-        self.path = path
-    def lookup(self, key):
-        if isinstance(key, str):
-            return key
-
-instance = gitrepo

mercurial/extensions/hg-git/hgrepo.py

-from mercurial.node import bin
-
-from git_handler import GitHandler
-from gitrepo import gitrepo
-
-
-def generate_repo_subclass(baseclass):
-    class hgrepo(baseclass):
-        def pull(self, remote, heads=None, force=False):
-            if isinstance(remote, gitrepo):
-                git = GitHandler(self, self.ui)
-                git.fetch(remote.path, heads)
-            else: #pragma: no cover
-                return super(hgrepo, self).pull(remote, heads, force)
-
-        def push(self, remote, force=False, revs=None):
-            if isinstance(remote, gitrepo):
-                git = GitHandler(self, self.ui)
-                git.push(remote.path, revs, force)
-            else: #pragma: no cover
-                return super(hgrepo, self).push(remote, force, revs)
-
-        def findoutgoing(self, remote, base=None, heads=None, force=False):
-            if isinstance(remote, gitrepo):
-                git = GitHandler(self, self.ui)
-                base, heads = git.get_refs(remote.path)
-                out, h = super(hgrepo, self).findoutgoing(remote, base, heads, force)
-                return out
-            else: #pragma: no cover
-                return super(hgrepo, self).findoutgoing(remote, base, heads, force)
-
-        def _findtags(self):
-            (tags, tagtypes) = super(hgrepo, self)._findtags()
-
-            git = GitHandler(self, self.ui)
-            for tag, rev in git.tags.iteritems():
-                if tag in tags:
-                    continue
-
-                tags[tag] = bin(rev)
-                tagtypes[tag] = 'git'
-
-            return (tags, tagtypes)
-
-        def tags(self):
-            if not hasattr(self, 'tagscache'):
-                # mercurial 1.4
-                return super(hgrepo, self).tags()
-
-            if self.tagscache:
-                return self.tagscache
-
-            git = GitHandler(self, self.ui)
-            tagscache = super(hgrepo, self).tags()
-            for tag, rev in git.tags.iteritems():
-                if tag in tagscache:
-                    continue
-
-                tagscache[tag] = bin(rev)
-                self._tagstypecache[tag] = 'git'
-
-            return tagscache
-
-    return hgrepo

mercurial/extensions/hg-git/lsprofcalltree.py

-
-
-def label(code):
-    if isinstance(code, str):
-        return ('~', 0, code)    # built-in functions ('~' sorts at the end)
-    else:
-        return '%s %s:%d' % (code.co_name, code.co_filename, code.co_firstlineno)
-
-
-
-class KCacheGrind(object):
-    def __init__(self, profiler):
-        self.data = profiler.getstats()
-        self.out_file = None
-
-    def output(self, out_file):
-        self.out_file = out_file
-        print >> out_file, 'events: Ticks'
-        self._print_summary()
-        for entry in self.data:
-            self._entry(entry)
-
-    def _print_summary(self):
-        max_cost = 0
-        for entry in self.data:
-            totaltime = int(entry.totaltime * 1000)
-            max_cost = max(max_cost, totaltime)
-        print >> self.out_file, 'summary: %d' % (max_cost,)
-
-    def _entry(self, entry):
-        out_file = self.out_file
-        code = entry.code
-        inlinetime = int(entry.inlinetime * 1000)
-        #print >> out_file, 'ob=%s' % (code.co_filename,)
-        if isinstance(code, str):
-            print >> out_file, 'fi=~'
-        else:
-            print >> out_file, 'fi=%s' % (code.co_filename,)
-        print >> out_file, 'fn=%s' % (label(code),)
-        if isinstance(code, str):
-            print >> out_file, '0 ', inlinetime
-        else:
-            print >> out_file, '%d %d' % (code.co_firstlineno, inlinetime)
-        # recursive calls are counted in entry.calls
-        if entry.calls:
-            calls = entry.calls
-        else:
-            calls = []
-        if isinstance(code, str):
-            lineno = 0
-        else:
-            lineno = code.co_firstlineno
-        for subentry in calls:
-            self._subentry(lineno, subentry)
-        print >> out_file
-
-    def _subentry(self, lineno, subentry):
-        out_file = self.out_file
-        code = subentry.code
-        totaltime = int(subentry.totaltime * 1000)
-        #print >> out_file, 'cob=%s' % (code.co_filename,)
-        print >> out_file, 'cfn=%s' % (label(code),)
-        if isinstance(code, str):
-            print >> out_file, 'cfi=~'
-            print >> out_file, 'calls=%d 0' % (subentry.callcount,)
-        else:
-            print >> out_file, 'cfi=%s' % (code.co_filename,)
-            print >> out_file, 'calls=%d %d' % (
-                subentry.callcount, code.co_firstlineno)
-        print >> out_file, '%d %d' % (lineno, totaltime)

mercurial/extensions/hg-git/tests/latin-1-encoding

-# -*- coding: latin-1 -*-
-
-# this file contains some latin-1 messages for test-encoding
-
-GIT_AUTHOR_NAME='t�st �nc�d�ng'; export GIT_AUTHOR_NAME
-echo beta > beta
-git add beta
-commit -m 'add beta'
-
-echo gamma > gamma
-git add gamma
-commit -m 'add g�mm�'
-
-# test the commit encoding field
-git config i18n.commitencoding latin-1
-echo delta > delta
-git add delta
-commit -m 'add d�lt�'

mercurial/extensions/hg-git/tests/run-tests.py

-#!/usr/bin/env python
-#
-# run-tests.py - Run a set of tests on Mercurial
-#
-# Copyright 2006 Matt Mackall <mpm@selenic.com>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import difflib
-import errno
-import optparse
-import os
-try:
-    import subprocess
-    subprocess.Popen  # trigger ImportError early
-    closefds = os.name == 'posix'
-    def Popen4(cmd, bufsize=-1):
-        p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
-                             close_fds=closefds,
-                             stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-                             stderr=subprocess.STDOUT)
-        p.fromchild = p.stdout
-        p.tochild = p.stdin
-        p.childerr = p.stderr
-        return p
-except ImportError:
-    subprocess = None
-    from popen2 import Popen4
-import shutil
-import signal
-import sys
-import tempfile
-import time
-
-# reserved exit code to skip test (used by hghave)
-SKIPPED_STATUS = 80
-SKIPPED_PREFIX = 'skipped: '
-FAILED_PREFIX  = 'hghave check failed: '
-PYTHON = sys.executable
-
-requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
-
-defaults = {
-    'jobs': ('HGTEST_JOBS', 1),
-    'timeout': ('HGTEST_TIMEOUT', 180),
-    'port': ('HGTEST_PORT', 20059),
-}
-
-def parseargs():
-    parser = optparse.OptionParser("%prog [options] [tests]")
-    parser.add_option("-C", "--annotate", action="store_true",
-        help="output files annotated with coverage")
-    parser.add_option("--child", type="int",
-        help="run as child process, summary to given fd")
-    parser.add_option("-c", "--cover", action="store_true",
-        help="print a test coverage report")
-    parser.add_option("-f", "--first", action="store_true",
-        help="exit on the first test failure")
-    parser.add_option("-i", "--interactive", action="store_true",
-        help="prompt to accept changed output")
-    parser.add_option("-j", "--jobs", type="int",
-        help="number of jobs to run in parallel"
-             " (default: $%s or %d)" % defaults['jobs'])
-    parser.add_option("--keep-tmpdir", action="store_true",
-        help="keep temporary directory after running tests"
-             " (best used with --tmpdir)")
-    parser.add_option("-R", "--restart", action="store_true",
-        help="restart at last error")
-    parser.add_option("-p", "--port", type="int",
-        help="port on which servers should listen"
-             " (default: $%s or %d)" % defaults['port'])
-    parser.add_option("-r", "--retest", action="store_true",
-        help="retest failed tests")
-    parser.add_option("-s", "--cover_stdlib", action="store_true",
-        help="print a test coverage report inc. standard libraries")
-    parser.add_option("-t", "--timeout", type="int",
-        help="kill errant tests after TIMEOUT seconds"
-             " (default: $%s or %d)" % defaults['timeout'])
-    parser.add_option("--tmpdir", type="string",
-        help="run tests in the given temporary directory")
-    parser.add_option("-v", "--verbose", action="store_true",
-        help="output verbose messages")
-    parser.add_option("-n", "--nodiff", action="store_true",
-        help="skip showing test changes")
-    parser.add_option("--with-hg", type="string",
-        help="test existing install at given location")
-    parser.add_option("--pure", action="store_true",
-        help="use pure Python code instead of C extensions")
-
-    for option, default in defaults.items():
-        defaults[option] = int(os.environ.get(*default))
-    parser.set_defaults(**defaults)
-    (options, args) = parser.parse_args()
-
-    global vlog
-    options.anycoverage = (options.cover or
-                           options.cover_stdlib or
-                           options.annotate)
-
-    if options.verbose:
-        def vlog(*msg):
-            for m in msg:
-                print m,
-            print
-    else:
-        vlog = lambda *msg: None
-
-    if options.jobs < 1:
-        print >> sys.stderr, 'ERROR: -j/--jobs must be positive'
-        sys.exit(1)
-    if options.interactive and options.jobs > 1:
-        print '(--interactive overrides --jobs)'
-        options.jobs = 1
-
-    return (options, args)
-
-def rename(src, dst):
-    """Like os.rename(), trade atomicity and opened files friendliness
-    for existing destination support.
-    """
-    shutil.copy(src, dst)
-    os.remove(src)
-
-def splitnewlines(text):
-    '''like str.splitlines, but only split on newlines.
-    keep line endings.'''
-    i = 0
-    lines = []
-    while True:
-        n = text.find('\n', i)
-        if n == -1:
-            last = text[i:]
-            if last:
-                lines.append(last)
-            return lines
-        lines.append(text[i:n+1])
-        i = n + 1
-
-def parsehghaveoutput(lines):
-    '''Parse hghave log lines.
-    Return tuple of lists (missing, failed):
-      * the missing/unknown features
-      * the features for which existence check failed'''
-    missing = []
-    failed = []
-    for line in lines:
-        if line.startswith(SKIPPED_PREFIX):
-            line = line.splitlines()[0]
-            missing.append(line[len(SKIPPED_PREFIX):])
-        elif line.startswith(FAILED_PREFIX):
-            line = line.splitlines()[0]
-            failed.append(line[len(FAILED_PREFIX):])
-
-    return missing, failed
-
-def showdiff(expected, output):
-    for line in difflib.unified_diff(expected, output,
-            "Expected output", "Test output"):
-        sys.stdout.write(line)
-
-def findprogram(program):
-    """Search PATH for a executable program"""
-    for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
-        name = os.path.join(p, program)
-        if os.access(name, os.X_OK):
-            return name
-    return None
-
-def checktools():
-    # Before we go any further, check for pre-requisite tools
-    # stuff from coreutils (cat, rm, etc) are not tested
-    for p in requiredtools:
-        if os.name == 'nt':
-            p += '.exe'
-        found = findprogram(p)
-        if found:
-            vlog("# Found prerequisite", p, "at", found)
-        else:
-            print "WARNING: Did not find prerequisite tool: "+p
-
-def cleanup(options):
-    if not options.keep_tmpdir:
-        if options.verbose:
-            print "# Cleaning up HGTMP", HGTMP
-        shutil.rmtree(HGTMP, True)
-
-def usecorrectpython():
-    # some tests run python interpreter. they must use same
-    # interpreter we use or bad things will happen.
-    exedir, exename = os.path.split(sys.executable)
-    if exename == 'python':
-        path = findprogram('python')
-        if os.path.dirname(path) == exedir:
-            return
-    vlog('# Making python executable in test path use correct Python')
-    mypython = os.path.join(BINDIR, 'python')
-    try:
-        os.symlink(sys.executable, mypython)
-    except AttributeError:
-        # windows fallback
-        shutil.copyfile(sys.executable, mypython)
-        shutil.copymode(sys.executable, mypython)
-
-def installhg(options):
-    global PYTHON
-    vlog("# Performing temporary installation of HG")
-    installerrs = os.path.join("tests", "install.err")
-    pure = options.pure and "--pure" or ""
-
-    # Run installer in hg root
-    os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '..'))
-    cmd = ('%s setup.py %s clean --all'
-           ' install --force --prefix="%s" --install-lib="%s"'
-           ' --install-scripts="%s" >%s 2>&1'
-           % (sys.executable, pure, INST, PYTHONDIR, BINDIR, installerrs))
-    vlog("# Running", cmd)
-    if os.system(cmd) == 0:
-        if not options.verbose:
-            os.remove(installerrs)
-    else:
-        f = open(installerrs)
-        for line in f:
-            print line,
-        f.close()
-        sys.exit(1)
-    os.chdir(TESTDIR)
-
-    os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
-
-    pydir = os.pathsep.join([PYTHONDIR, TESTDIR])
-    pythonpath = os.environ.get("PYTHONPATH")
-    if pythonpath:
-        pythonpath = pydir + os.pathsep + pythonpath
-    else:
-        pythonpath = pydir