Merge branch 'sr/transport-helper-fix'
* sr/transport-helper-fix: (21 commits) transport-helper: die early on encountering deleted refs transport-helper: implement marks location as capability transport-helper: Use capname for refspec capability too transport-helper: change import semantics transport-helper: update ref status after push with export transport-helper: use the new done feature where possible transport-helper: check status code of finish_command transport-helper: factor out push_update_refs_status fast-export: support done feature fast-import: introduce 'done' command git-remote-testgit: fix error handling git-remote-testgit: only push for non-local repositories remote-curl: accept empty line as terminator remote-helpers: export GIT_DIR variable to helpers git_remote_helpers: push all refs during a non-local export transport-helper: don't feed bogus refs to export push git-remote-testgit: import non-HEAD refs t5800: document some non-functional parts of remote helpers t5800: use skip_all instead of prereq t5800: factor out some ref tests ...
This commit is contained in:
commit
59d9ba869e
@ -83,6 +83,10 @@ marks the same across runs.
|
|||||||
allow that. So fake a tagger to be able to fast-import the
|
allow that. So fake a tagger to be able to fast-import the
|
||||||
output.
|
output.
|
||||||
|
|
||||||
|
--use-done-feature::
|
||||||
|
Start the stream with a 'feature done' stanza, and terminate
|
||||||
|
it with a 'done' command.
|
||||||
|
|
||||||
--no-data::
|
--no-data::
|
||||||
Skip output of blob objects and instead refer to blobs via
|
Skip output of blob objects and instead refer to blobs via
|
||||||
their original SHA-1 hash. This is useful when rewriting the
|
their original SHA-1 hash. This is useful when rewriting the
|
||||||
|
@ -102,6 +102,12 @@ OPTIONS
|
|||||||
when the `cat-blob` command is encountered in the stream.
|
when the `cat-blob` command is encountered in the stream.
|
||||||
The default behaviour is to write to `stdout`.
|
The default behaviour is to write to `stdout`.
|
||||||
|
|
||||||
|
--done::
|
||||||
|
Require a `done` command at the end of the stream.
|
||||||
|
This option might be useful for detecting errors that
|
||||||
|
cause the frontend to terminate before it has started to
|
||||||
|
write a stream.
|
||||||
|
|
||||||
--export-pack-edges=<file>::
|
--export-pack-edges=<file>::
|
||||||
After creating a packfile, print a line of data to
|
After creating a packfile, print a line of data to
|
||||||
<file> listing the filename of the packfile and the last
|
<file> listing the filename of the packfile and the last
|
||||||
@ -331,6 +337,11 @@ and control the current import process. More detailed discussion
|
|||||||
standard output. This command is optional and is not needed
|
standard output. This command is optional and is not needed
|
||||||
to perform an import.
|
to perform an import.
|
||||||
|
|
||||||
|
`done`::
|
||||||
|
Marks the end of the stream. This command is optional
|
||||||
|
unless the `done` feature was requested using the
|
||||||
|
`--done` command line option or `feature done` command.
|
||||||
|
|
||||||
`cat-blob`::
|
`cat-blob`::
|
||||||
Causes fast-import to print a blob in 'cat-file --batch'
|
Causes fast-import to print a blob in 'cat-file --batch'
|
||||||
format to the file descriptor set with `--cat-blob-fd` or
|
format to the file descriptor set with `--cat-blob-fd` or
|
||||||
@ -1021,6 +1032,11 @@ notes::
|
|||||||
Versions of fast-import not supporting notes will exit
|
Versions of fast-import not supporting notes will exit
|
||||||
with a message indicating so.
|
with a message indicating so.
|
||||||
|
|
||||||
|
done::
|
||||||
|
Error out if the stream ends without a 'done' command.
|
||||||
|
Without this feature, errors causing the frontend to end
|
||||||
|
abruptly at a convenient point in the stream can go
|
||||||
|
undetected.
|
||||||
|
|
||||||
`option`
|
`option`
|
||||||
~~~~~~~~
|
~~~~~~~~
|
||||||
@ -1050,6 +1066,15 @@ not be passed as option:
|
|||||||
* cat-blob-fd
|
* cat-blob-fd
|
||||||
* force
|
* force
|
||||||
|
|
||||||
|
`done`
|
||||||
|
~~~~~~
|
||||||
|
If the `done` feature is not in use, treated as if EOF was read.
|
||||||
|
This can be used to tell fast-import to finish early.
|
||||||
|
|
||||||
|
If the `--done` command line option or `feature done` command is
|
||||||
|
in use, the `done` command is mandatory and marks the end of the
|
||||||
|
stream.
|
||||||
|
|
||||||
Crash Reports
|
Crash Reports
|
||||||
-------------
|
-------------
|
||||||
If fast-import is supplied invalid input it will terminate with a
|
If fast-import is supplied invalid input it will terminate with a
|
||||||
|
@ -48,6 +48,9 @@ arguments. The first argument specifies a remote repository as in git;
|
|||||||
it is either the name of a configured remote or a URL. The second
|
it is either the name of a configured remote or a URL. The second
|
||||||
argument specifies a URL; it is usually of the form
|
argument specifies a URL; it is usually of the form
|
||||||
'<transport>://<address>', but any arbitrary string is possible.
|
'<transport>://<address>', but any arbitrary string is possible.
|
||||||
|
The 'GIT_DIR' environment variable is set up for the remote helper
|
||||||
|
and can be used to determine where to store additional data or from
|
||||||
|
which directory to invoke auxiliary git commands.
|
||||||
|
|
||||||
When git encounters a URL of the form '<transport>://<address>', where
|
When git encounters a URL of the form '<transport>://<address>', where
|
||||||
'<transport>' is a protocol that it cannot handle natively, it
|
'<transport>' is a protocol that it cannot handle natively, it
|
||||||
|
@ -26,6 +26,7 @@ static int progress;
|
|||||||
static enum { ABORT, VERBATIM, WARN, STRIP } signed_tag_mode = ABORT;
|
static enum { ABORT, VERBATIM, WARN, STRIP } signed_tag_mode = ABORT;
|
||||||
static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ABORT;
|
static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ABORT;
|
||||||
static int fake_missing_tagger;
|
static int fake_missing_tagger;
|
||||||
|
static int use_done_feature;
|
||||||
static int no_data;
|
static int no_data;
|
||||||
static int full_tree;
|
static int full_tree;
|
||||||
|
|
||||||
@ -627,6 +628,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
|||||||
"Fake a tagger when tags lack one"),
|
"Fake a tagger when tags lack one"),
|
||||||
OPT_BOOLEAN(0, "full-tree", &full_tree,
|
OPT_BOOLEAN(0, "full-tree", &full_tree,
|
||||||
"Output full tree for each commit"),
|
"Output full tree for each commit"),
|
||||||
|
OPT_BOOLEAN(0, "use-done-feature", &use_done_feature,
|
||||||
|
"Use the done feature to terminate the stream"),
|
||||||
{ OPTION_NEGBIT, 0, "data", &no_data, NULL,
|
{ OPTION_NEGBIT, 0, "data", &no_data, NULL,
|
||||||
"Skip output of blob data",
|
"Skip output of blob data",
|
||||||
PARSE_OPT_NOARG | PARSE_OPT_NEGHELP, NULL, 1 },
|
PARSE_OPT_NOARG | PARSE_OPT_NEGHELP, NULL, 1 },
|
||||||
@ -648,6 +651,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
|||||||
if (argc > 1)
|
if (argc > 1)
|
||||||
usage_with_options (fast_export_usage, options);
|
usage_with_options (fast_export_usage, options);
|
||||||
|
|
||||||
|
if (use_done_feature)
|
||||||
|
printf("feature done\n");
|
||||||
|
|
||||||
if (import_filename)
|
if (import_filename)
|
||||||
import_marks(import_filename);
|
import_marks(import_filename);
|
||||||
|
|
||||||
@ -675,5 +681,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
|||||||
if (export_filename)
|
if (export_filename)
|
||||||
export_marks(export_filename);
|
export_marks(export_filename);
|
||||||
|
|
||||||
|
if (use_done_feature)
|
||||||
|
printf("done\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -355,6 +355,7 @@ static unsigned int cmd_save = 100;
|
|||||||
static uintmax_t next_mark;
|
static uintmax_t next_mark;
|
||||||
static struct strbuf new_data = STRBUF_INIT;
|
static struct strbuf new_data = STRBUF_INIT;
|
||||||
static int seen_data_command;
|
static int seen_data_command;
|
||||||
|
static int require_explicit_termination;
|
||||||
|
|
||||||
/* Signal handling */
|
/* Signal handling */
|
||||||
static volatile sig_atomic_t checkpoint_requested;
|
static volatile sig_atomic_t checkpoint_requested;
|
||||||
@ -3140,6 +3141,8 @@ static int parse_one_feature(const char *feature, int from_stream)
|
|||||||
relative_marks_paths = 1;
|
relative_marks_paths = 1;
|
||||||
} else if (!strcmp(feature, "no-relative-marks")) {
|
} else if (!strcmp(feature, "no-relative-marks")) {
|
||||||
relative_marks_paths = 0;
|
relative_marks_paths = 0;
|
||||||
|
} else if (!strcmp(feature, "done")) {
|
||||||
|
require_explicit_termination = 1;
|
||||||
} else if (!strcmp(feature, "force")) {
|
} else if (!strcmp(feature, "force")) {
|
||||||
force_update = 1;
|
force_update = 1;
|
||||||
} else if (!strcmp(feature, "notes") || !strcmp(feature, "ls")) {
|
} else if (!strcmp(feature, "notes") || !strcmp(feature, "ls")) {
|
||||||
@ -3290,6 +3293,8 @@ int main(int argc, const char **argv)
|
|||||||
parse_reset_branch();
|
parse_reset_branch();
|
||||||
else if (!strcmp("checkpoint", command_buf.buf))
|
else if (!strcmp("checkpoint", command_buf.buf))
|
||||||
parse_checkpoint();
|
parse_checkpoint();
|
||||||
|
else if (!strcmp("done", command_buf.buf))
|
||||||
|
break;
|
||||||
else if (!prefixcmp(command_buf.buf, "progress "))
|
else if (!prefixcmp(command_buf.buf, "progress "))
|
||||||
parse_progress();
|
parse_progress();
|
||||||
else if (!prefixcmp(command_buf.buf, "feature "))
|
else if (!prefixcmp(command_buf.buf, "feature "))
|
||||||
@ -3309,6 +3314,9 @@ int main(int argc, const char **argv)
|
|||||||
if (!seen_data_command)
|
if (!seen_data_command)
|
||||||
parse_argv();
|
parse_argv();
|
||||||
|
|
||||||
|
if (require_explicit_termination && feof(stdin))
|
||||||
|
die("stream ends early");
|
||||||
|
|
||||||
end_packfile();
|
end_packfile();
|
||||||
|
|
||||||
dump_branches();
|
dump_branches();
|
||||||
|
@ -35,7 +35,7 @@ def get_repo(alias, url):
|
|||||||
prefix = 'refs/testgit/%s/' % alias
|
prefix = 'refs/testgit/%s/' % alias
|
||||||
debug("prefix: '%s'", prefix)
|
debug("prefix: '%s'", prefix)
|
||||||
|
|
||||||
repo.gitdir = ""
|
repo.gitdir = os.environ["GIT_DIR"]
|
||||||
repo.alias = alias
|
repo.alias = alias
|
||||||
repo.prefix = prefix
|
repo.prefix = prefix
|
||||||
|
|
||||||
@ -70,9 +70,19 @@ def do_capabilities(repo, args):
|
|||||||
|
|
||||||
print "import"
|
print "import"
|
||||||
print "export"
|
print "export"
|
||||||
print "gitdir"
|
|
||||||
print "refspec refs/heads/*:%s*" % repo.prefix
|
print "refspec refs/heads/*:%s*" % repo.prefix
|
||||||
|
|
||||||
|
dirname = repo.get_base_path(repo.gitdir)
|
||||||
|
|
||||||
|
if not os.path.exists(dirname):
|
||||||
|
os.makedirs(dirname)
|
||||||
|
|
||||||
|
path = os.path.join(dirname, 'testgit.marks')
|
||||||
|
|
||||||
|
print "*export-marks %s" % path
|
||||||
|
if os.path.exists(path):
|
||||||
|
print "*import-marks %s" % path
|
||||||
|
|
||||||
print # end capabilities
|
print # end capabilities
|
||||||
|
|
||||||
|
|
||||||
@ -121,8 +131,24 @@ def do_import(repo, args):
|
|||||||
if not repo.gitdir:
|
if not repo.gitdir:
|
||||||
die("Need gitdir to import")
|
die("Need gitdir to import")
|
||||||
|
|
||||||
|
ref = args[0]
|
||||||
|
refs = [ref]
|
||||||
|
|
||||||
|
while True:
|
||||||
|
line = sys.stdin.readline()
|
||||||
|
if line == '\n':
|
||||||
|
break
|
||||||
|
if not line.startswith('import '):
|
||||||
|
die("Expected import line.")
|
||||||
|
|
||||||
|
# strip of leading 'import '
|
||||||
|
ref = line[7:].strip()
|
||||||
|
refs.append(ref)
|
||||||
|
|
||||||
repo = update_local_repo(repo)
|
repo = update_local_repo(repo)
|
||||||
repo.exporter.export_repo(repo.gitdir)
|
repo.exporter.export_repo(repo.gitdir, refs)
|
||||||
|
|
||||||
|
print "done"
|
||||||
|
|
||||||
|
|
||||||
def do_export(repo, args):
|
def do_export(repo, args):
|
||||||
@ -132,32 +158,15 @@ def do_export(repo, args):
|
|||||||
if not repo.gitdir:
|
if not repo.gitdir:
|
||||||
die("Need gitdir to export")
|
die("Need gitdir to export")
|
||||||
|
|
||||||
dirname = repo.get_base_path(repo.gitdir)
|
|
||||||
|
|
||||||
if not os.path.exists(dirname):
|
|
||||||
os.makedirs(dirname)
|
|
||||||
|
|
||||||
path = os.path.join(dirname, 'testgit.marks')
|
|
||||||
print path
|
|
||||||
if os.path.exists(path):
|
|
||||||
print path
|
|
||||||
else:
|
|
||||||
print ""
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
update_local_repo(repo)
|
update_local_repo(repo)
|
||||||
repo.importer.do_import(repo.gitdir)
|
changed = repo.importer.do_import(repo.gitdir)
|
||||||
repo.non_local.push(repo.gitdir)
|
|
||||||
|
|
||||||
|
if not repo.local:
|
||||||
|
repo.non_local.push(repo.gitdir)
|
||||||
|
|
||||||
def do_gitdir(repo, args):
|
for ref in changed:
|
||||||
"""Stores the location of the gitdir.
|
print "ok %s" % ref
|
||||||
"""
|
print
|
||||||
|
|
||||||
if not args:
|
|
||||||
die("gitdir needs an argument")
|
|
||||||
|
|
||||||
repo.gitdir = ' '.join(args)
|
|
||||||
|
|
||||||
|
|
||||||
COMMANDS = {
|
COMMANDS = {
|
||||||
@ -165,7 +174,6 @@ COMMANDS = {
|
|||||||
'list': do_list,
|
'list': do_list,
|
||||||
'import': do_import,
|
'import': do_import,
|
||||||
'export': do_export,
|
'export': do_export,
|
||||||
'gitdir': do_gitdir,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@ import os
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from git_remote_helpers.util import check_call
|
||||||
|
|
||||||
|
|
||||||
class GitExporter(object):
|
class GitExporter(object):
|
||||||
"""An exporter for testgit repositories.
|
"""An exporter for testgit repositories.
|
||||||
@ -15,7 +17,7 @@ class GitExporter(object):
|
|||||||
|
|
||||||
self.repo = repo
|
self.repo = repo
|
||||||
|
|
||||||
def export_repo(self, base):
|
def export_repo(self, base, refs=None):
|
||||||
"""Exports a fast-export stream for the given directory.
|
"""Exports a fast-export stream for the given directory.
|
||||||
|
|
||||||
Simply delegates to git fast-epxort and pipes it through sed
|
Simply delegates to git fast-epxort and pipes it through sed
|
||||||
@ -23,8 +25,13 @@ class GitExporter(object):
|
|||||||
default refs/heads. This is to demonstrate how the export
|
default refs/heads. This is to demonstrate how the export
|
||||||
data can be stored under it's own ref (using the refspec
|
data can be stored under it's own ref (using the refspec
|
||||||
capability).
|
capability).
|
||||||
|
|
||||||
|
If None, refs defaults to ["HEAD"].
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if not refs:
|
||||||
|
refs = ["HEAD"]
|
||||||
|
|
||||||
dirname = self.repo.get_base_path(base)
|
dirname = self.repo.get_base_path(base)
|
||||||
path = os.path.abspath(os.path.join(dirname, 'testgit.marks'))
|
path = os.path.abspath(os.path.join(dirname, 'testgit.marks'))
|
||||||
|
|
||||||
@ -42,12 +49,10 @@ class GitExporter(object):
|
|||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
args.append("--import-marks=" + path)
|
args.append("--import-marks=" + path)
|
||||||
|
|
||||||
args.append("HEAD")
|
args.extend(refs)
|
||||||
|
|
||||||
p1 = subprocess.Popen(args, stdout=subprocess.PIPE)
|
p1 = subprocess.Popen(args, stdout=subprocess.PIPE)
|
||||||
|
|
||||||
args = ["sed", "s_refs/heads/_" + self.repo.prefix + "_g"]
|
args = ["sed", "s_refs/heads/_" + self.repo.prefix + "_g"]
|
||||||
|
|
||||||
child = subprocess.Popen(args, stdin=p1.stdout)
|
check_call(args, stdin=p1.stdout)
|
||||||
if child.wait() != 0:
|
|
||||||
raise CalledProcessError
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
from git_remote_helpers.util import check_call, check_output
|
||||||
|
|
||||||
|
|
||||||
class GitImporter(object):
|
class GitImporter(object):
|
||||||
"""An importer for testgit repositories.
|
"""An importer for testgit repositories.
|
||||||
@ -14,6 +16,18 @@ class GitImporter(object):
|
|||||||
|
|
||||||
self.repo = repo
|
self.repo = repo
|
||||||
|
|
||||||
|
def get_refs(self, gitdir):
|
||||||
|
"""Returns a dictionary with refs.
|
||||||
|
"""
|
||||||
|
args = ["git", "--git-dir=" + gitdir, "for-each-ref", "refs/heads"]
|
||||||
|
lines = check_output(args).strip().split('\n')
|
||||||
|
refs = {}
|
||||||
|
for line in lines:
|
||||||
|
value, name = line.split(' ')
|
||||||
|
name = name.strip('commit\t')
|
||||||
|
refs[name] = value
|
||||||
|
return refs
|
||||||
|
|
||||||
def do_import(self, base):
|
def do_import(self, base):
|
||||||
"""Imports a fast-import stream to the given directory.
|
"""Imports a fast-import stream to the given directory.
|
||||||
|
|
||||||
@ -30,11 +44,23 @@ class GitImporter(object):
|
|||||||
if not os.path.exists(dirname):
|
if not os.path.exists(dirname):
|
||||||
os.makedirs(dirname)
|
os.makedirs(dirname)
|
||||||
|
|
||||||
|
refs_before = self.get_refs(gitdir)
|
||||||
|
|
||||||
args = ["git", "--git-dir=" + gitdir, "fast-import", "--quiet", "--export-marks=" + path]
|
args = ["git", "--git-dir=" + gitdir, "fast-import", "--quiet", "--export-marks=" + path]
|
||||||
|
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
args.append("--import-marks=" + path)
|
args.append("--import-marks=" + path)
|
||||||
|
|
||||||
child = subprocess.Popen(args)
|
check_call(args)
|
||||||
if child.wait() != 0:
|
|
||||||
raise CalledProcessError
|
refs_after = self.get_refs(gitdir)
|
||||||
|
|
||||||
|
changed = {}
|
||||||
|
|
||||||
|
for name, value in refs_after.iteritems():
|
||||||
|
if refs_before.get(name) == value:
|
||||||
|
continue
|
||||||
|
|
||||||
|
changed[name] = value
|
||||||
|
|
||||||
|
return changed
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from git_remote_helpers.util import die, warn
|
from git_remote_helpers.util import check_call, die, warn
|
||||||
|
|
||||||
|
|
||||||
class NonLocalGit(object):
|
class NonLocalGit(object):
|
||||||
@ -29,9 +29,7 @@ class NonLocalGit(object):
|
|||||||
os.makedirs(path)
|
os.makedirs(path)
|
||||||
args = ["git", "clone", "--bare", "--quiet", self.repo.gitpath, path]
|
args = ["git", "clone", "--bare", "--quiet", self.repo.gitpath, path]
|
||||||
|
|
||||||
child = subprocess.Popen(args)
|
check_call(args)
|
||||||
if child.wait() != 0:
|
|
||||||
raise CalledProcessError
|
|
||||||
|
|
||||||
return path
|
return path
|
||||||
|
|
||||||
@ -45,14 +43,10 @@ class NonLocalGit(object):
|
|||||||
die("could not find repo at %s", path)
|
die("could not find repo at %s", path)
|
||||||
|
|
||||||
args = ["git", "--git-dir=" + path, "fetch", "--quiet", self.repo.gitpath]
|
args = ["git", "--git-dir=" + path, "fetch", "--quiet", self.repo.gitpath]
|
||||||
child = subprocess.Popen(args)
|
check_call(args)
|
||||||
if child.wait() != 0:
|
|
||||||
raise CalledProcessError
|
|
||||||
|
|
||||||
args = ["git", "--git-dir=" + path, "update-ref", "refs/heads/master", "FETCH_HEAD"]
|
args = ["git", "--git-dir=" + path, "update-ref", "refs/heads/master", "FETCH_HEAD"]
|
||||||
child = subprocess.Popen(args)
|
child = check_call(args)
|
||||||
if child.wait() != 0:
|
|
||||||
raise CalledProcessError
|
|
||||||
|
|
||||||
def push(self, base):
|
def push(self, base):
|
||||||
"""Pushes from the non-local repo to base.
|
"""Pushes from the non-local repo to base.
|
||||||
@ -63,7 +57,5 @@ class NonLocalGit(object):
|
|||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
die("could not find repo at %s", path)
|
die("could not find repo at %s", path)
|
||||||
|
|
||||||
args = ["git", "--git-dir=" + path, "push", "--quiet", self.repo.gitpath]
|
args = ["git", "--git-dir=" + path, "push", "--quiet", self.repo.gitpath, "--all"]
|
||||||
child = subprocess.Popen(args)
|
child = check_call(args)
|
||||||
if child.wait() != 0:
|
|
||||||
raise CalledProcessError
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
from git_remote_helpers.util import check_call
|
||||||
|
|
||||||
|
|
||||||
def sanitize(rev, sep='\t'):
|
def sanitize(rev, sep='\t'):
|
||||||
"""Converts a for-each-ref line to a name/value pair.
|
"""Converts a for-each-ref line to a name/value pair.
|
||||||
"""
|
"""
|
||||||
@ -53,9 +56,7 @@ class GitRepo(object):
|
|||||||
path = ".cached_revs"
|
path = ".cached_revs"
|
||||||
ofile = open(path, "w")
|
ofile = open(path, "w")
|
||||||
|
|
||||||
child = subprocess.Popen(args, stdout=ofile)
|
check_call(args, stdout=ofile)
|
||||||
if child.wait() != 0:
|
|
||||||
raise CalledProcessError
|
|
||||||
output = open(path).readlines()
|
output = open(path).readlines()
|
||||||
self.revmap = dict(sanitize(i) for i in output)
|
self.revmap = dict(sanitize(i) for i in output)
|
||||||
if "HEAD" in self.revmap:
|
if "HEAD" in self.revmap:
|
||||||
|
@ -11,6 +11,21 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
try:
|
||||||
|
from subprocess import CalledProcessError
|
||||||
|
except ImportError:
|
||||||
|
# from python2.7:subprocess.py
|
||||||
|
# Exception classes used by this module.
|
||||||
|
class CalledProcessError(Exception):
|
||||||
|
"""This exception is raised when a process run by check_call() returns
|
||||||
|
a non-zero exit status. The exit status will be stored in the
|
||||||
|
returncode attribute."""
|
||||||
|
def __init__(self, returncode, cmd):
|
||||||
|
self.returncode = returncode
|
||||||
|
self.cmd = cmd
|
||||||
|
def __str__(self):
|
||||||
|
return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
|
||||||
|
|
||||||
|
|
||||||
# Whether or not to show debug messages
|
# Whether or not to show debug messages
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
@ -128,6 +143,72 @@ def run_command (args, cwd = None, shell = False, add_env = None,
|
|||||||
return (exit_code, output, errors)
|
return (exit_code, output, errors)
|
||||||
|
|
||||||
|
|
||||||
|
# from python2.7:subprocess.py
|
||||||
|
def call(*popenargs, **kwargs):
|
||||||
|
"""Run command with arguments. Wait for command to complete, then
|
||||||
|
return the returncode attribute.
|
||||||
|
|
||||||
|
The arguments are the same as for the Popen constructor. Example:
|
||||||
|
|
||||||
|
retcode = call(["ls", "-l"])
|
||||||
|
"""
|
||||||
|
return subprocess.Popen(*popenargs, **kwargs).wait()
|
||||||
|
|
||||||
|
|
||||||
|
# from python2.7:subprocess.py
|
||||||
|
def check_call(*popenargs, **kwargs):
|
||||||
|
"""Run command with arguments. Wait for command to complete. If
|
||||||
|
the exit code was zero then return, otherwise raise
|
||||||
|
CalledProcessError. The CalledProcessError object will have the
|
||||||
|
return code in the returncode attribute.
|
||||||
|
|
||||||
|
The arguments are the same as for the Popen constructor. Example:
|
||||||
|
|
||||||
|
check_call(["ls", "-l"])
|
||||||
|
"""
|
||||||
|
retcode = call(*popenargs, **kwargs)
|
||||||
|
if retcode:
|
||||||
|
cmd = kwargs.get("args")
|
||||||
|
if cmd is None:
|
||||||
|
cmd = popenargs[0]
|
||||||
|
raise CalledProcessError(retcode, cmd)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# from python2.7:subprocess.py
|
||||||
|
def check_output(*popenargs, **kwargs):
|
||||||
|
r"""Run command with arguments and return its output as a byte string.
|
||||||
|
|
||||||
|
If the exit code was non-zero it raises a CalledProcessError. The
|
||||||
|
CalledProcessError object will have the return code in the returncode
|
||||||
|
attribute and output in the output attribute.
|
||||||
|
|
||||||
|
The arguments are the same as for the Popen constructor. Example:
|
||||||
|
|
||||||
|
>>> check_output(["ls", "-l", "/dev/null"])
|
||||||
|
'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n'
|
||||||
|
|
||||||
|
The stdout argument is not allowed as it is used internally.
|
||||||
|
To capture standard error in the result, use stderr=STDOUT.
|
||||||
|
|
||||||
|
>>> check_output(["/bin/sh", "-c",
|
||||||
|
... "ls -l non_existent_file ; exit 0"],
|
||||||
|
... stderr=STDOUT)
|
||||||
|
'ls: non_existent_file: No such file or directory\n'
|
||||||
|
"""
|
||||||
|
if 'stdout' in kwargs:
|
||||||
|
raise ValueError('stdout argument not allowed, it will be overridden.')
|
||||||
|
process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
|
||||||
|
output, unused_err = process.communicate()
|
||||||
|
retcode = process.poll()
|
||||||
|
if retcode:
|
||||||
|
cmd = kwargs.get("args")
|
||||||
|
if cmd is None:
|
||||||
|
cmd = popenargs[0]
|
||||||
|
raise subprocess.CalledProcessError(retcode, cmd)
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
def file_reader_method (missing_ok = False):
|
def file_reader_method (missing_ok = False):
|
||||||
"""Decorator for simplifying reading of files.
|
"""Decorator for simplifying reading of files.
|
||||||
|
|
||||||
|
@ -855,7 +855,14 @@ int main(int argc, const char **argv)
|
|||||||
http_init(remote);
|
http_init(remote);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (strbuf_getline(&buf, stdin, '\n') == EOF)
|
if (strbuf_getline(&buf, stdin, '\n') == EOF) {
|
||||||
|
if (ferror(stdin))
|
||||||
|
fprintf(stderr, "Error reading command stream\n");
|
||||||
|
else
|
||||||
|
fprintf(stderr, "Unexpected end of command stream\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (buf.len == 0)
|
||||||
break;
|
break;
|
||||||
if (!prefixcmp(buf.buf, "fetch ")) {
|
if (!prefixcmp(buf.buf, "fetch ")) {
|
||||||
if (nongit)
|
if (nongit)
|
||||||
@ -895,6 +902,7 @@ int main(int argc, const char **argv)
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
} else {
|
} else {
|
||||||
|
fprintf(stderr, "Unknown command '%s'\n", buf.buf);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
strbuf_reset(&buf);
|
strbuf_reset(&buf);
|
||||||
|
@ -7,17 +7,27 @@ test_description='Test remote-helper import and export commands'
|
|||||||
|
|
||||||
. ./test-lib.sh
|
. ./test-lib.sh
|
||||||
|
|
||||||
if test_have_prereq PYTHON && "$PYTHON_PATH" -c '
|
if ! test_have_prereq PYTHON ; then
|
||||||
|
skip_all='skipping git-remote-hg tests, python not available'
|
||||||
|
test_done
|
||||||
|
fi
|
||||||
|
|
||||||
|
"$PYTHON_PATH" -c '
|
||||||
import sys
|
import sys
|
||||||
if sys.hexversion < 0x02040000:
|
if sys.hexversion < 0x02040000:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
'
|
' || {
|
||||||
then
|
skip_all='skipping git-remote-hg tests, python version < 2.4'
|
||||||
# Requires Python 2.4 or newer
|
test_done
|
||||||
test_set_prereq PYTHON_24
|
}
|
||||||
fi
|
|
||||||
|
|
||||||
test_expect_success PYTHON_24 'setup repository' '
|
compare_refs() {
|
||||||
|
git --git-dir="$1/.git" rev-parse --verify $2 >expect &&
|
||||||
|
git --git-dir="$3/.git" rev-parse --verify $4 >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
}
|
||||||
|
|
||||||
|
test_expect_success 'setup repository' '
|
||||||
git init --bare server/.git &&
|
git init --bare server/.git &&
|
||||||
git clone server public &&
|
git clone server public &&
|
||||||
(cd public &&
|
(cd public &&
|
||||||
@ -27,54 +37,99 @@ test_expect_success PYTHON_24 'setup repository' '
|
|||||||
git push origin master)
|
git push origin master)
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success PYTHON_24 'cloning from local repo' '
|
test_expect_success 'cloning from local repo' '
|
||||||
git clone "testgit::${PWD}/server" localclone &&
|
git clone "testgit::${PWD}/server" localclone &&
|
||||||
test_cmp public/file localclone/file
|
test_cmp public/file localclone/file
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success PYTHON_24 'cloning from remote repo' '
|
test_expect_success 'cloning from remote repo' '
|
||||||
git clone "testgit::file://${PWD}/server" clone &&
|
git clone "testgit::file://${PWD}/server" clone &&
|
||||||
test_cmp public/file clone/file
|
test_cmp public/file clone/file
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success PYTHON_24 'create new commit on remote' '
|
test_expect_success 'create new commit on remote' '
|
||||||
(cd public &&
|
(cd public &&
|
||||||
echo content >>file &&
|
echo content >>file &&
|
||||||
git commit -a -m two &&
|
git commit -a -m two &&
|
||||||
git push)
|
git push)
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success PYTHON_24 'pulling from local repo' '
|
test_expect_success 'pulling from local repo' '
|
||||||
(cd localclone && git pull) &&
|
(cd localclone && git pull) &&
|
||||||
test_cmp public/file localclone/file
|
test_cmp public/file localclone/file
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success PYTHON_24 'pulling from remote remote' '
|
test_expect_success 'pulling from remote remote' '
|
||||||
(cd clone && git pull) &&
|
(cd clone && git pull) &&
|
||||||
test_cmp public/file clone/file
|
test_cmp public/file clone/file
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success PYTHON_24 'pushing to local repo' '
|
test_expect_success 'pushing to local repo' '
|
||||||
(cd localclone &&
|
(cd localclone &&
|
||||||
echo content >>file &&
|
echo content >>file &&
|
||||||
git commit -a -m three &&
|
git commit -a -m three &&
|
||||||
git push) &&
|
git push) &&
|
||||||
HEAD=$(git --git-dir=localclone/.git rev-parse --verify HEAD) &&
|
compare_refs localclone HEAD server HEAD
|
||||||
test $HEAD = $(git --git-dir=server/.git rev-parse --verify HEAD)
|
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success PYTHON_24 'synch with changes from localclone' '
|
test_expect_success 'synch with changes from localclone' '
|
||||||
(cd clone &&
|
(cd clone &&
|
||||||
git pull)
|
git pull)
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success PYTHON_24 'pushing remote local repo' '
|
test_expect_success 'pushing remote local repo' '
|
||||||
(cd clone &&
|
(cd clone &&
|
||||||
echo content >>file &&
|
echo content >>file &&
|
||||||
git commit -a -m four &&
|
git commit -a -m four &&
|
||||||
git push) &&
|
git push) &&
|
||||||
HEAD=$(git --git-dir=clone/.git rev-parse --verify HEAD) &&
|
compare_refs clone HEAD server HEAD
|
||||||
test $HEAD = $(git --git-dir=server/.git rev-parse --verify HEAD)
|
'
|
||||||
|
|
||||||
|
test_expect_success 'fetch new branch' '
|
||||||
|
(cd public &&
|
||||||
|
git checkout -b new &&
|
||||||
|
echo content >>file &&
|
||||||
|
git commit -a -m five &&
|
||||||
|
git push origin new
|
||||||
|
) &&
|
||||||
|
(cd localclone &&
|
||||||
|
git fetch origin new
|
||||||
|
) &&
|
||||||
|
compare_refs public HEAD localclone FETCH_HEAD
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'fetch multiple branches' '
|
||||||
|
(cd localclone &&
|
||||||
|
git fetch
|
||||||
|
) &&
|
||||||
|
compare_refs server master localclone refs/remotes/origin/master &&
|
||||||
|
compare_refs server new localclone refs/remotes/origin/new
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'push when remote has extra refs' '
|
||||||
|
(cd clone &&
|
||||||
|
echo content >>file &&
|
||||||
|
git commit -a -m six &&
|
||||||
|
git push
|
||||||
|
) &&
|
||||||
|
compare_refs clone master server master
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'push new branch by name' '
|
||||||
|
(cd clone &&
|
||||||
|
git checkout -b new-name &&
|
||||||
|
echo content >>file &&
|
||||||
|
git commit -a -m seven &&
|
||||||
|
git push origin new-name
|
||||||
|
) &&
|
||||||
|
compare_refs clone HEAD server refs/heads/new-name
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_failure 'push new branch with old:new refspec' '
|
||||||
|
(cd clone &&
|
||||||
|
git push origin new-name:new-refspec
|
||||||
|
) &&
|
||||||
|
compare_refs clone HEAD server refs/heads/new-refspec
|
||||||
'
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
@ -2197,6 +2197,48 @@ test_expect_success 'R: quiet option results in no stats being output' '
|
|||||||
test_cmp empty output
|
test_cmp empty output
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'R: feature done means terminating "done" is mandatory' '
|
||||||
|
echo feature done | test_must_fail git fast-import &&
|
||||||
|
test_must_fail git fast-import --done </dev/null
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'R: terminating "done" with trailing gibberish is ok' '
|
||||||
|
git fast-import <<-\EOF &&
|
||||||
|
feature done
|
||||||
|
done
|
||||||
|
trailing gibberish
|
||||||
|
EOF
|
||||||
|
git fast-import <<-\EOF
|
||||||
|
done
|
||||||
|
more trailing gibberish
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'R: terminating "done" within commit' '
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
OBJID
|
||||||
|
:000000 100644 OBJID OBJID A hello.c
|
||||||
|
:000000 100644 OBJID OBJID A hello2.c
|
||||||
|
EOF
|
||||||
|
git fast-import <<-EOF &&
|
||||||
|
commit refs/heads/done-ends
|
||||||
|
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
|
||||||
|
data <<EOT
|
||||||
|
Commit terminated by "done" command
|
||||||
|
EOT
|
||||||
|
M 100644 inline hello.c
|
||||||
|
data <<EOT
|
||||||
|
Hello, world.
|
||||||
|
EOT
|
||||||
|
C hello.c hello2.c
|
||||||
|
done
|
||||||
|
EOF
|
||||||
|
git rev-list done-ends |
|
||||||
|
git diff-tree -r --stdin --root --always |
|
||||||
|
sed -e "s/$_x40/OBJID/g" >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
cat >input <<EOF
|
cat >input <<EOF
|
||||||
option git non-existing-option
|
option git non-existing-option
|
||||||
EOF
|
EOF
|
||||||
|
@ -23,6 +23,8 @@ struct helper_data {
|
|||||||
push : 1,
|
push : 1,
|
||||||
connect : 1,
|
connect : 1,
|
||||||
no_disconnect_req : 1;
|
no_disconnect_req : 1;
|
||||||
|
char *export_marks;
|
||||||
|
char *import_marks;
|
||||||
/* These go from remote name (as in "list") to private name */
|
/* These go from remote name (as in "list") to private name */
|
||||||
struct refspec *refspecs;
|
struct refspec *refspecs;
|
||||||
int refspec_nr;
|
int refspec_nr;
|
||||||
@ -105,6 +107,12 @@ static struct child_process *get_helper(struct transport *transport)
|
|||||||
int refspec_alloc = 0;
|
int refspec_alloc = 0;
|
||||||
int duped;
|
int duped;
|
||||||
int code;
|
int code;
|
||||||
|
char git_dir_buf[sizeof(GIT_DIR_ENVIRONMENT) + PATH_MAX + 1];
|
||||||
|
const char *helper_env[] = {
|
||||||
|
git_dir_buf,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
if (data->helper)
|
if (data->helper)
|
||||||
return data->helper;
|
return data->helper;
|
||||||
@ -120,6 +128,10 @@ static struct child_process *get_helper(struct transport *transport)
|
|||||||
helper->argv[2] = remove_ext_force(transport->url);
|
helper->argv[2] = remove_ext_force(transport->url);
|
||||||
helper->git_cmd = 0;
|
helper->git_cmd = 0;
|
||||||
helper->silent_exec_failure = 1;
|
helper->silent_exec_failure = 1;
|
||||||
|
|
||||||
|
snprintf(git_dir_buf, sizeof(git_dir_buf), "%s=%s", GIT_DIR_ENVIRONMENT, get_git_dir());
|
||||||
|
helper->env = helper_env;
|
||||||
|
|
||||||
code = start_command(helper);
|
code = start_command(helper);
|
||||||
if (code < 0 && errno == ENOENT)
|
if (code < 0 && errno == ENOENT)
|
||||||
die("Unable to find remote helper for '%s'", data->name);
|
die("Unable to find remote helper for '%s'", data->name);
|
||||||
@ -171,14 +183,19 @@ static struct child_process *get_helper(struct transport *transport)
|
|||||||
ALLOC_GROW(refspecs,
|
ALLOC_GROW(refspecs,
|
||||||
refspec_nr + 1,
|
refspec_nr + 1,
|
||||||
refspec_alloc);
|
refspec_alloc);
|
||||||
refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec "));
|
refspecs[refspec_nr++] = strdup(capname + strlen("refspec "));
|
||||||
} else if (!strcmp(capname, "connect")) {
|
} else if (!strcmp(capname, "connect")) {
|
||||||
data->connect = 1;
|
data->connect = 1;
|
||||||
} else if (!strcmp(buf.buf, "gitdir")) {
|
} else if (!prefixcmp(capname, "export-marks ")) {
|
||||||
struct strbuf gitdir = STRBUF_INIT;
|
struct strbuf arg = STRBUF_INIT;
|
||||||
strbuf_addf(&gitdir, "gitdir %s\n", get_git_dir());
|
strbuf_addstr(&arg, "--export-marks=");
|
||||||
sendline(data, &gitdir);
|
strbuf_addstr(&arg, capname + strlen("export-marks "));
|
||||||
strbuf_release(&gitdir);
|
data->export_marks = strbuf_detach(&arg, NULL);
|
||||||
|
} else if (!prefixcmp(capname, "import-marks")) {
|
||||||
|
struct strbuf arg = STRBUF_INIT;
|
||||||
|
strbuf_addstr(&arg, "--import-marks=");
|
||||||
|
strbuf_addstr(&arg, capname + strlen("import-marks "));
|
||||||
|
data->import_marks = strbuf_detach(&arg, NULL);
|
||||||
} else if (mandatory) {
|
} else if (mandatory) {
|
||||||
die("Unknown mandatory capability %s. This remote "
|
die("Unknown mandatory capability %s. This remote "
|
||||||
"helper probably needs newer version of Git.\n",
|
"helper probably needs newer version of Git.\n",
|
||||||
@ -204,6 +221,7 @@ static int disconnect_helper(struct transport *transport)
|
|||||||
{
|
{
|
||||||
struct helper_data *data = transport->data;
|
struct helper_data *data = transport->data;
|
||||||
struct strbuf buf = STRBUF_INIT;
|
struct strbuf buf = STRBUF_INIT;
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
if (data->helper) {
|
if (data->helper) {
|
||||||
if (debug)
|
if (debug)
|
||||||
@ -215,13 +233,13 @@ static int disconnect_helper(struct transport *transport)
|
|||||||
close(data->helper->in);
|
close(data->helper->in);
|
||||||
close(data->helper->out);
|
close(data->helper->out);
|
||||||
fclose(data->out);
|
fclose(data->out);
|
||||||
finish_command(data->helper);
|
res = finish_command(data->helper);
|
||||||
free((char *)data->helper->argv[0]);
|
free((char *)data->helper->argv[0]);
|
||||||
free(data->helper->argv);
|
free(data->helper->argv);
|
||||||
free(data->helper);
|
free(data->helper);
|
||||||
data->helper = NULL;
|
data->helper = NULL;
|
||||||
}
|
}
|
||||||
return 0;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *unsupported_options[] = {
|
static const char *unsupported_options[] = {
|
||||||
@ -299,12 +317,13 @@ static void standard_options(struct transport *t)
|
|||||||
|
|
||||||
static int release_helper(struct transport *transport)
|
static int release_helper(struct transport *transport)
|
||||||
{
|
{
|
||||||
|
int res = 0;
|
||||||
struct helper_data *data = transport->data;
|
struct helper_data *data = transport->data;
|
||||||
free_refspec(data->refspec_nr, data->refspecs);
|
free_refspec(data->refspec_nr, data->refspecs);
|
||||||
data->refspecs = NULL;
|
data->refspecs = NULL;
|
||||||
disconnect_helper(transport);
|
res = disconnect_helper(transport);
|
||||||
free(transport->data);
|
free(transport->data);
|
||||||
return 0;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fetch_with_fetch(struct transport *transport,
|
static int fetch_with_fetch(struct transport *transport,
|
||||||
@ -362,10 +381,9 @@ static int get_importer(struct transport *transport, struct child_process *fasti
|
|||||||
|
|
||||||
static int get_exporter(struct transport *transport,
|
static int get_exporter(struct transport *transport,
|
||||||
struct child_process *fastexport,
|
struct child_process *fastexport,
|
||||||
const char *export_marks,
|
|
||||||
const char *import_marks,
|
|
||||||
struct string_list *revlist_args)
|
struct string_list *revlist_args)
|
||||||
{
|
{
|
||||||
|
struct helper_data *data = transport->data;
|
||||||
struct child_process *helper = get_helper(transport);
|
struct child_process *helper = get_helper(transport);
|
||||||
int argc = 0, i;
|
int argc = 0, i;
|
||||||
memset(fastexport, 0, sizeof(*fastexport));
|
memset(fastexport, 0, sizeof(*fastexport));
|
||||||
@ -373,12 +391,13 @@ static int get_exporter(struct transport *transport,
|
|||||||
/* we need to duplicate helper->in because we want to use it after
|
/* we need to duplicate helper->in because we want to use it after
|
||||||
* fastexport is done with it. */
|
* fastexport is done with it. */
|
||||||
fastexport->out = dup(helper->in);
|
fastexport->out = dup(helper->in);
|
||||||
fastexport->argv = xcalloc(4 + revlist_args->nr, sizeof(*fastexport->argv));
|
fastexport->argv = xcalloc(5 + revlist_args->nr, sizeof(*fastexport->argv));
|
||||||
fastexport->argv[argc++] = "fast-export";
|
fastexport->argv[argc++] = "fast-export";
|
||||||
if (export_marks)
|
fastexport->argv[argc++] = "--use-done-feature";
|
||||||
fastexport->argv[argc++] = export_marks;
|
if (data->export_marks)
|
||||||
if (import_marks)
|
fastexport->argv[argc++] = data->export_marks;
|
||||||
fastexport->argv[argc++] = import_marks;
|
if (data->import_marks)
|
||||||
|
fastexport->argv[argc++] = data->import_marks;
|
||||||
|
|
||||||
for (i = 0; i < revlist_args->nr; i++)
|
for (i = 0; i < revlist_args->nr; i++)
|
||||||
fastexport->argv[argc++] = revlist_args->items[i].string;
|
fastexport->argv[argc++] = revlist_args->items[i].string;
|
||||||
@ -410,8 +429,11 @@ static int fetch_with_import(struct transport *transport,
|
|||||||
sendline(data, &buf);
|
sendline(data, &buf);
|
||||||
strbuf_reset(&buf);
|
strbuf_reset(&buf);
|
||||||
}
|
}
|
||||||
disconnect_helper(transport);
|
|
||||||
finish_command(&fastimport);
|
write_constant(data->helper->in, "\n");
|
||||||
|
|
||||||
|
if (finish_command(&fastimport))
|
||||||
|
die("Error while running fast-import");
|
||||||
free(fastimport.argv);
|
free(fastimport.argv);
|
||||||
fastimport.argv = NULL;
|
fastimport.argv = NULL;
|
||||||
|
|
||||||
@ -554,6 +576,88 @@ static int fetch(struct transport *transport,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void push_update_ref_status(struct strbuf *buf,
|
||||||
|
struct ref **ref,
|
||||||
|
struct ref *remote_refs)
|
||||||
|
{
|
||||||
|
char *refname, *msg;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (!prefixcmp(buf->buf, "ok ")) {
|
||||||
|
status = REF_STATUS_OK;
|
||||||
|
refname = buf->buf + 3;
|
||||||
|
} else if (!prefixcmp(buf->buf, "error ")) {
|
||||||
|
status = REF_STATUS_REMOTE_REJECT;
|
||||||
|
refname = buf->buf + 6;
|
||||||
|
} else
|
||||||
|
die("expected ok/error, helper said '%s'\n", buf->buf);
|
||||||
|
|
||||||
|
msg = strchr(refname, ' ');
|
||||||
|
if (msg) {
|
||||||
|
struct strbuf msg_buf = STRBUF_INIT;
|
||||||
|
const char *end;
|
||||||
|
|
||||||
|
*msg++ = '\0';
|
||||||
|
if (!unquote_c_style(&msg_buf, msg, &end))
|
||||||
|
msg = strbuf_detach(&msg_buf, NULL);
|
||||||
|
else
|
||||||
|
msg = xstrdup(msg);
|
||||||
|
strbuf_release(&msg_buf);
|
||||||
|
|
||||||
|
if (!strcmp(msg, "no match")) {
|
||||||
|
status = REF_STATUS_NONE;
|
||||||
|
free(msg);
|
||||||
|
msg = NULL;
|
||||||
|
}
|
||||||
|
else if (!strcmp(msg, "up to date")) {
|
||||||
|
status = REF_STATUS_UPTODATE;
|
||||||
|
free(msg);
|
||||||
|
msg = NULL;
|
||||||
|
}
|
||||||
|
else if (!strcmp(msg, "non-fast forward")) {
|
||||||
|
status = REF_STATUS_REJECT_NONFASTFORWARD;
|
||||||
|
free(msg);
|
||||||
|
msg = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*ref)
|
||||||
|
*ref = find_ref_by_name(*ref, refname);
|
||||||
|
if (!*ref)
|
||||||
|
*ref = find_ref_by_name(remote_refs, refname);
|
||||||
|
if (!*ref) {
|
||||||
|
warning("helper reported unexpected status of %s", refname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((*ref)->status != REF_STATUS_NONE) {
|
||||||
|
/*
|
||||||
|
* Earlier, the ref was marked not to be pushed, so ignore the ref
|
||||||
|
* status reported by the remote helper if the latter is 'no match'.
|
||||||
|
*/
|
||||||
|
if (status == REF_STATUS_NONE)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*ref)->status = status;
|
||||||
|
(*ref)->remote_status = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void push_update_refs_status(struct helper_data *data,
|
||||||
|
struct ref *remote_refs)
|
||||||
|
{
|
||||||
|
struct strbuf buf = STRBUF_INIT;
|
||||||
|
struct ref *ref = remote_refs;
|
||||||
|
for (;;) {
|
||||||
|
recvline(data, &buf);
|
||||||
|
if (!buf.len)
|
||||||
|
break;
|
||||||
|
|
||||||
|
push_update_ref_status(&buf, &ref, remote_refs);
|
||||||
|
}
|
||||||
|
strbuf_release(&buf);
|
||||||
|
}
|
||||||
|
|
||||||
static int push_refs_with_push(struct transport *transport,
|
static int push_refs_with_push(struct transport *transport,
|
||||||
struct ref *remote_refs, int flags)
|
struct ref *remote_refs, int flags)
|
||||||
{
|
{
|
||||||
@ -608,76 +712,9 @@ static int push_refs_with_push(struct transport *transport,
|
|||||||
|
|
||||||
strbuf_addch(&buf, '\n');
|
strbuf_addch(&buf, '\n');
|
||||||
sendline(data, &buf);
|
sendline(data, &buf);
|
||||||
|
|
||||||
ref = remote_refs;
|
|
||||||
while (1) {
|
|
||||||
char *refname, *msg;
|
|
||||||
int status;
|
|
||||||
|
|
||||||
recvline(data, &buf);
|
|
||||||
if (!buf.len)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (!prefixcmp(buf.buf, "ok ")) {
|
|
||||||
status = REF_STATUS_OK;
|
|
||||||
refname = buf.buf + 3;
|
|
||||||
} else if (!prefixcmp(buf.buf, "error ")) {
|
|
||||||
status = REF_STATUS_REMOTE_REJECT;
|
|
||||||
refname = buf.buf + 6;
|
|
||||||
} else
|
|
||||||
die("expected ok/error, helper said '%s'\n", buf.buf);
|
|
||||||
|
|
||||||
msg = strchr(refname, ' ');
|
|
||||||
if (msg) {
|
|
||||||
struct strbuf msg_buf = STRBUF_INIT;
|
|
||||||
const char *end;
|
|
||||||
|
|
||||||
*msg++ = '\0';
|
|
||||||
if (!unquote_c_style(&msg_buf, msg, &end))
|
|
||||||
msg = strbuf_detach(&msg_buf, NULL);
|
|
||||||
else
|
|
||||||
msg = xstrdup(msg);
|
|
||||||
strbuf_release(&msg_buf);
|
|
||||||
|
|
||||||
if (!strcmp(msg, "no match")) {
|
|
||||||
status = REF_STATUS_NONE;
|
|
||||||
free(msg);
|
|
||||||
msg = NULL;
|
|
||||||
}
|
|
||||||
else if (!strcmp(msg, "up to date")) {
|
|
||||||
status = REF_STATUS_UPTODATE;
|
|
||||||
free(msg);
|
|
||||||
msg = NULL;
|
|
||||||
}
|
|
||||||
else if (!strcmp(msg, "non-fast forward")) {
|
|
||||||
status = REF_STATUS_REJECT_NONFASTFORWARD;
|
|
||||||
free(msg);
|
|
||||||
msg = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ref)
|
|
||||||
ref = find_ref_by_name(ref, refname);
|
|
||||||
if (!ref)
|
|
||||||
ref = find_ref_by_name(remote_refs, refname);
|
|
||||||
if (!ref) {
|
|
||||||
warning("helper reported unexpected status of %s", refname);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ref->status != REF_STATUS_NONE) {
|
|
||||||
/*
|
|
||||||
* Earlier, the ref was marked not to be pushed, so ignore the ref
|
|
||||||
* status reported by the remote helper if the latter is 'no match'.
|
|
||||||
*/
|
|
||||||
if (status == REF_STATUS_NONE)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ref->status = status;
|
|
||||||
ref->remote_status = msg;
|
|
||||||
}
|
|
||||||
strbuf_release(&buf);
|
strbuf_release(&buf);
|
||||||
|
|
||||||
|
push_update_refs_status(data, remote_refs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -687,7 +724,6 @@ static int push_refs_with_export(struct transport *transport,
|
|||||||
struct ref *ref;
|
struct ref *ref;
|
||||||
struct child_process *helper, exporter;
|
struct child_process *helper, exporter;
|
||||||
struct helper_data *data = transport->data;
|
struct helper_data *data = transport->data;
|
||||||
char *export_marks = NULL, *import_marks = NULL;
|
|
||||||
struct string_list revlist_args = STRING_LIST_INIT_NODUP;
|
struct string_list revlist_args = STRING_LIST_INIT_NODUP;
|
||||||
struct strbuf buf = STRBUF_INIT;
|
struct strbuf buf = STRBUF_INIT;
|
||||||
|
|
||||||
@ -695,26 +731,6 @@ static int push_refs_with_export(struct transport *transport,
|
|||||||
|
|
||||||
write_constant(helper->in, "export\n");
|
write_constant(helper->in, "export\n");
|
||||||
|
|
||||||
recvline(data, &buf);
|
|
||||||
if (debug)
|
|
||||||
fprintf(stderr, "Debug: Got export_marks '%s'\n", buf.buf);
|
|
||||||
if (buf.len) {
|
|
||||||
struct strbuf arg = STRBUF_INIT;
|
|
||||||
strbuf_addstr(&arg, "--export-marks=");
|
|
||||||
strbuf_addbuf(&arg, &buf);
|
|
||||||
export_marks = strbuf_detach(&arg, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
recvline(data, &buf);
|
|
||||||
if (debug)
|
|
||||||
fprintf(stderr, "Debug: Got import_marks '%s'\n", buf.buf);
|
|
||||||
if (buf.len) {
|
|
||||||
struct strbuf arg = STRBUF_INIT;
|
|
||||||
strbuf_addstr(&arg, "--import-marks=");
|
|
||||||
strbuf_addbuf(&arg, &buf);
|
|
||||||
import_marks = strbuf_detach(&arg, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
strbuf_reset(&buf);
|
strbuf_reset(&buf);
|
||||||
|
|
||||||
for (ref = remote_refs; ref; ref = ref->next) {
|
for (ref = remote_refs; ref; ref = ref->next) {
|
||||||
@ -728,18 +744,23 @@ static int push_refs_with_export(struct transport *transport,
|
|||||||
strbuf_addf(&buf, "^%s", private);
|
strbuf_addf(&buf, "^%s", private);
|
||||||
string_list_append(&revlist_args, strbuf_detach(&buf, NULL));
|
string_list_append(&revlist_args, strbuf_detach(&buf, NULL));
|
||||||
}
|
}
|
||||||
|
free(private);
|
||||||
|
|
||||||
string_list_append(&revlist_args, ref->name);
|
if (ref->deletion) {
|
||||||
|
die("remote-helpers do not support ref deletion");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ref->peer_ref)
|
||||||
|
string_list_append(&revlist_args, ref->peer_ref->name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_exporter(transport, &exporter,
|
if (get_exporter(transport, &exporter, &revlist_args))
|
||||||
export_marks, import_marks, &revlist_args))
|
|
||||||
die("Couldn't run fast-export");
|
die("Couldn't run fast-export");
|
||||||
|
|
||||||
data->no_disconnect_req = 1;
|
if (finish_command(&exporter))
|
||||||
finish_command(&exporter);
|
die("Error while running fast-export");
|
||||||
disconnect_helper(transport);
|
push_update_refs_status(data, remote_refs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user