Ryan Anderson [off-list ref] writes:
I've written a tool that will take a single commit, add it as a parent
of another commit, and recreate the history above that second commit in
a fully compatible manner.
I think the procedure is reproducible, which is a very nice
property to have for a tool like this, but I am not sure what
you mean by "in a fully compatible manner". What are you
compatible with?
Also another rhetorical, tongue-in-cheek question. What is your
plan to ripple the graft through to update signed tags? ;-)
Junio C Hamano wrote:
Ryan Anderson [off-list ref] writes:
quoted
I've written a tool that will take a single commit, add it as a parent
of another commit, and recreate the history above that second commit in
a fully compatible manner.
I think the procedure is reproducible, which is a very nice
property to have for a tool like this, but I am not sure what
you mean by "in a fully compatible manner". What are you
compatible with?
Well, what I meant was, "It creates a history that is purely a superset
of the old history, so merges should work cleanly from the pre-graft
subhistory to the fully merged history."
But clearly I was too ... terse.
IOW, this should work perfectly, assuming neither tree has been pulled
into since the history was merged into historical-graft tree:
$ cd linux-head
$ git branch -b ryan-hacking HEAD
$ quilt push -a
$ git commit -a -m "Apply quilt tree"
$ cd ../linux-historical-graft/
$ git pull ../linux-historical-graft/
Also another rhetorical, tongue-in-cheek question. What is your
plan to ripple the graft through to update signed tags? ;-)
:) Well, since I can't resist answering your rhetorical question:
They signed a specific DAG. I'm providing a richer, more complete DAG
that is a pure-superset of the one they signed. It is not, however,
equivalent, so their signature is not related to the superset DAG I have
created. In practice, however, I don't expect that any tag-signers
would state that there is a meaningful difference between the two DAGs,
from the perspective of their signature.
FYI - I don't think merging the trees like this is a good idea, from the
perspective of something like gitk - gitk took long enough to startup
and display something on my merged tree here that I gave up and killed
it off.
Hi, Junio C Hamano wrote:
Ryan Anderson [off-list ref] writes:
quoted
I've written a tool that will take a single commit, add it as a parent
of another commit, and recreate the history above that second commit in
a fully compatible manner.
You're not the only one. My tool is different, however, in that it accepts
a list of old=>new commits. I have used it successfully to re-graft my
changes from a CVS->GIT tree to the corresponding CVS->SVN->GIT tree
(which cannot be identical, because (a) SVN's timestamps are not accurate
enough and (b) SVN's CVS import is more accurate in reproducing CVS
archives than cvsps can be).
Also another rhetorical, tongue-in-cheek question. What is your
plan to ripple the graft through to update signed tags? ;-)
I'd suggest adding the capability of grafting something onto a tag...
#!/usr/bin/python
# This is a simple script which clones a git subtree to another.
import sys,re,optparse,os,subprocess
parser = optparse.OptionParser("corresponding_file [ old_commit ]", conflict_handler="resolve", description="""\
Transfer a list of commits from one repository to another.
The first argument is a list of SHA1 entries of the form
old new
It lists which commits are "the same".
old_commit and those of its parents which are not listed in the
corresponding_file are copied, i.e. new commit objects are created.
Their SHA1s are added to the file so that you may repeat the process
with other commits, or run it incrementally.
""")
parser.add_option("-h","--help","-?", action="help",
help="Print this help message and exit")
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
help="Report progress")
(options, args) = parser.parse_args()
if len(args) < 1 or len(args) > 3:
parser.error("requires one to three arguments")
def end(p):
try:
retcode = p.wait()
except OSError,e:
print >>sys.stderr, "git-rev-list failed:", e
else:
if retcode < 0:
print >>sys.stderr, "git-rev-list was terminated by signal", -retcode
sys.exit(1)
elif retcode > 0:
print >>sys.stderr, "git-rev-list exited with non-zero exit code", retcode
sys.exit(1)
re_cmt = re.compile(r'\s*#.*')
corr = {}
for l in open(args[0]):
l = re_cmt.sub("",l).strip()
if l == "": continue
try:
a,b = l.split()
except ValueError:
continue
if len(a) != 40: continue
if len(b) != 40: continue
corr[a]=b
corrf=open(args[0],"a")
if len(args) >= 2:
srctag = args[1]
else:
srctag = "HEAD"
srcrepo = os.path.curdir
commits=[]
cmd = ["git-rev-list",srctag]
for k in corr.iterkeys():
cmd.append("^"+k)
if options.verbose:
print cmd
p=subprocess.Popen(cmd, stdout=subprocess.PIPE)
for l in p.stdout:
l = l.strip()
commits.append(l)
end(p)
while len(commits):
c = commits.pop()
if c in corr: continue
if options.verbose:
print "Processing:",c
p=subprocess.Popen(["git-cat-file","commit",c], stdout=subprocess.PIPE)
qf=os.tempnam()
try:
q=open(qf,"w")
nx=False
for l in p.stdout:
if nx:
q.write(l)
continue
l = l.strip()
if l == "":
nx=True
q.write("\n")
continue
a,b = l.split(" ",1)
if a == "parent":
b = corr[b]
print >>q,a,b
q.close()
q=subprocess.Popen(["git-hash-object","-w","-t","commit",qf], stdout=subprocess.PIPE)
d = q.stdout.read().strip()
end(q)
finally:
os.unlink(qf)
end(p)
corr[c] = d
print >>corrf, c,d
if options.verbose:
print c,d,l
# OK, everything is done.
corrf.close()
print d
--
Matthias Urlichs | {M:U} IT Design @ m-u-it.de | smurf@smurf.noris.de
Disclaimer: The quote was selected randomly. Really. | http://smurf.noris.de
- -
It's not what you know or what you do, it's who you know.