* [PATCH v2 4/5] remote-bzr: add support for remote repositories
From: Felipe Contreras @ 2012-11-05 15:56 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, Johannes Schindelin, Felipe Contreras
In-Reply-To: <1352130980-3998-1-git-send-email-felipe.contreras@gmail.com>
Strictly speaking bzr doesn't need any changes to interact with remote
repositories, but it's dead slow.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/git-remote-bzr | 26 ++++++++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)
diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr
index 9e0062f..c981fda 100755
--- a/contrib/remote-helpers/git-remote-bzr
+++ b/contrib/remote-helpers/git-remote-bzr
@@ -547,7 +547,7 @@ def parse_reset(parser):
parsed_refs[ref] = mark_to_rev(from_mark)
def do_export(parser):
- global parsed_refs, dirname
+ global parsed_refs, dirname, peer
parser.next()
@@ -570,6 +570,8 @@ def do_export(parser):
for ref, revid in parsed_refs.iteritems():
if ref == 'refs/heads/master':
repo.generate_revision_history(revid, marks.get_tip('master'))
+ revno, revid = repo.last_revision_info()
+ peer.import_last_revision_info_and_tags(repo, revno, revid)
print "ok %s" % ref
print
@@ -598,8 +600,28 @@ def do_list(parser):
print
def get_repo(url, alias):
+ global dirname, peer
+
+ clone_path = os.path.join(dirname, 'clone')
origin = bzrlib.controldir.ControlDir.open(url)
- return origin.open_branch()
+ remote_branch = origin.open_branch()
+
+ if os.path.exists(clone_path):
+ # pull
+ d = bzrlib.controldir.ControlDir.open(clone_path)
+ branch = d.open_branch()
+ result = branch.pull(remote_branch, [], None, False)
+ else:
+ # clone
+ d = origin.sprout(clone_path, None,
+ hardlink=True, create_tree_if_local=False,
+ source_branch=remote_branch)
+ branch = d.open_branch()
+ branch.bind(remote_branch)
+
+ peer = remote_branch
+
+ return branch
def main(args):
global marks, prefix, dirname
--
1.8.0
^ permalink raw reply related
* [PATCH v2 5/5] remote-bzr: update working tree
From: Felipe Contreras @ 2012-11-05 15:56 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, Johannes Schindelin, Felipe Contreras
In-Reply-To: <1352130980-3998-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/git-remote-bzr | 2 ++
1 file changed, 2 insertions(+)
diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr
index c981fda..1a06a0a 100755
--- a/contrib/remote-helpers/git-remote-bzr
+++ b/contrib/remote-helpers/git-remote-bzr
@@ -572,6 +572,8 @@ def do_export(parser):
repo.generate_revision_history(revid, marks.get_tip('master'))
revno, revid = repo.last_revision_info()
peer.import_last_revision_info_and_tags(repo, revno, revid)
+ wt = peer.bzrdir.open_workingtree()
+ wt.update()
print "ok %s" % ref
print
--
1.8.0
^ permalink raw reply related
* [PATCH v2 2/5] remote-bzr: add simple tests
From: Felipe Contreras @ 2012-11-05 15:56 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, Johannes Schindelin, Felipe Contreras
In-Reply-To: <1352130980-3998-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/test-bzr.sh | 111 +++++++++++++++++++++++++++++++++++++
1 file changed, 111 insertions(+)
create mode 100755 contrib/remote-helpers/test-bzr.sh
diff --git a/contrib/remote-helpers/test-bzr.sh b/contrib/remote-helpers/test-bzr.sh
new file mode 100755
index 0000000..8594ffc
--- /dev/null
+++ b/contrib/remote-helpers/test-bzr.sh
@@ -0,0 +1,111 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Felipe Contreras
+#
+
+test_description='Test remote-bzr'
+
+. ./test-lib.sh
+
+if ! test_have_prereq PYTHON; then
+ skip_all='skipping remote-bzr tests; python not available'
+ test_done
+fi
+
+if ! "$PYTHON_PATH" -c 'import bzrlib'; then
+ skip_all='skipping remote-bzr tests; bzr not available'
+ test_done
+fi
+
+cmd=<<EOF
+import bzrlib
+bzrlib.initialize()
+import bzrlib.plugin
+bzrlib.plugin.load_plugins()
+import bzrlib.plugins.fastimport
+EOF
+
+if ! "$PYTHON_PATH" -c "$cmd"; then
+ echo "consider setting BZR_PLUGIN_PATH=$HOME/.bazaar/plugins" 1>&2
+ skip_all='skipping remote-bzr tests; bzr-fastimport not available'
+ test_done
+fi
+
+check () {
+ (cd $1 &&
+ git log --format='%s' -1 &&
+ git symbolic-ref HEAD) > actual &&
+ (echo $2 &&
+ echo "refs/heads/$3") > expected &&
+ test_cmp expected actual
+}
+
+bzr whoami "A U Thor <author@example.com>"
+
+test_expect_success 'cloning' '
+ (bzr init bzrrepo &&
+ cd bzrrepo &&
+ echo one > content &&
+ bzr add content &&
+ bzr commit -m one
+ ) &&
+
+ git clone "bzr::$PWD/bzrrepo" gitrepo &&
+ check gitrepo one master
+'
+
+test_expect_success 'pulling' '
+ (cd bzrrepo &&
+ echo two > content &&
+ bzr commit -m two
+ ) &&
+
+ (cd gitrepo && git pull) &&
+
+ check gitrepo two master
+'
+
+test_expect_success 'pushing' '
+ (cd gitrepo &&
+ echo three > content &&
+ git commit -a -m three &&
+ git push
+ ) &&
+
+ echo three > expected &&
+ cat bzrrepo/content > actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'roundtrip' '
+ (cd gitrepo &&
+ git pull &&
+ git log --format="%s" -1 origin/master > actual) &&
+ echo three > expected &&
+ test_cmp expected actual &&
+
+ (cd gitrepo && git push && git pull) &&
+
+ (cd bzrrepo &&
+ echo four > content &&
+ bzr commit -m four
+ ) &&
+
+ (cd gitrepo && git pull && git push) &&
+
+ check gitrepo four master &&
+
+ (cd gitrepo &&
+ echo five > content &&
+ git commit -a -m five &&
+ git push && git pull
+ ) &&
+
+ (cd bzrrepo && bzr revert) &&
+
+ echo five > expected &&
+ cat bzrrepo/content > actual &&
+ test_cmp expected actual
+'
+
+test_done
--
1.8.0
^ permalink raw reply related
* [PATCH v2 3/5] remote-bzr: add support for pushing
From: Felipe Contreras @ 2012-11-05 15:56 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, Johannes Schindelin, Felipe Contreras
In-Reply-To: <1352130980-3998-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/git-remote-bzr | 295 ++++++++++++++++++++++++++++++++++
1 file changed, 295 insertions(+)
diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr
index ed893b0..9e0062f 100755
--- a/contrib/remote-helpers/git-remote-bzr
+++ b/contrib/remote-helpers/git-remote-bzr
@@ -22,13 +22,17 @@ bzrlib.initialize()
import bzrlib.plugin
bzrlib.plugin.load_plugins()
+import bzrlib.generate_ids
+
import sys
import os
import json
import re
+import StringIO
NAME_RE = re.compile('^([^<>]+)')
AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$')
+RAW_AUTHOR_RE = re.compile('^(\w+) (.+)? <(.*)> (\d+) ([+-]\d+)')
def die(msg, *args):
sys.stderr.write('ERROR: %s\n' % (msg % args))
@@ -46,6 +50,7 @@ class Marks:
self.path = path
self.tips = {}
self.marks = {}
+ self.rev_marks = {}
self.last_mark = 0
self.load()
@@ -58,6 +63,9 @@ class Marks:
self.marks = tmp['marks']
self.last_mark = tmp['last-mark']
+ for rev, mark in self.marks.iteritems():
+ self.rev_marks[mark] = rev
+
def dict(self):
return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }
@@ -70,6 +78,9 @@ class Marks:
def from_rev(self, rev):
return self.marks[rev]
+ def to_rev(self, mark):
+ return self.rev_marks[mark]
+
def next_mark(self):
self.last_mark += 1
return self.last_mark
@@ -82,6 +93,11 @@ class Marks:
def is_marked(self, rev):
return self.marks.has_key(rev)
+ def new_mark(self, rev, mark):
+ self.marks[rev] = mark
+ self.rev_marks[mark] = rev
+ self.last_mark = mark
+
def get_tip(self, branch):
return self.tips.get(branch, None)
@@ -116,10 +132,35 @@ class Parser:
if self.line == 'done':
self.line = None
+ def get_mark(self):
+ i = self.line.index(':') + 1
+ return int(self.line[i:])
+
+ def get_data(self):
+ if not self.check('data'):
+ return None
+ i = self.line.index(' ') + 1
+ size = int(self.line[i:])
+ return sys.stdin.read(size)
+
+ def get_author(self):
+ m = RAW_AUTHOR_RE.match(self.line)
+ if not m:
+ return None
+ _, name, email, date, tz = m.groups()
+ committer = '%s <%s>' % (name, email)
+ tz = int(tz)
+ tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
+ return (committer, int(date), tz)
+
def rev_to_mark(rev):
global marks
return marks.from_rev(rev)
+def mark_to_rev(mark):
+ global marks
+ return marks.to_rev(mark)
+
def fixup_user(user):
name = mail = None
user = user.replace('"', '')
@@ -296,9 +337,255 @@ def do_import(parser):
sys.stdout.flush()
+def parse_blob(parser):
+ global blob_marks
+
+ parser.next()
+ mark = parser.get_mark()
+ parser.next()
+ data = parser.get_data()
+ blob_marks[mark] = data
+ parser.next()
+
+class CustomTree():
+
+ def __init__(self, repo, revid, parents, files):
+ global files_cache
+
+ self.repo = repo
+ self.revid = revid
+ self.parents = parents
+ self.updates = files
+
+ def copy_tree(revid):
+ files = files_cache[revid] = {}
+ tree = repo.repository.revision_tree(revid)
+ repo.lock_read()
+ try:
+ for path, entry in tree.iter_entries_by_dir():
+ files[path] = entry.file_id
+ finally:
+ repo.unlock()
+ return files
+
+ if len(parents) == 0:
+ self.base_id = bzrlib.revision.NULL_REVISION
+ self.base_files = {}
+ else:
+ self.base_id = parents[0]
+ self.base_files = files_cache.get(self.base_id, None)
+ if not self.base_files:
+ self.base_files = copy_tree(self.base_id)
+
+ self.files = files_cache[revid] = self.base_files.copy()
+
+ def last_revision(self):
+ return self.base_id
+
+ def iter_changes(self):
+ changes = []
+
+ def get_parent(dirname, basename):
+ parent_fid = self.base_files.get(dirname, None)
+ if parent_fid:
+ return parent_fid
+ parent_fid = self.files.get(dirname, None)
+ if parent_fid:
+ return parent_fid
+ if basename == '':
+ return None
+ d = add_entry(dirname, 'directory')
+ return d[0]
+
+ def add_entry(path, kind):
+ dirname, basename = os.path.split(path)
+ parent_fid = get_parent(dirname, basename)
+ fid = bzrlib.generate_ids.gen_file_id(path)
+ change = (fid,
+ (None, path),
+ True,
+ (False, True),
+ (None, parent_fid),
+ (None, basename),
+ (None, kind),
+ (None, False))
+ self.files[path] = change[0]
+ changes.append(change)
+ return change
+
+ def update_entry(path, kind):
+ dirname, basename = os.path.split(path)
+ fid = self.base_files[path]
+ parent_fid = get_parent(dirname, basename)
+ change = (fid,
+ (path, path),
+ True,
+ (True, True),
+ (parent_fid, parent_fid),
+ (basename, basename),
+ (kind, kind),
+ (False, False))
+ self.files[path] = change[0]
+ changes.append(change)
+ return change
+
+ def remove_entry(path, kind):
+ dirname, basename = os.path.split(path)
+ fid = self.base_files[path]
+ parent_fid = get_parent(dirname, basename)
+ change = (fid,
+ (path, None),
+ True,
+ (True, False),
+ (parent_fid, None),
+ (basename, None),
+ (kind, None),
+ (False, None))
+ del self.files[path]
+ changes.append(change)
+ return change
+
+ for path, f in self.updates.iteritems():
+ if 'deleted' in f:
+ remove_entry(path, 'file')
+ elif path in self.base_files:
+ update_entry(path, 'file')
+ else:
+ add_entry(path, 'file')
+
+ return changes
+
+ def get_file_with_stat(self, file_id, path=None):
+ return (StringIO.StringIO(self.updates[path]['data']), None)
+
+def parse_commit(parser):
+ global marks, blob_marks, bmarks, parsed_refs
+ global mode
+
+ parents = []
+
+ ref = parser[1]
+ parser.next()
+
+ if ref != 'refs/heads/master':
+ die("bzr doesn't support multiple branches; use 'master'")
+
+ commit_mark = parser.get_mark()
+ parser.next()
+ author = parser.get_author()
+ parser.next()
+ committer = parser.get_author()
+ parser.next()
+ data = parser.get_data()
+ parser.next()
+ if parser.check('from'):
+ parents.append(parser.get_mark())
+ parser.next()
+ while parser.check('merge'):
+ parents.append(parser.get_mark())
+ parser.next()
+
+ files = {}
+
+ for line in parser:
+ if parser.check('M'):
+ t, m, mark_ref, path = line.split(' ', 3)
+ mark = int(mark_ref[1:])
+ f = { 'mode' : m, 'data' : blob_marks[mark] }
+ elif parser.check('D'):
+ t, path = line.split(' ')
+ f = { 'deleted' : True }
+ else:
+ die('Unknown file command: %s' % line)
+ files[path] = f
+
+ repo = parser.repo
+
+ committer, date, tz = committer
+ parents = [str(mark_to_rev(p)) for p in parents]
+ revid = bzrlib.generate_ids.gen_revision_id(committer, date)
+ props = {}
+ props['branch-nick'] = repo.nick
+
+ mtree = CustomTree(repo, revid, parents, files)
+ changes = mtree.iter_changes()
+
+ repo.lock_write()
+ try:
+ builder = repo.get_commit_builder(parents, None, date, tz, committer, props, revid, False)
+ try:
+ list(builder.record_iter_changes(mtree, mtree.last_revision(), changes))
+ builder.finish_inventory()
+ builder.commit(data.decode('utf-8', 'replace'))
+ except Exception, e:
+ builder.abort()
+ raise
+ finally:
+ repo.unlock()
+
+ parsed_refs[ref] = revid
+ marks.new_mark(revid, commit_mark)
+
+def parse_reset(parser):
+ global parsed_refs
+
+ ref = parser[1]
+ parser.next()
+
+ if ref != 'refs/heads/master':
+ die("bzr doesn't support multiple branches; use 'master'")
+
+ # ugh
+ if parser.check('commit'):
+ parse_commit(parser)
+ return
+ if not parser.check('from'):
+ return
+ from_mark = parser.get_mark()
+ parser.next()
+
+ parsed_refs[ref] = mark_to_rev(from_mark)
+
+def do_export(parser):
+ global parsed_refs, dirname
+
+ parser.next()
+
+ for line in parser.each_block('done'):
+ if parser.check('blob'):
+ parse_blob(parser)
+ elif parser.check('commit'):
+ parse_commit(parser)
+ elif parser.check('reset'):
+ parse_reset(parser)
+ elif parser.check('tag'):
+ pass
+ elif parser.check('feature'):
+ pass
+ else:
+ die('unhandled export command: %s' % line)
+
+ repo = parser.repo
+
+ for ref, revid in parsed_refs.iteritems():
+ if ref == 'refs/heads/master':
+ repo.generate_revision_history(revid, marks.get_tip('master'))
+ print "ok %s" % ref
+ print
+
def do_capabilities(parser):
+ global dirname
+
print "import"
+ print "export"
print "refspec refs/heads/*:%s/heads/*" % prefix
+
+ path = os.path.join(dirname, 'marks-git')
+
+ if os.path.exists(path):
+ print "*import-marks %s" % path
+ print "*export-marks %s" % path
+
print
def do_list(parser):
@@ -317,6 +604,9 @@ def get_repo(url, alias):
def main(args):
global marks, prefix, dirname
global tags, filenodes
+ global blob_marks
+ global parsed_refs
+ global files_cache
alias = args[1]
url = args[2]
@@ -324,6 +614,9 @@ def main(args):
prefix = 'refs/bzr/%s' % alias
tags = {}
filenodes = {}
+ blob_marks = {}
+ parsed_refs = {}
+ files_cache = {}
gitdir = os.environ['GIT_DIR']
dirname = os.path.join(gitdir, 'bzr', alias)
@@ -344,6 +637,8 @@ def main(args):
do_list(parser)
elif parser.check('import'):
do_import(parser)
+ elif parser.check('export'):
+ do_export(parser)
else:
die('unhandled command: %s' % line)
sys.stdout.flush()
--
1.8.0
^ permalink raw reply related
* [PATCH v2 1/5] Add new remote-bzr transport helper
From: Felipe Contreras @ 2012-11-05 15:56 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, Johannes Schindelin, Felipe Contreras
In-Reply-To: <1352130980-3998-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/git-remote-bzr | 353 ++++++++++++++++++++++++++++++++++
1 file changed, 353 insertions(+)
create mode 100755 contrib/remote-helpers/git-remote-bzr
diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr
new file mode 100755
index 0000000..ed893b0
--- /dev/null
+++ b/contrib/remote-helpers/git-remote-bzr
@@ -0,0 +1,353 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2012 Felipe Contreras
+#
+
+#
+# Just copy to your ~/bin, or anywhere in your $PATH.
+# Then you can clone with:
+# % git clone bzr::/path/to/bzr/repo/or/url
+#
+# For example:
+# % git clone bzr::$HOME/myrepo
+# or
+# % git clone bzr::lp:myrepo
+#
+
+import sys
+
+import bzrlib
+bzrlib.initialize()
+
+import bzrlib.plugin
+bzrlib.plugin.load_plugins()
+
+import sys
+import os
+import json
+import re
+
+NAME_RE = re.compile('^([^<>]+)')
+AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$')
+
+def die(msg, *args):
+ sys.stderr.write('ERROR: %s\n' % (msg % args))
+ sys.exit(1)
+
+def warn(msg, *args):
+ sys.stderr.write('WARNING: %s\n' % (msg % args))
+
+def gittz(tz):
+ return '%+03d%02d' % (tz / 3600, tz % 3600 / 60)
+
+class Marks:
+
+ def __init__(self, path):
+ self.path = path
+ self.tips = {}
+ self.marks = {}
+ self.last_mark = 0
+ self.load()
+
+ def load(self):
+ if not os.path.exists(self.path):
+ return
+
+ tmp = json.load(open(self.path))
+ self.tips = tmp['tips']
+ self.marks = tmp['marks']
+ self.last_mark = tmp['last-mark']
+
+ def dict(self):
+ return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }
+
+ def store(self):
+ json.dump(self.dict(), open(self.path, 'w'))
+
+ def __str__(self):
+ return str(self.dict())
+
+ def from_rev(self, rev):
+ return self.marks[rev]
+
+ def next_mark(self):
+ self.last_mark += 1
+ return self.last_mark
+
+ def get_mark(self, rev):
+ self.last_mark += 1
+ self.marks[rev] = self.last_mark
+ return self.last_mark
+
+ def is_marked(self, rev):
+ return self.marks.has_key(rev)
+
+ def get_tip(self, branch):
+ return self.tips.get(branch, None)
+
+ def set_tip(self, branch, tip):
+ self.tips[branch] = tip
+
+class Parser:
+
+ def __init__(self, repo):
+ self.repo = repo
+ self.line = self.get_line()
+
+ def get_line(self):
+ return sys.stdin.readline().strip()
+
+ def __getitem__(self, i):
+ return self.line.split()[i]
+
+ def check(self, word):
+ return self.line.startswith(word)
+
+ def each_block(self, separator):
+ while self.line != separator:
+ yield self.line
+ self.line = self.get_line()
+
+ def __iter__(self):
+ return self.each_block('')
+
+ def next(self):
+ self.line = self.get_line()
+ if self.line == 'done':
+ self.line = None
+
+def rev_to_mark(rev):
+ global marks
+ return marks.from_rev(rev)
+
+def fixup_user(user):
+ name = mail = None
+ user = user.replace('"', '')
+ m = AUTHOR_RE.match(user)
+ if m:
+ name = m.group(1)
+ mail = m.group(2).strip()
+ else:
+ m = NAME_RE.match(user)
+ if m:
+ name = m.group(1).strip()
+
+ return '%s <%s>' % (name, mail)
+
+def get_filechanges(cur, prev):
+ modified = {}
+ removed = {}
+
+ changes = cur.changes_from(prev)
+
+ for path, fid, kind in changes.added:
+ modified[path] = fid
+ for path, fid, kind in changes.removed:
+ removed[path] = None
+ for path, fid, kind, mod, _ in changes.modified:
+ modified[path] = fid
+ for oldpath, newpath, fid, kind, mod, _ in changes.renamed:
+ removed[oldpath] = None
+ modified[newpath] = fid
+
+ return modified, removed
+
+def export_file(path, d):
+ print "M %s inline %s" % ('100644', path)
+ print "data %d" % len(d)
+ print d
+
+def export_files(tree, files):
+ global marks, filenodes
+
+ final = []
+ for path, fid in files.iteritems():
+ h = tree.get_file_sha1(fid)
+ d = tree.get_file_text(fid)
+
+ if h in filenodes:
+ mark = filenodes[h]
+ else:
+ mark = marks.next_mark()
+ filenodes[h] = mark
+
+ print "blob"
+ print "mark :%u" % mark
+ print "data %d" % len(d)
+ print d
+
+ final.append(('100644', mark, path))
+
+ return final
+
+def export_branch(branch, name):
+ global prefix, dirname
+
+ ref = '%s/heads/%s' % (prefix, name)
+ tip = marks.get_tip(name)
+
+ repo = branch.repository
+ repo.lock_read()
+ revs = branch.iter_merge_sorted_revisions(None, tip, 'exclude', 'forward')
+ count = 0
+
+ revs = [revid for revid, _, _, _ in revs if not marks.is_marked(revid)]
+
+ for revid in revs:
+
+ rev = repo.get_revision(revid)
+
+ parents = rev.parent_ids
+ time = rev.timestamp
+ tz = rev.timezone
+ committer = rev.committer.encode('utf-8')
+ committer = "%s %u %s" % (fixup_user(committer), time, gittz(tz))
+ author = committer
+ msg = rev.message.encode('utf-8')
+
+ msg += '\n'
+
+ if len(parents) == 0:
+ parent = bzrlib.revision.NULL_REVISION
+ else:
+ parent = parents[0]
+
+ cur_tree = repo.revision_tree(revid)
+ prev = repo.revision_tree(parent)
+ modified, removed = get_filechanges(cur_tree, prev)
+
+ modified_final = export_files(cur_tree, modified)
+
+ if len(parents) == 0:
+ print 'reset %s' % ref
+
+ print "commit %s" % ref
+ print "mark :%d" % (marks.get_mark(revid))
+ print "author %s" % (author)
+ print "committer %s" % (committer)
+ print "data %d" % (len(msg))
+ print msg
+
+ for i, p in enumerate(parents):
+ try:
+ m = rev_to_mark(p)
+ except KeyError:
+ # ghost?
+ continue
+ if i == 0:
+ print "from :%s" % m
+ else:
+ print "merge :%s" % m
+
+ for f in modified_final:
+ print "M %s :%u %s" % f
+ for f in removed:
+ print "D %s" % (f)
+ print
+
+ count += 1
+ if (count % 100 == 0):
+ print "progress revision %s (%d/%d)" % (revid, count, len(revs))
+ print "#############################################################"
+
+ repo.unlock()
+
+ revid = branch.last_revision()
+
+ # make sure the ref is updated
+ print "reset %s" % ref
+ print "from :%u" % rev_to_mark(revid)
+ print
+
+ marks.set_tip(name, revid)
+
+def export_tag(repo, name):
+ global tags
+ try:
+ print "reset refs/tags/%s" % name
+ print "from :%u" % rev_to_mark(tags[name])
+ print
+ except KeyError:
+ warn("TODO: fetch tag '%s'" % name)
+
+def do_import(parser):
+ global dirname
+
+ branch = parser.repo
+ path = os.path.join(dirname, 'marks-git')
+
+ print "feature done"
+ if os.path.exists(path):
+ print "feature import-marks=%s" % path
+ print "feature export-marks=%s" % path
+ sys.stdout.flush()
+
+ while parser.check('import'):
+ ref = parser[1]
+ if ref.startswith('refs/heads/'):
+ name = ref[len('refs/heads/'):]
+ export_branch(branch, name)
+ if ref.startswith('refs/tags/'):
+ name = ref[len('refs/tags/'):]
+ export_tag(branch, name)
+ parser.next()
+
+ print 'done'
+
+ sys.stdout.flush()
+
+def do_capabilities(parser):
+ print "import"
+ print "refspec refs/heads/*:%s/heads/*" % prefix
+ print
+
+def do_list(parser):
+ global tags
+ print "? refs/heads/%s" % 'master'
+ for tag, revid in parser.repo.tags.get_tag_dict().items():
+ print "? refs/tags/%s" % tag
+ tags[tag] = revid
+ print "@refs/heads/%s HEAD" % 'master'
+ print
+
+def get_repo(url, alias):
+ origin = bzrlib.controldir.ControlDir.open(url)
+ return origin.open_branch()
+
+def main(args):
+ global marks, prefix, dirname
+ global tags, filenodes
+
+ alias = args[1]
+ url = args[2]
+
+ prefix = 'refs/bzr/%s' % alias
+ tags = {}
+ filenodes = {}
+
+ gitdir = os.environ['GIT_DIR']
+ dirname = os.path.join(gitdir, 'bzr', alias)
+
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+
+ repo = get_repo(url, alias)
+
+ marks_path = os.path.join(dirname, 'marks-int')
+ marks = Marks(marks_path)
+
+ parser = Parser(repo)
+ for line in parser:
+ if parser.check('capabilities'):
+ do_capabilities(parser)
+ elif parser.check('list'):
+ do_list(parser)
+ elif parser.check('import'):
+ do_import(parser)
+ else:
+ die('unhandled command: %s' % line)
+ sys.stdout.flush()
+
+ marks.store()
+
+sys.exit(main(sys.argv))
--
1.8.0
^ permalink raw reply related
* [PATCH v2 0/5] New remote-bzr remote helper
From: Felipe Contreras @ 2012-11-05 15:56 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, Johannes Schindelin, Felipe Contreras
Hi,
I decided to get rid of bzr-fastimport; too much complexity for not really that much of a gain.
The only feature I know is missing is support for executable modes and links.
I haven't verified that the resulting output is exactly the same as with other
tools, so be careful while using this.
Also, for the moment I'm not making a distinction betwen local and remote
repositories; they all get a local clone.
Pushing is significantly slower than with bzr-fastimport, but they are using
too many hacks on top of bzrlib, one that has left it broken it for years for
many people, and nobody has bothered to fix it, I had to find it the hard way
and disable the hack. I think the hit in perfromance is completely OK given
that we are using a more conventional bzrlib API, and going to be less likely
to be broken in the future.
Cheers.
Changes sinve v1:
* Rewritten to avoid bzr-fastimport
Felipe Contreras (5):
Add new remote-bzr transport helper
remote-bzr: add simple tests
remote-bzr: add support for pushing
remote-bzr: add support for remote repositories
remote-bzr: update working tree
contrib/remote-helpers/git-remote-bzr | 672 ++++++++++++++++++++++++++++++++++
contrib/remote-helpers/test-bzr.sh | 111 ++++++
2 files changed, 783 insertions(+)
create mode 100755 contrib/remote-helpers/git-remote-bzr
create mode 100755 contrib/remote-helpers/test-bzr.sh
--
1.8.0
^ permalink raw reply
* Re: [PATCH v4 00/13] New remote-hg helper
From: Felipe Contreras @ 2012-11-05 15:36 UTC (permalink / raw)
To: Michael J Gruber
Cc: Jeff King, Johannes Schindelin, git, Junio C Hamano,
Sverre Rabbelier, Ilari Liusvaara, Daniel Barkalow
In-Reply-To: <5097C970.9010901@drmicha.warpmail.net>
On Mon, Nov 5, 2012 at 3:13 PM, Michael J Gruber
<git@drmicha.warpmail.net> wrote:
> Felipe Contreras venit, vidit, dixit 02.11.2012 19:01:
>> I talked with some people in #mercurial, and apparently there is a
>> concept of a 'changelog' that is supposed to store these changes, but
>> since the format has changed, the content of it is unreliable. That's
>> not a big problem because it's used mostly for reporting purposes
>> (log, query), not for doing anything reliable.
>
> Is the changelog stored in the repo (i.e. generated by the hg version at
> commit time) or generated on the fly (i.e. generated by the hg version
> at hand)? See also below.
I don't know. I would expect it to be the former, and then when the
format changes, generated by the tool that did the conversion.
>> To reliably see the changes, one has to compare the 'manifest' of the
>> revisions involved, which contain *all* the files in them.
>
> 'manifest' == '(exploded) tree', right? Just making sure my hg fu is not
> subzero.
Yeah, the tree. As I said, it contains all the files.
>> That's what I was doing already, but I found a more efficient way to
>> do it. msysGit is using the changelog, which is quite fast, but not
>> reliable.
>>
>> Unfortunately while going trough mercurial's code, I found an issue,
>> and it turns out that 1) is not correct.
>>
>> In mercurial, a file hash contains also the parent file nodes, which
>> means that even if two files have the same content, they would not
>> have the same hash, so there's no point in keeping track of them to
>> avoid extracting the data unnecessarily, because in order to make sure
>> they are different, you need to extract the data anyway, defeating the
>> purpose.
>
> Do I understand correctly that neither the msysgit version nor yours can
> detect duplicate blobs (without requesting them) because of that sha1 issue?
That's correct.
> I'm really wondering why a file blob hash carries its history along in
> the sha1. This appears completely strange to gitters (being brain washed
> about "content tracking"), but may be due to hg's extensive use of
> delta, or really: delta chains (which do have their merit on the server
> side).
It is a surprise to me too. I see absolutely no reason why that would be useful.
It seems like bazaar does store the file hashes without the parent
info, like git.
>> Which means mercurial doesn't really behave as one would expect:
>>
>> # add files with the same content
>>
>> $ echo a > a
>> $ hg ci -Am adda
>> adding a
>> $ echo a >> a
>> $ hg ci -m changea
>> $ echo a > a
>> $ hg st --rev 0
>> $ hg ci -m reverta
>> $ hg log -G --template '{rev} {desc}\n'
>> @ 2 reverta
>> |
>> o 1 changea
>> |
>> o 0 adda
>>
>> # check the difference between the first and the last revision
>>
>> $ hg st --rev 0:2
>> M a
>> $ hg cat -r 0 a
>> a
>> $ hg cat -r 2 a
>> a
>
> That is really scary. What use is "hg stat --rev" then? Not blaming you
> for hg, of course.
>
> On that tangent, I just noticed recently that hg has no python api.
> Seriously [1]. They even tell us not to use the internal python api.
> msysgit has been lacking support for newer hg, and you've had to add
> support for older versions (hg 1.9 will be around on quite some
> stable/LTS/EL distro releases) after developing on newer/current ones.
> I'm wondering how well that scales in the long term (telling from
> git-svn experience: it does not scale well), or whether using some
> stable api like 'hgapi' would be a huge bottleneck.
I don't know. I have never really used mercurial until recently. I
don't know how often they change their APIs and/or repository formats.
I would say the burden of updating to newer APIs is probably much less
than the burden of implementing code that accesses their repositories
directly, and eventually possibly rewriting the code when they change
the format.
If we were to access the repository directly, I would choose to use
Ruby for that, but given that 'we' is increasingly looking like 'I'. I
probably wouldn't.
Cheers.
--
Felipe Contreras
^ permalink raw reply
* Re: Lack of netiquette, was Re: [PATCH v4 00/13] New remote-hg helper
From: Felipe Contreras @ 2012-11-05 15:22 UTC (permalink / raw)
To: Michael J Gruber
Cc: Andreas Ericsson, René Scharfe, Junio C Hamano,
Johannes Schindelin, Jonathan Nieder, Jeff King, git,
Sverre Rabbelier, Ilari Liusvaara, Daniel Barkalow
In-Reply-To: <5097860E.5040607@drmicha.warpmail.net>
On Mon, Nov 5, 2012 at 10:25 AM, Michael J Gruber
<git@drmicha.warpmail.net> wrote:
> Felipe Contreras venit, vidit, dixit 02.11.2012 17:09:
>> On Fri, Nov 2, 2012 at 12:03 PM, Michael J Gruber
>> <git@drmicha.warpmail.net> wrote:
>>> Andreas Ericsson venit, vidit, dixit 02.11.2012 10:38:
>>>> On 11/01/2012 02:46 PM, René Scharfe wrote:
>>>>>
>>>>> Also, and I'm sure you didn't know that, "Jedem das Seine" (to each
>>>>> his own) was the slogan of the Buchenwald concentration camp. For
>>>>> that reason some (including me) hear the unspoken cynical
>>>>> half-sentence "and some people just have to be sent to the gas
>>>>> chamber" when someone uses this proverb.
>>>>>
>>>>
>>>> It goes further back than that.
>>>>
>>>> "Suum cuique pulchrum est" ("To each his own is a beautiful thing") is
>>>> a latin phrase said to be used frequently in the roman senate when
>>>> senators politely agreed to disagree and let a vote decide the outcome
>>>> rather than debating further.
>>>>
>>>> Please don't let the twisted views of whatever nazi idiot thought it
>>>> meant "you may have the wrong faith and therefore deserve to die, so you
>>>> shall" pollute it. The original meaning is both poetic and democratic,
>>>> and I firmly believe most people have the original meaning to the fore
>>>> of their mind when using it. After all, very few people knowingly quote
>>>> nazi concentration camp slogans.
>>>>
>>>
>>> In fact, many German terms and words are "forbidden area" since Nazi
>>> times, but I don't think this one carries the same connotation.
>>>
>>> But that is a side track.
>>>
>>> Collaboration (and code review is a form of collaboration) requires
>>> communication. The linked code of conduct pages describe quite well how
>>> to ensure a productive environment in which "everyone" feels comfortable
>>> communicating and collaborating.
>>
>> Yes, but that's assuming we want "everyone" to feel comfortable
>> communicating and collaborating.
>
> I put "everyone" in quotes because you can never reach 100%, so
> "everyone" means almost everyone.
>
> Undeniably, the answers in this and the other threads show that on the
> git mailing list, "everyone" wants "everyone" to feel comfortable
> communicating and collaborating.
And that might be a mistake. Because "everyone" doesn't include the
people that are able to put personal differences aside, and
concentrate on technical merits.
>> I cite again the example of the Linux
>> kernel, where certainly not "everyone" feels that way. But somehow
>
> It's a different list with different standards and tone, so it doesn't
> really matter for our list. That being said:
If you don't want to take into consideration what the most successful
software project in history does... up to you.
>> they manage to be perhaps the most successful software project in
>> history. And I would argue even more: it's _because_ not everyone
>> feels comfortable, it's because ideas and code are criticized freely,
>> and because only the ones that do have merit stand. If you are able to
>> take criticism, and you are not emotionally and personally attacked to
>> your code and your ideas, you would thrive in this environment. If you
>> don't want your precious little baby code to fight against the big
>> guys, then you shouldn't send it out to the world.
>
> For one thing, contributors on the kernel list are open to technical
> arguments, and that includes the arguments of others; just like we are
> here. On the other hand, you seem to rebuke "any" (most) technical
> argument in harsh words as if it were a personal attack; at least that's
> how your answers come across to me (and apparently others). That really
> makes it difficult for most of us here to argue with you technically,
> which is a pity. That lack of openness for the arguments of others would
> make your life difficult on the kernel list also.
It doesn't. And I don't.
There is no lack of openness from my part. I hear all technical
arguments, and I reply on a technical basis. The problem seems to be
is that you expect the code submitted to be criticized, but not the
criticism it receives. IOW; the submitter has to put up with anything
anybody says about his/her code and ideas, but the *reviewer* is
untouchable; the submitter cannot ever criticize the reviewer. I can
tell you that doesn't happen in the Linux kernel; the review process
is a _discussion_, not a one-way communication, and discussions can be
heated up, but the end result is better code, *both* sides are open to
criticism, the submitter, *and* the reviewer.
It seems to me that you think in the git mailing list the submitter
should never put in question the criticism of the reviewer.
If that's not the case, show me a single instance when I rebuke
technical arguments in *harsh* words... perhaps, you think any
rebuking is "harsh". Specifically, show me an instance were *I* was
harsh, and the reviewer was not.
If you cannot show instances of this, then your statement that I
rebuke harshly doesn't stand; I rebuke, that's all.
> A completely different issue is that of language. You talk German on a
> German list and English on an international list. You talk "kernel
> English" on the kernel list, which is full of words and phrases you
> would never use in a normal social setting where you talk to people in
> person; it would be completely unacceptable. Here on the Git list, we
> prefer to talk like in a normal, albeit colloquial social setting. If
> you're open for advice: just imagine talking to the people here in
> person, to colleagues across your desk, and you have a good guideline.
If a submitter cannot rebuke, why would I want to contribute to such a
project? If we cannot speak openly why would I want to contribute?
I wouldn't.
> And no, using the same or similar language does not make us the same at
> all. Using the same language is the natural prerequisite for successful
> communication.
Nobody said otherwise.
> Felipe, please try to see the efforts many of us are making here in
> order to keep you as a contributor, and reward it by accepting the
> advice to revise your language: colleague to colleague.
Thanks, but no thanks. I contribute on my free time, and if
contributing is not a fun process, why would I?
It seems to me that you feel you are not only entitled to my code, but
to never criticize back. I've seen this happen multiple times now,
when I send patches, which are a *contribution*, and the reviewers
expect me to address every and all issues they raise without
criticizing back, or that somehow it's my *responsibility* to address
their concerns, even if I don't agree. I'm sorry but it's not.
In a truly technical project code speaks, and if you as a reviewer
feel something has to be changed, and the submitter (which is acting
on his/her own volition and free time) is not willing to do it, he/her
himself/herself can take the code and do whatever modifications to it,
or the commit message, seem necessary. *Not* to keep bashing the
submitter until they do it exactly as they want as if somehow it was
their *responsibility*.
The spirit of open source is *collaboration*, and what you seem to
expect here is that I do everything. Not only do I have to come up
with the code, I have to come up with a full book chapter of commit
message explaining all the history and introducing how the code works
to people unfamiliar with it, and I have to hear review criticism
without arguing back, and I have to implement it even if I disagree,
and I have to be careful about what every word I say might be taken by
people from other cultures (but they don't), and I can never say
anything that might under certain circumstances and assumptions be
considered offensive (even though they can). And never criticize back
the hidden guidelines.
This is not collaborative, this only ensures that you will get a very
specific kind of contributors.
If what you want is a closely-knitted circle of friends that are
like-minded, then this seems like the right approach.
If what you want is to have a good project, with good code, it might not.
Cheers.
--
Felipe Contreras
^ permalink raw reply
* Re: checkout-index: unable to create file foo (File exists)
From: Brian J. Murrell @ 2012-11-05 15:25 UTC (permalink / raw)
To: git
In-Reply-To: <20121104221018.GB9160@padd.com>
[-- Attachment #1: Type: text/plain, Size: 1383 bytes --]
On 12-11-04 05:10 PM, Pete Wyckoff wrote:
>
> Fascinating problem and observations.
I thought so as well.
> We've been using NFS with git for quite a while and have never
> seen such an error.
Could be because NFS manages to operate more atomically given that it's
just the network exporting of local filesystem.
> man 7 signal (linux man-pages 3.42) describes open() as restartable.
Indeed. The question is just what is to be assumed by the code that has
asked for the restartability.
> Which network filesystem and OS are you using?
The filesystem is Lustre. So not only is it networked, it is
distributed where the namespace and data store are handled by different
nodes, to it's not at all as atomic as NFS-on-(say-)ext4. Given that,
it's entirely possible to imagine a scenario where a namespace (MDT in
the Lustre nomenclature) operation could get interrupted after the
namespace entry has been created but before the open(2) completes. So
the question here is who's responsibility is it to handle that situation?
> The third option is
> that there is a bug in the filesystem client.
Yep. But before we can go on to determining a bug, the proper/expected
behavior needs to be determined. I guess that's taking this a bit OT
for this list though. I'm not really sure where else to go to determine
this though. :-(
b.
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 259 bytes --]
^ permalink raw reply
* Re: Support for a series of patches, i.e. patchset or changeset?
From: Michael J Gruber @ 2012-11-05 14:40 UTC (permalink / raw)
To: Eric Miao; +Cc: git
In-Reply-To: <CAMPhdO-1ar52QGuSzbyFBSKMf48fDb6Bbxw5u3PCuVYxkO2=3w@mail.gmail.com>
Eric Miao venit, vidit, dixit 05.11.2012 15:12:
> The problem is, most cases we have no idea of the base rev1, and commit rev2
> which it's leading up to. E.g. for a single patch which is between
> commit rev1..rev2,
> how do we find out rev1 and rev2.
So, then the question is: What do you know/have? Is your patch the
output of "git format-patch", "git diff", or just some sort of diff
without any git information?
Michael
^ permalink raw reply
* Re: git log --graph --oneline - false parent-child visualization
From: Michael J Gruber @ 2012-11-05 14:17 UTC (permalink / raw)
To: martyone; +Cc: git
In-Reply-To: <CAMn6BBaeWfwa_mAyTBFOmFYs0GwUauzqZZP+NWM=8f0_hKDz3Q@mail.gmail.com>
martyone venit, vidit, dixit 05.11.2012 13:57:
> Hello,
>
> the combination of --graph and --oneline sometimes produces output
> which is -- at least for me -- not clear about parent-child relation
> between commits. I noticed it when using with --simplify-by-decoration
> switch but it should not be specific to the use of this switch.
>
> See this example output (git version 1.7.11.3)
>
> | | | * eead15f (origin/branchA) Lorem ipsum dolor sit amet
> | |_|/
> |/| |
> | | | * 8da3b9f (origin/branchB) Consectetur adipisicing elit
> | | |/
> | | | * c4d6b9a (origin/branchC) Sed do eiusmod tempor incididunt ut labore
> | | | * d623246 (origin/branchD) Ut enim ad minim veniam
> | | |/
> | | | * 458d305 (origin/btanchE) Quis nostrud exercitation ullamco laboris
> | | |/
>
> Here it seems commit c4d6b9a is based on d623246. But when the format
> is more-than-oneline (or when checked with gitk), it is clear there is
> no relation between commits c4d6b9a and d623246.
>
> | | | * commit eead15f (origin/branchA)
> | |_|/ Author: John Doe <john.doe@example.net>
> |/| |
> | | | Lorem ipsum dolor sit amet
> | | |
> | | | * commit 8da3b9f (origin/branchB)
> | | |/ Author: John Doe <john.doe@example.net>
> | | |
> | | | Consectetur adipisicing elit
> | | |
> | | | * commit c4d6b9a (origin/branchC)
> | | | Author: John Doe <john.doe@example.net>
> | | |
> | | | Sed do eiusmod tempor incididunt ut labore
> | | |
> | | | * commit d623246 (origin/branchD)
> | | |/ Author: John Doe <john.doe@example.net>
> | | |
> | | | Ut enim ad minim veniam
> | | |
> | | | * commit 458d305 (origin/branchE)
> | | |/ Author: John Doe <john.doe@example.net>
> | | |
> | | | Quis nostrud exercitation ullamco laboris
> | | |
>
> Correct output produced with --oneline switch should output an extra
> newline when commit has no parent listed:
>
> | | | * eead15f (origin/branchA) Lorem ipsum dolor sit amet
> | |_|/
> |/| |
> | | | * 8da3b9f (origin/branchB) Consectetur adipisicing elit
> | | |/
> | | | * c4d6b9a (origin/branchC) Sed do eiusmod tempor incididunt ut labore
> | | |
> | | | * d623246 (origin/branchD) Ut enim ad minim veniam
> | | |/
> | | | * 458d305 (origin/btanchE) Quis nostrud exercitation ullamco laboris
> | | |/
>
> Best Regards,
> Martin
>
Yes, you have the same problem when you simply have two disjoint
branches: They're listed "concatenated". The problem is that log --graph
uses the single symbol "*" for many different cases, independent of the
number of ingoing or outgoing lines. There are two solutions:
- use more spacing
- use more symbols (like tig does) and stay compact
Feel free to experiment ;)
Michael
^ permalink raw reply
* Re: [PATCH v4 00/13] New remote-hg helper
From: Michael J Gruber @ 2012-11-05 14:13 UTC (permalink / raw)
To: Felipe Contreras
Cc: Jeff King, Johannes Schindelin, git, Junio C Hamano,
Sverre Rabbelier, Ilari Liusvaara, Daniel Barkalow
In-Reply-To: <CAMP44s1mbNBUspJ8SX=VwGSXthxWAHkrQLFRxzyCzkupLYSagA@mail.gmail.com>
Felipe Contreras venit, vidit, dixit 02.11.2012 19:01:
> On Fri, Nov 2, 2012 at 5:41 PM, Felipe Contreras
> <felipe.contreras@gmail.com> wrote:
>> On Fri, Nov 2, 2012 at 3:48 PM, Jeff King <peff@peff.net> wrote:
>>> On Thu, Nov 01, 2012 at 05:08:52AM +0100, Felipe Contreras wrote:
>>>
>>>>> Turns out msysgit's remote-hg is not exporting the whole repository,
>>>>> that's why it's faster =/
>>>>
>>>> It seems the reason is that it would only export to the point where
>>>> the branch is checked out. After updating the to the tip I noticed
>>>> there was a performance difference.
>>>>
>>>> I investigated and found two reasons:
>>>>
>>>> 1) msysgit's version doesn't export files twice, I've now implemented the same
>>>> 2) msysgit's version uses a very simple algorithm to find out file changes
>>>>
>>>> This second point causes msysgit to miss some file changes. Using the
>>>> same algorithm I get the same performance, but the output is not
>>>> correct.
>>>
>>> Do you have a test case that demonstrates this? It would be helpful for
>>> reviewers, but also helpful to msysgit people if they want to fix their
>>> implementation.
>>
>> Cloning the mercurial repo:
>>
>> % hg log --stat -r 131
>> changeset: 131:c9d51742471c
>> parent: 127:44538462d3c8
>> user: jake@edge2.net
>> date: Sat May 21 11:35:26 2005 -0700
>> summary: moving hgweb to mercurial subdir
>>
>> hgweb.py | 377
>> ------------------------------------------------------------------------------------------
>> mercurial/hgweb.py | 377
>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 377 insertions(+), 377 deletions(-)
>>
>> % git show --stat 1f9bcfe7cc3d7af7b4533895181acd316ce172d8
>> commit 1f9bcfe7cc3d7af7b4533895181acd316ce172d8
>> Author: jake@edge2.net <none@none>
>> Date: Sat May 21 11:35:26 2005 -0700
>>
>> moving hgweb to mercurial subdir
>>
>> mercurial/hgweb.py | 377
>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 377 insertions(+)
>
> I talked with some people in #mercurial, and apparently there is a
> concept of a 'changelog' that is supposed to store these changes, but
> since the format has changed, the content of it is unreliable. That's
> not a big problem because it's used mostly for reporting purposes
> (log, query), not for doing anything reliable.
Is the changelog stored in the repo (i.e. generated by the hg version at
commit time) or generated on the fly (i.e. generated by the hg version
at hand)? See also below.
> To reliably see the changes, one has to compare the 'manifest' of the
> revisions involved, which contain *all* the files in them.
'manifest' == '(exploded) tree', right? Just making sure my hg fu is not
subzero.
> That's what I was doing already, but I found a more efficient way to
> do it. msysGit is using the changelog, which is quite fast, but not
> reliable.
>
> Unfortunately while going trough mercurial's code, I found an issue,
> and it turns out that 1) is not correct.
>
> In mercurial, a file hash contains also the parent file nodes, which
> means that even if two files have the same content, they would not
> have the same hash, so there's no point in keeping track of them to
> avoid extracting the data unnecessarily, because in order to make sure
> they are different, you need to extract the data anyway, defeating the
> purpose.
Do I understand correctly that neither the msysgit version nor yours can
detect duplicate blobs (without requesting them) because of that sha1 issue?
I'm really wondering why a file blob hash carries its history along in
the sha1. This appears completely strange to gitters (being brain washed
about "content tracking"), but may be due to hg's extensive use of
delta, or really: delta chains (which do have their merit on the server
side).
> Which means mercurial doesn't really behave as one would expect:
>
> # add files with the same content
>
> $ echo a > a
> $ hg ci -Am adda
> adding a
> $ echo a >> a
> $ hg ci -m changea
> $ echo a > a
> $ hg st --rev 0
> $ hg ci -m reverta
> $ hg log -G --template '{rev} {desc}\n'
> @ 2 reverta
> |
> o 1 changea
> |
> o 0 adda
>
> # check the difference between the first and the last revision
>
> $ hg st --rev 0:2
> M a
> $ hg cat -r 0 a
> a
> $ hg cat -r 2 a
> a
That is really scary. What use is "hg stat --rev" then? Not blaming you
for hg, of course.
On that tangent, I just noticed recently that hg has no python api.
Seriously [1]. They even tell us not to use the internal python api.
msysgit has been lacking support for newer hg, and you've had to add
support for older versions (hg 1.9 will be around on quite some
stable/LTS/EL distro releases) after developing on newer/current ones.
I'm wondering how well that scales in the long term (telling from
git-svn experience: it does not scale well), or whether using some
stable api like 'hgapi' would be a huge bottleneck.
Cheers,
Michael
[1] http://mercurial.selenic.com/wiki/MercurialApi
Really funny to see they recommend the command line as api ;)
^ permalink raw reply
* Re: Support for a series of patches, i.e. patchset or changeset?
From: Eric Miao @ 2012-11-05 14:12 UTC (permalink / raw)
To: Michael J Gruber; +Cc: git
In-Reply-To: <5097C190.80406@drmicha.warpmail.net>
The problem is, most cases we have no idea of the base rev1, and commit rev2
which it's leading up to. E.g. for a single patch which is between
commit rev1..rev2,
how do we find out rev1 and rev2.
On Mon, Nov 5, 2012 at 9:39 PM, Michael J Gruber
<git@drmicha.warpmail.net> wrote:
> Eric Miao venit, vidit, dixit 05.11.2012 03:26:
>> Hi All,
>>
>> Does anyone know if git has sort of support for a series of patches, i.e.
>> a patchset or changeset? So whenever we know the SHA1 id of a single
>> patch/commit, we know the patchset it belongs to. This is normal when
>> we do big changes and split that into smaller pieces and doing only one
>> simple thing in a single commit.
>>
>> This will be especially useful when tracking and cherry-picking changes,
>> i.e. monitoring on the changes of some specific files, and if a specific
>> patch is interesting, we may want to apply the whole changeset, not only
>> that specific one.
>
> First of all, if you know the sha1 of a commit, then all its ancestors
> are determined by that. If you want to describe a set of patches, say
> based on rev1 and leading up to rev2, then the expression
>
> rev2 ^rev1
>
> describes that set uniquely. Often you can do without ^rev1, e.g. if you
> know that all patch series are developed bases on origin/master, then
> specifying rev2 is enough as "git rev-list rev2 ^origin/master" will
> give you all commits in the series (unless they have been integrated,
> i.e. merged).
>
> Or are you thinking about patches "independent" of a base?
>
> Cheers,
> Michael
>
^ permalink raw reply
* Re: Support for a series of patches, i.e. patchset or changeset?
From: Michael J Gruber @ 2012-11-05 13:39 UTC (permalink / raw)
To: Eric Miao; +Cc: git
In-Reply-To: <CAMPhdO_33CPJv2hAvPuJ10KZ7v_fgP9P2kV_WLVK2tapjQQ5=A@mail.gmail.com>
Eric Miao venit, vidit, dixit 05.11.2012 03:26:
> Hi All,
>
> Does anyone know if git has sort of support for a series of patches, i.e.
> a patchset or changeset? So whenever we know the SHA1 id of a single
> patch/commit, we know the patchset it belongs to. This is normal when
> we do big changes and split that into smaller pieces and doing only one
> simple thing in a single commit.
>
> This will be especially useful when tracking and cherry-picking changes,
> i.e. monitoring on the changes of some specific files, and if a specific
> patch is interesting, we may want to apply the whole changeset, not only
> that specific one.
First of all, if you know the sha1 of a commit, then all its ancestors
are determined by that. If you want to describe a set of patches, say
based on rev1 and leading up to rev2, then the expression
rev2 ^rev1
describes that set uniquely. Often you can do without ^rev1, e.g. if you
know that all patch series are developed bases on origin/master, then
specifying rev2 is enough as "git rev-list rev2 ^origin/master" will
give you all commits in the series (unless they have been integrated,
i.e. merged).
Or are you thinking about patches "independent" of a base?
Cheers,
Michael
^ permalink raw reply
* git log --graph --oneline - false parent-child visualization
From: martyone @ 2012-11-05 12:57 UTC (permalink / raw)
To: git
Hello,
the combination of --graph and --oneline sometimes produces output
which is -- at least for me -- not clear about parent-child relation
between commits. I noticed it when using with --simplify-by-decoration
switch but it should not be specific to the use of this switch.
See this example output (git version 1.7.11.3)
| | | * eead15f (origin/branchA) Lorem ipsum dolor sit amet
| |_|/
|/| |
| | | * 8da3b9f (origin/branchB) Consectetur adipisicing elit
| | |/
| | | * c4d6b9a (origin/branchC) Sed do eiusmod tempor incididunt ut labore
| | | * d623246 (origin/branchD) Ut enim ad minim veniam
| | |/
| | | * 458d305 (origin/btanchE) Quis nostrud exercitation ullamco laboris
| | |/
Here it seems commit c4d6b9a is based on d623246. But when the format
is more-than-oneline (or when checked with gitk), it is clear there is
no relation between commits c4d6b9a and d623246.
| | | * commit eead15f (origin/branchA)
| |_|/ Author: John Doe <john.doe@example.net>
|/| |
| | | Lorem ipsum dolor sit amet
| | |
| | | * commit 8da3b9f (origin/branchB)
| | |/ Author: John Doe <john.doe@example.net>
| | |
| | | Consectetur adipisicing elit
| | |
| | | * commit c4d6b9a (origin/branchC)
| | | Author: John Doe <john.doe@example.net>
| | |
| | | Sed do eiusmod tempor incididunt ut labore
| | |
| | | * commit d623246 (origin/branchD)
| | |/ Author: John Doe <john.doe@example.net>
| | |
| | | Ut enim ad minim veniam
| | |
| | | * commit 458d305 (origin/branchE)
| | |/ Author: John Doe <john.doe@example.net>
| | |
| | | Quis nostrud exercitation ullamco laboris
| | |
Correct output produced with --oneline switch should output an extra
newline when commit has no parent listed:
| | | * eead15f (origin/branchA) Lorem ipsum dolor sit amet
| |_|/
|/| |
| | | * 8da3b9f (origin/branchB) Consectetur adipisicing elit
| | |/
| | | * c4d6b9a (origin/branchC) Sed do eiusmod tempor incididunt ut labore
| | |
| | | * d623246 (origin/branchD) Ut enim ad minim veniam
| | |/
| | | * 458d305 (origin/btanchE) Quis nostrud exercitation ullamco laboris
| | |/
Best Regards,
Martin
^ permalink raw reply
* Re: git-p4 clone @all error
From: Arthur @ 2012-11-05 10:02 UTC (permalink / raw)
To: git
In-Reply-To: <20121103231305.GB11267@padd.com>
Hi,
Here is my import :
Importing from //depot@all into XXXXX
Initialized empty Git repository in
/home/arthur/projets_git/XXXXX/XXXXX/.git/
Importing revision 4258 (43%)
Importing new branch depot/DEV_DATA
Resuming with change 4258
Importing revision 5828 (63%)
Importing new branch depot/RELEASE_1.0
Resuming with change 5828
Importing revision 7720 (88%)
Importing new branch depot/RELEASE_1.0.0
Resuming with change 7720
Importing revision 8588 (100%)
Updated branches: DEV_DATA RELEASE_1.0 MAINLINE/02_SubSystem/10_ARINC_429
MAINLINE RELEASE_1.0.0
fast-import failed: error: unable to resolve reference
refs/remotes/p4/depot/MAINLINE/02_SubSystem/10_ARINC_429: Not a directory
error: Unable to lock
refs/remotes/p4/depot/MAINLINE/02_SubSystem/10_ARINC_429
error: unable to resolve reference
refs/remotes/p4/depot/MAINLINE/02_SubSystem/10_ARINC_429: Not a directory
error: Unable to lock
refs/remotes/p4/depot/MAINLINE/02_SubSystem/10_ARINC_429
error: unable to resolve reference
refs/remotes/p4/depot/MAINLINE/02_SubSystem/10_ARINC_429: Not a directory
error: Unable to lock
refs/remotes/p4/depot/MAINLINE/02_SubSystem/10_ARINC_429
error: unable to resolve reference
refs/remotes/p4/depot/MAINLINE/02_SubSystem/10_ARINC_429: Not a directory
error: Unable to lock
refs/remotes/p4/depot/MAINLINE/02_SubSystem/10_ARINC_429
error: unable to resolve reference
refs/remotes/p4/depot/MAINLINE/02_SubSystem/10_ARINC_429: Not a directory
error: Unable to lock
refs/remotes/p4/depot/MAINLINE/02_SubSystem/10_ARINC_429
error: unable to resolve reference
refs/remotes/p4/depot/MAINLINE/02_SubSystem/10_ARINC_429: Not a directory
error: Unable to lock
refs/remotes/p4/depot/MAINLINE/02_SubSystem/10_ARINC_429
error: unable to resolve reference
refs/remotes/p4/depot/MAINLINE/02_SubSystem/10_ARINC_429: Not a directory
error: Unable to lock
refs/remotes/p4/depot/MAINLINE/02_SubSystem/10_ARINC_429
git-fast-import statistics:
---------------------------------------------------------------------
Alloc'd objects: 170000
Total objects: 169644 ( 195421 duplicates )
blobs : 92182 ( 186294 duplicates 74565 deltas)
trees : 70889 ( 9127 duplicates 57686 deltas)
commits: 6573 ( 0 duplicates 0 deltas)
tags : 0 ( 0 duplicates 0 deltas)
Total branches: 8 ( 8 loads )
marks: 1024 ( 0 unique )
atoms: 19603
Memory total: 19217 KiB
pools: 12576 KiB
objects: 6640 KiB
---------------------------------------------------------------------
pack_report: getpagesize() = 4096
pack_report: core.packedGitWindowSize = 33554432
pack_report: core.packedGitLimit = 268435456
pack_report: pack_used_ctr = 19803
pack_report: pack_mmap_calls = 65
pack_report: pack_open_windows = 10 / 11
pack_report: pack_mapped = 257715823 / 268009874
---------------------------------------------------------------------
Import crash after importing revision, my import have not files
--
View this message in context: http://git.661346.n2.nabble.com/git-p4-clone-all-error-tp7570219p7570575.html
Sent from the git mailing list archive at Nabble.com.
^ permalink raw reply
* Re: Lack of netiquette, was Re: [PATCH v4 00/13] New remote-hg helper
From: Michael J Gruber @ 2012-11-05 9:25 UTC (permalink / raw)
To: Felipe Contreras
Cc: Andreas Ericsson, René Scharfe, Junio C Hamano,
Johannes Schindelin, Jonathan Nieder, Jeff King, git,
Sverre Rabbelier, Ilari Liusvaara, Daniel Barkalow
In-Reply-To: <CAMP44s0yk3k1awYbJCcReBDEAjMyfHtKH70S7v2ZOJ1u5OcBAw@mail.gmail.com>
Felipe Contreras venit, vidit, dixit 02.11.2012 17:09:
> On Fri, Nov 2, 2012 at 12:03 PM, Michael J Gruber
> <git@drmicha.warpmail.net> wrote:
>> Andreas Ericsson venit, vidit, dixit 02.11.2012 10:38:
>>> On 11/01/2012 02:46 PM, René Scharfe wrote:
>>>>
>>>> Also, and I'm sure you didn't know that, "Jedem das Seine" (to each
>>>> his own) was the slogan of the Buchenwald concentration camp. For
>>>> that reason some (including me) hear the unspoken cynical
>>>> half-sentence "and some people just have to be sent to the gas
>>>> chamber" when someone uses this proverb.
>>>>
>>>
>>> It goes further back than that.
>>>
>>> "Suum cuique pulchrum est" ("To each his own is a beautiful thing") is
>>> a latin phrase said to be used frequently in the roman senate when
>>> senators politely agreed to disagree and let a vote decide the outcome
>>> rather than debating further.
>>>
>>> Please don't let the twisted views of whatever nazi idiot thought it
>>> meant "you may have the wrong faith and therefore deserve to die, so you
>>> shall" pollute it. The original meaning is both poetic and democratic,
>>> and I firmly believe most people have the original meaning to the fore
>>> of their mind when using it. After all, very few people knowingly quote
>>> nazi concentration camp slogans.
>>>
>>
>> In fact, many German terms and words are "forbidden area" since Nazi
>> times, but I don't think this one carries the same connotation.
>>
>> But that is a side track.
>>
>> Collaboration (and code review is a form of collaboration) requires
>> communication. The linked code of conduct pages describe quite well how
>> to ensure a productive environment in which "everyone" feels comfortable
>> communicating and collaborating.
>
> Yes, but that's assuming we want "everyone" to feel comfortable
> communicating and collaborating.
I put "everyone" in quotes because you can never reach 100%, so
"everyone" means almost everyone.
Undeniably, the answers in this and the other threads show that on the
git mailing list, "everyone" wants "everyone" to feel comfortable
communicating and collaborating.
> I cite again the example of the Linux
> kernel, where certainly not "everyone" feels that way. But somehow
It's a different list with different standards and tone, so it doesn't
really matter for our list. That being said:
> they manage to be perhaps the most successful software project in
> history. And I would argue even more: it's _because_ not everyone
> feels comfortable, it's because ideas and code are criticized freely,
> and because only the ones that do have merit stand. If you are able to
> take criticism, and you are not emotionally and personally attacked to
> your code and your ideas, you would thrive in this environment. If you
> don't want your precious little baby code to fight against the big
> guys, then you shouldn't send it out to the world.
For one thing, contributors on the kernel list are open to technical
arguments, and that includes the arguments of others; just like we are
here. On the other hand, you seem to rebuke "any" (most) technical
argument in harsh words as if it were a personal attack; at least that's
how your answers come across to me (and apparently others). That really
makes it difficult for most of us here to argue with you technically,
which is a pity. That lack of openness for the arguments of others would
make your life difficult on the kernel list also.
A completely different issue is that of language. You talk German on a
German list and English on an international list. You talk "kernel
English" on the kernel list, which is full of words and phrases you
would never use in a normal social setting where you talk to people in
person; it would be completely unacceptable. Here on the Git list, we
prefer to talk like in a normal, albeit colloquial social setting. If
you're open for advice: just imagine talking to the people here in
person, to colleagues across your desk, and you have a good guideline.
And no, using the same or similar language does not make us the same at
all. Using the same language is the natural prerequisite for successful
communication.
Felipe, please try to see the efforts many of us are making here in
order to keep you as a contributor, and reward it by accepting the
advice to revise your language: colleague to colleague.
Michael
^ permalink raw reply
* [PATCH 2/2] link_alt_odb_entries(): take (char *, len) rather than two pointers
From: Michael Haggerty @ 2012-11-05 8:41 UTC (permalink / raw)
To: Jeff King; +Cc: Junio C Hamano, git, Michael Haggerty
In-Reply-To: <1352104883-21053-1-git-send-email-mhagger@alum.mit.edu>
Change link_alt_odb_entries() to take the length of the "alt"
parameter rather than a pointer to the end of the "alt" string. This
is the more common calling convention and simplifies the code a tiny
bit.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
sha1_file.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/sha1_file.c b/sha1_file.c
index c352413..40b2329 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -317,7 +317,7 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base, int
return 0;
}
-static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
+static void link_alt_odb_entries(const char *alt, int len, int sep,
const char *relative_base, int depth)
{
struct string_list entries = STRING_LIST_INIT_NODUP;
@@ -330,7 +330,7 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
return;
}
- alt_copy = xmemdupz(alt, ep - alt);
+ alt_copy = xmemdupz(alt, len);
string_list_split_in_place(&entries, alt_copy, sep, -1);
for (i = 0; i < entries.nr; i++) {
const char *entry = entries.items[i].string;
@@ -371,7 +371,7 @@ void read_info_alternates(const char * relative_base, int depth)
map = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
- link_alt_odb_entries(map, map + mapsz, '\n', relative_base, depth);
+ link_alt_odb_entries(map, mapsz, '\n', relative_base, depth);
munmap(map, mapsz);
}
@@ -385,7 +385,7 @@ void add_to_alternates_file(const char *reference)
if (commit_lock_file(lock))
die("could not close alternates file");
if (alt_odb_tail)
- link_alt_odb_entries(alt, alt + strlen(alt), '\n', NULL, 0);
+ link_alt_odb_entries(alt, strlen(alt), '\n', NULL, 0);
}
void foreach_alt_odb(alt_odb_fn fn, void *cb)
@@ -409,7 +409,7 @@ void prepare_alt_odb(void)
if (!alt) alt = "";
alt_odb_tail = &alt_odb_list;
- link_alt_odb_entries(alt, alt + strlen(alt), PATH_SEP, NULL, 0);
+ link_alt_odb_entries(alt, strlen(alt), PATH_SEP, NULL, 0);
read_info_alternates(get_object_directory(), 0);
}
--
1.8.0
^ permalink raw reply related
* [PATCH 1/2] link_alt_odb_entries(): use string_list_split_in_place()
From: Michael Haggerty @ 2012-11-05 8:41 UTC (permalink / raw)
To: Jeff King; +Cc: Junio C Hamano, git, Michael Haggerty
In-Reply-To: <1352104883-21053-1-git-send-email-mhagger@alum.mit.edu>
Change link_alt_odb_entry() to take a NUL-terminated string instead of
(char *, len). Use string_list_split_in_place() rather than inline
code in link_alt_odb_entries().
This approach saves some code and also avoids the (probably harmless)
error of passing a non-NUL-terminated string to is_absolute_path().
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
sha1_file.c | 42 ++++++++++++++++++------------------------
1 file changed, 18 insertions(+), 24 deletions(-)
diff --git a/sha1_file.c b/sha1_file.c
index 9152974..c352413 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -7,6 +7,7 @@
* creation etc.
*/
#include "cache.h"
+#include "string-list.h"
#include "delta.h"
#include "pack.h"
#include "blob.h"
@@ -246,7 +247,7 @@ static int git_open_noatime(const char *name);
* SHA1, an extra slash for the first level indirection, and the
* terminating NUL.
*/
-static int link_alt_odb_entry(const char * entry, int len, const char * relative_base, int depth)
+static int link_alt_odb_entry(const char *entry, const char *relative_base, int depth)
{
const char *objdir = get_object_directory();
struct alternate_object_database *ent;
@@ -258,7 +259,7 @@ static int link_alt_odb_entry(const char * entry, int len, const char * relative
strbuf_addstr(&pathbuf, real_path(relative_base));
strbuf_addch(&pathbuf, '/');
}
- strbuf_add(&pathbuf, entry, len);
+ strbuf_addstr(&pathbuf, entry);
normalize_path_copy(pathbuf.buf, pathbuf.buf);
@@ -319,7 +320,9 @@ static int link_alt_odb_entry(const char * entry, int len, const char * relative
static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
const char *relative_base, int depth)
{
- const char *cp, *last;
+ struct string_list entries = STRING_LIST_INIT_NODUP;
+ char *alt_copy;
+ int i;
if (depth > 5) {
error("%s: ignoring alternate object stores, nesting too deep.",
@@ -327,30 +330,21 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
return;
}
- last = alt;
- while (last < ep) {
- cp = last;
- if (cp < ep && *cp == '#') {
- while (cp < ep && *cp != sep)
- cp++;
- last = cp + 1;
+ alt_copy = xmemdupz(alt, ep - alt);
+ string_list_split_in_place(&entries, alt_copy, sep, -1);
+ for (i = 0; i < entries.nr; i++) {
+ const char *entry = entries.items[i].string;
+ if (entry[0] == '\0' || entry[0] == '#')
continue;
+ if (!is_absolute_path(entry) && depth) {
+ error("%s: ignoring relative alternate object store %s",
+ relative_base, entry);
+ } else {
+ link_alt_odb_entry(entry, relative_base, depth);
}
- while (cp < ep && *cp != sep)
- cp++;
- if (last != cp) {
- if (!is_absolute_path(last) && depth) {
- error("%s: ignoring relative alternate object store %s",
- relative_base, last);
- } else {
- link_alt_odb_entry(last, cp - last,
- relative_base, depth);
- }
- }
- while (cp < ep && *cp == sep)
- cp++;
- last = cp;
}
+ string_list_clear(&entries, 0);
+ free(alt_copy);
}
void read_info_alternates(const char * relative_base, int depth)
--
1.8.0
^ permalink raw reply related
* [PATCH 0/2] Another minor cleanup involving string_lists
From: Michael Haggerty @ 2012-11-05 8:41 UTC (permalink / raw)
To: Jeff King; +Cc: Junio C Hamano, git, Michael Haggerty
Nothing really earthshattering here. But it's funny how every time I
look closely at a site where I think string_lists could be used, I
find problems with the old code. In this case is_absolute_path() is
called with an argument that is not a null-terminated string, which is
incorrect (though harmless because the function only looks at the
first two bytes of the string).
Another peculiarity of the (old and new) code is that it rejects
"comments" even in paths taken from the colon-separated environment
variable GIT_ALTERNATE_OBJECT_DIRECTORIES. The fix would be to change
link_alt_odb_entries() to take a string_list and let the callers strip
out comments when appropriate. But it didn't seem worth the extra
code.
Michael Haggerty (2):
link_alt_odb_entries(): use string_list_split_in_place()
link_alt_odb_entries(): take (char *, len) rather than two pointers
sha1_file.c | 50 ++++++++++++++++++++++----------------------------
1 file changed, 22 insertions(+), 28 deletions(-)
--
1.8.0
^ permalink raw reply
* [PATCH] Clarify how content states are to be built as the fast-import stream is interpreted.
From: Eric S. Raymond @ 2012-11-05 4:31 UTC (permalink / raw)
To: git
---
Documentation/git-fast-import.txt | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
index 6603a7a..959e4d3 100644
--- a/Documentation/git-fast-import.txt
+++ b/Documentation/git-fast-import.txt
@@ -442,7 +442,9 @@ their syntax.
^^^^^^
The `from` command is used to specify the commit to initialize
this branch from. This revision will be the first ancestor of the
-new commit.
+new commit. The state of the tree built at this commit will begin
+with the state at the `from` commit, and be altered by the content
+modifications in this commit.
Omitting the `from` command in the first commit of a new branch
will cause fast-import to create that commit with no ancestor. This
@@ -492,7 +494,9 @@ existing value of the branch.
`merge`
^^^^^^^
-Includes one additional ancestor commit. If the `from` command is
+Includes one additional ancestor commit. The additional ancestry
+link does not change the way the tree state is built at this commit.
+If the `from` command is
omitted when creating a new branch, the first `merge` commit will be
the first ancestor of the current commit, and the branch will start
out with no files. An unlimited number of `merge` commands per
--
1.7.9.5
--
<a href="http://www.catb.org/~esr/">Eric S. Raymond</a>
^ permalink raw reply related
* [PATCH v2 5/5] push: update remote tags only with force
From: Chris Rorvick @ 2012-11-05 3:08 UTC (permalink / raw)
To: git
Cc: Chris Rorvick, Felipe Contreras, Jeff King, Michael Haggerty,
Angelo Borsotti, Philip Oakley, Johannes Sixt, Kacper Kornet
In-Reply-To: <1352084908-32333-1-git-send-email-chris@rorvick.com>
References are allowed to update from one commit-ish to another if the
former is a ancestor of the latter. This behavior is oriented to
branches which are expected to move with commits. Tag references are
expected to be static in a repository, though, thus an update to a
tag (lightweight and annotated) should be rejected unless the update is
forced.
To enable this functionality, the following checks have been added to
set_ref_status_for_push() for updating refs (i.e, not new or deletion)
to restrict fast-forwarding in pushes:
1) The old and new references must be commits. If this fails,
it is not a valid update for a branch.
2) The reference name cannot start with "refs/tags/". This
catches lightweight tags which (usually) point to commits
and therefore would not be caught by (1).
If either of these checks fails, then it is flagged (by default) with a
status indicating the update is being rejected due to the reference
already existing in the remote. This can be overridden by passing
--force to git push.
Signed-off-by: Chris Rorvick <chris@rorvick.com>
---
Documentation/git-push.txt | 10 +++++-----
builtin/push.c | 3 +--
builtin/send-pack.c | 6 ++++++
cache.h | 1 +
remote.c | 8 +++++++-
t/t5516-fetch-push.sh | 30 +++++++++++++++++++++++++++++-
transport-helper.c | 6 ++++++
transport.c | 8 ++++++--
8 files changed, 61 insertions(+), 11 deletions(-)
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 22d2580..7107d76 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -51,11 +51,11 @@ be named. If `:`<dst> is omitted, the same ref as <src> will be
updated.
+
The object referenced by <src> is used to update the <dst> reference
-on the remote side, but by default this is only allowed if the
-update can fast-forward <dst>. By having the optional leading `+`,
-you can tell git to update the <dst> ref even when the update is not a
-fast-forward. This does *not* attempt to merge <src> into <dst>. See
-EXAMPLES below for details.
+on the remote side. By default this is only allowed if the update is
+a branch, and then only if it can fast-forward <dst>. By having the
+optional leading `+`, you can tell git to update the <dst> ref even when
+the update is not a branch or it is not a fast-forward. This does *not*
+attempt to merge <src> into <dst>. See EXAMPLES below for details.
+
`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
+
diff --git a/builtin/push.c b/builtin/push.c
index 77340c0..d097348 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -222,8 +222,7 @@ static const char message_advice_checkout_pull_push[] =
static const char message_advice_ref_already_exists[] =
N_("Updates were rejected because a matching reference already exists in\n"
- "the remote and the update is not a fast-forward. Use git push -f if\n"
- "you really want to make this update.");
+ "the remote. Use git push -f if you really want to make this update.");
static void advise_pull_before_push(void)
{
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 7d05064..f159ec3 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -202,6 +202,11 @@ static void print_helper_status(struct ref *ref)
msg = "non-fast forward";
break;
+ case REF_STATUS_REJECT_ALREADY_EXISTS:
+ res = "error";
+ msg = "already exists";
+ break;
+
case REF_STATUS_REJECT_NODELETE:
case REF_STATUS_REMOTE_REJECT:
res = "error";
@@ -288,6 +293,7 @@ int send_pack(struct send_pack_args *args,
/* Check for statuses set by set_ref_status_for_push() */
switch (ref->status) {
case REF_STATUS_REJECT_NONFASTFORWARD:
+ case REF_STATUS_REJECT_ALREADY_EXISTS:
case REF_STATUS_UPTODATE:
continue;
default:
diff --git a/cache.h b/cache.h
index b74c744..77786b6 100644
--- a/cache.h
+++ b/cache.h
@@ -1011,6 +1011,7 @@ struct ref {
REF_STATUS_NONE = 0,
REF_STATUS_OK,
REF_STATUS_REJECT_NONFASTFORWARD,
+ REF_STATUS_REJECT_ALREADY_EXISTS,
REF_STATUS_REJECT_NODELETE,
REF_STATUS_UPTODATE,
REF_STATUS_REMOTE_REJECT,
diff --git a/remote.c b/remote.c
index 552afd0..3e04f47 100644
--- a/remote.c
+++ b/remote.c
@@ -1335,7 +1335,13 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
(!has_sha1_file(ref->old_sha1)
|| !ref_newer(ref->new_sha1, ref->old_sha1));
- if (ref->nonfastforward) {
+ if (!ref->forwardable) {
+ ref->requires_force = 1;
+ if (!force_ref_update) {
+ ref->status = REF_STATUS_REJECT_ALREADY_EXISTS;
+ continue;
+ }
+ } else if (ref->nonfastforward) {
ref->requires_force = 1;
if (!force_ref_update) {
ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index b5417cc..afb9b1b 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -368,7 +368,7 @@ test_expect_success 'push with colon-less refspec (2)' '
git branch -D frotz
fi &&
git tag -f frotz &&
- git push testrepo frotz &&
+ git push -f testrepo frotz &&
check_push_result $the_commit tags/frotz &&
check_push_result $the_first_commit heads/frotz
@@ -929,6 +929,34 @@ test_expect_success 'push into aliased refs (inconsistent)' '
)
'
+test_expect_success 'push tag requires --force to update remote tag' '
+ mk_test heads/master &&
+ mk_child child1 &&
+ mk_child child2 &&
+ (
+ cd child1 &&
+ git tag lw_tag &&
+ git tag -a -m "message 1" ann_tag &&
+ git push ../child2 lw_tag &&
+ git push ../child2 ann_tag &&
+ >file1 &&
+ git add file1 &&
+ git commit -m "file1" &&
+ git tag -f lw_tag &&
+ git tag -f -a -m "message 2" ann_tag &&
+ test_must_fail git push ../child2 lw_tag &&
+ test_must_fail git push ../child2 ann_tag &&
+ git push --force ../child2 lw_tag &&
+ git push --force ../child2 ann_tag &&
+ git tag -f lw_tag HEAD~ &&
+ git tag -f -a -m "message 3" ann_tag &&
+ test_must_fail git push ../child2 lw_tag &&
+ test_must_fail git push ../child2 ann_tag &&
+ git push --force ../child2 lw_tag &&
+ git push --force ../child2 ann_tag
+ )
+'
+
test_expect_success 'push --porcelain' '
mk_empty &&
echo >.git/foo "To testrepo" &&
diff --git a/transport-helper.c b/transport-helper.c
index cfe0988..ef9a6f8 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -643,6 +643,11 @@ static void push_update_ref_status(struct strbuf *buf,
free(msg);
msg = NULL;
}
+ else if (!strcmp(msg, "already exists")) {
+ status = REF_STATUS_REJECT_ALREADY_EXISTS;
+ free(msg);
+ msg = NULL;
+ }
}
if (*ref)
@@ -702,6 +707,7 @@ static int push_refs_with_push(struct transport *transport,
/* Check for statuses set by set_ref_status_for_push() */
switch (ref->status) {
case REF_STATUS_REJECT_NONFASTFORWARD:
+ case REF_STATUS_REJECT_ALREADY_EXISTS:
case REF_STATUS_UPTODATE:
continue;
default:
diff --git a/transport.c b/transport.c
index 632f8b0..c183971 100644
--- a/transport.c
+++ b/transport.c
@@ -695,6 +695,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
"non-fast-forward", porcelain);
break;
+ case REF_STATUS_REJECT_ALREADY_EXISTS:
+ print_ref_status('!', "[rejected]", ref, ref->peer_ref,
+ "already exists", porcelain);
+ break;
case REF_STATUS_REMOTE_REJECT:
print_ref_status('!', "[remote rejected]", ref,
ref->deletion ? NULL : ref->peer_ref,
@@ -740,12 +744,12 @@ void transport_print_push_status(const char *dest, struct ref *refs,
ref->status != REF_STATUS_OK)
n += print_one_push_status(ref, dest, n, porcelain);
if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) {
- if (!ref->forwardable)
- *reject_mask |= REJECT_ALREADY_EXISTS;
if (!strcmp(head, ref->name))
*reject_mask |= REJECT_NON_FF_HEAD;
else
*reject_mask |= REJECT_NON_FF_OTHER;
+ } else if (ref->status == REF_STATUS_REJECT_ALREADY_EXISTS) {
+ *reject_mask |= REJECT_ALREADY_EXISTS;
}
}
}
--
1.7.1
^ permalink raw reply related
* [PATCH v2 0/5] push: update remote tags only with force
From: Chris Rorvick @ 2012-11-05 3:08 UTC (permalink / raw)
To: git
Cc: Chris Rorvick, Felipe Contreras, Jeff King, Michael Haggerty,
Angelo Borsotti, Philip Oakley, Johannes Sixt, Kacper Kornet
Patch series to prevent push from updating remote tags w/o forcing them.
Split out original patch to ease review.
Chris Rorvick (5):
push: return reject reasons via a mask
push: add advice for rejected tag reference
push: flag updates
push: flag updates that require force
push: update remote tags only with force
Documentation/git-push.txt | 10 +++++-----
builtin/push.c | 24 +++++++++++++++---------
builtin/send-pack.c | 6 ++++++
cache.h | 7 ++++++-
remote.c | 39 +++++++++++++++++++++++++++++++--------
t/t5516-fetch-push.sh | 30 +++++++++++++++++++++++++++++-
transport-helper.c | 6 ++++++
transport.c | 25 +++++++++++++++----------
transport.h | 10 ++++++----
9 files changed, 119 insertions(+), 38 deletions(-)
^ permalink raw reply
* [PATCH v2 4/5] push: flag updates that require force
From: Chris Rorvick @ 2012-11-05 3:08 UTC (permalink / raw)
To: git
Cc: Chris Rorvick, Felipe Contreras, Jeff King, Michael Haggerty,
Angelo Borsotti, Philip Oakley, Johannes Sixt, Kacper Kornet
In-Reply-To: <1352084908-32333-1-git-send-email-chris@rorvick.com>
Add a flag for indicating an update to a reference requires force.
Currently the nonfastforward flag of a ref is used for this when
generating status the status message. A separate flag insulates the
status logic from the details of set_ref_status_for_push().
Signed-off-by: Chris Rorvick <chris@rorvick.com>
---
cache.h | 4 +++-
remote.c | 11 ++++++++---
transport.c | 2 +-
3 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/cache.h b/cache.h
index 1d10761..b74c744 100644
--- a/cache.h
+++ b/cache.h
@@ -999,7 +999,9 @@ struct ref {
unsigned char old_sha1[20];
unsigned char new_sha1[20];
char *symref;
- unsigned int force:1,
+ unsigned int
+ force:1,
+ requires_force:1,
merge:1,
nonfastforward:1,
forwardable:1,
diff --git a/remote.c b/remote.c
index 3d43bb5..552afd0 100644
--- a/remote.c
+++ b/remote.c
@@ -1285,6 +1285,8 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
struct ref *ref;
for (ref = remote_refs; ref; ref = ref->next) {
+ int force_ref_update = ref->force || force_update;
+
if (ref->peer_ref)
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
else if (!send_mirror)
@@ -1333,9 +1335,12 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
(!has_sha1_file(ref->old_sha1)
|| !ref_newer(ref->new_sha1, ref->old_sha1));
- if (ref->nonfastforward && !ref->force && !force_update) {
- ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
- continue;
+ if (ref->nonfastforward) {
+ ref->requires_force = 1;
+ if (!force_ref_update) {
+ ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
+ continue;
+ }
}
}
}
diff --git a/transport.c b/transport.c
index 1657798..632f8b0 100644
--- a/transport.c
+++ b/transport.c
@@ -659,7 +659,7 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)
const char *msg;
strcpy(quickref, status_abbrev(ref->old_sha1));
- if (ref->nonfastforward) {
+ if (ref->requires_force) {
strcat(quickref, "...");
type = '+';
msg = "forced update";
--
1.7.1
^ permalink raw reply related
* [PATCH v2 3/5] push: flag updates
From: Chris Rorvick @ 2012-11-05 3:08 UTC (permalink / raw)
To: git
Cc: Chris Rorvick, Felipe Contreras, Jeff King, Michael Haggerty,
Angelo Borsotti, Philip Oakley, Johannes Sixt, Kacper Kornet
In-Reply-To: <1352084908-32333-1-git-send-email-chris@rorvick.com>
If the reference exists on the remote and the the update is not a
delete, then mark as an update. This is in preparation for handling
tags and branches differently when pushing.
Signed-off-by: Chris Rorvick <chris@rorvick.com>
---
cache.h | 1 +
remote.c | 19 ++++++++++++-------
2 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/cache.h b/cache.h
index bc2fc9a..1d10761 100644
--- a/cache.h
+++ b/cache.h
@@ -1003,6 +1003,7 @@ struct ref {
merge:1,
nonfastforward:1,
forwardable:1,
+ update:1,
deletion:1;
enum {
REF_STATUS_NONE = 0,
diff --git a/remote.c b/remote.c
index 5ecd58d..3d43bb5 100644
--- a/remote.c
+++ b/remote.c
@@ -1323,15 +1323,20 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
old->type == OBJ_COMMIT && new->type == OBJ_COMMIT);
}
- ref->nonfastforward =
+ ref->update =
!ref->deletion &&
- !is_null_sha1(ref->old_sha1) &&
- (!has_sha1_file(ref->old_sha1)
- || !ref_newer(ref->new_sha1, ref->old_sha1));
+ !is_null_sha1(ref->old_sha1);
- if (ref->nonfastforward && !ref->force && !force_update) {
- ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
- continue;
+ if (ref->update) {
+ ref->nonfastforward =
+ ref->update &&
+ (!has_sha1_file(ref->old_sha1)
+ || !ref_newer(ref->new_sha1, ref->old_sha1));
+
+ if (ref->nonfastforward && !ref->force && !force_update) {
+ ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
+ continue;
+ }
}
}
}
--
1.7.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox