In a triangular workflow, each branch may have two distinct
points of interest: the @{upstream} that you normally pull
from, and the destination that you normally push to. There
isn't a shorthand for the latter, but it's useful to have.

For instance, you may want to know which commits you haven't
pushed yet:

  git log @{push}..

Or as a more complicated example, imagine that you normally
pull changes from origin/master (which you set as your
@{upstream}), and push changes to your own personal fork
(e.g., as myfork/topic). You may push to your fork from
multiple machines, requiring you to integrate the changes
from the push destination, rather than upstream. With this
patch, you can just do:

  git rebase @{push}

rather than typing out the full name.

The heavy lifting is all done by branch_get_push; here we
just wire it up to the "@{push}" syntax.

Signed-off-by: Jeff King <p...@peff.net>
---
 Documentation/revisions.txt | 25 ++++++++++++++++++
 sha1_name.c                 | 14 +++++++++-
 t/t1514-rev-parse-push.sh   | 63 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 101 insertions(+), 1 deletion(-)
 create mode 100755 t/t1514-rev-parse-push.sh

diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 0796118..d85e303 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -98,6 +98,31 @@ some output processing may assume ref names in UTF-8.
   `branch.<name>.merge`).  A missing branchname defaults to the
   current one.
 
+'<branchname>@\{push\}', e.g. 'master@\{push\}', '@\{push\}'::
+  The suffix '@\{push}' reports the branch "where we would push to" if
+  `git push` were run while `branchname` was checked out (or the current
+  'HEAD' if no branchname is specified). Since our push destination is
+  in a remote repository, of course, we report the local tracking branch
+  that corresponds to that branch (i.e., something in 'refs/remotes/').
++
+Here's an example to make it more clear:
++
+------------------------------
+$ git config push.default current
+$ git config remote.pushdefault myfork
+$ git checkout -b mybranch origin/master
+
+$ git rev-parse --symbolic-full-name @{upstream}
+refs/remotes/origin/master
+
+$ git rev-parse --symbolic-full-name @{push}
+refs/remotes/myfork/mybranch
+------------------------------
++
+Note in the example that we set up a triangular workflow, where we pull
+from one location and push to another. In a non-triangular workflow,
+'@\{push}' is the same as '@\{upstream}', and there is no need for it.
+
 '<rev>{caret}', e.g. 'HEAD{caret}, v1.5.1{caret}0'::
   A suffix '{caret}' to a revision parameter means the first parent of
   that commit object.  '{caret}<n>' means the <n>th parent (i.e.
diff --git a/sha1_name.c b/sha1_name.c
index 506e0c9..1096943 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -435,6 +435,12 @@ static inline int upstream_mark(const char *string, int 
len)
        return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
 }
 
+static inline int push_mark(const char *string, int len)
+{
+       const char *suffix[] = { "@{push}" };
+       return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
+}
+
 static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned 
lookup_flags);
 static int interpret_nth_prior_checkout(const char *name, int namelen, struct 
strbuf *buf);
 
@@ -482,7 +488,8 @@ static int get_sha1_basic(const char *str, int len, 
unsigned char *sha1,
                                        nth_prior = 1;
                                        continue;
                                }
-                               if (!upstream_mark(str + at, len - at)) {
+                               if (!upstream_mark(str + at, len - at) &&
+                                   !push_mark(str + at, len - at)) {
                                        reflog_len = (len-1) - (at+2);
                                        len = at;
                                }
@@ -1145,6 +1152,11 @@ int interpret_branch_name(const char *name, int namelen, 
struct strbuf *buf)
                                            upstream_mark, branch_get_upstream);
                if (len > 0)
                        return len;
+
+               len = interpret_branch_mark(name, namelen, at - name, buf,
+                                           push_mark, branch_get_push);
+               if (len > 0)
+                       return len;
        }
 
        return -1;
diff --git a/t/t1514-rev-parse-push.sh b/t/t1514-rev-parse-push.sh
new file mode 100755
index 0000000..7214f5b
--- /dev/null
+++ b/t/t1514-rev-parse-push.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+test_description='test <branch>@{push} syntax'
+. ./test-lib.sh
+
+resolve () {
+       echo "$2" >expect &&
+       git rev-parse --symbolic-full-name "$1" >actual &&
+       test_cmp expect actual
+}
+
+test_expect_success 'setup' '
+       git init --bare parent.git &&
+       git init --bare other.git &&
+       git remote add origin parent.git &&
+       git remote add other other.git &&
+       test_commit base &&
+       git push origin HEAD &&
+       git branch --set-upstream-to=origin/master master &&
+       git branch --track topic origin/master &&
+       git push origin topic &&
+       git push other topic
+'
+
+test_expect_success '@{push} with default=nothing' '
+       test_config push.default nothing &&
+       test_must_fail git rev-parse master@{push}
+'
+
+test_expect_success '@{push} with default=simple' '
+       test_config push.default simple &&
+       resolve master@{push} refs/remotes/origin/master
+'
+
+test_expect_success 'triangular @{push} fails with default=simple' '
+       test_config push.default simple &&
+       test_must_fail git rev-parse topic@{push}
+'
+
+test_expect_success '@{push} with default=current' '
+       test_config push.default current &&
+       resolve topic@{push} refs/remotes/origin/topic
+'
+
+test_expect_success '@{push} with default=matching' '
+       test_config push.default matching &&
+       resolve topic@{push} refs/remotes/origin/topic
+'
+
+test_expect_success '@{push} with pushremote defined' '
+       test_config push.default current &&
+       test_config branch.topic.pushremote other &&
+       resolve topic@{push} refs/remotes/other/topic
+'
+
+test_expect_success '@{push} with push refspecs' '
+       test_config push.default nothing &&
+       test_config remote.origin.push refs/heads/*:refs/heads/magic/* &&
+       git push &&
+       resolve topic@{push} refs/remotes/origin/magic/topic
+'
+
+test_done
-- 
2.4.1.528.g00591e3

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to