52 Commits

Author SHA1 Message Date
Mark Nauwelaerts
8f9d2797fd Add notes based on current notes ref
See felipec/git-remote-hg#58
2016-07-02 19:57:48 +02:00
Felipe Contreras
822c6e4b03 Avoid deprecated bookmarks.write()
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2016-05-17 21:26:18 -05:00
Felipe Contreras
b6e9475918 readme: mention Mercurial dependency
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2016-05-16 23:37:08 -05:00
Lars Noschinski
517ceb91ac Fix import of broken committers
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2016-05-16 22:44:49 -05:00
Felipe Contreras
114804f0cb travis: add more Mercurial versions
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2016-05-16 22:29:50 -05:00
Felipe Contreras
b022367aef test: temporarily disable hg-git tests
They work, but they need too many changes in different packages.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2016-05-16 22:29:50 -05:00
Felipe Contreras
18626d346f Revert "test: use C.UTF-8 locale in tests"
This reverts commit f53a8653ab.

It turns out Debian and Fedora do provide the C.UTF-8 locale, but other
distributions don't.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2016-05-16 22:29:50 -05:00
Felipe Contreras
b81ec14c2e Improve hg-git compatibility
Recent versions of Mercurial check if a filectx is the same as the
parents, and avoid creating a new filelog. This is what we want, but
hg-git doesn't do that.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2016-05-16 22:29:50 -05:00
Felipe Contreras
1e279075dc Add compatibility for Mercurial v3.2
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2016-05-16 22:29:50 -05:00
Felipe Contreras
02a0a59a4b travis: force version of hg-git
The latest that works.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2016-05-16 17:56:25 -05:00
Felipe Contreras
185852eac4 test: cleanup hg-git test
We don't need hgext.bookmarks since a long long time (ever).

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-06-03 06:00:02 -05:00
Felipe Contreras
29a0d8a0e3 test: skip tests with broken hg-git compatibility
https://bitbucket.org/durin42/hg-git/issue/115/

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-06-02 18:42:17 -05:00
Felipe Contreras
aa528c9649 Fix memfilectx API change
It will change in 3.1.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-06-02 17:49:56 -05:00
Felipe Contreras
018aa4753b Improve version parsing
Development versions have extra stuff in the form of
'version+distance-id'.

Let's assume we are already in the next major version when in
development.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-06-02 17:49:29 -05:00
Felipe Contreras
f173208406 build: don't install docs by default
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-31 15:58:33 -05:00
Felipe Contreras
e7df347fab readme: fix not about v2.0
It didn't get in.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-31 15:49:28 -05:00
Brian Gernhardt
0de8aa91f4 build: avoid non-portable install -D
BSD install doesn't understand the -D option. Use a separate install
command to create the directory (with the -d option) before installing
the file.

Signed-off-by: Brian Gernhardt <brian@gernhardtsoftware.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-31 15:41:28 -05:00
Paul Wise
22d9794c11 test: add compatibility with Debian hg-git package
Debian has named the hggit python module as hgext.git.

Signed-off-by: Paul Wise <pabs3@bonedaddy.net>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-30 17:37:01 -05:00
Paul Wise
f53a8653ab test: use C.UTF-8 locale in tests
The en_US.UTF-8 locale is not guaranteed to exist, and if it doesn't,
several tests fail.

Signed-off-by: Paul Wise <pabs3@bonedaddy.net>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-30 17:37:01 -05:00
Paul Wise
b4c63539f2 Ignore the generated manual page
Signed-off-by: Paul Wise <pabs3@bonedaddy.net>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-30 17:37:01 -05:00
Paul Wise
38070007aa build: general improvements
Allow customising the bin and manual page dirs.

Signed-off-by: Paul Wise <pabs3@bonedaddy.net>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-30 17:37:01 -05:00
Felipe Contreras
fadd5f698b test: improve test locations
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-22 22:09:28 -05:00
Felipe Contreras
1eb8fa4805 readme: add contributing section
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-13 14:26:45 -05:00
Felipe Contreras
19f31c1c84 doc: add help for sending patches
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-13 14:26:32 -05:00
Felipe Contreras
ff221de459 Split multiple import lines
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-10 23:54:33 -05:00
Felipe Contreras
179fefda96 More pep8 fixes
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-10 23:49:49 -05:00
Felipe Contreras
c226ba3904 remote-hg,bzr: conform more to pep8
Reported-by: William Giokas <1007380@gmail.com>

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-10 01:01:20 -05:00
Felipe Contreras
776e36c147 build: fix install location
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 10:29:52 -05:00
Felipe Contreras
5b6d5283cb Use python2 instead of python
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 09:11:14 -05:00
Felipe Contreras
5738ee42d8 test: add missing redirection
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 09:10:57 -05:00
Felipe Contreras
1d27390dd0 readme: fix link location
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 09:09:16 -05:00
Felipe Contreras
cad5c95465 travis: add initial configuration
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 09:04:31 -05:00
Felipe Contreras
259838a342 test: fix redirection style
To be sane, a redirection operation has to have spaces, like any binary
operand: 'a > b'.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:55:51 -05:00
Felipe Contreras
55bbd81a75 test: trivial style cleanups
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:49:30 -05:00
Felipe Contreras
8db5b9a537 Add support for hg v3.0
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:43:07 -05:00
Felipe Contreras
bbc4009acf test: trivial cleanups and fixes
There was a broken && chain, and cat is simpler than echo in a subshell.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:43:07 -05:00
Felipe Contreras
c84feb364b test: dd file operation tests
Inspired by gitifyhg.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:43:07 -05:00
Felipe Contreras
990152c0c8 Add more tests
Inspired by the tests in gitifyhg.

One test is failing, but that's because of a limitation of
remote-helpers.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:43:07 -05:00
Felipe Contreras
6ba42cdf98 Simplify hg-git regex
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:43:07 -05:00
Felipe Contreras
ef00e40d7c remote-hg: make sure we omit multiple heads
We want to ignore secondary heads, otherwise we will import revisions
that won't have any ref pointing to them and might eventually be pruned,
which would cause problems with the synchronization of marks.

This can only be expressed properly as '::b - ::a', but that's not
efficient, specially in older versions of Mercurial.
'ancestor(a,b)::b - ancestor(a,b)::a' might be better, but it's not
particularly pretty. Mercurial v3.0 will have a new 'only(b, a)' that
does the same, but that hasn't even been released yet. Either way all of
these require repo.revs() which is only available after Mercurial v2.1.

Also, we would need special considerations for when there's no base
revision (importing from root).

It's much better to implement our own function to get a range of
revisions.

The new gitrange() is inspired by Mercurial's revset.missingancestors().

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:43:07 -05:00
Max Horn
1c72617831 Do not fail on invalid bookmarks
Mercurial can have bookmarks pointing to "nullid" (the empty root
revision), while Git can not have references to it.

Warn the user about the invalid reference, and do not advertise these
bookmarks as head refs, but otherwise continue the import. In
particular, we still keep track of the fact that the remote repository
has a bookmark of the given name, in case the user wants to modify that
bookmark.

Reported-by: Antoine Pelisse <apelisse@gmail.com>
Signed-off-by: Max Horn <max@quendi.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:43:07 -05:00
Daniel Liew
184551c71d Use internal clone's hgrc
Use the hgrc configuration file in the internal mercurial repository in
addition to the other system wide hgrc files. This is done by using the
'ui' object from the 'repository' object which will have loaded the
repository hgrc file if it exists.

Signed-off-by: Dan Liew <delcypher@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:43:07 -05:00
Felipe Contreras
32d4f36f22 test: split into setup test
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:43:07 -05:00
Felipe Contreras
ccb3f13d69 Properly detect missing contexts
This can happen when there's a synchronization issue between marks-git
and marks-hg; a key is missing in marks-hg, and when we receive a reset
command the value of ctx basically comes from None.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:43:07 -05:00
Felipe Contreras
2958556bec Store marks only on success
Commit 2594a79 (remote-hg: fix bad state issue) originally introduced
this code in order to avoid synchronization issues while pushing,
because `git fast-export` might end up writing the marks before a crash
in the remote helper occurs.

However, the problem is in `git fast-export`; the marks should only be
written after both have finished successfully.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:43:06 -05:00
Felipe Contreras
4ea2fa76b3 Update to 'public' phase when pushing
This is what Mercurial does.

Reported-by: Nathan Palmer
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:43:06 -05:00
Felipe Contreras
84b8b482a4 Fix parsing of custom committer
Other tools use the 'committer' extra field differently, so let's make
the parsing more reliable and don't assume it's in a certain format.

Reported-by: Kevin Cox <kevincox@kevincox.ca>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:43:06 -05:00
Felipe Contreras
978314a4be Always normalize paths
Apparently Mercurial can have paths such as 'foo//bar', so normalize all
paths.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:43:06 -05:00
Felipe Contreras
51eabd4a17 doc: add manpage
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:41:57 -05:00
Felipe Contreras
98c3535c3f build: add install target
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:41:57 -05:00
Felipe Contreras
3abf376e9e Add README
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:36:33 -05:00
Felipe Contreras
0b71ca38e7 Reorganize tests
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2014-05-09 08:33:07 -05:00
12 changed files with 914 additions and 224 deletions

28
.travis.yml Normal file
View File

@@ -0,0 +1,28 @@
language: python
install:
- if [ "$HG_VERSION" != "dev" ];
then pip install -q Mercurial${HG_VERSION+==$HG_VERSION};
else pip install -q http://selenic.com/repo/hg/archive/tip.tar.gz;
fi
- pip install -q dulwich hg-git==0.6.1 || true
before_script:
- hg --version || true
- pip show hg-git dulwich
script:
- make test
matrix:
include:
- env: HG_VERSION=2.9.1
- env: HG_VERSION=2.8.2
- env: HG_VERSION=2.7.2
- env: HG_VERSION=3.0
- env: HG_VERSION=3.5.2
- env: HG_VERSION=3.6.3
- env: HG_VERSION=3.7
- env: HG_VERSION=dev
- python: 2.7
- python: 2.6

View File

@@ -1,6 +1,29 @@
all:
prefix := $(HOME)
bindir := $(prefix)/bin
mandir := $(prefix)/share/man/man1
all: doc
doc: doc/git-remote-hg.1
test:
$(MAKE) -C test
.PHONY: all test
doc/git-remote-hg.1: doc/git-remote-hg.txt
a2x -d manpage -f manpage $<
clean:
$(RM) doc/git-remote-hg.1
D = $(DESTDIR)
install:
install -d -m 755 $(D)$(bindir)/
install -m 755 git-remote-hg $(D)$(bindir)/git-remote-hg
install-doc: doc
install -d -m 755 $(D)$(mandir)/
install -m 644 doc/git-remote-hg.1 $(D)$(mandir)/git-remote-hg.1
.PHONY: all test install install-doc clean

131
README.asciidoc Normal file
View File

@@ -0,0 +1,131 @@
'git-remote-hg' is the semi-official Mercurial bridge from Git project, once
installed, it allows you to clone, fetch and push to and from Mercurial
repositories as if they were Git ones:
--------------------------------------
git clone "hg::http://selenic.com/repo/hello"
--------------------------------------
To enable this, simply add the 'git-remote-hg' script anywhere in your `$PATH`:
--------------------------------------
wget https://raw.github.com/felipec/git-remote-hg/master/git-remote-hg -O ~/bin/git-remote-hg
chmod +x ~/bin/git-remote-hg
--------------------------------------
That's it :)
Obviously you will need Mercurial installed.
== Configuration ==
If you want to see Mercurial revisions as Git commit notes:
--------------------------------------
% git config core.notesRef refs/notes/hg
--------------------------------------
If you are not interested in Mercurial permanent and global branches (aka. commit labels):
--------------------------------------
% git config --global remote-hg.track-branches false
--------------------------------------
With this configuration, the 'branches/foo' refs won't appear.
If you want the equivalent of 'hg clone --insecure':
--------------------------------------
% git config --global remote-hg.insecure true
--------------------------------------
If you want 'git-remote-hg' to be compatible with 'hg-git', and generate exactly the same commits:
--------------------------------------
% git config --global remote-hg.hg-git-compat true
--------------------------------------
== Notes ==
Remember to run `git gc --aggressive` after cloning a repository, specially if
it's a big one. Otherwise lots of space will be wasted.
The oldest version of mercurial supported is 1.9. For the most part 1.8 works,
but you might experience some issues.
=== Pushing branches ===
To push a branch, you need to use the "branches/" prefix:
--------------------------------------
% git checkout branches/next
# do stuff
% git push origin branches/next
--------------------------------------
All the pushed commits will receive the "next" Mercurial named branch.
*Note*: Make sure you don't have +remote-hg.track-branches+ disabled.
=== Cloning HTTPS ===
The simplest way is to specify the user and password in the URL:
--------------------------------------
git clone hg::https://user:password@bitbucket.org/user/repo
--------------------------------------
You can also use the http://mercurial.selenic.com/wiki/SchemesExtension[schemes extension]:
--------------------------------------
[auth]
bb.prefix = https://bitbucket.org/user/
bb.username = user
bb.password = password
--------------------------------------
Finally, you can also use the
https://pypi.python.org/pypi/mercurial_keyring[keyring extension].
However, some of these features require very new versions of 'git-remote-hg',
so you might have better luck simply specifying the username and password in
the URL.
=== Caveats ===
The only major incompatibility is that Git octopus merges (a merge with more
than two parents) are not supported.
Mercurial branches and bookmarks have some limitations of Git branches: you
can't have both 'dev/feature' and 'dev' (as Git uses files and directories to
store them).
Multiple anonymous heads (which are useless anyway) are not supported; you
would only see the latest head.
Closed branches are not supported; they are not shown and you can't close or
reopen. Additionally in certain rare situations a synchronization issue can
occur (https://github.com/felipec/git/issues/65[Bug #65]).
Limitations of the remote-helpers' framework apply. In particular, these
commands don't work:
* `git push origin :branch-to-delete`
* `git push origin old:new` (it will push 'old') (patches available)
* `git push --dry-run origin branch` (it will push) (patches available)
== Other projects ==
There are other 'git-remote-hg' projects out there, do not confuse this one,
this is the one distributed officially by the Git project:
* https://github.com/msysgit/msysgit/wiki/Guide-to-git-remote-hg[msysgit's git-remote-hg]
* https://github.com/rfk/git-remote-hg[rfk's git-remote-hg]
For a comparison between these and other projects go
https://github.com/felipec/git/wiki/Comparison-of-git-remote-hg-alternatives[here].
== Contributing ==
Send your patches to the mailing list git-fc@googlegroups.com (no need to
subscribe).

1
doc/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
git-remote-hg.1

5
doc/SubmittingPatches Normal file
View File

@@ -0,0 +1,5 @@
Please send your patches using `git format-patch` to the mailing list:
git-fc@googlegroups.com.
Make sure all the tests pass by running `make test`, and if possible add a new
test to exercise the code you are submitting.

121
doc/git-remote-hg.txt Normal file
View File

@@ -0,0 +1,121 @@
git-remote-hg(1)
================
NAME
----
git-remote-hg - bidirectional bridge between Git and Mercurial
SYNOPSIS
--------
[verse]
'git clone' hg::<hg repository>
DESCRIPTION
-----------
This tool allows you to transparently clone, fetch and push to and from Mercurial
repositories as if they were Git ones.
To use it you simply need to use the "'hg::'" prefix when specifying a remote URL
(e.g. when cloning).
EXAMPLE
-------
------------
$ git clone hg::http://selenic.com/repo/hello
------------
CONFIGURATION
-------------
If you want to see Mercurial revisions as Git commit notes:
--------------------------------------
% git config core.notesRef refs/notes/hg
--------------------------------------
If you are not interested in Mercurial permanent and global branches (aka. commit labels):
--------------------------------------
% git config --global remote-hg.track-branches false
--------------------------------------
With this configuration, the 'branches/foo' refs won't appear.
If you want the equivalent of `hg clone --insecure`:
--------------------------------------
% git config --global remote-hg.insecure true
--------------------------------------
If you want 'git-remote-hg' to be compatible with 'hg-git', and generate exactly the same commits:
--------------------------------------
% git config --global remote-hg.hg-git-compat true
--------------------------------------
NOTES
-----
Remember to run `git gc --aggressive` after cloning a repository, specially if
it's a big one. Otherwise lots of space will be wasted.
The oldest version of Mercurial supported is 1.9. For the most part 1.8 works,
but you might experience some issues.
Pushing branches
~~~~~~~~~~~~~~~~
To push a Mercurial named branch, you need to use the "branches/" prefix:
--------------------------------------
% git checkout branches/next
# do stuff
% git push origin branches/next
--------------------------------------
All the pushed commits will receive the "next" Mercurial named branch.
*Note*: Make sure you don't have +remote-hg.track-branches+ disabled.
Cloning HTTPS
~~~~~~~~~~~~~
The simplest way is to specify the user and password in the URL:
--------------------------------------
git clone hg::https://user:password@bitbucket.org/user/repo
--------------------------------------
You can also use the http://mercurial.selenic.com/wiki/SchemesExtension[schemes extension]:
--------------------------------------
[auth]
bb.prefix = https://bitbucket.org/user/
bb.username = user
bb.password = password
--------------------------------------
Finally, you can also use the
https://pypi.python.org/pypi/mercurial_keyring[keyring extension].
CAVEATS
-------
The only major incompatibility is that Git octopus merges (a merge with more
than two parents) are not supported.
Mercurial branches and bookmarks have some limitations of Git branches: you
can't have both 'dev/feature' and 'dev' (as Git uses files and directories to
store them).
Multiple anonymous heads (which are useless anyway) are not supported; you
would only see the latest head.
Closed branches are not supported; they are not shown and you can't close or
reopen. Additionally in certain rare situations a synchronization issue can
occur (https://github.com/felipec/git/issues/65[Bug #65]).

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python2
#
# Copyright (c) 2012 Felipe Contreras
#
@@ -12,7 +12,9 @@
# For remote repositories a local clone is stored in
# "$GIT_DIR/hg/origin/clone/.hg/".
from mercurial import hg, ui, bookmarks, context, encoding, node, error, extensions, discovery, util
from mercurial import hg, ui, bookmarks, context, encoding
from mercurial import node, error, extensions, discovery, util
from mercurial import changegroup
import re
import sys
@@ -22,7 +24,8 @@ import shutil
import subprocess
import urllib
import atexit
import urlparse, hashlib
import urlparse
import hashlib
import time as ptime
#
@@ -53,7 +56,7 @@ import time as ptime
NAME_RE = re.compile('^([^<>]+)')
AUTHOR_RE = re.compile('^([^<>]+?)? ?[<>]([^<>]*)(?:$|>)')
EMAIL_RE = re.compile(r'([^ \t<>]+@[^ \t<>]+)')
AUTHOR_HG_RE = re.compile('^(.*?) ?<(.*?)(?:>(.+)?)?$')
AUTHOR_HG_RE = re.compile('^(.*?) ?<(.*?)(?:>(.*))?$')
RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.*)> (\d+) ([+-]\d+)')
VERSION = 2
@@ -107,6 +110,12 @@ def get_config_bool(config, default=False):
else:
return default
def rev_parse(rev):
cmd = ['git', 'rev-parse', '--verify', '-q', rev]
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
output, _ = process.communicate()
return output
class Marks:
def __init__(self, path, repo):
@@ -156,7 +165,9 @@ class Marks:
self.version = 2
def dict(self):
return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark, 'version' : self.version, 'last-note' : self.last_note }
return { 'tips': self.tips, 'marks': self.marks,
'last-mark': self.last_mark, 'version': self.version,
'last-note': self.last_note }
def store(self):
json.dump(self.dict(), open(self.path, 'w'))
@@ -260,6 +271,7 @@ class Parser:
return (user, int(date), -tz)
def fix_file_path(path):
path = os.path.normpath(path)
if not os.path.isabs(path):
return path
return os.path.relpath(path, '/')
@@ -367,6 +379,19 @@ def updatebookmarks(repo, peer):
for k, v in remotemarks.iteritems():
localmarks[k] = hgbin(v)
if check_version(3, 6):
lock = tr = None
try:
lock = repo.lock()
tr = repo.transaction('bookmark')
localmarks.recordchange(tr)
tr.close()
finally:
if tr is not None:
tr.release()
if lock is not None:
lock.release()
else:
if hasattr(localmarks, 'write'):
localmarks.write()
else:
@@ -421,9 +446,14 @@ def get_repo(url, alias):
repo = hg.repository(myui, local_path)
try:
peer = hg.peer(myui, {}, url)
peer = hg.peer(repo.ui, {}, url)
except:
die('Repository error')
if check_version(3, 0):
from mercurial import exchange
exchange.pull(repo, peer, heads=None, force=True)
else:
repo.pull(peer, heads=None, force=True)
updatebookmarks(repo, peer)
@@ -436,16 +466,46 @@ def rev_to_mark(rev):
def mark_to_rev(mark):
return marks.to_rev(mark)
# Get a range of revisions in the form of a..b (git committish)
def gitrange(repo, a, b):
positive = []
pending = set([int(b)])
negative = set([int(a)])
for cur in xrange(b, -1, -1):
if not pending:
break
parents = [p for p in repo.changelog.parentrevs(cur) if p >= 0]
if cur in pending:
positive.append(cur)
pending.remove(cur)
for p in parents:
if p not in negative:
pending.add(p)
elif cur in negative:
negative.remove(cur)
for p in parents:
if p not in pending:
negative.add(p)
else:
pending.discard(p)
positive.reverse()
return positive
def export_ref(repo, name, kind, head):
ename = '%s/%s' % (kind, name)
try:
tip = marks.get_tip(ename)
tip = repo[tip].rev()
tip = repo[tip]
except:
tip = 0
tip = repo[-1]
revs = gitrange(repo, tip, head)
revs = xrange(tip, head.rev() + 1)
total = len(revs)
tip = tip.rev()
for rev in revs:
@@ -460,8 +520,12 @@ def export_ref(repo, name, kind, head):
author = "%s %d %s" % (fixup_user(user), time, gittz(tz))
if 'committer' in extra:
user, time, tz = extra['committer'].rsplit(' ', 2)
committer = "%s %s %s" % (user, time, gittz(int(tz)))
try:
cuser, ctime, ctz = extra['committer'].rsplit(' ', 2)
committer = "%s %s %s" % (fixup_user(cuser), ctime, gittz(int(ctz)))
except ValueError:
cuser = extra['committer']
committer = "%s %d %s" % (fixup_user(cuser), time, gittz(tz))
else:
committer = author
@@ -544,8 +608,11 @@ def export_ref(repo, name, kind, head):
desc = "Notes for %s\n" % (name)
print "data %d" % (len(desc))
print desc
if marks.last_note:
print "from :%u" % marks.last_note
# continue incrementally on current notes branch (whenever possible)
# to avoid wiping out present content upon fetch of new repo
current_note = rev_parse(ref)
if current_note:
print 'from %s^0' % (ref)
for rev in pending_revs:
notes.add(rev)
@@ -614,7 +681,7 @@ def list_head(repo, cur):
return
node = repo[branch_tip('default')]
head = 'master' if not 'master' in bmarks else 'default'
head = 'master' if 'master' not in bmarks else 'default'
fake_bmark = head
bmarks[head] = node
@@ -643,6 +710,9 @@ def do_list(parser):
print "? refs/heads/branches/%s" % gitref(branch)
for bmark in bmarks:
if bmarks[bmark].hex() == '0' * 40:
warn("Ignoring invalid bookmark '%s'", bmark)
else:
print "? refs/heads/%s" % gitref(bmark)
for tag, node in repo.tagslist():
@@ -760,12 +830,30 @@ def parse_commit(parser):
def getfilectx(repo, memctx, f):
of = files[f]
if 'deleted' in of:
if check_version(3, 2):
return None
else:
raise IOError
if 'ctx' in of:
if mode == 'hg':
ctx = of['ctx']
is_exec = ctx.isexec()
is_link = ctx.islink()
if check_version(3, 1):
return context.memfilectx(repo, f, ctx.data(),
is_link, is_exec)
else:
return context.memfilectx(f, ctx.data(),
is_link, is_exec)
else:
return of['ctx']
is_exec = of['mode'] == 'x'
is_link = of['mode'] == 'l'
rename = of.get('rename', None)
if check_version(3, 1):
return context.memfilectx(repo, f, of['data'],
is_link, is_exec, rename)
else:
return context.memfilectx(f, of['data'],
is_link, is_exec, rename)
@@ -870,6 +958,9 @@ def write_tag(repo, tag, node, msg, author):
except error.ManifestLookupError:
data = ""
content = data + "%s %s\n" % (node, tag)
if check_version(3, 1):
return context.memfilectx(repo, f, content, False, False, None)
else:
return context.memfilectx(f, content, False, False, None)
p1 = tip.hex()
@@ -903,12 +994,17 @@ def write_tag(repo, tag, node, msg, author):
def checkheads_bmark(repo, ref, ctx):
bmark = ref[len('refs/heads/'):]
if not bmark in bmarks:
if bmark not in bmarks:
# new bmark
return True
ctx_old = bmarks[bmark]
ctx_new = ctx
if not ctx.rev():
print "error %s unknown" % ref
return False
if not repo.changelog.descendant(ctx_old.rev(), ctx_new.rev()):
if force_push:
print "ok %s forced update" % ref
@@ -931,7 +1027,7 @@ def checkheads(repo, remote, p_revs):
for node, ref in p_revs.iteritems():
ctx = repo[node]
branch = ctx.branch()
if not branch in remotemap:
if branch not in remotemap:
# new branch
continue
if not ref.startswith('refs/heads/branches'):
@@ -981,15 +1077,28 @@ def push_unsafe(repo, remote, parsed_refs, p_revs):
if not checkheads(repo, remote, p_revs):
return None
if check_version(3, 2):
cg = changegroup.getchangegroup(repo, 'push', heads=list(p_revs), common=common)
elif check_version(3, 0):
cg = changegroup.getbundle(repo, 'push', heads=list(p_revs), common=common)
else:
cg = repo.getbundle('push', heads=list(p_revs), common=common)
unbundle = remote.capable('unbundle')
if unbundle:
if force:
remoteheads = ['force']
return remote.unbundle(cg, remoteheads, 'push')
ret = remote.unbundle(cg, remoteheads, 'push')
else:
return remote.addchangegroup(cg, 'push', repo.url())
ret = remote.addchangegroup(cg, 'push', repo.url())
phases = remote.listkeys('phases')
if phases:
for head in p_revs:
# update to public
remote.pushkey('phases', hghex(head), '1', '0')
return ret
def push(repo, remote, parsed_refs, p_revs):
if hasattr(remote, 'canpush') and not remote.canpush():
@@ -1207,7 +1316,11 @@ def main(args):
filenodes = {}
fake_bmark = None
try:
hg_version = tuple(int(e) for e in util.version().split('.'))
version, _, extra = util.version().partition('+')
version = list(int(e) for e in version.split('.'))
if extra:
version[1] += 1
hg_version = tuple(version)
except:
hg_version = None
dry_run = False
@@ -1242,12 +1355,10 @@ def main(args):
die('unhandled command: %s' % line)
sys.stdout.flush()
def bye():
if not marks:
return
if not is_tmp:
marks.store()
else:
def bye():
if is_tmp:
shutil.rmtree(dirname)
atexit.register(bye)

3
test/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
test-results/
trash directory.*/
.prove

View File

@@ -1,6 +1,6 @@
RM ?= rm -f
T = $(wildcard ../test-*.sh)
T = main.t bidi.t
TEST_DIRECTORY := $(CURDIR)
export TEST_DIRECTORY

View File

@@ -8,7 +8,8 @@
test_description='Test bidirectionality of remote-hg'
. ./test-lib.sh
test -n "$TEST_DIRECTORY" || TEST_DIRECTORY=$(dirname $0)/
. "$TEST_DIRECTORY"/test-lib.sh
if ! test_have_prereq PYTHON
then
@@ -16,7 +17,7 @@ then
test_done
fi
if ! python -c 'import mercurial'
if ! python2 -c 'import mercurial' > /dev/null 2>&1
then
skip_all='skipping remote-hg tests; mercurial not available'
test_done
@@ -54,17 +55,17 @@ hg_log () {
}
setup () {
(
echo "[ui]"
echo "username = A U Thor <author@example.com>"
echo "[defaults]"
echo "backout = -d \"0 0\""
echo "commit = -d \"0 0\""
echo "debugrawcommit = -d \"0 0\""
echo "tag = -d \"0 0\""
echo "[extensions]"
echo "graphlog ="
) >>"$HOME"/.hgrc &&
cat > "$HOME"/.hgrc <<-EOF &&
[ui]
username = A U Thor <author@example.com>
[defaults]
backout = -d "0 0"
commit = -d "0 0"
debugrawcommit = -d "0 0"
tag = -d "0 0"
[extensions]"
graphlog =
EOF
git config --global remote-hg.hg-git-compat true
git config --global remote-hg.track-branches true

View File

@@ -8,7 +8,8 @@
test_description='Test remote-hg output compared to hg-git'
. ./test-lib.sh
test -n "$TEST_DIRECTORY" || TEST_DIRECTORY=$(dirname $0)/
. "$TEST_DIRECTORY"/test-lib.sh
if ! test_have_prereq PYTHON
then
@@ -16,18 +17,32 @@ then
test_done
fi
if ! python -c 'import mercurial'
if ! python2 -c 'import mercurial' > /dev/null 2>&1
then
skip_all='skipping remote-hg tests; mercurial not available'
test_done
fi
if ! python -c 'import hggit'
if python2 -c 'import hggit' > /dev/null 2>&1
then
hggit=hggit
elif python2 -c 'import hgext.git' > /dev/null 2>&1
then
hggit=hgext.git
else
skip_all='skipping remote-hg tests; hg-git not available'
test_done
fi
hg_version=$(python2 -c 'from mercurial import util; print util.version()')
case $hg_version in
3.0*+*)
skip_all='skipping remote-hg tests; unsuported version of hg by hg-git'
test_done
;;
esac
# clone to a git repo with git
git_clone_git () {
git clone -q "hg::$1" $2 &&
@@ -91,19 +106,18 @@ git_log () {
}
setup () {
(
echo "[ui]"
echo "username = A U Thor <author@example.com>"
echo "[defaults]"
echo "backout = -d \"0 0\""
echo "commit = -d \"0 0\""
echo "debugrawcommit = -d \"0 0\""
echo "tag = -d \"0 0\""
echo "[extensions]"
echo "hgext.bookmarks ="
echo "hggit ="
echo "graphlog ="
) >>"$HOME"/.hgrc &&
cat > "$HOME"/.hgrc <<-EOF &&
[ui]
username = A U Thor <author@example.com>
[defaults]
backout = -d "0 0"
commit = -d "0 0"
debugrawcommit = -d "0 0"
tag = -d "0 0"
[extensions]
$hggit =
graphlog =
EOF
git config --global receive.denycurrentbranch warn
git config --global remote-hg.hg-git-compat true
git config --global remote-hg.track-branches false

View File

@@ -8,7 +8,7 @@
test_description='Test remote-hg'
test -n "$TEST_DIRECTORY" || TEST_DIRECTORY=${0%/*}/../../t
test -n "$TEST_DIRECTORY" || TEST_DIRECTORY=$(dirname $0)/
. "$TEST_DIRECTORY"/test-lib.sh
if ! test_have_prereq PYTHON
@@ -17,7 +17,7 @@ then
test_done
fi
if ! python -c 'import mercurial'
if ! python2 -c 'import mercurial' > /dev/null 2>&1
then
skip_all='skipping remote-hg tests; mercurial not available'
test_done
@@ -25,7 +25,7 @@ fi
check () {
echo $3 > expected &&
git --git-dir=$1/.git log --format='%s' -1 $2 >actual
git --git-dir=$1/.git log --format='%s' -1 $2 > actual &&
test_cmp expected actual
}
@@ -53,6 +53,17 @@ check_bookmark () {
fi
}
check_files () {
git --git-dir=$1/.git ls-files > actual &&
if test $# -gt 1
then
printf "%s\n" "$2" > expected
else
> expected
fi &&
test_cmp expected actual
}
check_push () {
expected_ret=$1 ret=0 ref_ret=0
@@ -92,12 +103,12 @@ check_push () {
}
setup () {
(
echo "[ui]"
echo "username = H G Wells <wells@example.com>"
echo "[extensions]"
echo "mq ="
) >>"$HOME"/.hgrc &&
cat > "$HOME"/.hgrc <<-EOF &&
[ui]
username = H G Wells <wells@example.com>
[extensions]
mq =
EOF
GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230" &&
GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" &&
@@ -106,17 +117,18 @@ setup () {
setup
test_expect_success 'cloning' '
test_when_finished "rm -rf gitrepo*" &&
test_expect_success 'setup' '
(
hg init hgrepo &&
cd hgrepo &&
echo zero > content &&
hg add content &&
hg commit -m zero
) &&
)
'
test_expect_success 'cloning' '
test_when_finished "rm -rf gitrepo*" &&
git clone "hg::hgrepo" gitrepo &&
check gitrepo HEAD zero
'
@@ -772,4 +784,244 @@ test_expect_success 'remote double failed push' '
)
'
test_expect_success 'clone remote with null bookmark, then push' '
test_when_finished "rm -rf gitrepo* hgrepo*" &&
(
hg init hgrepo &&
cd hgrepo &&
echo a > a &&
hg add a &&
hg commit -m a &&
hg bookmark -r null bookmark
) &&
(
git clone "hg::hgrepo" gitrepo &&
check gitrepo HEAD a &&
cd gitrepo &&
git checkout --quiet -b bookmark &&
git remote -v &&
echo b > b &&
git add b &&
git commit -m b &&
git push origin bookmark
)
'
test_expect_success 'notes' '
test_when_finished "rm -rf hgrepo gitrepo" &&
(
hg init hgrepo &&
cd hgrepo &&
echo one > content &&
hg add content &&
hg commit -m one &&
echo two > content &&
hg commit -m two
) &&
git clone "hg::hgrepo" gitrepo &&
hg -R hgrepo log --template "{node}\n\n" > expected &&
git --git-dir=gitrepo/.git log --pretty="tformat:%N" --notes=hg > actual &&
test_cmp expected actual
'
test_expect_failure 'push updates notes' '
test_when_finished "rm -rf hgrepo gitrepo" &&
(
hg init hgrepo &&
cd hgrepo &&
echo one > content &&
hg add content &&
hg commit -m one
) &&
git clone "hg::hgrepo" gitrepo &&
(
cd gitrepo &&
echo two > content &&
git commit -a -m two
git push
) &&
hg -R hgrepo log --template "{node}\n\n" > expected &&
git --git-dir=gitrepo/.git log --pretty="tformat:%N" --notes=hg > actual &&
test_cmp expected actual
'
test_expect_success 'pull tags' '
test_when_finished "rm -rf hgrepo gitrepo" &&
(
hg init hgrepo &&
cd hgrepo &&
echo one > content &&
hg add content &&
hg commit -m one
) &&
git clone "hg::hgrepo" gitrepo &&
(cd hgrepo && hg tag v1.0) &&
(cd gitrepo && git pull) &&
echo "v1.0" > expected &&
git --git-dir=gitrepo/.git tag > actual &&
test_cmp expected actual
'
test_expect_success 'push merged named branch' '
test_when_finished "rm -rf hgrepo gitrepo" &&
(
hg init hgrepo &&
cd hgrepo &&
echo one > content &&
hg add content &&
hg commit -m one &&
hg branch feature &&
echo two > content &&
hg commit -m two &&
hg update default &&
echo three > content &&
hg commit -m three
) &&
(
git clone "hg::hgrepo" gitrepo &&
cd gitrepo &&
git merge -m Merge -Xtheirs origin/branches/feature &&
git push
) &&
cat > expected <<-EOF
Merge
three
two
one
EOF
hg -R hgrepo log --template "{desc}\n" > actual &&
test_cmp expected actual
'
test_expect_success 'light tag sets author' '
test_when_finished "rm -rf hgrepo gitrepo" &&
(
hg init hgrepo &&
cd hgrepo &&
echo one > content &&
hg add content &&
hg commit -m one
) &&
(
git clone "hg::hgrepo" gitrepo &&
cd gitrepo &&
git tag v1.0 &&
git push --tags
) &&
echo "C O Mitter <committer@example.com>" > expected &&
hg -R hgrepo log --template "{author}\n" -r tip > actual &&
test_cmp expected actual
'
test_expect_success 'push tag different branch' '
test_when_finished "rm -rf hgrepo gitrepo" &&
(
hg init hgrepo &&
cd hgrepo &&
echo one > content &&
hg add content &&
hg commit -m one
hg branch feature &&
echo two > content &&
hg commit -m two
) &&
(
git clone "hg::hgrepo" gitrepo &&
cd gitrepo &&
git branch &&
git checkout branches/feature &&
git tag v1.0 &&
git push --tags
) &&
echo feature > expected &&
hg -R hgrepo log --template="{branch}\n" -r tip > actual &&
test_cmp expected actual
'
test_expect_success 'cloning a removed file works' '
test_when_finished "rm -rf hgrepo gitrepo" &&
(
hg init hgrepo &&
cd hgrepo &&
echo test > test_file &&
hg add test_file &&
hg commit -m add &&
hg rm test_file &&
hg commit -m remove
) &&
git clone "hg::hgrepo" gitrepo &&
check_files gitrepo
'
test_expect_success 'cloning a file replaced with a directory' '
test_when_finished "rm -rf hgrepo gitrepo" &&
(
hg init hgrepo &&
cd hgrepo &&
echo test > dir_or_file &&
hg add dir_or_file &&
hg commit -m add &&
hg rm dir_or_file &&
mkdir dir_or_file &&
echo test > dir_or_file/test_file &&
hg add dir_or_file/test_file &&
hg commit -m replase
) &&
git clone "hg::hgrepo" gitrepo &&
check_files gitrepo "dir_or_file/test_file"
'
test_expect_success 'clone replace directory with a file' '
test_when_finished "rm -rf hgrepo gitrepo" &&
(
hg init hgrepo &&
cd hgrepo &&
mkdir dir_or_file &&
echo test > dir_or_file/test_file &&
hg add dir_or_file/test_file &&
hg commit -m add &&
hg rm dir_or_file/test_file &&
echo test > dir_or_file &&
hg add dir_or_file &&
hg commit -m add &&
hg rm dir_or_file
) &&
git clone "hg::hgrepo" gitrepo &&
check_files gitrepo "dir_or_file"
'
test_done