mirror of
				https://github.com/mnauw/git-remote-hg.git
				synced 2025-10-31 00:25:48 +01:00 
			
		
		
		
	Compare commits
	
		
			43 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | e71a8963af | ||
|  | f20dbc33c3 | ||
|  | e4aeae6d4b | ||
|  | aa528c9649 | ||
|  | 018aa4753b | ||
|  | f173208406 | ||
|  | e7df347fab | ||
|  | 0de8aa91f4 | ||
|  | 22d9794c11 | ||
|  | f53a8653ab | ||
|  | b4c63539f2 | ||
|  | 38070007aa | ||
|  | fadd5f698b | ||
|  | 1eb8fa4805 | ||
|  | 19f31c1c84 | ||
|  | ff221de459 | ||
|  | 179fefda96 | ||
|  | c226ba3904 | ||
|  | 776e36c147 | ||
|  | 5b6d5283cb | ||
|  | 5738ee42d8 | ||
|  | 1d27390dd0 | ||
|  | cad5c95465 | ||
|  | 259838a342 | ||
|  | 55bbd81a75 | ||
|  | 8db5b9a537 | ||
|  | bbc4009acf | ||
|  | c84feb364b | ||
|  | 990152c0c8 | ||
|  | 6ba42cdf98 | ||
|  | ef00e40d7c | ||
|  | 1c72617831 | ||
|  | 184551c71d | ||
|  | 32d4f36f22 | ||
|  | ccb3f13d69 | ||
|  | 2958556bec | ||
|  | 4ea2fa76b3 | ||
|  | 84b8b482a4 | ||
|  | 978314a4be | ||
|  | 51eabd4a17 | ||
|  | 98c3535c3f | ||
|  | 3abf376e9e | ||
|  | 0b71ca38e7 | 
							
								
								
									
										26
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| 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 || true | ||||
|  | ||||
| before_script: | ||||
|   - hg --version || true | ||||
|   - pip show hg-git dulwich | ||||
|  | ||||
| script: | ||||
|   - export TEST_OPTS='-v' | ||||
|   - 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=dev | ||||
|     - python: 2.7 | ||||
|     - python: 2.6 | ||||
							
								
								
									
										27
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								Makefile
									
									
									
									
									
								
							| @@ -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 | ||||
|   | ||||
							
								
								
									
										129
									
								
								README.asciidoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								README.asciidoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | ||||
| '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 :) | ||||
|  | ||||
| == 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
									
								
							
							
						
						
									
										1
									
								
								doc/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| git-remote-hg.1 | ||||
							
								
								
									
										5
									
								
								doc/SubmittingPatches
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								doc/SubmittingPatches
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										121
									
								
								doc/git-remote-hg.txt
									
									
									
									
									
										Normal 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]). | ||||
							
								
								
									
										112
									
								
								git-remote-hg
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								git-remote-hg
									
									
									
									
									
								
							| @@ -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 | ||||
| @@ -156,7 +159,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 +265,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, '/') | ||||
| @@ -421,7 +427,7 @@ 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') | ||||
|         repo.pull(peer, heads=None, force=True) | ||||
| @@ -436,16 +442,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 +496,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" % (cuser, ctime, gittz(int(ctz))) | ||||
|             except ValueError: | ||||
|                 cuser = extra['committer'] | ||||
|                 committer = "%s %d %s" % (fixup_user(cuser), time, gittz(tz)) | ||||
|         else: | ||||
|             committer = author | ||||
|  | ||||
| @@ -614,7 +654,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 +683,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(): | ||||
| @@ -766,6 +809,10 @@ def parse_commit(parser): | ||||
|         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 +917,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 +953,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 +986,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 +1036,26 @@ def push_unsafe(repo, remote, parsed_refs, p_revs): | ||||
|     if not checkheads(repo, remote, p_revs): | ||||
|         return None | ||||
|  | ||||
|     if 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 +1273,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 +1312,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
									
								
							
							
						
						
									
										3
									
								
								test/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| test-results/ | ||||
| trash directory.*/ | ||||
| .prove | ||||
| @@ -1,6 +1,6 @@ | ||||
| RM ?= rm -f | ||||
|  | ||||
| T = $(wildcard ../test-*.sh) | ||||
| T = $(wildcard *.t) | ||||
| TEST_DIRECTORY := $(CURDIR) | ||||
|  | ||||
| export TEST_DIRECTORY | ||||
|   | ||||
| @@ -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 | ||||
| 
 | ||||
| @@ -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,34 @@ 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()') | ||||
| 
 | ||||
| echo "hg_version: $hg_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 +108,19 @@ 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] | ||||
| 	hgext.bookmarks = | ||||
| 	$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 | ||||
| @@ -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 | ||||
| ' | ||||
| @@ -458,7 +470,7 @@ rm -rf hgrepo | ||||
| test_expect_success 'fetch special filenames' ' | ||||
| 	test_when_finished "rm -rf hgrepo gitrepo && LC_ALL=C" && | ||||
| 
 | ||||
| 	LC_ALL=en_US.UTF-8 | ||||
| 	LC_ALL=C.UTF-8 | ||||
| 	export LC_ALL | ||||
| 
 | ||||
| 	( | ||||
| @@ -491,7 +503,7 @@ test_expect_success 'push special filenames' ' | ||||
| 
 | ||||
| 	mkdir -p tmp && cd tmp && | ||||
| 
 | ||||
| 	LC_ALL=en_US.UTF-8 | ||||
| 	LC_ALL=C.UTF-8 | ||||
| 	export LC_ALL | ||||
| 
 | ||||
| 	( | ||||
| @@ -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 | ||||
		Reference in New Issue
	
	Block a user