summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelipe Contreras <felipe.contreras@gmail.com>2014-04-20 14:45:01 -0500
committerJunio C Hamano <gitster@pobox.com>2014-04-21 11:46:16 -0700
commitae0455a56da8bf06f3fd44fbd918ade060c832d8 (patch)
tree5d6c6d058c19ff58f2323612e0a961da9f65b6a2
parentbcf0d5482a6222aca7c858a4a17236b593d6c89d (diff)
downloadgit-ae0455a56da8bf06f3fd44fbd918ade060c832d8.tar.gz
branch: add --set-publish-to option
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
-rw-r--r--Documentation/git-branch.txt11
-rw-r--r--branch.c44
-rw-r--r--branch.h2
-rw-r--r--builtin/branch.c57
-rwxr-xr-xt/t3200-branch.sh76
5 files changed, 185 insertions, 5 deletions
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 311b33674e..914fd6220d 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -14,7 +14,9 @@ SYNOPSIS
[(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]
'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
+'git branch' (--set-publish-to=<publish> | -p <publish>) [<branchname>]
'git branch' --unset-upstream [<branchname>]
+'git branch' --unset-publish [<branchname>]
'git branch' (-m | -M) [<oldbranch>] <newbranch>
'git branch' (-d | -D) [-r] <branchname>...
'git branch' --edit-description [<branchname>]
@@ -191,6 +193,15 @@ start-point is either a local or remote-tracking branch.
Remove the upstream information for <branchname>. If no branch
is specified it defaults to the current branch.
+-p <publish>::
+--set-publish-to=<publish>::
+ Set up <branchname>'s publish tracking information. If no
+ <branchname> is specified, then it defaults to the current branch.
+
+--unset-publish::
+ Remove the publish information for <branchname>. If no branch
+ is specified it defaults to the current branch.
+
--edit-description::
Open an editor and edit the text to explain what the branch is
for, to be used by various other commands (e.g. `request-pull`).
diff --git a/branch.c b/branch.c
index 660097bc29..cb56c0a35f 100644
--- a/branch.c
+++ b/branch.c
@@ -140,6 +140,50 @@ static int setup_tracking(const char *new_ref, const char *orig_ref,
return 0;
}
+void install_branch_publish(const char *name, const char *remote, const char *remote_ref)
+{
+ struct strbuf key = STRBUF_INIT;
+
+ if (!remote && !strcmp(name, remote_ref + 11) &&
+ starts_with(remote_ref, "refs/heads")) {
+ warning(_("Not setting branch %s as its own publish branch."), name);
+ return;
+ }
+
+ strbuf_addf(&key, "branch.%s.pushremote", name);
+ git_config_set(key.buf, remote ? remote : ".");
+
+ strbuf_reset(&key);
+ strbuf_addf(&key, "branch.%s.push", name);
+ git_config_set(key.buf, remote_ref);
+
+ strbuf_release(&key);
+}
+
+int setup_publish(const char *name, const char *ref)
+{
+ struct tracking tracking;
+ const char *remote, *remote_ref;
+
+ memset(&tracking, 0, sizeof(tracking));
+ tracking.spec.dst = (char *)ref;
+ if (for_each_remote(find_tracked_branch, &tracking))
+ return 1;
+
+ if (tracking.matches > 1)
+ return error(_("Not tracking: ambiguous information for ref %s"),
+ ref);
+
+ remote = tracking.remote;
+ remote_ref = tracking.src ? tracking.src : ref;
+
+ install_branch_publish(name, remote, remote_ref);
+
+ free(tracking.src);
+
+ return 0;
+}
+
struct branch_desc_cb {
const char *config_name;
const char *value;
diff --git a/branch.h b/branch.h
index 64173abf4d..c9b6aa94dd 100644
--- a/branch.h
+++ b/branch.h
@@ -51,5 +51,7 @@ extern void install_branch_config(int flag, const char *local, const char *origi
* Read branch description
*/
extern int read_branch_desc(struct strbuf *, const char *branch_name);
+extern int setup_publish(const char *name, const char *ref);
+extern void install_branch_publish(const char *name, const char *remote, const char *remote_ref);
#endif
diff --git a/builtin/branch.c b/builtin/branch.c
index 652b1d2d14..a101cf4c70 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -793,8 +793,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
int delete = 0, rename = 0, force_create = 0, list = 0;
int verbose = 0, abbrev = -1, detached = 0;
int reflog = 0, edit_description = 0;
- int quiet = 0, unset_upstream = 0;
- const char *new_upstream = NULL;
+ int quiet = 0, unset_upstream = 0, unset_publish = 0;
+ const char *new_upstream = NULL, *publish = NULL;
enum branch_track track;
int kinds = REF_LOCAL_BRANCH;
struct commit_list *with_commit = NULL;
@@ -809,7 +809,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_SET_INT( 0, "set-upstream", &track, N_("change upstream info"),
BRANCH_TRACK_OVERRIDE),
OPT_STRING('u', "set-upstream-to", &new_upstream, "upstream", "change the upstream info"),
+ OPT_STRING('p', "set-publish-to", &publish, "publish", "change the publish info"),
OPT_BOOL(0, "unset-upstream", &unset_upstream, "Unset the upstream info"),
+ OPT_BOOL(0, "unset-publish", &unset_publish, "Unset the publish info"),
OPT__COLOR(&branch_use_color, N_("use colored output")),
OPT_SET_INT('r', "remotes", &kinds, N_("act on remote-tracking branches"),
REF_REMOTE_BRANCH),
@@ -878,14 +880,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
0);
- if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0)
+ if (!delete && !rename && !edit_description && !new_upstream && !publish &&
+ !unset_upstream && !unset_publish && argc == 0)
list = 1;
if (with_commit || merge_filter != NO_FILTER)
list = 1;
- if (!!delete + !!rename + !!force_create + !!new_upstream +
- list + unset_upstream > 1)
+ if (!!delete + !!rename + !!force_create + !!new_upstream + !!publish +
+ list + unset_upstream + unset_publish > 1)
usage_with_options(builtin_branch_usage, options);
if (abbrev == -1)
@@ -990,6 +993,50 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
strbuf_addf(&buf, "branch.%s.merge", branch->name);
git_config_set_multivar(buf.buf, NULL, NULL, 1);
strbuf_release(&buf);
+ } else if (publish) {
+ struct branch *branch = branch_get(argv[0]);
+ char *real_ref = NULL;
+ unsigned char sha1[20];
+
+ if (argc > 1)
+ die(_("too many branches to set new publish branch"));
+
+ if (!branch) {
+ if (!argc || !strcmp(argv[0], "HEAD"))
+ die(_("could not set publish branch of HEAD when "
+ "it does not point to any branch."));
+ die(_("no such branch '%s'"), argv[0]);
+ }
+
+ if (!ref_exists(branch->refname))
+ die(_("branch '%s' does not exist"), branch->name);
+
+ if (dwim_ref(publish, strlen(publish), sha1, &real_ref) != 1 ||
+ setup_publish(branch->name, real_ref))
+ die(_("Cannot setup publish branch to '%s'."), publish);
+ } else if (unset_publish) {
+ struct branch *branch = branch_get(argv[0]);
+ struct strbuf buf = STRBUF_INIT;
+
+ if (argc > 1)
+ die(_("too many branches to unset publish branch"));
+
+ if (!branch) {
+ if (!argc || !strcmp(argv[0], "HEAD"))
+ die(_("could not unset publish branch of HEAD when "
+ "it does not point to any branch."));
+ die(_("no such branch '%s'"), argv[0]);
+ }
+
+ if (!branch->push_name)
+ die(_("Branch '%s' has no publish information"), branch->name);
+
+ strbuf_addf(&buf, "branch.%s.pushremote", branch->name);
+ git_config_set_multivar(buf.buf, NULL, NULL, 1);
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "branch.%s.push", branch->name);
+ git_config_set_multivar(buf.buf, NULL, NULL, 1);
+ strbuf_release(&buf);
} else if (argc > 0 && argc <= 2) {
struct branch *branch = branch_get(argv[0]);
int branch_existed = 0, remote_tracking = 0;
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index ac31b711f2..19408a1518 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -909,4 +909,80 @@ test_expect_success 'tracking with unexpected .fetch refspec' '
)
'
+test_expect_success '--set-publish-to fails on multiple branches' '
+ test_must_fail git branch --set-publish-to master a b c
+'
+
+test_expect_success '--set-publish-to fails on detached HEAD' '
+ test_when_finished "git checkout master" &&
+ git checkout master^{} &&
+ test_must_fail git branch --set-publish-to master
+'
+
+test_expect_success '--set-publish-to fails on a missing dst branch' '
+ test_must_fail git branch --set-publish-to master does-not-exist
+'
+
+test_expect_success '--set-publish-to fails on a missing src branch' '
+ test_must_fail git branch --set-publish-to does-not-exist master
+'
+
+test_expect_success '--set-publish-to fails on a non-ref' '
+ test_must_fail git branch --set-publish-to HEAD^{}
+'
+
+test_expect_success 'use --set-publish-to modify HEAD' '
+ git checkout master &&
+ test_config branch.master.pushremote foo &&
+ test_config branch.master.push foo &&
+ git branch -f test &&
+ git branch --set-publish-to test &&
+ test "$(git config branch.master.pushremote)" = "." &&
+ test "$(git config branch.master.push)" = "refs/heads/test"
+'
+
+test_expect_success 'use --set-publish-to modify a particular branch' '
+ git branch -f test &&
+ git branch -f test2 &&
+ git branch --set-publish-to test2 test &&
+ test "$(git config branch.test.pushremote)" = "." &&
+ test "$(git config branch.test.push)" = "refs/heads/test2"
+'
+
+test_expect_success '--unset-publish should fail if given a non-existent branch' '
+ test_must_fail git branch --unset-publish i-dont-exist
+'
+
+test_expect_success 'test --unset-publish on HEAD' '
+ git checkout master &&
+ git branch -f test &&
+ test_config branch.master.pushremote foo &&
+ test_config branch.master.push foo &&
+ git branch --set-publish-to test &&
+ git branch --unset-publish &&
+ test_must_fail git config branch.master.pushremote &&
+ test_must_fail git config branch.master.push &&
+ # fail for a branch without publish set
+ test_must_fail git branch --unset-publish
+'
+
+test_expect_success '--unset-publish should fail on multiple branches' '
+ test_must_fail git branch --unset-publish a b c
+'
+
+test_expect_success '--unset-publish should fail on detached HEAD' '
+ test_when_finished "git checkout -" &&
+ git checkout HEAD^{} &&
+ test_must_fail git branch --unset-publish
+'
+
+test_expect_success 'test --unset-publish on a particular branch' '
+ git branch -f test &&
+ git branch -f test2 &&
+ git branch --set-publish-to test2 test &&
+ git branch --unset-publish test &&
+ test_must_fail git config branch.test2.pushremote &&
+ test_must_fail git config branch.test2.push
+'
+
test_done