* New commands and options.
+ - "git log --date=<format>" can use more formats: iso8601, rfc2822.
+
- The hunk header output from "git diff" family can be customized
with the attributes mechanism. See gitattributes(5) for details.
- "git gc --aggressive" tells the command to spend more cycles
to optimize the repository harder.
+ - "git repack" learned a "window-memory" limit which
+ dynamically reduces the window size to stay within the
+ specified memory usage.
+
- "git repack" can be told to split resulting packs to avoid
exceeding limit specified with "--max-pack-size".
* Updated behavior of existing commands.
+ - "git rm --cached" does not complain when removing a newly
+ added file from the index anymore.
+
- "git svn dcommit" retains local merge information.
- "git config" to set values also honors type flags like --bool
concatenate them into a single line and treat the result as
"oneline".
+ - "git p4import" has been demoted to contrib status. For
+ a superior option, checkout the git-p4 front end to
+ git-fast-import (also in contrib). The man page and p4
+ rpm have been removed as well.
+
* Builds
- old-style function definitions (most notably, a function
--
exec >/var/tmp/1
-O=v1.5.3-rc1
+O=v1.5.3-rc2
echo O=`git describe refs/heads/master`
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
[listingblock]
<example><title>{title}</title>
<literallayout>
+ifdef::doctype-manpage[]
+ .ft C
+endif::doctype-manpage[]
|
+ifdef::doctype-manpage[]
+ .ft
+endif::doctype-manpage[]
</literallayout>
{title#}</example>
endif::backend-docbook[]
------------
$ git clone git://git.kernel.org/.../git.git my.git
$ cd my.git
-$ git branch -d -r todo html man <1>
-$ git branch -D test <2>
+$ git branch -d -r origin/todo origin/html origin/man <1>
+$ git branch -D test <2>
------------
+
<1> delete remote-tracking branches "todo", "html", "man"
+++ /dev/null
-git-p4import(1)
-===============
-
-NAME
-----
-git-p4import - Import a Perforce repository into git
-
-
-SYNOPSIS
---------
-[verse]
-`git-p4import` [-q|-v] [--notags] [--authors <file>] [-t <timezone>]
- <//p4repo/path> <branch>
-`git-p4import` --stitch <//p4repo/path>
-`git-p4import`
-
-
-DESCRIPTION
------------
-Import a Perforce repository into an existing git repository. When
-a <//p4repo/path> and <branch> are specified a new branch with the
-given name will be created and the initial import will begin.
-
-Once the initial import is complete you can do an incremental import
-of new commits from the Perforce repository. You do this by checking
-out the appropriate git branch and then running `git-p4import` without
-any options.
-
-The standard p4 client is used to communicate with the Perforce
-repository; it must be configured correctly in order for `git-p4import`
-to operate (see below).
-
-
-OPTIONS
--------
--q::
- Do not display any progress information.
-
--v::
- Give extra progress information.
-
-\--authors::
- Specify an authors file containing a mapping of Perforce user
- ids to full names and email addresses (see Notes below).
-
-\--notags::
- Do not create a tag for each imported commit.
-
-\--stitch::
- Import the contents of the given perforce branch into the
- currently checked out git branch.
-
-\--log::
- Store debugging information in the specified file.
-
--t::
- Specify that the remote repository is in the specified timezone.
- Timezone must be in the format "US/Pacific" or "Europe/London"
- etc. You only need to specify this once, it will be saved in
- the git config file for the repository.
-
-<//p4repo/path>::
- The Perforce path that will be imported into the specified branch.
-
-<branch>::
- The new branch that will be created to hold the Perforce imports.
-
-
-P4 Client
----------
-You must make the `p4` client command available in your $PATH and
-configure it to communicate with the target Perforce repository.
-Typically this means you must set the "$P4PORT" and "$P4CLIENT"
-environment variables.
-
-You must also configure a `p4` client "view" which maps the Perforce
-branch into the top level of your git repository, for example:
-
-------------
-Client: myhost
-
-Root: /home/sean/import
-
-Options: noallwrite clobber nocompress unlocked modtime rmdir
-
-View:
- //public/jam/... //myhost/jam/...
-------------
-
-With the above `p4` client setup, you could import the "jam"
-perforce branch into a branch named "jammy", like so:
-
-------------
-$ mkdir -p /home/sean/import/jam
-$ cd /home/sean/import/jam
-$ git init
-$ git p4import //public/jam jammy
-------------
-
-
-Multiple Branches
------------------
-Note that by creating multiple "views" you can use `git-p4import`
-to import additional branches into the same git repository.
-However, the `p4` client has a limitation in that it silently
-ignores all but the last "view" that maps into the same local
-directory. So the following will *not* work:
-
-------------
-View:
- //public/jam/... //myhost/jam/...
- //public/other/... //myhost/jam/...
- //public/guest/... //myhost/jam/...
-------------
-
-If you want more than one Perforce branch to be imported into the
-same directory you must employ a workaround. A simple option is
-to adjust your `p4` client before each import to only include a
-single view.
-
-Another option is to create multiple symlinks locally which all
-point to the same directory in your git repository and then use
-one per "view" instead of listing the actual directory.
-
-
-Tags
-----
-A git tag of the form p4/xx is created for every change imported from
-the Perforce repository where xx is the Perforce changeset number.
-Therefore after the import you can use git to access any commit by its
-Perforce number, e.g. git show p4/327.
-
-The tag associated with the HEAD commit is also how `git-p4import`
-determines if there are new changes to incrementally import from the
-Perforce repository.
-
-If you import from a repository with many thousands of changes
-you will have an equal number of p4/xxxx git tags. Git tags can
-be expensive in terms of disk space and repository operations.
-If you don't need to perform further incremental imports, you
-may delete the tags.
-
-
-Notes
------
-You can interrupt the import (e.g. ctrl-c) at any time and restart it
-without worry.
-
-Author information is automatically determined by querying the
-Perforce "users" table using the id associated with each change.
-However, if you want to manually supply these mappings you can do
-so with the "--authors" option. It accepts a file containing a list
-of mappings with each line containing one mapping in the format:
-
-------------
- perforce_id = Full Name <email@address.com>
-------------
-
-
-Author
-------
-Written by Sean Estabrooks <seanlkml@sympatico.ca>
-
-
-GIT
----
-Part of the gitlink:git[7] suite
SYNOPSIS
--------
[verse]
-'git-stash' (save | list | show [<stash>] | apply [<stash>] | clear)
+'git-stash' (list | show [<stash>] | apply [<stash>] | clear)
+'git-stash' [save] [message...]
DESCRIPTION
-----------
`git-stash list`, inspected with `git-stash show`, and restored
(potentially on top of a different commit) with `git-stash apply`.
Calling git-stash without any arguments is equivalent to `git-stash
-save`.
+save`. A stash is by default listed as "WIP on 'branchname' ...", but
+you can give a more descriptive message on the command line when
+you create one.
The latest stash you created is stored in `$GIT_DIR/refs/stash`; older
stashes are found in the reflog of this reference and can be named using
based on.
+
----------------------------------------------------------------
-stash@{0}: submit: 6ebd0e2... Add git-stash
-stash@{1}: master: 9cc0589... Merge branch 'master' of gfi
+stash@{0}: WIP on submit: 6ebd0e2... Update git-stash documentation
+stash@{1}: On master: 9cc0589... Add git-stash
----------------------------------------------------------------
show [<stash>]::
- "zlib", the compression library. Git won't build without it.
- - "openssl". The git-rev-list program uses bignum support from
- openssl, and unless you specify otherwise, you'll also get the
- SHA1 library from here.
+ - "openssl". Unless you specify otherwise, you'll get the SHA1
+ library from here.
If you don't have openssl, you can use one of the SHA1 libraries
that come with git (git includes the one from Mozilla, and has
management over DAV. Similar to "curl" above, this is optional.
- "wish", the Tcl/Tk windowing shell is used in gitk to show the
- history graphically
+ history graphically, and in git-gui.
- "ssh" is used to push and pull over the net
# Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
# MakeMaker (e.g. using ActiveState under Cygwin).
#
-# Define WITH_P4IMPORT to build and install Python git-p4import script.
-#
# Define NO_TCLTK if you do not want Tcl/Tk GUI.
#
# The TCL_PATH variable governs the location of the Tcl interpreter
git-svnimport.perl git-cvsexportcommit.perl \
git-send-email.perl git-svn.perl
-SCRIPT_PYTHON = \
- git-p4import.py
-
-ifdef WITH_P4IMPORT
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
- $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
git-status git-instaweb
-else
-SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
- $(patsubst %.perl,%,$(SCRIPT_PERL)) \
- git-status git-instaweb
-endif
-
# ... and all the rest that could be moved out of bindir to gitexecdir
PROGRAMS = \
ifndef PERL_PATH
PERL_PATH = /usr/bin/perl
endif
-ifndef PYTHON_PATH
- PYTHON_PATH = /usr/local/bin/python
-endif
export PERL_PATH
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
-PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
LIBS = $(GITLIBS) $(EXTLIBS)
$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
-$(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
- $(RM) $@ $@+
- sed -e '1s|#!.*/python|#!$(PYTHON_PATH_SQ)|' \
- -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
- -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
- $@.py >$@+
- chmod +x $@+
- mv $@+ $@
-
perl/perl.mak: GIT-CFLAGS
$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
return 0;
}
+/*
+ * When there is no known charset, guess.
+ *
+ * Right now we assume that if the target is UTF-8 (the default),
+ * and it already looks like UTF-8 (which includes US-ASCII as its
+ * subset, of course) then that is what it is and there is nothing
+ * to do.
+ *
+ * Otherwise, we default to assuming it is Latin1 for historical
+ * reasons.
+ */
+static const char *guess_charset(const char *line, const char *target_charset)
+{
+ if (is_encoding_utf8(target_charset)) {
+ if (is_utf8(line))
+ return NULL;
+ }
+ return "latin1";
+}
+
static void convert_to_utf8(char *line, const char *charset)
{
- static const char latin_one[] = "latin1";
- const char *input_charset = *charset ? charset : latin_one;
- char *out = reencode_string(line, metainfo_charset, input_charset);
+ char *out;
+
+ if (!charset || !*charset) {
+ charset = guess_charset(line, metainfo_charset);
+ if (!charset)
+ return;
+ }
+ out = reencode_string(line, metainfo_charset, charset);
if (!out)
die("cannot convert from %s to %s\n",
- input_charset, metainfo_charset);
+ charset, metainfo_charset);
strcpy(line, out);
free(out);
}
install: all
$(INSTALL) -d $(DESTDIR)$(emacsdir)
- $(INSTALL_ELC) $(ELC) $(DESTDIR)$(emacsdir)
+ $(INSTALL_ELC) $(ELC:.elc=.el) $(ELC) $(DESTDIR)$(emacsdir)
%.elc: %.el
$(EMACS) -batch -f batch-byte-compile $<
--- /dev/null
+Please see contrib/fast-import/git-p4 for a better Perforce importer.
--- /dev/null
+#!/usr/bin/python
+#
+# This tool is copyright (c) 2006, Sean Estabrooks.
+# It is released under the Gnu Public License, version 2.
+#
+# Import Perforce branches into Git repositories.
+# Checking out the files is done by calling the standard p4
+# client which you must have properly configured yourself
+#
+
+import marshal
+import os
+import sys
+import time
+import getopt
+
+from signal import signal, \
+ SIGPIPE, SIGINT, SIG_DFL, \
+ default_int_handler
+
+signal(SIGPIPE, SIG_DFL)
+s = signal(SIGINT, SIG_DFL)
+if s != default_int_handler:
+ signal(SIGINT, s)
+
+def die(msg, *args):
+ for a in args:
+ msg = "%s %s" % (msg, a)
+ print "git-p4import fatal error:", msg
+ sys.exit(1)
+
+def usage():
+ print "USAGE: git-p4import [-q|-v] [--authors=<file>] [-t <timezone>] [//p4repo/path <branch>]"
+ sys.exit(1)
+
+verbosity = 1
+logfile = "/dev/null"
+ignore_warnings = False
+stitch = 0
+tagall = True
+
+def report(level, msg, *args):
+ global verbosity
+ global logfile
+ for a in args:
+ msg = "%s %s" % (msg, a)
+ fd = open(logfile, "a")
+ fd.writelines(msg)
+ fd.close()
+ if level <= verbosity:
+ print msg
+
+class p4_command:
+ def __init__(self, _repopath):
+ try:
+ global logfile
+ self.userlist = {}
+ if _repopath[-1] == '/':
+ self.repopath = _repopath[:-1]
+ else:
+ self.repopath = _repopath
+ if self.repopath[-4:] != "/...":
+ self.repopath= "%s/..." % self.repopath
+ f=os.popen('p4 -V 2>>%s'%logfile, 'rb')
+ a = f.readlines()
+ if f.close():
+ raise
+ except:
+ die("Could not find the \"p4\" command")
+
+ def p4(self, cmd, *args):
+ global logfile
+ cmd = "%s %s" % (cmd, ' '.join(args))
+ report(2, "P4:", cmd)
+ f=os.popen('p4 -G %s 2>>%s' % (cmd,logfile), 'rb')
+ list = []
+ while 1:
+ try:
+ list.append(marshal.load(f))
+ except EOFError:
+ break
+ self.ret = f.close()
+ return list
+
+ def sync(self, id, force=False, trick=False, test=False):
+ if force:
+ ret = self.p4("sync -f %s@%s"%(self.repopath, id))[0]
+ elif trick:
+ ret = self.p4("sync -k %s@%s"%(self.repopath, id))[0]
+ elif test:
+ ret = self.p4("sync -n %s@%s"%(self.repopath, id))[0]
+ else:
+ ret = self.p4("sync %s@%s"%(self.repopath, id))[0]
+ if ret['code'] == "error":
+ data = ret['data'].upper()
+ if data.find('VIEW') > 0:
+ die("Perforce reports %s is not in client view"% self.repopath)
+ elif data.find('UP-TO-DATE') < 0:
+ die("Could not sync files from perforce", self.repopath)
+
+ def changes(self, since=0):
+ try:
+ list = []
+ for rec in self.p4("changes %s@%s,#head" % (self.repopath, since+1)):
+ list.append(rec['change'])
+ list.reverse()
+ return list
+ except:
+ return []
+
+ def authors(self, filename):
+ f=open(filename)
+ for l in f.readlines():
+ self.userlist[l[:l.find('=')].rstrip()] = \
+ (l[l.find('=')+1:l.find('<')].rstrip(),l[l.find('<')+1:l.find('>')])
+ f.close()
+ for f,e in self.userlist.items():
+ report(2, f, ":", e[0], " <", e[1], ">")
+
+ def _get_user(self, id):
+ if not self.userlist.has_key(id):
+ try:
+ user = self.p4("users", id)[0]
+ self.userlist[id] = (user['FullName'], user['Email'])
+ except:
+ self.userlist[id] = (id, "")
+ return self.userlist[id]
+
+ def _format_date(self, ticks):
+ symbol='+'
+ name = time.tzname[0]
+ offset = time.timezone
+ if ticks[8]:
+ name = time.tzname[1]
+ offset = time.altzone
+ if offset < 0:
+ offset *= -1
+ symbol = '-'
+ localo = "%s%02d%02d %s" % (symbol, offset / 3600, offset % 3600, name)
+ tickso = time.strftime("%a %b %d %H:%M:%S %Y", ticks)
+ return "%s %s" % (tickso, localo)
+
+ def where(self):
+ try:
+ return self.p4("where %s" % self.repopath)[-1]['path']
+ except:
+ return ""
+
+ def describe(self, num):
+ desc = self.p4("describe -s", num)[0]
+ self.msg = desc['desc']
+ self.author, self.email = self._get_user(desc['user'])
+ self.date = self._format_date(time.localtime(long(desc['time'])))
+ return self
+
+class git_command:
+ def __init__(self):
+ try:
+ self.version = self.git("--version")[0][12:].rstrip()
+ except:
+ die("Could not find the \"git\" command")
+ try:
+ self.gitdir = self.get_single("rev-parse --git-dir")
+ report(2, "gdir:", self.gitdir)
+ except:
+ die("Not a git repository... did you forget to \"git init\" ?")
+ try:
+ self.cdup = self.get_single("rev-parse --show-cdup")
+ if self.cdup != "":
+ os.chdir(self.cdup)
+ self.topdir = os.getcwd()
+ report(2, "topdir:", self.topdir)
+ except:
+ die("Could not find top git directory")
+
+ def git(self, cmd):
+ global logfile
+ report(2, "GIT:", cmd)
+ f=os.popen('git %s 2>>%s' % (cmd,logfile), 'rb')
+ r=f.readlines()
+ self.ret = f.close()
+ return r
+
+ def get_single(self, cmd):
+ return self.git(cmd)[0].rstrip()
+
+ def current_branch(self):
+ try:
+ testit = self.git("rev-parse --verify HEAD")[0]
+ return self.git("symbolic-ref HEAD")[0][11:].rstrip()
+ except:
+ return None
+
+ def get_config(self, variable):
+ try:
+ return self.git("config --get %s" % variable)[0].rstrip()
+ except:
+ return None
+
+ def set_config(self, variable, value):
+ try:
+ self.git("config %s %s"%(variable, value) )
+ except:
+ die("Could not set %s to " % variable, value)
+
+ def make_tag(self, name, head):
+ self.git("tag -f %s %s"%(name,head))
+
+ def top_change(self, branch):
+ try:
+ a=self.get_single("name-rev --tags refs/heads/%s" % branch)
+ loc = a.find(' tags/') + 6
+ if a[loc:loc+3] != "p4/":
+ raise
+ return int(a[loc+3:][:-2])
+ except:
+ return 0
+
+ def update_index(self):
+ self.git("ls-files -m -d -o -z | git update-index --add --remove -z --stdin")
+
+ def checkout(self, branch):
+ self.git("checkout %s" % branch)
+
+ def repoint_head(self, branch):
+ self.git("symbolic-ref HEAD refs/heads/%s" % branch)
+
+ def remove_files(self):
+ self.git("ls-files | xargs rm")
+
+ def clean_directories(self):
+ self.git("clean -d")
+
+ def fresh_branch(self, branch):
+ report(1, "Creating new branch", branch)
+ self.git("ls-files | xargs rm")
+ os.remove(".git/index")
+ self.repoint_head(branch)
+ self.git("clean -d")
+
+ def basedir(self):
+ return self.topdir
+
+ def commit(self, author, email, date, msg, id):
+ self.update_index()
+ fd=open(".msg", "w")
+ fd.writelines(msg)
+ fd.close()
+ try:
+ current = self.get_single("rev-parse --verify HEAD")
+ head = "-p HEAD"
+ except:
+ current = ""
+ head = ""
+ tree = self.get_single("write-tree")
+ for r,l in [('DATE',date),('NAME',author),('EMAIL',email)]:
+ os.environ['GIT_AUTHOR_%s'%r] = l
+ os.environ['GIT_COMMITTER_%s'%r] = l
+ commit = self.get_single("commit-tree %s %s < .msg" % (tree,head))
+ os.remove(".msg")
+ self.make_tag("p4/%s"%id, commit)
+ self.git("update-ref HEAD %s %s" % (commit, current) )
+
+try:
+ opts, args = getopt.getopt(sys.argv[1:], "qhvt:",
+ ["authors=","help","stitch=","timezone=","log=","ignore","notags"])
+except getopt.GetoptError:
+ usage()
+
+for o, a in opts:
+ if o == "-q":
+ verbosity = 0
+ if o == "-v":
+ verbosity += 1
+ if o in ("--log"):
+ logfile = a
+ if o in ("--notags"):
+ tagall = False
+ if o in ("-h", "--help"):
+ usage()
+ if o in ("--ignore"):
+ ignore_warnings = True
+
+git = git_command()
+branch=git.current_branch()
+
+for o, a in opts:
+ if o in ("-t", "--timezone"):
+ git.set_config("perforce.timezone", a)
+ if o in ("--stitch"):
+ git.set_config("perforce.%s.path" % branch, a)
+ stitch = 1
+
+if len(args) == 2:
+ branch = args[1]
+ git.checkout(branch)
+ if branch == git.current_branch():
+ die("Branch %s already exists!" % branch)
+ report(1, "Setting perforce to ", args[0])
+ git.set_config("perforce.%s.path" % branch, args[0])
+elif len(args) != 0:
+ die("You must specify the perforce //depot/path and git branch")
+
+p4path = git.get_config("perforce.%s.path" % branch)
+if p4path == None:
+ die("Do not know Perforce //depot/path for git branch", branch)
+
+p4 = p4_command(p4path)
+
+for o, a in opts:
+ if o in ("-a", "--authors"):
+ p4.authors(a)
+
+localdir = git.basedir()
+if p4.where()[:len(localdir)] != localdir:
+ report(1, "**WARNING** Appears p4 client is misconfigured")
+ report(1, " for sync from %s to %s" % (p4.repopath, localdir))
+ if ignore_warnings != True:
+ die("Reconfigure or use \"--ignore\" on command line")
+
+if stitch == 0:
+ top = git.top_change(branch)
+else:
+ top = 0
+changes = p4.changes(top)
+count = len(changes)
+if count == 0:
+ report(1, "Already up to date...")
+ sys.exit(0)
+
+ptz = git.get_config("perforce.timezone")
+if ptz:
+ report(1, "Setting timezone to", ptz)
+ os.environ['TZ'] = ptz
+ time.tzset()
+
+if stitch == 1:
+ git.remove_files()
+ git.clean_directories()
+ p4.sync(changes[0], force=True)
+elif top == 0 and branch != git.current_branch():
+ p4.sync(changes[0], test=True)
+ report(1, "Creating new initial commit");
+ git.fresh_branch(branch)
+ p4.sync(changes[0], force=True)
+else:
+ p4.sync(changes[0], trick=True)
+
+report(1, "processing %s changes from p4 (%s) to git (%s)" % (count, p4.repopath, branch))
+for id in changes:
+ report(1, "Importing changeset", id)
+ change = p4.describe(id)
+ p4.sync(id)
+ if tagall :
+ git.commit(change.author, change.email, change.date, change.msg, id)
+ else:
+ git.commit(change.author, change.email, change.date, change.msg, "import")
+ if stitch == 1:
+ git.clean_directories()
+ stitch = 0
--- /dev/null
+git-p4import(1)
+===============
+
+NAME
+----
+git-p4import - Import a Perforce repository into git
+
+
+SYNOPSIS
+--------
+[verse]
+`git-p4import` [-q|-v] [--notags] [--authors <file>] [-t <timezone>]
+ <//p4repo/path> <branch>
+`git-p4import` --stitch <//p4repo/path>
+`git-p4import`
+
+
+DESCRIPTION
+-----------
+Import a Perforce repository into an existing git repository. When
+a <//p4repo/path> and <branch> are specified a new branch with the
+given name will be created and the initial import will begin.
+
+Once the initial import is complete you can do an incremental import
+of new commits from the Perforce repository. You do this by checking
+out the appropriate git branch and then running `git-p4import` without
+any options.
+
+The standard p4 client is used to communicate with the Perforce
+repository; it must be configured correctly in order for `git-p4import`
+to operate (see below).
+
+
+OPTIONS
+-------
+-q::
+ Do not display any progress information.
+
+-v::
+ Give extra progress information.
+
+\--authors::
+ Specify an authors file containing a mapping of Perforce user
+ ids to full names and email addresses (see Notes below).
+
+\--notags::
+ Do not create a tag for each imported commit.
+
+\--stitch::
+ Import the contents of the given perforce branch into the
+ currently checked out git branch.
+
+\--log::
+ Store debugging information in the specified file.
+
+-t::
+ Specify that the remote repository is in the specified timezone.
+ Timezone must be in the format "US/Pacific" or "Europe/London"
+ etc. You only need to specify this once, it will be saved in
+ the git config file for the repository.
+
+<//p4repo/path>::
+ The Perforce path that will be imported into the specified branch.
+
+<branch>::
+ The new branch that will be created to hold the Perforce imports.
+
+
+P4 Client
+---------
+You must make the `p4` client command available in your $PATH and
+configure it to communicate with the target Perforce repository.
+Typically this means you must set the "$P4PORT" and "$P4CLIENT"
+environment variables.
+
+You must also configure a `p4` client "view" which maps the Perforce
+branch into the top level of your git repository, for example:
+
+------------
+Client: myhost
+
+Root: /home/sean/import
+
+Options: noallwrite clobber nocompress unlocked modtime rmdir
+
+View:
+ //public/jam/... //myhost/jam/...
+------------
+
+With the above `p4` client setup, you could import the "jam"
+perforce branch into a branch named "jammy", like so:
+
+------------
+$ mkdir -p /home/sean/import/jam
+$ cd /home/sean/import/jam
+$ git init
+$ git p4import //public/jam jammy
+------------
+
+
+Multiple Branches
+-----------------
+Note that by creating multiple "views" you can use `git-p4import`
+to import additional branches into the same git repository.
+However, the `p4` client has a limitation in that it silently
+ignores all but the last "view" that maps into the same local
+directory. So the following will *not* work:
+
+------------
+View:
+ //public/jam/... //myhost/jam/...
+ //public/other/... //myhost/jam/...
+ //public/guest/... //myhost/jam/...
+------------
+
+If you want more than one Perforce branch to be imported into the
+same directory you must employ a workaround. A simple option is
+to adjust your `p4` client before each import to only include a
+single view.
+
+Another option is to create multiple symlinks locally which all
+point to the same directory in your git repository and then use
+one per "view" instead of listing the actual directory.
+
+
+Tags
+----
+A git tag of the form p4/xx is created for every change imported from
+the Perforce repository where xx is the Perforce changeset number.
+Therefore after the import you can use git to access any commit by its
+Perforce number, e.g. git show p4/327.
+
+The tag associated with the HEAD commit is also how `git-p4import`
+determines if there are new changes to incrementally import from the
+Perforce repository.
+
+If you import from a repository with many thousands of changes
+you will have an equal number of p4/xxxx git tags. Git tags can
+be expensive in terms of disk space and repository operations.
+If you don't need to perform further incremental imports, you
+may delete the tags.
+
+
+Notes
+-----
+You can interrupt the import (e.g. ctrl-c) at any time and restart it
+without worry.
+
+Author information is automatically determined by querying the
+Perforce "users" table using the id associated with each change.
+However, if you want to manually supply these mappings you can do
+so with the "--authors" option. It accepts a file containing a list
+of mappings with each line containing one mapping in the format:
+
+------------
+ perforce_id = Full Name <email@address.com>
+------------
+
+
+Author
+------
+Written by Sean Estabrooks <seanlkml@sympatico.ca>
+
+
+GIT
+---
+Part of the gitlink:git[7] suite
const char *slash = path;
while ((slash = strchr(slash+1, '/')) != NULL) {
+ struct stat st;
+ int stat_status;
+
len = slash - path;
memcpy(buf, path, len);
buf[len] = 0;
+
+ if (len <= state->base_dir_len)
+ /*
+ * checkout-index --prefix=<dir>; <dir> is
+ * allowed to be a symlink to an existing
+ * directory.
+ */
+ stat_status = stat(buf, &st);
+ else
+ /*
+ * if there currently is a symlink, we would
+ * want to replace it with a real directory.
+ */
+ stat_status = lstat(buf, &st);
+
+ if (!stat_status && S_ISDIR(st.st_mode))
+ continue; /* ok, it is already a directory. */
+
+ /*
+ * We know stat_status == 0 means something exists
+ * there and this mkdir would fail, but that is an
+ * error codepath; we do not care, as we unlink and
+ * mkdir again in such a case.
+ */
if (mkdir(buf, 0777)) {
- if (errno == EEXIST) {
- struct stat st;
- if (len > state->base_dir_len && state->force && !unlink(buf) && !mkdir(buf, 0777))
- continue;
- if (!stat(buf, &st) && S_ISDIR(st.st_mode))
- continue; /* ok */
- }
+ if (errno == EEXIST && state->force &&
+ !unlink(buf) && !mkdir(buf, 0777))
+ continue;
die("cannot create directory at %s", buf);
}
}
# a new branch. You can specify a number of filters to modify the commits,
# files and trees.
-set -e
-
USAGE="git-filter-branch [-d TEMPDIR] [FILTERS] DESTBRANCH [REV-RANGE]"
. git-sh-setup
die "branch $dstbranch already exists"
test ! -e "$tempdir" || die "$tempdir already exists, please remove it"
-mkdir -p "$tempdir/t"
-cd "$tempdir/t"
-workdir="$(pwd)"
+mkdir -p "$tempdir/t" &&
+cd "$tempdir/t" &&
+workdir="$(pwd)" ||
+die ""
case "$GIT_DIR" in
/*)
export GIT_DIR GIT_WORK_TREE=.
export GIT_INDEX_FILE="$(pwd)/../index"
-git read-tree # seed the index file
+git read-tree || die "Could not seed the index"
ret=0
-
-mkdir ../map # map old->new commit ids for rewriting parents
+# map old->new commit ids for rewriting parents
+mkdir ../map || die "Could not create map/ directory"
case "$filter_subdir" in
"")
*)
git rev-list --reverse --topo-order --default HEAD \
--parents --full-history "$@" -- "$filter_subdir"
-esac > ../revs
+esac > ../revs || die "Could not get the commits"
commits=$(wc -l <../revs | tr -d " ")
test $commits -eq 0 && die "Found nothing to rewrite"
;;
*)
git read-tree -i -m $commit:"$filter_subdir"
- esac
+ esac || die "Could not initialize the index"
export GIT_COMMIT=$commit
- git cat-file commit "$commit" >../commit
+ git cat-file commit "$commit" >../commit ||
+ die "Cannot read commit $commit"
eval "$(set_ident AUTHOR <../commit)" ||
die "setting author failed for commit $commit"
die "env filter failed: $filter_env"
if [ "$filter_tree" ]; then
- git checkout-index -f -u -a
+ git checkout-index -f -u -a ||
+ die "Could not checkout the index"
# files that $commit removed are now still in the working tree;
# remove them, else they would be added again
git ls-files -z --others | xargs -0 rm -f
echo Nothing rewritten
;;
*)
- git update-ref refs/heads/"$dstbranch" $target_head
+ git update-ref refs/heads/"$dstbranch" $target_head ||
+ die "Could not update $dstbranch with $target_head"
if [ $(wc -l <../map/$src_head) -gt 1 ]; then
echo "WARNING: Your commit filter caused the head commit to expand to several rewritten commits. Only the first such commit was recorded as the current $dstbranch head but you will need to resolve the situation now (probably by manually merging the other commits). These are all the commits:" >&2
sed 's/^/ /' ../map/$src_head >&2
warn "unreferencing tag object $sha1t"
fi
- git update-ref "refs/tags/$new_ref" "$new_sha1"
+ git update-ref "refs/tags/$new_ref" "$new_sha1" ||
+ die "Could not write tag $new_ref"
done
fi
+++ /dev/null
-#!/usr/bin/python
-#
-# This tool is copyright (c) 2006, Sean Estabrooks.
-# It is released under the Gnu Public License, version 2.
-#
-# Import Perforce branches into Git repositories.
-# Checking out the files is done by calling the standard p4
-# client which you must have properly configured yourself
-#
-
-import marshal
-import os
-import sys
-import time
-import getopt
-
-from signal import signal, \
- SIGPIPE, SIGINT, SIG_DFL, \
- default_int_handler
-
-signal(SIGPIPE, SIG_DFL)
-s = signal(SIGINT, SIG_DFL)
-if s != default_int_handler:
- signal(SIGINT, s)
-
-def die(msg, *args):
- for a in args:
- msg = "%s %s" % (msg, a)
- print "git-p4import fatal error:", msg
- sys.exit(1)
-
-def usage():
- print "USAGE: git-p4import [-q|-v] [--authors=<file>] [-t <timezone>] [//p4repo/path <branch>]"
- sys.exit(1)
-
-verbosity = 1
-logfile = "/dev/null"
-ignore_warnings = False
-stitch = 0
-tagall = True
-
-def report(level, msg, *args):
- global verbosity
- global logfile
- for a in args:
- msg = "%s %s" % (msg, a)
- fd = open(logfile, "a")
- fd.writelines(msg)
- fd.close()
- if level <= verbosity:
- print msg
-
-class p4_command:
- def __init__(self, _repopath):
- try:
- global logfile
- self.userlist = {}
- if _repopath[-1] == '/':
- self.repopath = _repopath[:-1]
- else:
- self.repopath = _repopath
- if self.repopath[-4:] != "/...":
- self.repopath= "%s/..." % self.repopath
- f=os.popen('p4 -V 2>>%s'%logfile, 'rb')
- a = f.readlines()
- if f.close():
- raise
- except:
- die("Could not find the \"p4\" command")
-
- def p4(self, cmd, *args):
- global logfile
- cmd = "%s %s" % (cmd, ' '.join(args))
- report(2, "P4:", cmd)
- f=os.popen('p4 -G %s 2>>%s' % (cmd,logfile), 'rb')
- list = []
- while 1:
- try:
- list.append(marshal.load(f))
- except EOFError:
- break
- self.ret = f.close()
- return list
-
- def sync(self, id, force=False, trick=False, test=False):
- if force:
- ret = self.p4("sync -f %s@%s"%(self.repopath, id))[0]
- elif trick:
- ret = self.p4("sync -k %s@%s"%(self.repopath, id))[0]
- elif test:
- ret = self.p4("sync -n %s@%s"%(self.repopath, id))[0]
- else:
- ret = self.p4("sync %s@%s"%(self.repopath, id))[0]
- if ret['code'] == "error":
- data = ret['data'].upper()
- if data.find('VIEW') > 0:
- die("Perforce reports %s is not in client view"% self.repopath)
- elif data.find('UP-TO-DATE') < 0:
- die("Could not sync files from perforce", self.repopath)
-
- def changes(self, since=0):
- try:
- list = []
- for rec in self.p4("changes %s@%s,#head" % (self.repopath, since+1)):
- list.append(rec['change'])
- list.reverse()
- return list
- except:
- return []
-
- def authors(self, filename):
- f=open(filename)
- for l in f.readlines():
- self.userlist[l[:l.find('=')].rstrip()] = \
- (l[l.find('=')+1:l.find('<')].rstrip(),l[l.find('<')+1:l.find('>')])
- f.close()
- for f,e in self.userlist.items():
- report(2, f, ":", e[0], " <", e[1], ">")
-
- def _get_user(self, id):
- if not self.userlist.has_key(id):
- try:
- user = self.p4("users", id)[0]
- self.userlist[id] = (user['FullName'], user['Email'])
- except:
- self.userlist[id] = (id, "")
- return self.userlist[id]
-
- def _format_date(self, ticks):
- symbol='+'
- name = time.tzname[0]
- offset = time.timezone
- if ticks[8]:
- name = time.tzname[1]
- offset = time.altzone
- if offset < 0:
- offset *= -1
- symbol = '-'
- localo = "%s%02d%02d %s" % (symbol, offset / 3600, offset % 3600, name)
- tickso = time.strftime("%a %b %d %H:%M:%S %Y", ticks)
- return "%s %s" % (tickso, localo)
-
- def where(self):
- try:
- return self.p4("where %s" % self.repopath)[-1]['path']
- except:
- return ""
-
- def describe(self, num):
- desc = self.p4("describe -s", num)[0]
- self.msg = desc['desc']
- self.author, self.email = self._get_user(desc['user'])
- self.date = self._format_date(time.localtime(long(desc['time'])))
- return self
-
-class git_command:
- def __init__(self):
- try:
- self.version = self.git("--version")[0][12:].rstrip()
- except:
- die("Could not find the \"git\" command")
- try:
- self.gitdir = self.get_single("rev-parse --git-dir")
- report(2, "gdir:", self.gitdir)
- except:
- die("Not a git repository... did you forget to \"git init\" ?")
- try:
- self.cdup = self.get_single("rev-parse --show-cdup")
- if self.cdup != "":
- os.chdir(self.cdup)
- self.topdir = os.getcwd()
- report(2, "topdir:", self.topdir)
- except:
- die("Could not find top git directory")
-
- def git(self, cmd):
- global logfile
- report(2, "GIT:", cmd)
- f=os.popen('git %s 2>>%s' % (cmd,logfile), 'rb')
- r=f.readlines()
- self.ret = f.close()
- return r
-
- def get_single(self, cmd):
- return self.git(cmd)[0].rstrip()
-
- def current_branch(self):
- try:
- testit = self.git("rev-parse --verify HEAD")[0]
- return self.git("symbolic-ref HEAD")[0][11:].rstrip()
- except:
- return None
-
- def get_config(self, variable):
- try:
- return self.git("config --get %s" % variable)[0].rstrip()
- except:
- return None
-
- def set_config(self, variable, value):
- try:
- self.git("config %s %s"%(variable, value) )
- except:
- die("Could not set %s to " % variable, value)
-
- def make_tag(self, name, head):
- self.git("tag -f %s %s"%(name,head))
-
- def top_change(self, branch):
- try:
- a=self.get_single("name-rev --tags refs/heads/%s" % branch)
- loc = a.find(' tags/') + 6
- if a[loc:loc+3] != "p4/":
- raise
- return int(a[loc+3:][:-2])
- except:
- return 0
-
- def update_index(self):
- self.git("ls-files -m -d -o -z | git update-index --add --remove -z --stdin")
-
- def checkout(self, branch):
- self.git("checkout %s" % branch)
-
- def repoint_head(self, branch):
- self.git("symbolic-ref HEAD refs/heads/%s" % branch)
-
- def remove_files(self):
- self.git("ls-files | xargs rm")
-
- def clean_directories(self):
- self.git("clean -d")
-
- def fresh_branch(self, branch):
- report(1, "Creating new branch", branch)
- self.git("ls-files | xargs rm")
- os.remove(".git/index")
- self.repoint_head(branch)
- self.git("clean -d")
-
- def basedir(self):
- return self.topdir
-
- def commit(self, author, email, date, msg, id):
- self.update_index()
- fd=open(".msg", "w")
- fd.writelines(msg)
- fd.close()
- try:
- current = self.get_single("rev-parse --verify HEAD")
- head = "-p HEAD"
- except:
- current = ""
- head = ""
- tree = self.get_single("write-tree")
- for r,l in [('DATE',date),('NAME',author),('EMAIL',email)]:
- os.environ['GIT_AUTHOR_%s'%r] = l
- os.environ['GIT_COMMITTER_%s'%r] = l
- commit = self.get_single("commit-tree %s %s < .msg" % (tree,head))
- os.remove(".msg")
- self.make_tag("p4/%s"%id, commit)
- self.git("update-ref HEAD %s %s" % (commit, current) )
-
-try:
- opts, args = getopt.getopt(sys.argv[1:], "qhvt:",
- ["authors=","help","stitch=","timezone=","log=","ignore","notags"])
-except getopt.GetoptError:
- usage()
-
-for o, a in opts:
- if o == "-q":
- verbosity = 0
- if o == "-v":
- verbosity += 1
- if o in ("--log"):
- logfile = a
- if o in ("--notags"):
- tagall = False
- if o in ("-h", "--help"):
- usage()
- if o in ("--ignore"):
- ignore_warnings = True
-
-git = git_command()
-branch=git.current_branch()
-
-for o, a in opts:
- if o in ("-t", "--timezone"):
- git.set_config("perforce.timezone", a)
- if o in ("--stitch"):
- git.set_config("perforce.%s.path" % branch, a)
- stitch = 1
-
-if len(args) == 2:
- branch = args[1]
- git.checkout(branch)
- if branch == git.current_branch():
- die("Branch %s already exists!" % branch)
- report(1, "Setting perforce to ", args[0])
- git.set_config("perforce.%s.path" % branch, args[0])
-elif len(args) != 0:
- die("You must specify the perforce //depot/path and git branch")
-
-p4path = git.get_config("perforce.%s.path" % branch)
-if p4path == None:
- die("Do not know Perforce //depot/path for git branch", branch)
-
-p4 = p4_command(p4path)
-
-for o, a in opts:
- if o in ("-a", "--authors"):
- p4.authors(a)
-
-localdir = git.basedir()
-if p4.where()[:len(localdir)] != localdir:
- report(1, "**WARNING** Appears p4 client is misconfigured")
- report(1, " for sync from %s to %s" % (p4.repopath, localdir))
- if ignore_warnings != True:
- die("Reconfigure or use \"--ignore\" on command line")
-
-if stitch == 0:
- top = git.top_change(branch)
-else:
- top = 0
-changes = p4.changes(top)
-count = len(changes)
-if count == 0:
- report(1, "Already up to date...")
- sys.exit(0)
-
-ptz = git.get_config("perforce.timezone")
-if ptz:
- report(1, "Setting timezone to", ptz)
- os.environ['TZ'] = ptz
- time.tzset()
-
-if stitch == 1:
- git.remove_files()
- git.clean_directories()
- p4.sync(changes[0], force=True)
-elif top == 0 and branch != git.current_branch():
- p4.sync(changes[0], test=True)
- report(1, "Creating new initial commit");
- git.fresh_branch(branch)
- p4.sync(changes[0], force=True)
-else:
- p4.sync(changes[0], trick=True)
-
-report(1, "processing %s changes from p4 (%s) to git (%s)" % (count, p4.repopath, branch))
-for id in changes:
- report(1, "Importing changeset", id)
- change = p4.describe(id)
- p4.sync(id)
- if tagall :
- git.commit(change.author, change.email, change.date, change.msg, id)
- else:
- git.commit(change.author, change.email, change.date, change.msg, "import")
- if stitch == 1:
- git.clean_directories()
- stitch = 0
my $log = $cmd eq 'log';
while (<$authors>) {
chomp;
- next unless /^(\S+?|\(no author\))\s*=\s*(.+?)\s*<(.+)>\s*$/;
+ next unless /^(.+?|\(no author\))\s*=\s*(.+?)\s*<(.+)>\s*$/;
my ($user, $name, $email) = ($1, $2, $3);
if ($log) {
$Git::SVN::Log::rusers{"$name <$email>"} = $user;
sub url_path {
my ($self, $path) = @_;
+ if ($self->{url} =~ m#^https?://#) {
+ $path =~ s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
+ }
$self->{url} . '/' . $self->repo_path($path);
}
Source: http://kernel.org/pub/software/scm/git/%{name}-%{version}.tar.gz
BuildRequires: zlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel %{!?_without_docs:, xmlto, asciidoc > 6.0.3}
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-Requires: git-core, git-svn, git-cvs, git-arch, git-email, gitk, git-gui, git-p4, perl-Git
+Requires: git-core, git-svn, git-cvs, git-arch, git-email, gitk, git-gui, perl-Git
%description
Git is a fast, scalable, distributed revision control system with an
%description arch
Git tools for importing Arch repositories.
-%package p4
-Summary: Git tools for importing Perforce repositories
-Group: Development/Tools
-Requires: git-core = %{version}-%{release}, python
-%description p4
-Git tools for importing Perforce repositories.
-
%package email
Summary: Git tools for sending email
Group: Development/Tools
%setup -q
%build
-make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" WITH_P4IMPORT=YesPlease \
+make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" \
ETC_GITCONFIG=/etc/gitconfig \
- prefix=%{_prefix} PYTHON_PATH=%{python_path} all %{!?_without_docs: doc}
+ prefix=%{_prefix} all %{!?_without_docs: doc}
%install
rm -rf $RPM_BUILD_ROOT
make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" DESTDIR=$RPM_BUILD_ROOT \
- WITH_P4IMPORT=YesPlease prefix=%{_prefix} mandir=%{_mandir} \
+ prefix=%{_prefix} mandir=%{_mandir} \
ETC_GITCONFIG=/etc/gitconfig \
PYTHON_PATH=%{python_path} \
INSTALLDIRS=vendor install %{!?_without_docs: install-doc}
find $RPM_BUILD_ROOT -type f -name '*.bs' -empty -exec rm -f {} ';'
find $RPM_BUILD_ROOT -type f -name perllocal.pod -exec rm -f {} ';'
-(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "p4import|archimport|svn|cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@) > bin-man-doc-files
+(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "archimport|svn|cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@) > bin-man-doc-files
(find $RPM_BUILD_ROOT%{perl_vendorlib} -type f | sed -e s@^$RPM_BUILD_ROOT@@) >> perl-files
%if %{!?_without_docs:1}0
-(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "p4import|archimport|svn|git-cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
+(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "archimport|svn|git-cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
%else
rm -rf $RPM_BUILD_ROOT%{_mandir}
%endif
%{!?_without_docs: %{_mandir}/man1/git-archimport.1*}
%{!?_without_docs: %doc Documentation/git-archimport.html }
-%files p4
-%defattr(-,root,root)
-%doc Documentation/git-p4import.txt
-%{_bindir}/git-p4import
-%{!?_without_docs: %{_mandir}/man1/git-p4import.1*}
-%{!?_without_docs: %doc Documentation/git-p4import.html }
-
%files email
%defattr(-,root,root)
%doc Documentation/*email*.txt
%{!?_without_docs: %doc Documentation/technical}
%changelog
+* Sun Jul 15 2007 Sean Estabrooks <seanlkml@sympatico.ca>
+- Removed p4import.
+
* Tue Jun 26 2007 Quy Tonthat <qtonthat@gmail.com>
- Fixed problems looking for wrong manpages.
poke() {
test-chmtime +1 "$1"
}
+
+SVN_HTTPD_MODULE_PATH=${SVN_HTTPD_MODULE_PATH-'/usr/lib/apache2/modules'}
+SVN_HTTPD_PATH=${SVN_HTTPD_PATH-'/usr/sbin/apache2'}
+
+start_httpd () {
+ if test -z "$SVN_HTTPD_PORT"
+ then
+ echo >&2 'SVN_HTTPD_PORT is not defined!'
+ return
+ fi
+
+ mkdir "$GIT_DIR"/logs
+
+ cat > "$GIT_DIR/httpd.conf" <<EOF
+ServerName "git-svn test"
+ServerRoot "$GIT_DIR"
+DocumentRoot "$GIT_DIR"
+PidFile "$GIT_DIR/httpd.pid"
+Listen 127.0.0.1:$SVN_HTTPD_PORT
+LoadModule dav_module $SVN_HTTPD_MODULE_PATH/mod_dav.so
+LoadModule dav_svn_module $SVN_HTTPD_MODULE_PATH/mod_dav_svn.so
+<Location /svn>
+ DAV svn
+ SVNPath $rawsvnrepo
+</Location>
+EOF
+ "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k start
+ svnrepo=http://127.0.0.1:$SVN_HTTPD_PORT/svn
+}
+
+stop_httpd () {
+ test -z "$SVN_HTTPD_PORT" && return
+ "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k stop
+}
# -add an entry to .gitmodules for submodule 'example'
#
test_expect_success 'Prepare submodule testing' '
+ : > t &&
+ git-add t &&
+ git-commit -m "initial commit" &&
+ git branch initial HEAD &&
mkdir lib &&
cd lib &&
git init &&
git-submodule status | grep "^ $rev1"
'
+test_expect_success 'checkout superproject with subproject already present' '
+ git-checkout initial &&
+ git-checkout master
+'
+
test_done
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+
+
+test_description='git-svn dcommit can commit renames of files with ugly names'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'load repository with strange names' "
+ svnadmin load -q $rawsvnrepo < ../t9115/funky-names.dump &&
+ start_httpd
+ "
+
+test_expect_success 'init and fetch repository' "
+ git svn init $svnrepo &&
+ git svn fetch &&
+ git reset --hard git-svn
+ "
+
+test_expect_success 'create file in existing ugly and empty dir' '
+ mkdir "#{bad_directory_name}" &&
+ echo hi > "#{bad_directory_name}/ foo" &&
+ git update-index --add "#{bad_directory_name}/ foo" &&
+ git commit -m "new file in ugly parent" &&
+ git svn dcommit
+ '
+
+test_expect_success 'rename ugly file' '
+ git mv "#{bad_directory_name}/ foo" "file name with feces" &&
+ git commit -m "rename ugly file" &&
+ git svn dcommit
+ '
+
+test_expect_success 'rename pretty file' '
+ echo :x > pretty &&
+ git update-index --add pretty &&
+ git commit -m "pretty :x" &&
+ git svn dcommit &&
+ mkdir regular_dir_name &&
+ git mv pretty regular_dir_name/pretty &&
+ git commit -m "moved pretty file" &&
+ git svn dcommit
+ '
+
+test_expect_success 'rename pretty file into ugly one' '
+ git mv regular_dir_name/pretty "#{bad_directory_name}/ booboo" &&
+ git commit -m booboo &&
+ git svn dcommit
+ '
+
+stop_httpd
+
+test_done
--- /dev/null
+SVN-fs-dump-format-version: 2
+
+UUID: 819c44fe-2bcc-4066-88e4-985e2bc0b418
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2007-07-12T07:54:26.062914Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 152
+Content-length: 152
+
+K 7
+svn:log
+V 44
+what will those wacky people think of next?
+
+K 10
+svn:author
+V 12
+normalperson
+K 8
+svn:date
+V 27
+2007-07-12T08:00:05.011573Z
+PROPS-END
+
+Node-path: leading space
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: leading space file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 5
+Text-content-md5: e4fa20c67542cdc21271e08d329397ab
+Content-length: 15
+
+PROPS-END
+ugly
+
+
+Node-path: #{bad_directory_name}
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: #{cool_name}
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 18
+Text-content-md5: 87dac40ca337dfa3dcc8911388c3ddda
+Content-length: 28
+
+PROPS-END
+strange name here
+
+
+Node-path: dir name with spaces
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: file name with spaces
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 7
+Text-content-md5: c1f10cfd640618484a2a475c11410fd3
+Content-length: 17
+
+PROPS-END
+spaces
+
+
+Node-path: regular_dir_name
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
#include "cache-tree.h"
#include "unpack-trees.h"
#include "progress.h"
+#include "refs.h"
#define DBRT_DEBUG 1
cache_tree_invalidate_path(active_cache_tree, ce->name);
}
-static int verify_clean_subdirectory(const char *path, const char *action,
+/*
+ * Check that checking out ce->sha1 in subdir ce->name is not
+ * going to overwrite any working files.
+ *
+ * Currently, git does not checkout subprojects during a superproject
+ * checkout, so it is not going to overwrite anything.
+ */
+static int verify_clean_submodule(struct cache_entry *ce, const char *action,
+ struct unpack_trees_options *o)
+{
+ return 0;
+}
+
+static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
struct unpack_trees_options *o)
{
/*
- * we are about to extract "path"; we would not want to lose
+ * we are about to extract "ce->name"; we would not want to lose
* anything in the existing directory there.
*/
int namelen;
struct dir_struct d;
char *pathbuf;
int cnt = 0;
+ unsigned char sha1[20];
+
+ if (S_ISGITLINK(ntohl(ce->ce_mode)) &&
+ resolve_gitlink_ref(ce->name, "HEAD", sha1) == 0) {
+ /* If we are not going to update the submodule, then
+ * we don't care.
+ */
+ if (!hashcmp(sha1, ce->sha1))
+ return 0;
+ return verify_clean_submodule(ce, action, o);
+ }
/*
* First let's make sure we do not have a local modification
* in that directory.
*/
- namelen = strlen(path);
- pos = cache_name_pos(path, namelen);
+ namelen = strlen(ce->name);
+ pos = cache_name_pos(ce->name, namelen);
if (0 <= pos)
return cnt; /* we have it as nondirectory */
pos = -pos - 1;
struct cache_entry *ce = active_cache[i];
int len = ce_namelen(ce);
if (len < namelen ||
- strncmp(path, ce->name, namelen) ||
+ strncmp(ce->name, ce->name, namelen) ||
ce->name[namelen] != '/')
break;
/*
* present file that is not ignored.
*/
pathbuf = xmalloc(namelen + 2);
- memcpy(pathbuf, path, namelen);
+ memcpy(pathbuf, ce->name, namelen);
strcpy(pathbuf+namelen, "/");
memset(&d, 0, sizeof(d));
if (o->dir)
d.exclude_per_dir = o->dir->exclude_per_dir;
- i = read_directory(&d, path, pathbuf, namelen+1, NULL);
+ i = read_directory(&d, ce->name, pathbuf, namelen+1, NULL);
if (i)
die("Updating '%s' would lose untracked files in it",
- path);
+ ce->name);
free(pathbuf);
return cnt;
}
* We do not want to remove or overwrite a working tree file that
* is not tracked, unless it is ignored.
*/
-static void verify_absent(const char *path, const char *action,
+static void verify_absent(struct cache_entry *ce, const char *action,
struct unpack_trees_options *o)
{
struct stat st;
if (o->index_only || o->reset || !o->update)
return;
- if (has_symlink_leading_path(path, NULL))
+ if (has_symlink_leading_path(ce->name, NULL))
return;
- if (!lstat(path, &st)) {
+ if (!lstat(ce->name, &st)) {
int cnt;
- if (o->dir && excluded(o->dir, path))
+ if (o->dir && excluded(o->dir, ce->name))
/*
- * path is explicitly excluded, so it is Ok to
+ * ce->name is explicitly excluded, so it is Ok to
* overwrite it.
*/
return;
* files that are in "foo/" we would lose
* it.
*/
- cnt = verify_clean_subdirectory(path, action, o);
+ cnt = verify_clean_subdirectory(ce, action, o);
/*
* If this removed entries from the index,
* delete this path, which is in a subdirectory that
* is being replaced with a blob.
*/
- cnt = cache_name_pos(path, strlen(path));
+ cnt = cache_name_pos(ce->name, strlen(ce->name));
if (0 <= cnt) {
struct cache_entry *ce = active_cache[cnt];
if (!ce_stage(ce) && !ce->ce_mode)
}
die("Untracked working tree file '%s' "
- "would be %s by merge.", path, action);
+ "would be %s by merge.", ce->name, action);
}
}
}
}
else {
- verify_absent(merge->name, "overwritten", o);
+ verify_absent(merge, "overwritten", o);
invalidate_ce_path(merge);
}
if (old)
verify_uptodate(old, o);
else
- verify_absent(ce->name, "removed", o);
+ verify_absent(ce, "removed", o);
ce->ce_mode = 0;
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
invalidate_ce_path(ce);
if (o->aggressive) {
int head_deleted = !head && !df_conflict_head;
int remote_deleted = !remote && !df_conflict_remote;
- const char *path = NULL;
+ struct cache_entry *ce = NULL;
if (index)
- path = index->name;
+ ce = index;
else if (head)
- path = head->name;
+ ce = head;
else if (remote)
- path = remote->name;
+ ce = remote;
else {
for (i = 1; i < o->head_idx; i++) {
if (stages[i] && stages[i] != o->df_conflict_entry) {
- path = stages[i]->name;
+ ce = stages[i];
break;
}
}
(remote_deleted && head && head_match)) {
if (index)
return deleted_entry(index, index, o);
- else if (path && !head_deleted)
- verify_absent(path, "removed", o);
+ else if (ce && !head_deleted)
+ verify_absent(ce, "removed", o);
return 0;
}
/*