diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 2653388..0d6fcaa 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -9,7 +9,7 @@ git-push - Update remote refs along with associated objects
SYNOPSIS
--------
[verse]
-'git push' [--all | --mirror | --tags] [--dry-run] [--receive-pack=<git-receive-pack>]
+'git push' [--all | --mirror | --tags] [--current] [--dry-run] [--receive-pack=<git-receive-pack>]
[--repo=<repository>] [-f | --force] [-v | --verbose]
[<repository> <refspec>...]
@@ -71,6 +71,15 @@ nor in any Push line of the corresponding remotes file---see below).
Instead of naming each ref to push, specifies that all
refs under `$GIT_DIR/refs/heads/` be pushed.
+--current::
+ Independent of the other options, restrict pushing to the current
+ HEAD.
+
+ Refspecs given in the configuration is still used to find the
+ destination name of the current branch. However, this option
+ cannot be specified if an explicit refspec is given on the
+ command line, because it would be useless and possibly confusing.
+
--mirror::
Instead of naming each ref to push, specifies that all
refs under `$GIT_DIR/refs/` (which includes but is not
diff --git a/builtin-push.c b/builtin-push.c
index 0a0297f..8921d53 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -10,7 +10,7 @@
#include "parse-options.h"
static const char * const push_usage[] = {
- "git push [--all | --mirror] [--dry-run] [--porcelain] [--tags] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [-v] [<repository> <refspec>...]",
+ "git push [--all | --mirror] [--current] [--dry-run] [--porcelain] [--tags] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [-v] [<repository> <refspec>...]",
NULL,
};
@@ -199,6 +199,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
OPT_BIT( 0 , "mirror", &flags, "mirror all refs",
(TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
OPT_BOOLEAN( 0 , "tags", &tags, "push tags"),
+ OPT_BIT( 0 , "current", &flags, "push current HEAD only", TRANSPORT_PUSH_CURRENT),
OPT_BIT( 0 , "dry-run", &flags, "dry run", TRANSPORT_PUSH_DRY_RUN),
OPT_BIT( 0, "porcelain", &flags, "machine-readable output", TRANSPORT_PUSH_PORCELAIN),
OPT_BIT('f', "force", &flags, "force updates", TRANSPORT_PUSH_FORCE),@@ -210,6 +211,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, push_usage, 0);
+ if ((argc > 1 || tags) && (flags & TRANSPORT_PUSH_CURRENT))
+ return error ("Cannot give --current together with --tags or a refspec.");
if (tags)
add_refspec("refs/tags/*");
diff --git a/http-push.c b/http-push.c
index 00e83dc..9c93e91 100644
--- a/http-push.c
+++ b/http-push.c
@@ -14,7 +14,7 @@
#include <expat.h>
static const char http_push_usage[] =
-"git http-push [--all] [--dry-run] [--force] [--verbose] <remote> [<head>...]\n";
+"git http-push [--all] [--current] [--dry-run] [--force] [--verbose] <remote> [<head>...]\n";
#ifndef XML_STATUS_OK
enum XML_Status {@@ -75,7 +75,7 @@ static int aborted;
static signed char remote_dir_exists[256];
static int push_verbosely;
-static int push_all = MATCH_REFS_NONE;
+static int match_flags = MATCH_REFS_NONE;
static int force_all;
static int dry_run;
@@ -1802,7 +1802,11 @@ int main(int argc, char **argv)
if (*arg == '-') {
if (!strcmp(arg, "--all")) {
- push_all = MATCH_REFS_ALL;
+ match_flags |= MATCH_REFS_ALL;
+ continue;
+ }
+ if (!strcmp(arg, "--current")) {
+ match_flags |= MATCH_REFS_HEAD_ONLY;
continue;
}
if (!strcmp(arg, "--force")) {@@ -1904,7 +1908,17 @@ int main(int argc, char **argv)
fetch_indices();
/* Get a list of all local and remote heads to validate refspecs */
- local_refs = get_local_heads();
+ if (match_flags && MATCH_REFS_HEAD_ONLY) {
+ local_refs = get_current_head();
+ if (!local_refs) {
+ fprintf(stderr, "--current specified with no current branch.\n");
+ rc = -1;
+ goto cleanup;
+ }
+ }
+ else
+ local_refs = get_local_heads();
+
fprintf(stderr, "Fetching remote heads...\n");
get_dav_remote_heads();
run_request_queue();@@ -1919,7 +1933,7 @@ int main(int argc, char **argv)
/* match them up */
if (match_refs(local_refs, &remote_refs,
- nr_refspec, (const char **) refspec, push_all)) {
+ nr_refspec, (const char **) refspec, match_flags)) {
rc = -1;
goto cleanup;
}@@ -2005,7 +2019,8 @@ int main(int argc, char **argv)
old_sha1_hex = NULL;
commit_argv[1] = "--objects";
commit_argv[2] = new_sha1_hex;
- if (!push_all && !is_null_sha1(ref->old_sha1)) {
+ if (!(match_flags & MATCH_REFS_ALL)
+ && !is_null_sha1(ref->old_sha1)) {
old_sha1_hex = xmalloc(42);
sprintf(old_sha1_hex, "^%s",
sha1_to_hex(ref->old_sha1));diff --git a/remote.c b/remote.c
index c3ada2d..b5bf9a6 100644
--- a/remote.c
+++ b/remote.c
@@ -990,7 +990,7 @@ static char *guess_ref(const char *name, struct ref *peer)
static int match_explicit(struct ref *src, struct ref *dst,
struct ref ***dst_tail,
- struct refspec *rs)
+ struct refspec *rs, int head_only)
{
struct ref *matched_src, *matched_dst;
int copy_src;@@ -1007,14 +1007,26 @@ static int match_explicit(struct ref *src, struct ref *dst,
copy_src = 1;
break;
case 0:
- /* The source could be in the get_sha1() format
+ /*
+ * The source could be in the get_sha1() format
* not a reference name. :refs/other is a
* way to delete 'other' ref at the remote end.
+ * This case however could cause unwanted references
+ * to be stored in matched_dst->peer_ref for --current.
+ * In that case, all we can/want handle is HEAD.
*/
- matched_src = try_explicit_object_name(rs->src);
+ if (head_only) {
+ assert (!src->next);
+ if (strcmp(rs->src, "HEAD"))
+ return 0;
+ matched_src = src;
+ copy_src = 1;
+ } else {
+ matched_src = try_explicit_object_name(rs->src);
+ copy_src = 0;
+ }
if (!matched_src)
return error("src refspec %s does not match any.", rs->src);
- copy_src = 0;
break;
default:
return error("src refspec %s matches more than one.", rs->src);@@ -1068,11 +1080,11 @@ static int match_explicit(struct ref *src, struct ref *dst,
static int match_explicit_refs(struct ref *src, struct ref *dst,
struct ref ***dst_tail, struct refspec *rs,
- int rs_nr)
+ int rs_nr, int head_only)
{
int i, errs;
for (i = errs = 0; i < rs_nr; i++)
- errs += match_explicit(src, dst, dst_tail, &rs[i]);
+ errs += match_explicit(src, dst, dst_tail, &rs[i], head_only);
return errs;
}
@@ -1118,6 +1130,7 @@ int match_refs(struct ref *src, struct ref **dst,
struct refspec *rs;
int send_all = flags & MATCH_REFS_ALL;
int send_mirror = flags & MATCH_REFS_MIRROR;
+ int head_only = flags & MATCH_REFS_HEAD_ONLY;
int errs;
static const char *default_refspec[] = { ":", NULL };
struct ref **dst_tail = tail_ref(dst);@@ -1127,7 +1140,8 @@ int match_refs(struct ref *src, struct ref **dst,
refspec = default_refspec;
}
rs = parse_push_refspec(nr_refspec, (const char **) refspec);
- errs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec);
+ errs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec,
+ head_only);
/* pick the remainder */
for ( ; src; src = src->next) {@@ -1527,6 +1541,18 @@ struct ref *get_local_heads(void)
return local_refs;
}
+struct ref *get_current_head(void)
+{
+ struct ref *local_refs = NULL, **local_tail = &local_refs;
+ struct branch *branch = branch_get(NULL);
+ unsigned char sha1[20];
+ if (branch) {
+ get_sha1(branch->refname, sha1);
+ one_local_ref(branch->refname, sha1, 0, &local_tail);
+ }
+ return local_refs;
+}
+
struct ref *guess_remote_head(const struct ref *head,
const struct ref *refs,
int all)diff --git a/remote.h b/remote.h
index 5db8420..8e5d5b4 100644
--- a/remote.h
+++ b/remote.h
@@ -137,6 +137,7 @@ enum match_refs_flags {
MATCH_REFS_NONE = 0,
MATCH_REFS_ALL = (1 << 0),
MATCH_REFS_MIRROR = (1 << 1),
+ MATCH_REFS_HEAD_ONLY = (1 << 2),
};
/* Reporting of tracking info */@@ -144,6 +145,7 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs);
int format_tracking_info(struct branch *branch, struct strbuf *sb);
struct ref *get_local_heads(void);
+struct ref *get_current_head(void);
/*
* Find refs from a list which are likely to be pointed to by the given HEAD
* ref. If 'all' is false, returns the most likely ref; otherwise, returns a
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 2d2633f..3333ce9 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -586,4 +586,56 @@ test_expect_success 'push with branches containing #' '
git checkout master
'
+test_expect_success 'push --current fails on empty repository' '
+ git init &&
+ mkdir b.git &&
+ (cd b.git && git init --bare) &&
+ echo a > b &&
+ git add b &&
+ git commit -m a &&
+ git checkout -b branch &&
+ echo bb > b &&
+ git add b &&
+ git commit -m branch &&
+ git checkout master &&
+ ! git push --current b.git
+'
+
+test_expect_success 'push --current succeeds when push is configured' '
+ git config remote.bremote.url b.git &&
+ git config remote.bremote.push refs/heads/master:refs/heads/master &&
+ git push --current bremote &&
+ test `git rev-parse master` = `cd b.git && git rev-parse master`
+'
+
+test_expect_success 'push --current does nothing when current branch does not exist' '
+ git checkout branch &&
+ git push --current b.git 2>&1 | grep "Everything up-to-date" &&
+ git push b.git branch
+'
+
+test_expect_success 'push --current does not push other branches' '
+ git checkout master &&
+ echo aa > b &&
+ git commit -m master2 b &&
+ git checkout branch &&
+ git push --current b.git 2>&1 | grep "Everything up-to-date" &&
+ test `git rev-parse master^` = `cd b.git && git rev-parse master`
+'
+
+test_expect_success 'push --current does update the current branches' '
+ echo cc > b &&
+ git commit -m branch2 b &&
+ git checkout master &&
+ git push --current b.git &&
+ test `git rev-parse master` = `cd b.git && git rev-parse master` &&
+ test `git rev-parse branch^` = `cd b.git && git rev-parse branch`
+'
+
+test_expect_success 'push --current respects configuration' '
+ git checkout branch &&
+ git push --current bremote 2>&1 | grep "Everything up-to-date" &&
+ test `git rev-parse branch^` = `cd b.git && git rev-parse branch`
+'
+
test_done
diff --git a/transport.c b/transport.c
index de0d587..c7b6aaa 100644
--- a/transport.c
+++ b/transport.c
@@ -327,6 +327,11 @@ static int rsync_transport_push(struct transport *transport,
if (flags & TRANSPORT_PUSH_ALL) {
if (for_each_ref(write_one_ref, &temp_dir))
return -1;
+ } else if (flags & TRANSPORT_PUSH_CURRENT) {
+ struct branch *branch = branch_get(NULL);
+ unsigned char sha1[20];
+ get_sha1(branch->name, sha1);
+ write_one_ref(branch->name, sha1, 0, &temp_dir);
} else if (write_refs_to_temp_dir(&temp_dir, refspec_nr, refspec))
return -1;
@@ -406,6 +411,8 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons
argc = 1;
if (flags & TRANSPORT_PUSH_ALL)
argv[argc++] = "--all";
+ if (flags & TRANSPORT_PUSH_CURRENT)
+ argv[argc++] = "--current";
if (flags & TRANSPORT_PUSH_FORCE)
argv[argc++] = "--force";
if (flags & TRANSPORT_PUSH_DRY_RUN)
@@ -1001,12 +1008,19 @@ int transport_push(struct transport *transport,
{
verify_remote_names(refspec_nr, refspec);
+ if (flags & TRANSPORT_PUSH_CURRENT) {
+ struct branch *branch = branch_get(NULL);
+ if (!branch)
+ return error("Tried to push current branch, but there "
+ "is no current branch!");
+ }
+
if (transport->push)
return transport->push(transport, refspec_nr, refspec, flags);
if (transport->push_refs) {
struct ref *remote_refs =
transport->get_refs_list(transport, 1);
- struct ref *local_refs = get_local_heads();
+ struct ref *local_refs;
int match_flags = MATCH_REFS_NONE;
int verbose = flags & TRANSPORT_PUSH_VERBOSE;
int porcelain = flags & TRANSPORT_PUSH_PORCELAIN;@@ -1017,6 +1031,12 @@ int transport_push(struct transport *transport,
if (flags & TRANSPORT_PUSH_MIRROR)
match_flags |= MATCH_REFS_MIRROR;
+ if (flags & TRANSPORT_PUSH_CURRENT) {
+ local_refs = get_current_head();
+ match_flags |= MATCH_REFS_HEAD_ONLY;
+ } else
+ local_refs = get_local_heads();
+
if (match_refs(local_refs, &remote_refs,
refspec_nr, refspec, match_flags)) {
return -1;diff --git a/transport.h b/transport.h
index 51b5397..62aa243 100644
--- a/transport.h
+++ b/transport.h
@@ -36,6 +36,7 @@ struct transport {
#define TRANSPORT_PUSH_MIRROR 8
#define TRANSPORT_PUSH_VERBOSE 16
#define TRANSPORT_PUSH_PORCELAIN 32
+#define TRANSPORT_PUSH_CURRENT 64
/* Returns a transport suitable for the url */
struct transport *transport_get(struct remote *, const char *);--
1.6.2.5