# HG changeset patch # User Eric Eisner # Date 1289776526 18000 # Node ID 2b73a3279a9fe2d0af10f67f4cdb8f89c14b7a8a # Parent 23de1a7f8e82b4cb051437e0e7abf3e3bc935a10 subrepo: support for adding a git subrepo gitsubrepo based on patch from David Soria Parra: http://bitbucket.org/segv/davids-poor-git-subrepo-attempt/ diff -r 23de1a7f8e82 -r 2b73a3279a9f mercurial/subrepo.py --- a/mercurial/subrepo.py Mon Nov 15 10:57:49 2010 -0600 +++ b/mercurial/subrepo.py Sun Nov 14 18:15:26 2010 -0500 @@ -5,7 +5,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import errno, os, re, xml.dom.minidom, shutil, urlparse, posixpath +import errno, os, re, xml.dom.minidom, shutil, subprocess, urlparse, posixpath from i18n import _ import config, util, node, error, cmdutil hg = None @@ -576,7 +576,87 @@ return self._svncommand(['cat'], name) +class gitsubrepo(object): + def __init__(self, ctx, path, state): + # TODO add git version check. + self._state = state + self._ctx = ctx + self._path = ctx._repo.wjoin(path) + self._ui = ctx._repo.ui + + def _gitcommand(self, commands): + return self._gitdir(commands)[0] + + def _gitdir(self, commands): + commands = ['--no-pager', '--git-dir=%s/.git' % self._path, + '--work-tree=%s' % self._path] + commands + return self._gitnodir(commands) + + def _gitnodir(self, commands): + """Calls the git command + + The methods tries to call the git command. versions previor to 1.6.0 + are not supported and very probably fail. + """ + cmd = ['git'] + commands + cmd = [util.shellquote(arg) for arg in cmd] + cmd = util.quotecommand(' '.join(cmd)) + + # print git's stderr, which is mostly progress and useful info + p = subprocess.Popen(cmd, shell=True, bufsize=-1, + close_fds=(os.name == 'posix'), + stdout=subprocess.PIPE) + retdata = p.stdout.read() + # wait for the child to exit to avoid race condition. + p.wait() + + if p.returncode != 0: + # there are certain error codes that are ok + command = None + for arg in commands: + if not arg.startswith('-'): + command = arg + break + if command == 'cat-file': + return retdata, p.returncode + if command in ('commit', 'status') and p.returncode == 1: + return retdata, p.returncode + # for all others, abort + raise util.Abort('git %s error %d' % (command, p.returncode)) + + return retdata, p.returncode + + def _gitstate(self): + return self._gitcommand(['rev-parse', 'HEAD']).strip() + + def _githavelocally(self, revision): + out, code = self._gitdir(['cat-file', '-e', revision]) + return code == 0 + + def dirty(self): + if self._state[1] != self._gitstate(): # version checked out changed? + return True + # check for staged changes or modified files; ignore untracked files + # docs say --porcelain flag is future-proof format + changed = self._gitcommand(['status', '--porcelain', + '--untracked-files=no']) + return bool(changed.strip()) + + def commit(self, text, user, date): + cmd = ['commit', '-a', '-m', text] + if user: + cmd += ['--author', user] + if date: + # git's date parser silently ignores when seconds < 1e9 + # convert to ISO8601 + cmd += ['--date', util.datestr(date, '%Y-%m-%dT%H:%M:%S %1%2')] + self._gitcommand(cmd) + # make sure commit works otherwise HEAD might not exist under certain + # circumstances + return self._gitstate() + types = { 'hg': hgsubrepo, 'svn': svnsubrepo, + 'git': gitsubrepo, } diff -r 23de1a7f8e82 -r 2b73a3279a9f tests/test-subrepo-git.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-subrepo-git.t Sun Nov 14 18:15:26 2010 -0500 @@ -0,0 +1,58 @@ + $ "$TESTDIR/hghave" git || exit 80 + +make git commits repeatable + + $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME + $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL + $ GIT_AUTHOR_DATE='1234567891 +0000'; export GIT_AUTHOR_DATE + $ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME + $ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL + $ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE + +root hg repo + + $ hg init t + $ cd t + $ echo a > a + $ hg add a + $ hg commit -m a + $ cd .. + +new external git repo + + $ mkdir gitroot + $ cd gitroot + $ git init -q + $ echo g > g + $ git add g + $ git commit -q -m g + +add subrepo clone + + $ cd ../t + $ echo 's = [git]../gitroot' > .hgsub + $ git clone -q ../gitroot s + $ hg add .hgsub + $ hg commit -m 'new git subrepo' + committing subrepository $TESTTMP/t/s + $ hg debugsub + path s + source ../gitroot + revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7 + +record a new commit from upstream + + $ cd ../gitroot + $ echo gg >> g + $ git commit -q -a -m gg + + $ cd ../t/s + $ git pull -q + + $ cd .. + $ hg commit -m 'update git subrepo' + committing subrepository $TESTTMP/t/s + $ hg debugsub + path s + source ../gitroot + revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a