mirror of
https://github.com/git/git.git
synced 2024-05-21 19:46:09 +02:00
git-p4: add option to preserve user names
Patches from git passed into p4 end up with the committer being identified as the person who ran git-p4. With "submit --preserve-user", git-p4 modifies the p4 changelist (after it has been submitted), setting the p4 author field. The submitter is required to have sufficient p4 permissions or git-p4 refuses to proceed. If the git author is not known to p4, the submit will be abandoned unless git-p4.allowMissingP4Users is true. Signed-off-by: Luke Diamand <luke@diamand.org> Acked-by: Pete Wyckoff <pw@padd.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
ec014eac0e
commit
3ea2cfd488
|
@ -474,6 +474,47 @@ class Command:
|
||||||
self.usage = "usage: %prog [options]"
|
self.usage = "usage: %prog [options]"
|
||||||
self.needsGit = True
|
self.needsGit = True
|
||||||
|
|
||||||
|
class P4UserMap:
|
||||||
|
def __init__(self):
|
||||||
|
self.userMapFromPerforceServer = False
|
||||||
|
|
||||||
|
def getUserCacheFilename(self):
|
||||||
|
home = os.environ.get("HOME", os.environ.get("USERPROFILE"))
|
||||||
|
return home + "/.gitp4-usercache.txt"
|
||||||
|
|
||||||
|
def getUserMapFromPerforceServer(self):
|
||||||
|
if self.userMapFromPerforceServer:
|
||||||
|
return
|
||||||
|
self.users = {}
|
||||||
|
self.emails = {}
|
||||||
|
|
||||||
|
for output in p4CmdList("users"):
|
||||||
|
if not output.has_key("User"):
|
||||||
|
continue
|
||||||
|
self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
|
||||||
|
self.emails[output["Email"]] = output["User"]
|
||||||
|
|
||||||
|
|
||||||
|
s = ''
|
||||||
|
for (key, val) in self.users.items():
|
||||||
|
s += "%s\t%s\n" % (key.expandtabs(1), val.expandtabs(1))
|
||||||
|
|
||||||
|
open(self.getUserCacheFilename(), "wb").write(s)
|
||||||
|
self.userMapFromPerforceServer = True
|
||||||
|
|
||||||
|
def loadUserMapFromCache(self):
|
||||||
|
self.users = {}
|
||||||
|
self.userMapFromPerforceServer = False
|
||||||
|
try:
|
||||||
|
cache = open(self.getUserCacheFilename(), "rb")
|
||||||
|
lines = cache.readlines()
|
||||||
|
cache.close()
|
||||||
|
for line in lines:
|
||||||
|
entry = line.strip().split("\t")
|
||||||
|
self.users[entry[0]] = entry[1]
|
||||||
|
except IOError:
|
||||||
|
self.getUserMapFromPerforceServer()
|
||||||
|
|
||||||
class P4Debug(Command):
|
class P4Debug(Command):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Command.__init__(self)
|
Command.__init__(self)
|
||||||
|
@ -554,13 +595,16 @@ class P4RollBack(Command):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
class P4Submit(Command):
|
class P4Submit(Command, P4UserMap):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Command.__init__(self)
|
Command.__init__(self)
|
||||||
|
P4UserMap.__init__(self)
|
||||||
self.options = [
|
self.options = [
|
||||||
optparse.make_option("--verbose", dest="verbose", action="store_true"),
|
optparse.make_option("--verbose", dest="verbose", action="store_true"),
|
||||||
optparse.make_option("--origin", dest="origin"),
|
optparse.make_option("--origin", dest="origin"),
|
||||||
optparse.make_option("-M", dest="detectRenames", action="store_true"),
|
optparse.make_option("-M", dest="detectRenames", action="store_true"),
|
||||||
|
# preserve the user, requires relevant p4 permissions
|
||||||
|
optparse.make_option("--preserve-user", dest="preserveUser", action="store_true"),
|
||||||
]
|
]
|
||||||
self.description = "Submit changes from git to the perforce depot."
|
self.description = "Submit changes from git to the perforce depot."
|
||||||
self.usage += " [name of git branch to submit into perforce depot]"
|
self.usage += " [name of git branch to submit into perforce depot]"
|
||||||
|
@ -568,6 +612,7 @@ class P4Submit(Command):
|
||||||
self.origin = ""
|
self.origin = ""
|
||||||
self.detectRenames = False
|
self.detectRenames = False
|
||||||
self.verbose = False
|
self.verbose = False
|
||||||
|
self.preserveUser = gitConfig("git-p4.preserveUser").lower() == "true"
|
||||||
self.isWindows = (platform.system() == "Windows")
|
self.isWindows = (platform.system() == "Windows")
|
||||||
|
|
||||||
def check(self):
|
def check(self):
|
||||||
|
@ -602,6 +647,75 @@ class P4Submit(Command):
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def p4UserForCommit(self,id):
|
||||||
|
# Return the tuple (perforce user,git email) for a given git commit id
|
||||||
|
self.getUserMapFromPerforceServer()
|
||||||
|
gitEmail = read_pipe("git log --max-count=1 --format='%%ae' %s" % id)
|
||||||
|
gitEmail = gitEmail.strip()
|
||||||
|
if not self.emails.has_key(gitEmail):
|
||||||
|
return (None,gitEmail)
|
||||||
|
else:
|
||||||
|
return (self.emails[gitEmail],gitEmail)
|
||||||
|
|
||||||
|
def checkValidP4Users(self,commits):
|
||||||
|
# check if any git authors cannot be mapped to p4 users
|
||||||
|
for id in commits:
|
||||||
|
(user,email) = self.p4UserForCommit(id)
|
||||||
|
if not user:
|
||||||
|
msg = "Cannot find p4 user for email %s in commit %s." % (email, id)
|
||||||
|
if gitConfig('git-p4.allowMissingP4Users').lower() == "true":
|
||||||
|
print "%s" % msg
|
||||||
|
else:
|
||||||
|
die("Error: %s\nSet git-p4.allowMissingP4Users to true to allow this." % msg)
|
||||||
|
|
||||||
|
def lastP4Changelist(self):
|
||||||
|
# Get back the last changelist number submitted in this client spec. This
|
||||||
|
# then gets used to patch up the username in the change. If the same
|
||||||
|
# client spec is being used by multiple processes then this might go
|
||||||
|
# wrong.
|
||||||
|
results = p4CmdList("client -o") # find the current client
|
||||||
|
client = None
|
||||||
|
for r in results:
|
||||||
|
if r.has_key('Client'):
|
||||||
|
client = r['Client']
|
||||||
|
break
|
||||||
|
if not client:
|
||||||
|
die("could not get client spec")
|
||||||
|
results = p4CmdList("changes -c %s -m 1" % client)
|
||||||
|
for r in results:
|
||||||
|
if r.has_key('change'):
|
||||||
|
return r['change']
|
||||||
|
die("Could not get changelist number for last submit - cannot patch up user details")
|
||||||
|
|
||||||
|
def modifyChangelistUser(self, changelist, newUser):
|
||||||
|
# fixup the user field of a changelist after it has been submitted.
|
||||||
|
changes = p4CmdList("change -o %s" % changelist)
|
||||||
|
for c in changes:
|
||||||
|
if c.has_key('User'):
|
||||||
|
c['User'] = newUser
|
||||||
|
input = marshal.dumps(changes[0])
|
||||||
|
result = p4CmdList("change -f -i", stdin=input)
|
||||||
|
for r in result:
|
||||||
|
if r.has_key('code'):
|
||||||
|
if r['code'] == 'error':
|
||||||
|
die("Could not modify user field of changelist %s to %s:%s" % (changelist, newUser, r['data']))
|
||||||
|
if r.has_key('data'):
|
||||||
|
print("Updated user field for changelist %s to %s" % (changelist, newUser))
|
||||||
|
return
|
||||||
|
die("Could not modify user field of changelist %s to %s" % (changelist, newUser))
|
||||||
|
|
||||||
|
def canChangeChangelists(self):
|
||||||
|
# check to see if we have p4 admin or super-user permissions, either of
|
||||||
|
# which are required to modify changelists.
|
||||||
|
results = p4CmdList("-G protects %s" % self.depotPath)
|
||||||
|
for r in results:
|
||||||
|
if r.has_key('perm'):
|
||||||
|
if r['perm'] == 'admin':
|
||||||
|
return 1
|
||||||
|
if r['perm'] == 'super':
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
def prepareSubmitTemplate(self):
|
def prepareSubmitTemplate(self):
|
||||||
# remove lines in the Files section that show changes to files outside the depot path we're committing into
|
# remove lines in the Files section that show changes to files outside the depot path we're committing into
|
||||||
template = ""
|
template = ""
|
||||||
|
@ -631,6 +745,9 @@ class P4Submit(Command):
|
||||||
def applyCommit(self, id):
|
def applyCommit(self, id):
|
||||||
print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
|
print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
|
||||||
|
|
||||||
|
if self.preserveUser:
|
||||||
|
(p4User, gitEmail) = self.p4UserForCommit(id)
|
||||||
|
|
||||||
if not self.detectRenames:
|
if not self.detectRenames:
|
||||||
# If not explicitly set check the config variable
|
# If not explicitly set check the config variable
|
||||||
self.detectRenames = gitConfig("git-p4.detectRenames").lower() == "true"
|
self.detectRenames = gitConfig("git-p4.detectRenames").lower() == "true"
|
||||||
|
@ -781,8 +898,13 @@ class P4Submit(Command):
|
||||||
editor = read_pipe("git var GIT_EDITOR").strip()
|
editor = read_pipe("git var GIT_EDITOR").strip()
|
||||||
system(editor + " " + fileName)
|
system(editor + " " + fileName)
|
||||||
|
|
||||||
|
if gitConfig("git-p4.skipSubmitEditCheck") == "true":
|
||||||
|
checkModTime = False
|
||||||
|
else:
|
||||||
|
checkModTime = True
|
||||||
|
|
||||||
response = "y"
|
response = "y"
|
||||||
if os.stat(fileName).st_mtime <= mtime:
|
if checkModTime and (os.stat(fileName).st_mtime <= mtime):
|
||||||
response = "x"
|
response = "x"
|
||||||
while response != "y" and response != "n":
|
while response != "y" and response != "n":
|
||||||
response = raw_input("Submit template unchanged. Submit anyway? [y]es, [n]o (skip this patch) ")
|
response = raw_input("Submit template unchanged. Submit anyway? [y]es, [n]o (skip this patch) ")
|
||||||
|
@ -795,6 +917,14 @@ class P4Submit(Command):
|
||||||
if self.isWindows:
|
if self.isWindows:
|
||||||
submitTemplate = submitTemplate.replace("\r\n", "\n")
|
submitTemplate = submitTemplate.replace("\r\n", "\n")
|
||||||
p4_write_pipe("submit -i", submitTemplate)
|
p4_write_pipe("submit -i", submitTemplate)
|
||||||
|
|
||||||
|
if self.preserveUser:
|
||||||
|
if p4User:
|
||||||
|
# Get last changelist number. Cannot easily get it from
|
||||||
|
# the submit command output as the output is unmarshalled.
|
||||||
|
changelist = self.lastP4Changelist()
|
||||||
|
self.modifyChangelistUser(changelist, p4User)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for f in editedFiles:
|
for f in editedFiles:
|
||||||
p4_system("revert \"%s\"" % f);
|
p4_system("revert \"%s\"" % f);
|
||||||
|
@ -831,6 +961,10 @@ class P4Submit(Command):
|
||||||
if len(self.origin) == 0:
|
if len(self.origin) == 0:
|
||||||
self.origin = upstream
|
self.origin = upstream
|
||||||
|
|
||||||
|
if self.preserveUser:
|
||||||
|
if not self.canChangeChangelists():
|
||||||
|
die("Cannot preserve user names without p4 super-user or admin permissions")
|
||||||
|
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
print "Origin branch is " + self.origin
|
print "Origin branch is " + self.origin
|
||||||
|
|
||||||
|
@ -858,6 +992,9 @@ class P4Submit(Command):
|
||||||
commits.append(line.strip())
|
commits.append(line.strip())
|
||||||
commits.reverse()
|
commits.reverse()
|
||||||
|
|
||||||
|
if self.preserveUser:
|
||||||
|
self.checkValidP4Users(commits)
|
||||||
|
|
||||||
while len(commits) > 0:
|
while len(commits) > 0:
|
||||||
commit = commits[0]
|
commit = commits[0]
|
||||||
commits = commits[1:]
|
commits = commits[1:]
|
||||||
|
@ -877,11 +1014,12 @@ class P4Submit(Command):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
class P4Sync(Command):
|
class P4Sync(Command, P4UserMap):
|
||||||
delete_actions = ( "delete", "move/delete", "purge" )
|
delete_actions = ( "delete", "move/delete", "purge" )
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Command.__init__(self)
|
Command.__init__(self)
|
||||||
|
P4UserMap.__init__(self)
|
||||||
self.options = [
|
self.options = [
|
||||||
optparse.make_option("--branch", dest="branch"),
|
optparse.make_option("--branch", dest="branch"),
|
||||||
optparse.make_option("--detect-branches", dest="detectBranches", action="store_true"),
|
optparse.make_option("--detect-branches", dest="detectBranches", action="store_true"),
|
||||||
|
@ -1236,41 +1374,6 @@ class P4Sync(Command):
|
||||||
print ("Tag %s does not match with change %s: file count is different."
|
print ("Tag %s does not match with change %s: file count is different."
|
||||||
% (labelDetails["label"], change))
|
% (labelDetails["label"], change))
|
||||||
|
|
||||||
def getUserCacheFilename(self):
|
|
||||||
home = os.environ.get("HOME", os.environ.get("USERPROFILE"))
|
|
||||||
return home + "/.gitp4-usercache.txt"
|
|
||||||
|
|
||||||
def getUserMapFromPerforceServer(self):
|
|
||||||
if self.userMapFromPerforceServer:
|
|
||||||
return
|
|
||||||
self.users = {}
|
|
||||||
|
|
||||||
for output in p4CmdList("users"):
|
|
||||||
if not output.has_key("User"):
|
|
||||||
continue
|
|
||||||
self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
|
|
||||||
|
|
||||||
|
|
||||||
s = ''
|
|
||||||
for (key, val) in self.users.items():
|
|
||||||
s += "%s\t%s\n" % (key.expandtabs(1), val.expandtabs(1))
|
|
||||||
|
|
||||||
open(self.getUserCacheFilename(), "wb").write(s)
|
|
||||||
self.userMapFromPerforceServer = True
|
|
||||||
|
|
||||||
def loadUserMapFromCache(self):
|
|
||||||
self.users = {}
|
|
||||||
self.userMapFromPerforceServer = False
|
|
||||||
try:
|
|
||||||
cache = open(self.getUserCacheFilename(), "rb")
|
|
||||||
lines = cache.readlines()
|
|
||||||
cache.close()
|
|
||||||
for line in lines:
|
|
||||||
entry = line.strip().split("\t")
|
|
||||||
self.users[entry[0]] = entry[1]
|
|
||||||
except IOError:
|
|
||||||
self.getUserMapFromPerforceServer()
|
|
||||||
|
|
||||||
def getLabels(self):
|
def getLabels(self):
|
||||||
self.labels = {}
|
self.labels = {}
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,12 @@ is not your current git branch you can also pass that as an argument:
|
||||||
|
|
||||||
You can override the reference branch with the --origin=mysourcebranch option.
|
You can override the reference branch with the --origin=mysourcebranch option.
|
||||||
|
|
||||||
|
The Perforce changelists will be created with the user who ran git-p4. If you
|
||||||
|
use --preserve-user then git-p4 will attempt to create Perforce changelists
|
||||||
|
with the Perforce user corresponding to the git commit author. You need to
|
||||||
|
have sufficient permissions within Perforce, and the git users need to have
|
||||||
|
Perforce accounts. Permissions can be granted using 'p4 protect'.
|
||||||
|
|
||||||
If a submit fails you may have to "p4 resolve" and submit manually. You can
|
If a submit fails you may have to "p4 resolve" and submit manually. You can
|
||||||
continue importing the remaining changes with
|
continue importing the remaining changes with
|
||||||
|
|
||||||
|
@ -196,6 +202,29 @@ able to find the relevant client. This client spec will be used to
|
||||||
both filter the files cloned by git and set the directory layout as
|
both filter the files cloned by git and set the directory layout as
|
||||||
specified in the client (this implies --keep-path style semantics).
|
specified in the client (this implies --keep-path style semantics).
|
||||||
|
|
||||||
|
git-p4.skipSubmitModTimeCheck
|
||||||
|
|
||||||
|
git config [--global] git-p4.skipSubmitModTimeCheck false
|
||||||
|
|
||||||
|
If true, submit will not check if the p4 change template has been modified.
|
||||||
|
|
||||||
|
git-p4.preserveUser
|
||||||
|
|
||||||
|
git config [--global] git-p4.preserveUser false
|
||||||
|
|
||||||
|
If true, attempt to preserve user names by modifying the p4 changelists. See
|
||||||
|
the "--preserve-user" submit option.
|
||||||
|
|
||||||
|
git-p4.allowMissingPerforceUsers
|
||||||
|
|
||||||
|
git config [--global] git-p4.allowMissingP4Users false
|
||||||
|
|
||||||
|
If git-p4 is setting the perforce user for a commit (--preserve-user) then
|
||||||
|
if there is no perforce user corresponding to the git author, git-p4 will
|
||||||
|
stop. With allowMissingPerforceUsers set to true, git-p4 will use the
|
||||||
|
current user (i.e. the behavior without --preserve-user) and carry on with
|
||||||
|
the perforce commit.
|
||||||
|
|
||||||
Implementation Details...
|
Implementation Details...
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ test_description='git-p4 tests'
|
||||||
GITP4=$GIT_BUILD_DIR/contrib/fast-import/git-p4
|
GITP4=$GIT_BUILD_DIR/contrib/fast-import/git-p4
|
||||||
P4DPORT=10669
|
P4DPORT=10669
|
||||||
|
|
||||||
|
export P4PORT=localhost:$P4DPORT
|
||||||
|
|
||||||
db="$TRASH_DIRECTORY/db"
|
db="$TRASH_DIRECTORY/db"
|
||||||
cli="$TRASH_DIRECTORY/cli"
|
cli="$TRASH_DIRECTORY/cli"
|
||||||
git="$TRASH_DIRECTORY/git"
|
git="$TRASH_DIRECTORY/git"
|
||||||
|
@ -129,6 +131,88 @@ test_expect_success 'clone bare' '
|
||||||
rm -rf "$git" && mkdir "$git"
|
rm -rf "$git" && mkdir "$git"
|
||||||
'
|
'
|
||||||
|
|
||||||
|
p4_add_user() {
|
||||||
|
name=$1
|
||||||
|
fullname=$2
|
||||||
|
p4 user -f -i <<EOF &&
|
||||||
|
User: $name
|
||||||
|
Email: $name@localhost
|
||||||
|
FullName: $fullname
|
||||||
|
EOF
|
||||||
|
p4 passwd -P secret $name
|
||||||
|
}
|
||||||
|
|
||||||
|
p4_grant_admin() {
|
||||||
|
name=$1
|
||||||
|
p4 protect -o |\
|
||||||
|
awk "{print}END{print \" admin user $name * //depot/...\"}" |\
|
||||||
|
p4 protect -i
|
||||||
|
}
|
||||||
|
|
||||||
|
p4_check_commit_author() {
|
||||||
|
file=$1
|
||||||
|
user=$2
|
||||||
|
if p4 changes -m 1 //depot/$file | grep $user > /dev/null ; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "file $file not modified by user $user" 1>&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test username support, submitting as user 'alice'
|
||||||
|
test_expect_success 'preserve users' '
|
||||||
|
p4_add_user alice Alice &&
|
||||||
|
p4_add_user bob Bob &&
|
||||||
|
p4_grant_admin alice &&
|
||||||
|
"$GITP4" clone --dest="$git" //depot &&
|
||||||
|
cd "$git" &&
|
||||||
|
echo "username: a change by alice" >> file1 &&
|
||||||
|
echo "username: a change by bob" >> file2 &&
|
||||||
|
git commit --author "Alice <alice@localhost>" -m "a change by alice" file1 &&
|
||||||
|
git commit --author "Bob <bob@localhost>" -m "a change by bob" file2 &&
|
||||||
|
git config git-p4.skipSubmitEditCheck true &&
|
||||||
|
P4EDITOR=touch P4USER=alice P4PASSWD=secret "$GITP4" commit --preserve-user &&
|
||||||
|
p4_check_commit_author file1 alice &&
|
||||||
|
p4_check_commit_author file2 bob &&
|
||||||
|
cd "$TRASH_DIRECTORY" &&
|
||||||
|
rm -rf "$git" && mkdir "$git"
|
||||||
|
'
|
||||||
|
|
||||||
|
# Test username support, submitting as bob, who lacks admin rights. Should
|
||||||
|
# not submit change to p4 (git diff should show deltas).
|
||||||
|
test_expect_success 'refuse to preserve users without perms' '
|
||||||
|
"$GITP4" clone --dest="$git" //depot &&
|
||||||
|
cd "$git" &&
|
||||||
|
echo "username-noperms: a change by alice" >> file1 &&
|
||||||
|
git commit --author "Alice <alice@localhost>" -m "perms: a change by alice" file1 &&
|
||||||
|
! P4EDITOR=touch P4USER=bob P4PASSWD=secret "$GITP4" commit --preserve-user &&
|
||||||
|
! git diff --exit-code HEAD..p4/master > /dev/null &&
|
||||||
|
cd "$TRASH_DIRECTORY" &&
|
||||||
|
rm -rf "$git" && mkdir "$git"
|
||||||
|
'
|
||||||
|
|
||||||
|
# What happens with unknown author? Without allowMissingP4Users it should fail.
|
||||||
|
test_expect_success 'preserve user where author is unknown to p4' '
|
||||||
|
"$GITP4" clone --dest="$git" //depot &&
|
||||||
|
cd "$git" &&
|
||||||
|
git config git-p4.skipSubmitEditCheck true
|
||||||
|
echo "username-bob: a change by bob" >> file1 &&
|
||||||
|
git commit --author "Bob <bob@localhost>" -m "preserve: a change by bob" file1 &&
|
||||||
|
echo "username-unknown: a change by charlie" >> file1 &&
|
||||||
|
git commit --author "Charlie <charlie@localhost>" -m "preserve: a change by charlie" file1 &&
|
||||||
|
! P4EDITOR=touch P4USER=alice P4PASSWD=secret "$GITP4" commit --preserve-user &&
|
||||||
|
! git diff --exit-code HEAD..p4/master > /dev/null &&
|
||||||
|
echo "$0: repeat with allowMissingP4Users enabled" &&
|
||||||
|
git config git-p4.allowMissingP4Users true &&
|
||||||
|
git config git-p4.preserveUser true &&
|
||||||
|
P4EDITOR=touch P4USER=alice P4PASSWD=secret "$GITP4" commit &&
|
||||||
|
git diff --exit-code HEAD..p4/master > /dev/null &&
|
||||||
|
p4_check_commit_author file1 alice &&
|
||||||
|
cd "$TRASH_DIRECTORY" &&
|
||||||
|
rm -rf "$git" && mkdir "$git"
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'shutdown' '
|
test_expect_success 'shutdown' '
|
||||||
pid=`pgrep -f p4d` &&
|
pid=`pgrep -f p4d` &&
|
||||||
test -n "$pid" &&
|
test -n "$pid" &&
|
||||||
|
|
Loading…
Reference in New Issue