contrib: remove git-p4import

The git p4import documentation has suggested git p4 as a better alternative for more than 6 years. (According to the mailing list discussion when it was moved to contrib/, git-p4import has serious bugs --- e.g., its incremental mode just doesn't work.) Since then, git p4 has been actively developed and was promoted to a standard git command alongside git svn. Searches on google.com/trends and stackoverflow suggest that no one is looking for git-p4import any more. Remove it. Noticed while considering marking the contrib/p4import/git-p4import.py script executable as part of a wider sweep. Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Acked-by: Pete Wyckoff <pw@padd.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jonathan Nieder committed Nov 25, 2013 at 12:58 UTC 30a3318ac06c639f0ccb25b3abf627b3120c4dc9
3 files changed -533
contrib/p4import/README deleted
-1
@@ -1 +0,0 @@
1 -Please see contrib/fast-import/git-p4 for a better Perforce importer.
contrib/p4import/git-p4import.py deleted
-365
@@ -1,365 +0,0 @@
1 -#!/usr/bin/env python
2 -#
3 -# This tool is copyright (c) 2006, Sean Estabrooks.
4 -# It is released under the Gnu Public License, version 2.
5 -#
6 -# Import Perforce branches into Git repositories.
7 -# Checking out the files is done by calling the standard p4
8 -# client which you must have properly configured yourself
9 -#
10 -
11 -import marshal
12 -import os
13 -import sys
14 -import time
15 -import getopt
16 -
17 -if sys.hexversion < 0x02020000:
18 - # The behavior of the marshal module changed significantly in 2.2
19 - sys.stderr.write("git-p4import.py: requires Python 2.2 or later.\n")
20 - sys.exit(1)
21 -
22 -from signal import signal, \
23 - SIGPIPE, SIGINT, SIG_DFL, \
24 - default_int_handler
25 -
26 -signal(SIGPIPE, SIG_DFL)
27 -s = signal(SIGINT, SIG_DFL)
28 -if s != default_int_handler:
29 - signal(SIGINT, s)
30 -
31 -def die(msg, *args):
32 - for a in args:
33 - msg = "%s %s" % (msg, a)
34 - print "git-p4import fatal error:", msg
35 - sys.exit(1)
36 -
37 -def usage():
38 - print "USAGE: git-p4import [-q|-v] [--authors=<file>] [-t <timezone>] [//p4repo/path <branch>]"
39 - sys.exit(1)
40 -
41 -verbosity = 1
42 -logfile = "/dev/null"
43 -ignore_warnings = False
44 -stitch = 0
45 -tagall = True
46 -
47 -def report(level, msg, *args):
48 - global verbosity
49 - global logfile
50 - for a in args:
51 - msg = "%s %s" % (msg, a)
52 - fd = open(logfile, "a")
53 - fd.writelines(msg)
54 - fd.close()
55 - if level <= verbosity:
56 - print msg
57 -
58 -class p4_command:
59 - def __init__(self, _repopath):
60 - try:
61 - global logfile
62 - self.userlist = {}
63 - if _repopath[-1] == '/':
64 - self.repopath = _repopath[:-1]
65 - else:
66 - self.repopath = _repopath
67 - if self.repopath[-4:] != "/...":
68 - self.repopath= "%s/..." % self.repopath
69 - f=os.popen('p4 -V 2>>%s'%logfile, 'rb')
70 - a = f.readlines()
71 - if f.close():
72 - raise
73 - except:
74 - die("Could not find the \"p4\" command")
75 -
76 - def p4(self, cmd, *args):
77 - global logfile
78 - cmd = "%s %s" % (cmd, ' '.join(args))
79 - report(2, "P4:", cmd)
80 - f=os.popen('p4 -G %s 2>>%s' % (cmd,logfile), 'rb')
81 - list = []
82 - while 1:
83 - try:
84 - list.append(marshal.load(f))
85 - except EOFError:
86 - break
87 - self.ret = f.close()
88 - return list
89 -
90 - def sync(self, id, force=False, trick=False, test=False):
91 - if force:
92 - ret = self.p4("sync -f %s@%s"%(self.repopath, id))[0]
93 - elif trick:
94 - ret = self.p4("sync -k %s@%s"%(self.repopath, id))[0]
95 - elif test:
96 - ret = self.p4("sync -n %s@%s"%(self.repopath, id))[0]
97 - else:
98 - ret = self.p4("sync %s@%s"%(self.repopath, id))[0]
99 - if ret['code'] == "error":
100 - data = ret['data'].upper()
101 - if data.find('VIEW') > 0:
102 - die("Perforce reports %s is not in client view"% self.repopath)
103 - elif data.find('UP-TO-DATE') < 0:
104 - die("Could not sync files from perforce", self.repopath)
105 -
106 - def changes(self, since=0):
107 - try:
108 - list = []
109 - for rec in self.p4("changes %s@%s,#head" % (self.repopath, since+1)):
110 - list.append(rec['change'])
111 - list.reverse()
112 - return list
113 - except:
114 - return []
115 -
116 - def authors(self, filename):
117 - f=open(filename)
118 - for l in f.readlines():
119 - self.userlist[l[:l.find('=')].rstrip()] = \
120 - (l[l.find('=')+1:l.find('<')].rstrip(),l[l.find('<')+1:l.find('>')])
121 - f.close()
122 - for f,e in self.userlist.items():
123 - report(2, f, ":", e[0], " <", e[1], ">")
124 -
125 - def _get_user(self, id):
126 - if not self.userlist.has_key(id):
127 - try:
128 - user = self.p4("users", id)[0]
129 - self.userlist[id] = (user['FullName'], user['Email'])
130 - except:
131 - self.userlist[id] = (id, "")
132 - return self.userlist[id]
133 -
134 - def _format_date(self, ticks):
135 - symbol='+'
136 - name = time.tzname[0]
137 - offset = time.timezone
138 - if ticks[8]:
139 - name = time.tzname[1]
140 - offset = time.altzone
141 - if offset < 0:
142 - offset *= -1
143 - symbol = '-'
144 - localo = "%s%02d%02d %s" % (symbol, offset / 3600, offset % 3600, name)
145 - tickso = time.strftime("%a %b %d %H:%M:%S %Y", ticks)
146 - return "%s %s" % (tickso, localo)
147 -
148 - def where(self):
149 - try:
150 - return self.p4("where %s" % self.repopath)[-1]['path']
151 - except:
152 - return ""
153 -
154 - def describe(self, num):
155 - desc = self.p4("describe -s", num)[0]
156 - self.msg = desc['desc']
157 - self.author, self.email = self._get_user(desc['user'])
158 - self.date = self._format_date(time.localtime(long(desc['time'])))
159 - return self
160 -
161 -class git_command:
162 - def __init__(self):
163 - try:
164 - self.version = self.git("--version")[0][12:].rstrip()
165 - except:
166 - die("Could not find the \"git\" command")
167 - try:
168 - self.gitdir = self.get_single("rev-parse --git-dir")
169 - report(2, "gdir:", self.gitdir)
170 - except:
171 - die("Not a git repository... did you forget to \"git init\" ?")
172 - try:
173 - self.cdup = self.get_single("rev-parse --show-cdup")
174 - if self.cdup != "":
175 - os.chdir(self.cdup)
176 - self.topdir = os.getcwd()
177 - report(2, "topdir:", self.topdir)
178 - except:
179 - die("Could not find top git directory")
180 -
181 - def git(self, cmd):
182 - global logfile
183 - report(2, "GIT:", cmd)
184 - f=os.popen('git %s 2>>%s' % (cmd,logfile), 'rb')
185 - r=f.readlines()
186 - self.ret = f.close()
187 - return r
188 -
189 - def get_single(self, cmd):
190 - return self.git(cmd)[0].rstrip()
191 -
192 - def current_branch(self):
193 - try:
194 - testit = self.git("rev-parse --verify HEAD")[0]
195 - return self.git("symbolic-ref HEAD")[0][11:].rstrip()
196 - except:
197 - return None
198 -
199 - def get_config(self, variable):
200 - try:
201 - return self.git("config --get %s" % variable)[0].rstrip()
202 - except:
203 - return None
204 -
205 - def set_config(self, variable, value):
206 - try:
207 - self.git("config %s %s"%(variable, value) )
208 - except:
209 - die("Could not set %s to " % variable, value)
210 -
211 - def make_tag(self, name, head):
212 - self.git("tag -f %s %s"%(name,head))
213 -
214 - def top_change(self, branch):
215 - try:
216 - a=self.get_single("name-rev --tags refs/heads/%s" % branch)
217 - loc = a.find(' tags/') + 6
218 - if a[loc:loc+3] != "p4/":
219 - raise
220 - return int(a[loc+3:][:-2])
221 - except:
222 - return 0
223 -
224 - def update_index(self):
225 - self.git("ls-files -m -d -o -z | git update-index --add --remove -z --stdin")
226 -
227 - def checkout(self, branch):
228 - self.git("checkout %s" % branch)
229 -
230 - def repoint_head(self, branch):
231 - self.git("symbolic-ref HEAD refs/heads/%s" % branch)
232 -
233 - def remove_files(self):
234 - self.git("ls-files | xargs rm")
235 -
236 - def clean_directories(self):
237 - self.git("clean -d")
238 -
239 - def fresh_branch(self, branch):
240 - report(1, "Creating new branch", branch)
241 - self.git("ls-files | xargs rm")
242 - os.remove(".git/index")
243 - self.repoint_head(branch)
244 - self.git("clean -d")
245 -
246 - def basedir(self):
247 - return self.topdir
248 -
249 - def commit(self, author, email, date, msg, id):
250 - self.update_index()
251 - fd=open(".msg", "w")
252 - fd.writelines(msg)
253 - fd.close()
254 - try:
255 - current = self.get_single("rev-parse --verify HEAD")
256 - head = "-p HEAD"
257 - except:
258 - current = ""
259 - head = ""
260 - tree = self.get_single("write-tree")
261 - for r,l in [('DATE',date),('NAME',author),('EMAIL',email)]:
262 - os.environ['GIT_AUTHOR_%s'%r] = l
263 - os.environ['GIT_COMMITTER_%s'%r] = l
264 - commit = self.get_single("commit-tree %s %s < .msg" % (tree,head))
265 - os.remove(".msg")
266 - self.make_tag("p4/%s"%id, commit)
267 - self.git("update-ref HEAD %s %s" % (commit, current) )
268 -
269 -try:
270 - opts, args = getopt.getopt(sys.argv[1:], "qhvt:",
271 - ["authors=","help","stitch=","timezone=","log=","ignore","notags"])
272 -except getopt.GetoptError:
273 - usage()
274 -
275 -for o, a in opts:
276 - if o == "-q":
277 - verbosity = 0
278 - if o == "-v":
279 - verbosity += 1
280 - if o in ("--log"):
281 - logfile = a
282 - if o in ("--notags"):
283 - tagall = False
284 - if o in ("-h", "--help"):
285 - usage()
286 - if o in ("--ignore"):
287 - ignore_warnings = True
288 -
289 -git = git_command()
290 -branch=git.current_branch()
291 -
292 -for o, a in opts:
293 - if o in ("-t", "--timezone"):
294 - git.set_config("perforce.timezone", a)
295 - if o in ("--stitch"):
296 - git.set_config("perforce.%s.path" % branch, a)
297 - stitch = 1
298 -
299 -if len(args) == 2:
300 - branch = args[1]
301 - git.checkout(branch)
302 - if branch == git.current_branch():
303 - die("Branch %s already exists!" % branch)
304 - report(1, "Setting perforce to ", args[0])
305 - git.set_config("perforce.%s.path" % branch, args[0])
306 -elif len(args) != 0:
307 - die("You must specify the perforce //depot/path and git branch")
308 -
309 -p4path = git.get_config("perforce.%s.path" % branch)
310 -if p4path == None:
311 - die("Do not know Perforce //depot/path for git branch", branch)
312 -
313 -p4 = p4_command(p4path)
314 -
315 -for o, a in opts:
316 - if o in ("-a", "--authors"):
317 - p4.authors(a)
318 -
319 -localdir = git.basedir()
320 -if p4.where()[:len(localdir)] != localdir:
321 - report(1, "**WARNING** Appears p4 client is misconfigured")
322 - report(1, " for sync from %s to %s" % (p4.repopath, localdir))
323 - if ignore_warnings != True:
324 - die("Reconfigure or use \"--ignore\" on command line")
325 -
326 -if stitch == 0:
327 - top = git.top_change(branch)
328 -else:
329 - top = 0
330 -changes = p4.changes(top)
331 -count = len(changes)
332 -if count == 0:
333 - report(1, "Already up to date...")
334 - sys.exit(0)
335 -
336 -ptz = git.get_config("perforce.timezone")
337 -if ptz:
338 - report(1, "Setting timezone to", ptz)
339 - os.environ['TZ'] = ptz
340 - time.tzset()
341 -
342 -if stitch == 1:
343 - git.remove_files()
344 - git.clean_directories()
345 - p4.sync(changes[0], force=True)
346 -elif top == 0 and branch != git.current_branch():
347 - p4.sync(changes[0], test=True)
348 - report(1, "Creating new initial commit");
349 - git.fresh_branch(branch)
350 - p4.sync(changes[0], force=True)
351 -else:
352 - p4.sync(changes[0], trick=True)
353 -
354 -report(1, "processing %s changes from p4 (%s) to git (%s)" % (count, p4.repopath, branch))
355 -for id in changes:
356 - report(1, "Importing changeset", id)
357 - change = p4.describe(id)
358 - p4.sync(id)
359 - if tagall :
360 - git.commit(change.author, change.email, change.date, change.msg, id)
361 - else:
362 - git.commit(change.author, change.email, change.date, change.msg, "import")
363 - if stitch == 1:
364 - git.clean_directories()
365 - stitch = 0
contrib/p4import/git-p4import.txt deleted
-167
@@ -1,167 +0,0 @@
1 -git-p4import(1)
2 -===============
3 -
4 -NAME
5 -----
6 -git-p4import - Import a Perforce repository into git
7 -
8 -
9 -SYNOPSIS
10 ---------
11 -[verse]
12 -`git-p4import` [-q|-v] [--notags] [--authors <file>] [-t <timezone>]
13 - <//p4repo/path> <branch>
14 -`git-p4import` --stitch <//p4repo/path>
15 -`git-p4import`
16 -
17 -
18 -DESCRIPTION
19 ------------
20 -Import a Perforce repository into an existing git repository. When
21 -a <//p4repo/path> and <branch> are specified a new branch with the
22 -given name will be created and the initial import will begin.
23 -
24 -Once the initial import is complete you can do an incremental import
25 -of new commits from the Perforce repository. You do this by checking
26 -out the appropriate git branch and then running `git-p4import` without
27 -any options.
28 -
29 -The standard p4 client is used to communicate with the Perforce
30 -repository; it must be configured correctly in order for `git-p4import`
31 -to operate (see below).
32 -
33 -
34 -OPTIONS
35 --------
36 --q::
37 - Do not display any progress information.
38 -
39 --v::
40 - Give extra progress information.
41 -
42 -\--authors::
43 - Specify an authors file containing a mapping of Perforce user
44 - ids to full names and email addresses (see Notes below).
45 -
46 -\--notags::
47 - Do not create a tag for each imported commit.
48 -
49 -\--stitch::
50 - Import the contents of the given perforce branch into the
51 - currently checked out git branch.
52 -
53 -\--log::
54 - Store debugging information in the specified file.
55 -
56 --t::
57 - Specify that the remote repository is in the specified timezone.
58 - Timezone must be in the format "US/Pacific" or "Europe/London"
59 - etc. You only need to specify this once, it will be saved in
60 - the git config file for the repository.
61 -
62 -<//p4repo/path>::
63 - The Perforce path that will be imported into the specified branch.
64 -
65 -<branch>::
66 - The new branch that will be created to hold the Perforce imports.
67 -
68 -
69 -P4 Client
70 ----------
71 -You must make the `p4` client command available in your $PATH and
72 -configure it to communicate with the target Perforce repository.
73 -Typically this means you must set the "$P4PORT" and "$P4CLIENT"
74 -environment variables.
75 -
76 -You must also configure a `p4` client "view" which maps the Perforce
77 -branch into the top level of your git repository, for example:
78 -
79 -------------
80 -Client: myhost
81 -
82 -Root: /home/sean/import
83 -
84 -Options: noallwrite clobber nocompress unlocked modtime rmdir
85 -
86 -View:
87 - //public/jam/... //myhost/jam/...
88 -------------
89 -
90 -With the above `p4` client setup, you could import the "jam"
91 -perforce branch into a branch named "jammy", like so:
92 -
93 -------------
94 -$ mkdir -p /home/sean/import/jam
95 -$ cd /home/sean/import/jam
96 -$ git init
97 -$ git p4import //public/jam jammy
98 -------------
99 -
100 -
101 -Multiple Branches
102 ------------------
103 -Note that by creating multiple "views" you can use `git-p4import`
104 -to import additional branches into the same git repository.
105 -However, the `p4` client has a limitation in that it silently
106 -ignores all but the last "view" that maps into the same local
107 -directory. So the following will *not* work:
108 -
109 -------------
110 -View:
111 - //public/jam/... //myhost/jam/...
112 - //public/other/... //myhost/jam/...
113 - //public/guest/... //myhost/jam/...
114 -------------
115 -
116 -If you want more than one Perforce branch to be imported into the
117 -same directory you must employ a workaround. A simple option is
118 -to adjust your `p4` client before each import to only include a
119 -single view.
120 -
121 -Another option is to create multiple symlinks locally which all
122 -point to the same directory in your git repository and then use
123 -one per "view" instead of listing the actual directory.
124 -
125 -
126 -Tags
127 -----
128 -A git tag of the form p4/xx is created for every change imported from
129 -the Perforce repository where xx is the Perforce changeset number.
130 -Therefore after the import you can use git to access any commit by its
131 -Perforce number, e.g. git show p4/327.
132 -
133 -The tag associated with the HEAD commit is also how `git-p4import`
134 -determines if there are new changes to incrementally import from the
135 -Perforce repository.
136 -
137 -If you import from a repository with many thousands of changes
138 -you will have an equal number of p4/xxxx git tags. Git tags can
139 -be expensive in terms of disk space and repository operations.
140 -If you don't need to perform further incremental imports, you
141 -may delete the tags.
142 -
143 -
144 -Notes
145 ------
146 -You can interrupt the import (e.g. ctrl-c) at any time and restart it
147 -without worry.
148 -
149 -Author information is automatically determined by querying the
150 -Perforce "users" table using the id associated with each change.
151 -However, if you want to manually supply these mappings you can do
152 -so with the "--authors" option. It accepts a file containing a list
153 -of mappings with each line containing one mapping in the format:
154 -
155 -------------
156 - perforce_id = Full Name <email@address.com>
157 -------------
158 -
159 -
160 -Author
161 -------
162 -Written by Sean Estabrooks <seanlkml@sympatico.ca>
163 -
164 -
165 -GIT
166 ----
167 -Part of the gitlink:git[7] suite