summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/RelNotes-1.5.4.txt14
-rw-r--r--Documentation/git-rebase.txt9
-rw-r--r--Documentation/git-send-email.txt35
-rwxr-xr-xGIT-VERSION-GEN2
l---------RelNotes2
-rw-r--r--builtin-fetch--tool.c21
-rw-r--r--builtin-update-ref.c9
-rwxr-xr-xcontrib/fast-import/git-p4342
-rwxr-xr-xgit-rebase.sh6
-rwxr-xr-xgit-send-email.perl101
-rw-r--r--refs.c27
-rw-r--r--refs.h6
-rw-r--r--send-pack.c12
13 files changed, 394 insertions, 192 deletions
diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes-1.5.4.txt
new file mode 100644
index 0000000000..1df66af9ce
--- /dev/null
+++ b/Documentation/RelNotes-1.5.4.txt
@@ -0,0 +1,14 @@
+GIT v1.5.4 Release Notes
+========================
+
+Updates since v1.5.3
+--------------------
+
+
+
+Fixes since v1.5.3
+------------------
+
+All of the fixes in v1.5.3 maintenance series are included in
+this release, unless otherwise noted.
+
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 61b1810dba..0858fa8a63 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -8,8 +8,9 @@ git-rebase - Forward-port local commits to the updated upstream head
SYNOPSIS
--------
[verse]
-'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge] [-C<n>]
- [-p | --preserve-merges] [--onto <newbase>] <upstream> [<branch>]
+'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge]
+ [-C<n>] [ --whitespace=<option>] [-p | --preserve-merges]
+ [--onto <newbase>] <upstream> [<branch>]
'git-rebase' --continue | --skip | --abort
DESCRIPTION
@@ -209,6 +210,10 @@ OPTIONS
context exist they all must match. By default no context is
ever ignored.
+--whitespace=<nowarn|warn|error|error-all|strip>::
+ This flag is passed to the `git-apply` program
+ (see gitlink:git-apply[1]) that applies the patch.
+
-i, \--interactive::
Make a list of the commits which are about to be rebased. Let the
user edit that list before rebasing. This mode can also be used to
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 16bfd7be22..1ec61affab 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -75,6 +75,12 @@ The --cc option must be repeated for each user you want on the cc list.
Make git-send-email less verbose. One line per email should be
all that is output.
+--identity::
+ A configuration identity. When given, causes values in the
+ 'sendemail.<identity>' subsection to take precedence over
+ values in the 'sendemail' section. The default identity is
+ the value of 'sendemail.identity'.
+
--smtp-server::
If set, specifies the outgoing SMTP server to use (e.g.
`smtp.example.com` or a raw IP address). Alternatively it can
@@ -85,6 +91,17 @@ The --cc option must be repeated for each user you want on the cc list.
`/usr/lib/sendmail` if such program is available, or
`localhost` otherwise.
+--smtp-user, --smtp-pass::
+ Username and password for SMTP-AUTH. Defaults are the values of
+ the configuration values 'sendemail.smtpuser' and
+ 'sendemail.smtppass', but see also 'sendemail.identity'.
+ If not set, authentication is not attempted.
+
+--smtp-ssl::
+ If set, connects to the SMTP server using SSL.
+ Default is the value of the 'sendemail.smtpssl' configuration value;
+ if that is unspecified, does not use SSL.
+
--subject::
Specify the initial subject of the email thread.
Only necessary if --compose is also set. If --compose
@@ -122,6 +139,13 @@ The --to option must be repeated for each user you want on the to list.
CONFIGURATION
-------------
+sendemail.identity::
+ The default configuration identity. When specified,
+ 'sendemail.<identity>.<item>' will have higher precedence than
+ 'sendemail.<item>'. This is useful to declare multiple SMTP
+ identities and to hoist sensitive authentication information
+ out of the repository and into the global configuation file.
+
sendemail.aliasesfile::
To avoid typing long email addresses, point this to one or more
email aliases files. You must also supply 'sendemail.aliasfiletype'.
@@ -141,7 +165,16 @@ sendemail.chainreplyto::
parameter.
sendemail.smtpserver::
- Default smtp server to use.
+ Default SMTP server to use.
+
+sendemail.smtpuser::
+ Default SMTP-AUTH username.
+
+sendemail.smtppass::
+ Default SMTP-AUTH password.
+
+sendemail.smtpssl::
+ Boolean value specifying the default to the '--smtp-ssl' parameter.
Author
------
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 3835fb3965..3c0032cec5 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.5.3.1.GIT
+DEF_VER=v1.5.3.GIT
LF='
'
diff --git a/RelNotes b/RelNotes
index ea8f800cbb..46308cee0b 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.5.3.1.txt \ No newline at end of file
+Documentation/RelNotes-1.5.4.txt \ No newline at end of file
diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c
index e2f8ede9ae..24c7e6f7db 100644
--- a/builtin-fetch--tool.c
+++ b/builtin-fetch--tool.c
@@ -31,24 +31,19 @@ static void show_new(enum object_type type, unsigned char *sha1_new)
find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
}
-static int update_ref(const char *action,
+static int update_ref_env(const char *action,
const char *refname,
unsigned char *sha1,
unsigned char *oldval)
{
char msg[1024];
char *rla = getenv("GIT_REFLOG_ACTION");
- static struct ref_lock *lock;
if (!rla)
rla = "(reflog update)";
- snprintf(msg, sizeof(msg), "%s: %s", rla, action);
- lock = lock_any_ref_for_update(refname, oldval, 0);
- if (!lock)
- return 1;
- if (write_ref_sha1(lock, sha1, msg) < 0)
- return 1;
- return 0;
+ if (snprintf(msg, sizeof(msg), "%s: %s", rla, action) >= sizeof(msg))
+ warning("reflog message too long: %.*s...", 50, msg);
+ return update_ref(msg, refname, sha1, oldval, 0, QUIET_ON_ERR);
}
static int update_local_ref(const char *name,
@@ -88,7 +83,7 @@ static int update_local_ref(const char *name,
fprintf(stderr, "* %s: storing %s\n",
name, note);
show_new(type, sha1_new);
- return update_ref(msg, name, sha1_new, NULL);
+ return update_ref_env(msg, name, sha1_new, NULL);
}
if (!hashcmp(sha1_old, sha1_new)) {
@@ -102,7 +97,7 @@ static int update_local_ref(const char *name,
if (!strncmp(name, "refs/tags/", 10)) {
fprintf(stderr, "* %s: updating with %s\n", name, note);
show_new(type, sha1_new);
- return update_ref("updating tag", name, sha1_new, NULL);
+ return update_ref_env("updating tag", name, sha1_new, NULL);
}
current = lookup_commit_reference(sha1_old);
@@ -117,7 +112,7 @@ static int update_local_ref(const char *name,
fprintf(stderr, "* %s: fast forward to %s\n",
name, note);
fprintf(stderr, " old..new: %s..%s\n", oldh, newh);
- return update_ref("fast forward", name, sha1_new, sha1_old);
+ return update_ref_env("fast forward", name, sha1_new, sha1_old);
}
if (!force) {
fprintf(stderr,
@@ -131,7 +126,7 @@ static int update_local_ref(const char *name,
"* %s: forcing update to non-fast forward %s\n",
name, note);
fprintf(stderr, " old...new: %s...%s\n", oldh, newh);
- return update_ref("forced-update", name, sha1_new, sha1_old);
+ return update_ref_env("forced-update", name, sha1_new, sha1_old);
}
static int append_fetch_head(FILE *fp,
diff --git a/builtin-update-ref.c b/builtin-update-ref.c
index 8339cf19e2..fe1f74c9f3 100644
--- a/builtin-update-ref.c
+++ b/builtin-update-ref.c
@@ -8,7 +8,6 @@ static const char git_update_ref_usage[] =
int cmd_update_ref(int argc, const char **argv, const char *prefix)
{
const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
- struct ref_lock *lock;
unsigned char sha1[20], oldsha1[20];
int i, delete, ref_flags;
@@ -62,10 +61,6 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
if (oldval && *oldval && get_sha1(oldval, oldsha1))
die("%s: not a valid old SHA1", oldval);
- lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, ref_flags);
- if (!lock)
- die("%s: cannot lock the ref", refname);
- if (write_ref_sha1(lock, sha1, msg) < 0)
- die("%s: cannot update the ref", refname);
- return 0;
+ return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
+ ref_flags, DIE_ON_ERR);
}
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index 55778c5775..adaaae6633 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -281,6 +281,19 @@ def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent
def originP4BranchesExist():
return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master")
+def p4ChangesForPaths(depotPaths, changeRange):
+ assert depotPaths
+ output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, changeRange)
+ for p in depotPaths]))
+
+ changes = []
+ for line in output:
+ changeNum = line.split(" ")[1]
+ changes.append(int(changeNum))
+
+ changes.sort()
+ return changes
+
class Command:
def __init__(self):
self.usage = "usage: %prog [options]"
@@ -664,9 +677,8 @@ class P4Submit(Command):
f.close();
os.chdir(self.clientPath)
- response = raw_input("Do you want to sync %s with p4 sync? [y]es/[n]o " % self.clientPath)
- if response == "y" or response == "yes":
- system("p4 sync ...")
+ print "Syncronizing p4 checkout..."
+ system("p4 sync ...")
if self.reset:
self.firstTime = True
@@ -705,10 +717,14 @@ class P4Submit(Command):
else:
print "All changes applied!"
os.chdir(self.oldWorkingDirectory)
- response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ")
+
+ sync = P4Sync()
+ sync.run([])
+
+ response = raw_input("Do you want to rebase current HEAD from Perforce now using git-p4 rebase? [y]es/[n]o ")
if response == "y" or response == "yes":
rebase = P4Rebase()
- rebase.run([])
+ rebase.rebase()
os.remove(self.configFile)
return True
@@ -1102,6 +1118,186 @@ class P4Sync(Command):
self.keepRepoPath = (d.has_key('options')
and ('keepRepoPath' in d['options']))
+ def gitRefForBranch(self, branch):
+ if branch == "main":
+ return self.refPrefix + "master"
+
+ if len(branch) <= 0:
+ return branch
+
+ return self.refPrefix + self.projectName + branch
+
+ def gitCommitByP4Change(self, ref, change):
+ if self.verbose:
+ print "looking in ref " + ref + " for change %s using bisect..." % change
+
+ earliestCommit = ""
+ latestCommit = parseRevision(ref)
+
+ while True:
+ if self.verbose:
+ print "trying: earliest %s latest %s" % (earliestCommit, latestCommit)
+ next = read_pipe("git rev-list --bisect %s %s" % (latestCommit, earliestCommit)).strip()
+ if len(next) == 0:
+ if self.verbose:
+ print "argh"
+ return ""
+ log = extractLogMessageFromGitCommit(next)
+ settings = extractSettingsGitLog(log)
+ currentChange = int(settings['change'])
+ if self.verbose:
+ print "current change %s" % currentChange
+
+ if currentChange == change:
+ if self.verbose:
+ print "found %s" % next
+ return next
+
+ if currentChange < change:
+ earliestCommit = "^%s" % next
+ else:
+ latestCommit = "%s" % next
+
+ return ""
+
+ def importNewBranch(self, branch, maxChange):
+ # make fast-import flush all changes to disk and update the refs using the checkpoint
+ # command so that we can try to find the branch parent in the git history
+ self.gitStream.write("checkpoint\n\n");
+ self.gitStream.flush();
+ branchPrefix = self.depotPaths[0] + branch + "/"
+ range = "@1,%s" % maxChange
+ #print "prefix" + branchPrefix
+ changes = p4ChangesForPaths([branchPrefix], range)
+ if len(changes) <= 0:
+ return False
+ firstChange = changes[0]
+ #print "first change in branch: %s" % firstChange
+ sourceBranch = self.knownBranches[branch]
+ sourceDepotPath = self.depotPaths[0] + sourceBranch
+ sourceRef = self.gitRefForBranch(sourceBranch)
+ #print "source " + sourceBranch
+
+ branchParentChange = int(p4Cmd("changes -m 1 %s...@1,%s" % (sourceDepotPath, firstChange))["change"])
+ #print "branch parent: %s" % branchParentChange
+ gitParent = self.gitCommitByP4Change(sourceRef, branchParentChange)
+ if len(gitParent) > 0:
+ self.initialParents[self.gitRefForBranch(branch)] = gitParent
+ #print "parent git commit: %s" % gitParent
+
+ self.importChanges(changes)
+ return True
+
+ def importChanges(self, changes):
+ cnt = 1
+ for change in changes:
+ description = p4Cmd("describe %s" % change)
+ self.updateOptionDict(description)
+
+ if not self.silent:
+ sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
+ sys.stdout.flush()
+ cnt = cnt + 1
+
+ try:
+ if self.detectBranches:
+ branches = self.splitFilesIntoBranches(description)
+ for branch in branches.keys():
+ ## HACK --hwn
+ branchPrefix = self.depotPaths[0] + branch + "/"
+
+ parent = ""
+
+ filesForCommit = branches[branch]
+
+ if self.verbose:
+ print "branch is %s" % branch
+
+ self.updatedBranches.add(branch)
+
+ if branch not in self.createdBranches:
+ self.createdBranches.add(branch)
+ parent = self.knownBranches[branch]
+ if parent == branch:
+ parent = ""
+ else:
+ fullBranch = self.projectName + branch
+ if fullBranch not in self.p4BranchesInGit:
+ if not self.silent:
+ print("\n Importing new branch %s" % fullBranch);
+ if self.importNewBranch(branch, change - 1):
+ parent = ""
+ self.p4BranchesInGit.append(fullBranch)
+ if not self.silent:
+ print("\n Resuming with change %s" % change);
+
+ if self.verbose:
+ print "parent determined through known branches: %s" % parent
+
+ branch = self.gitRefForBranch(branch)
+ parent = self.gitRefForBranch(parent)
+
+ if self.verbose:
+ print "looking for initial parent for %s; current parent is %s" % (branch, parent)
+
+ if len(parent) == 0 and branch in self.initialParents:
+ parent = self.initialParents[branch]
+ del self.initialParents[branch]
+
+ self.commit(description, filesForCommit, branch, [branchPrefix], parent)
+ else:
+ files = self.extractFilesFromCommit(description)
+ self.commit(description, files, self.branch, self.depotPaths,
+ self.initialParent)
+ self.initialParent = ""
+ except IOError:
+ print self.gitError.read()
+ sys.exit(1)
+
+ def importHeadRevision(self, revision):
+ print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch)
+
+ details = { "user" : "git perforce import user", "time" : int(time.time()) }
+ details["desc"] = ("Initial import of %s from the state at revision %s"
+ % (' '.join(self.depotPaths), revision))
+ details["change"] = revision
+ newestRevision = 0
+
+ fileCnt = 0
+ for info in p4CmdList("files "
+ + ' '.join(["%s...%s"
+ % (p, revision)
+ for p in self.depotPaths])):
+
+ if info['code'] == 'error':
+ sys.stderr.write("p4 returned an error: %s\n"
+ % info['data'])
+ sys.exit(1)
+
+
+ change = int(info["change"])
+ if change > newestRevision:
+ newestRevision = change
+
+ if info["action"] == "delete":
+ # don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
+ #fileCnt = fileCnt + 1
+ continue
+
+ for prop in ["depotFile", "rev", "action", "type" ]:
+ details["%s%s" % (prop, fileCnt)] = info[prop]
+
+ fileCnt = fileCnt + 1
+
+ details["change"] = newestRevision
+ self.updateOptionDict(details)
+ try:
+ self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
+ except IOError:
+ print "IO error with git fast-import. Is your git version recent enough?"
+ print self.gitError.read()
+
+
def run(self, args):
self.depotPaths = []
self.changeRange = ""
@@ -1199,7 +1395,7 @@ class P4Sync(Command):
self.depotPaths = sorted(args)
- self.revision = ""
+ revision = ""
self.users = {}
newPaths = []
@@ -1210,15 +1406,15 @@ class P4Sync(Command):
if self.changeRange == "@all":
self.changeRange = ""
elif ',' not in self.changeRange:
- self.revision = self.changeRange
+ revision = self.changeRange
self.changeRange = ""
p = p[:atIdx]
elif p.find("#") != -1:
hashIdx = p.index("#")
- self.revision = p[hashIdx:]
+ revision = p[hashIdx:]
p = p[:hashIdx]
elif self.previousDepotPaths == []:
- self.revision = "#head"
+ revision = "#head"
p = re.sub ("\.\.\.$", "", p)
if not p.endswith("/"):
@@ -1259,49 +1455,8 @@ class P4Sync(Command):
self.gitStream = importProcess.stdin
self.gitError = importProcess.stderr
- if self.revision:
- print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), self.revision, self.branch)
-
- details = { "user" : "git perforce import user", "time" : int(time.time()) }
- details["desc"] = ("Initial import of %s from the state at revision %s"
- % (' '.join(self.depotPaths), self.revision))
- details["change"] = self.revision
- newestRevision = 0
-
- fileCnt = 0
- for info in p4CmdList("files "
- + ' '.join(["%s...%s"
- % (p, self.revision)
- for p in self.depotPaths])):
-
- if info['code'] == 'error':
- sys.stderr.write("p4 returned an error: %s\n"
- % info['data'])
- sys.exit(1)
-
-
- change = int(info["change"])
- if change > newestRevision:
- newestRevision = change
-
- if info["action"] == "delete":
- # don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
- #fileCnt = fileCnt + 1
- continue
-
- for prop in ["depotFile", "rev", "action", "type" ]:
- details["%s%s" % (prop, fileCnt)] = info[prop]
-
- fileCnt = fileCnt + 1
-
- details["change"] = newestRevision
- self.updateOptionDict(details)
- try:
- self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
- except IOError:
- print "IO error with git fast-import. Is your git version recent enough?"
- print self.gitError.read()
-
+ if revision:
+ self.importHeadRevision(revision)
else:
changes = []
@@ -1319,15 +1474,7 @@ class P4Sync(Command):
if self.verbose:
print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
self.changeRange)
- assert self.depotPaths
- output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, self.changeRange)
- for p in self.depotPaths]))
-
- for line in output:
- changeNum = line.split(" ")[1]
- changes.append(int(changeNum))
-
- changes.sort()
+ changes = p4ChangesForPaths(self.depotPaths, self.changeRange)
if len(self.maxChanges) > 0:
changes = changes[:min(int(self.maxChanges), len(changes))]
@@ -1342,74 +1489,7 @@ class P4Sync(Command):
self.updatedBranches = set()
- cnt = 1
- for change in changes:
- description = p4Cmd("describe %s" % change)
- self.updateOptionDict(description)
-
- if not self.silent:
- sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
- sys.stdout.flush()
- cnt = cnt + 1
-
- try:
- if self.detectBranches:
- branches = self.splitFilesIntoBranches(description)
- for branch in branches.keys():
- ## HACK --hwn
- branchPrefix = self.depotPaths[0] + branch + "/"
-
- parent = ""
-
- filesForCommit = branches[branch]
-
- if self.verbose:
- print "branch is %s" % branch
-
- self.updatedBranches.add(branch)
-
- if branch not in self.createdBranches:
- self.createdBranches.add(branch)
- parent = self.knownBranches[branch]
- if parent == branch:
- parent = ""
- elif self.verbose:
- print "parent determined through known branches: %s" % parent
-
- # main branch? use master
- if branch == "main":
- branch = "master"
- else:
-
- ## FIXME
- branch = self.projectName + branch
-
- if parent == "main":
- parent = "master"
- elif len(parent) > 0:
- ## FIXME
- parent = self.projectName + parent
-
- branch = self.refPrefix + branch
- if len(parent) > 0:
- parent = self.refPrefix + parent
-
- if self.verbose:
- print "looking for initial parent for %s; current parent is %s" % (branch, parent)
-
- if len(parent) == 0 and branch in self.initialParents:
- parent = self.initialParents[branch]
- del self.initialParents[branch]
-
- self.commit(description, filesForCommit, branch, [branchPrefix], parent)
- else:
- files = self.extractFilesFromCommit(description)
- self.commit(description, files, self.branch, self.depotPaths,
- self.initialParent)
- self.initialParent = ""
- except IOError:
- print self.gitError.read()
- sys.exit(1)
+ self.importChanges(changes)
if not self.silent:
print ""
@@ -1419,7 +1499,6 @@ class P4Sync(Command):
sys.stdout.write("%s " % b)
sys.stdout.write("\n")
-
self.gitStream.close()
if importProcess.wait() != 0:
die("fast-import failed: %s" % self.gitError.read())
@@ -1440,6 +1519,9 @@ class P4Rebase(Command):
sync = P4Sync()
sync.run([])
+ return self.rebase()
+
+ def rebase(self):
[upstream, settings] = findUpstreamBranchPoint()
if len(upstream) == 0:
die("Cannot find upstream branchpoint for rebase")
diff --git a/git-rebase.sh b/git-rebase.sh
index 3bd66b0a04..c9942f2400 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -216,9 +216,11 @@ do
-v|--verbose)
verbose=t
;;
+ --whitespace=*)
+ git_am_opt="$git_am_opt $1"
+ ;;
-C*)
- git_am_opt=$1
- shift
+ git_am_opt="$git_am_opt $1"
;;
-*)
usage
diff --git a/git-send-email.perl b/git-send-email.perl
index e0b7d1245e..dd7560b180 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -73,9 +73,18 @@ Options:
--signed-off-cc Automatically add email addresses that appear in
Signed-off-by: or Cc: lines to the cc: list. Defaults to on.
+ --identity The configuration identity, a subsection to prioritise over
+ the default section.
+
--smtp-server If set, specifies the outgoing SMTP server to use.
Defaults to localhost.
+ --smtp-user The username for SMTP-AUTH.
+
+ --smtp-pass The password for SMTP-AUTH.
+
+ --smtp-ssl If set, connects to the SMTP server using SSL.
+
--suppress-from Suppress sending emails to yourself if your address
appears in a From: line. Defaults to off.
@@ -145,7 +154,6 @@ my $compose_filename = ".msg.$$";
my (@to,@cc,@initial_cc,@bcclist,@xh,
$initial_reply_to,$initial_subject,@files,$author,$sender,$compose,$time);
-my $smtp_server;
my $envelope_sender;
# Example reply to:
@@ -164,24 +172,26 @@ my ($quiet, $dry_run) = (0, 0);
# Variables with corresponding config settings
my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc, $cc_cmd);
+my ($smtp_server, $smtp_authuser, $smtp_authpass, $smtp_ssl);
+my ($identity, $aliasfiletype, @alias_files);
-my %config_settings = (
+my %config_bool_settings = (
"thread" => [\$thread, 1],
"chainreplyto" => [\$chain_reply_to, 1],
"suppressfrom" => [\$suppress_from, 0],
"signedoffcc" => [\$signed_off_cc, 1],
- "cccmd" => [\$cc_cmd, ""],
+ "smtpssl" => [\$smtp_ssl, 0],
);
-foreach my $setting (keys %config_settings) {
- my $config = $repo->config_bool("sendemail.$setting");
- ${$config_settings{$setting}->[0]} = (defined $config) ? $config : $config_settings{$setting}->[1];
-}
-
-@bcclist = $repo->config('sendemail.bcc');
-if (!@bcclist or !$bcclist[0]) {
- @bcclist = ();
-}
+my %config_settings = (
+ "smtpserver" => \$smtp_server,
+ "smtpuser" => \$smtp_authuser,
+ "smtppass" => \$smtp_authpass,
+ "cccmd" => \$cc_cmd,
+ "aliasfiletype" => \$aliasfiletype,
+ "bcc" => \@bcclist,
+ "aliasesfile" => \@alias_files,
+);
# Begin by accumulating all the variables (defined above), that we will end up
# needing, first, from the command line:
@@ -194,6 +204,10 @@ my $rc = GetOptions("sender|from=s" => \$sender,
"bcc=s" => \@bcclist,
"chain-reply-to!" => \$chain_reply_to,
"smtp-server=s" => \$smtp_server,
+ "smtp-user=s" => \$smtp_authuser,
+ "smtp-pass=s" => \$smtp_authpass,
+ "smtp-ssl!" => \$smtp_ssl,
+ "identity=s" => \$identity,
"compose" => \$compose,
"quiet" => \$quiet,
"cc-cmd=s" => \$cc_cmd,
@@ -208,6 +222,43 @@ unless ($rc) {
usage();
}
+# Now, let's fill any that aren't set in with defaults:
+
+sub read_config {
+ my ($prefix) = @_;
+
+ foreach my $setting (keys %config_bool_settings) {
+ my $target = $config_bool_settings{$setting}->[0];
+ $$target = $repo->config_bool("$prefix.$setting") unless (defined $$target);
+ }
+
+ foreach my $setting (keys %config_settings) {
+ my $target = $config_settings{$setting};
+ if (ref($target) eq "ARRAY") {
+ unless (@$target) {
+ my @values = $repo->config("$prefix.$setting");
+ @$target = @values if (@values && defined $values[0]);
+ }
+ }
+ else {
+ $$target = $repo->config("$prefix.$setting") unless (defined $$target);
+ }
+ }
+}
+
+# read configuration from [sendemail "$identity"], fall back on [sendemail]
+$identity = $repo->config("sendemail.identity") unless (defined $identity);
+read_config("sendemail.$identity") if (defined $identity);
+read_config("sendemail");
+
+# fall back on builtin bool defaults
+foreach my $setting (values %config_bool_settings) {
+ ${$setting->[0]} = $setting->[1] unless (defined (${$setting->[0]}));
+}
+
+my ($repoauthor) = $repo->ident_person('author');
+my ($repocommitter) = $repo->ident_person('committer');
+
# Verify the user input
foreach my $entry (@to) {
@@ -222,14 +273,7 @@ foreach my $entry (@bcclist) {
die "Comma in --bcclist entry: $entry'\n" unless $entry !~ m/,/;
}
-# Now, let's fill any that aren't set in with defaults:
-
-my ($repoauthor) = $repo->ident_person('author');
-my ($repocommitter) = $repo->ident_person('committer');
-
my %aliases;
-my @alias_files = $repo->config('sendemail.aliasesfile');
-my $aliasfiletype = $repo->config('sendemail.aliasfiletype');
my %parse_alias = (
# multiline formats can be supported in the future
mutt => sub { my $fh = shift; while (<$fh>) {
@@ -320,10 +364,7 @@ if ($thread && !defined $initial_reply_to && $prompting) {
$initial_reply_to =~ s/(^\s+|\s+$)//g;
}
-if (!$smtp_server) {
- $smtp_server = $repo->config('sendemail.smtpserver');
-}
-if (!$smtp_server) {
+if (!defined $smtp_server) {
foreach (qw( /usr/sbin/sendmail /usr/lib/sendmail )) {
if (-x $_) {
$smtp_server = $_;
@@ -553,8 +594,16 @@ X-Mailer: git-send-email $gitversion
print $sm "$header\n$message";
close $sm or die $?;
} else {
- require Net::SMTP;
- $smtp ||= Net::SMTP->new( $smtp_server );
+ if ($smtp_ssl) {
+ require Net::SMTP::SSL;
+ $smtp ||= Net::SMTP::SSL->new( $smtp_server, Port => 465 );
+ }
+ else {
+ require Net::SMTP;
+ $smtp ||= Net::SMTP->new( $smtp_server );
+ }
+ $smtp->auth( $smtp_authuser, $smtp_authpass )
+ or die $smtp->message if (defined $smtp_authuser);
$smtp->mail( $raw_from ) or die $smtp->message;
$smtp->to( @recipients ) or die $smtp->message;
$smtp->data or die $smtp->message;
@@ -661,7 +710,7 @@ foreach my $t (@files) {
}
close F;
- if ($cc_cmd ne "") {
+ if (defined $cc_cmd) {
open(F, "$cc_cmd $t |")
or die "(cc-cmd) Could not execute '$cc_cmd'";
while(<F>) {
diff --git a/refs.c b/refs.c
index 09a2c87fc2..7fb3350789 100644
--- a/refs.c
+++ b/refs.c
@@ -1455,3 +1455,30 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)
{
return do_for_each_reflog("", fn, cb_data);
}
+
+int update_ref(const char *action, const char *refname,
+ const unsigned char *sha1, const unsigned char *oldval,
+ int flags, enum action_on_err onerr)
+{
+ static struct ref_lock *lock;
+ lock = lock_any_ref_for_update(refname, oldval, flags);
+ if (!lock) {
+ const char *str = "Cannot lock the ref '%s'.";
+ switch (onerr) {
+ case MSG_ON_ERR: error(str, refname); break;
+ case DIE_ON_ERR: die(str, refname); break;
+ case QUIET_ON_ERR: break;
+ }
+ return 1;
+ }
+ if (write_ref_sha1(lock, sha1, action) < 0) {
+ const char *str = "Cannot update the ref '%s'.";
+ switch (onerr) {
+ case MSG_ON_ERR: error(str, refname); break;
+ case DIE_ON_ERR: die(str, refname); break;
+ case QUIET_ON_ERR: break;
+ }
+ return 1;
+ }
+ return 0;
+}
diff --git a/refs.h b/refs.h
index f234eb76ba..6eb98a4caf 100644
--- a/refs.h
+++ b/refs.h
@@ -64,4 +64,10 @@ extern int rename_ref(const char *oldref, const char *newref, const char *logmsg
/** resolve ref in nested "gitlink" repository */
extern int resolve_gitlink_ref(const char *name, const char *refname, unsigned char *result);
+/** lock a ref and then write its file */
+enum action_on_err { MSG_ON_ERR, DIE_ON_ERR, QUIET_ON_ERR };
+int update_ref(const char *action, const char *refname,
+ const unsigned char *sha1, const unsigned char *oldval,
+ int flags, enum action_on_err onerr);
+
#endif /* REFS_H */
diff --git a/send-pack.c b/send-pack.c
index 9fc8a812f4..f74e66a8ba 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -307,20 +307,14 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha
rs.src = ref->name;
rs.dst = NULL;
if (!remote_find_tracking(remote, &rs)) {
- struct ref_lock *lock;
fprintf(stderr, " Also local %s\n", rs.dst);
if (will_delete_ref) {
if (delete_ref(rs.dst, NULL)) {
error("Failed to delete");
}
- } else {
- lock = lock_any_ref_for_update(rs.dst, NULL, 0);
- if (!lock)
- error("Failed to lock");
- else
- write_ref_sha1(lock, ref->new_sha1,
- "update by push");
- }
+ } else
+ update_ref("update by push", rs.dst,
+ ref->new_sha1, NULL, 0, 0);
free(rs.dst);
}
}