To track a upstream branch from a local branch we need to pass
<repository> and <refspec> to --set-upstream (in case of git push)
or to --set-upstream-to (in case of git branch). In most cases,
users track the upstream branch with the same name as the local
branch they are currently on. For example, users most of the time
do 'git push <repository> <current_branch_refspec>'.
So, it would be great if 'git push -u' by default do this. This
patch series address this. The patches of this patch-set set
some default values for <repository> and <refspec> if they are
not given. It first tries to get the value of <repository> from
'branch.<current_branch>.remote'. If not then it will set the
value of <repository> as 'origin'. <refspec>'s value would be
the short name of the current branch.
The first patch implements it for push command. However, before
moving to the 'git branch' part, it would be great to have
discussions about the proposed changes in this patch and whether
the current changes are the best way to address it or not.
Abhradeep Chakraborty (1):
push: make '-u' have default arguments
Documentation/git-push.txt | 6 +++++
builtin/push.c | 48 ++++++++++++++++++++++++++++----------
t/t5523-push-upstream.sh | 11 +++++++++
3 files changed, 53 insertions(+), 12 deletions(-)
--
2.17.1
For now, -u in 'push' command requires two arguments (<repository>
and <refspec>) to successfully track upstream branch. In most cases,
users want to set an upstream branch for the local branch they are
currently on and the short names of these two branches are same in
most of the cases. There are plenty of configurations to set default
branches for push but again users can't run argumentless pull, rebase
etc. So it will be good to have '-u' having default arguments.
This commit gives ability to '-u' to have default arguments. 'git push
-u' runs normally if <repository> and <refspec> are given. But
if those are not given then it tries to get the value of <repository>
from 'branch.<current_branch>.remote'. If not found, it sets 'origin'
as the value of <repository>. <refspec> would be the current branch's
short name.
However 'git push -u --all' work normally as before.
Signed-off-by: Abhradeep Chakraborty <redacted>
---
Documentation/git-push.txt | 6 +++++
builtin/push.c | 48 ++++++++++++++++++++++++++++----------
t/t5523-push-upstream.sh | 11 +++++++++
3 files changed, 53 insertions(+), 12 deletions(-)
@@ -375,6 +375,12 @@ Specifying `--no-force-if-includes` disables this behavior. upstream (tracking) reference, used by argument-less linkgit:git-pull[1] and other commands. For more information, see `branch.<name>.merge` in linkgit:git-config[1].+++If you use -u without any arguments (i.e. no <repository> and <refspec>),+it will first try to get the <repository> from current branch's remote+configuration (i.e. from `branch.<name>.remote`). If not found, it will set+`origin` as the value of <repository> and <refspec> will be the current+branch's refspec. --[no-]thin:: These options are passed to linkgit:git-send-pack[1]. A thin transfer
@@ -527,6 +527,25 @@ static int git_push_config(const char *k, const char *v, void *cb)returngit_default_config(k,v,NULL);}+staticstructremote*pushremote_get_remote(constchar*repo)+{+structremote*remote=pushremote_get(repo);+if(!remote){+if(repo)+die(_("bad repository '%s'"),repo);+die(_("No configured push destination.\n"+"Either specify the URL from the command-line or configure a remote repository using\n"+"\n"+" git remote add <name> <url>\n"+"\n"+"and then push using the remote name\n"+"\n"+" git push <name>\n"));+}++returnremote;+}+intcmd_push(intargc,constchar**argv,constchar*prefix){intflags=0;
@@ -603,23 +623,27 @@ int cmd_push(int argc, const char **argv, const char *prefix)if(tags)refspec_append(&rs,"refs/tags/*");+if((argc==0)&&(flags&TRANSPORT_PUSH_SET_UPSTREAM)&&!(flags&TRANSPORT_PUSH_ALL)){+structbranch*branch=branch_get(NULL);+if(branch){+argc+=2;+default_remote=pushremote_get_remote(repo);+argv[0]=default_remote->name;+argv[1]=branch->name;+}+}+if(argc>0){repo=argv[0];set_refspecs(argv+1,argc-1,repo);}-remote=pushremote_get(repo);-if(!remote){-if(repo)-die(_("bad repository '%s'"),repo);-die(_("No configured push destination.\n"-"Either specify the URL from the command-line or configure a remote repository using\n"-"\n"-" git remote add <name> <url>\n"-"\n"-"and then push using the remote name\n"-"\n"-" git push <name>\n"));+if(default_remote){+remote=default_remote;+default_remote=NULL;+}+else{+remote=pushremote_get_remote(repo);}if(remote->mirror)
From: Philip Oakley <hidden> Date: 2021-12-03 11:32:28
On 02/12/2021 14:43, Abhradeep Chakraborty wrote:
To track a upstream branch from a local branch we need to pass
<repository> and <refspec> to --set-upstream (in case of git push)
or to --set-upstream-to (in case of git branch). In most cases,
users track the upstream branch with the same name as the local
branch they are currently on. For example, users most of the time
do 'git push <repository> <current_branch_refspec>'.
So, it would be great if 'git push -u' by default do this. This
patch series address this. The patches of this patch-set set
some default values for <repository> and <refspec> if they are
not given. It first tries to get the value of <repository> from
'branch.<current_branch>.remote'. If not then it will set the
value of <repository> as 'origin'. <refspec>'s value would be
the short name of the current branch.
Can we protect the expectations of a user with a `pushDefault` setting?
If the user has one set, then the upstream won't be where they push in a
triangular repo workflow.
Philip
The first patch implements it for push command. However, before
moving to the 'git branch' part, it would be great to have
discussions about the proposed changes in this patch and whether
the current changes are the best way to address it or not.
Abhradeep Chakraborty (1):
push: make '-u' have default arguments
Documentation/git-push.txt | 6 +++++
builtin/push.c | 48 ++++++++++++++++++++++++++++----------
t/t5523-push-upstream.sh | 11 +++++++++
3 files changed, 53 insertions(+), 12 deletions(-)
Can we protect the expectations of a user with a `pushDefault` setting?
Are you talking about 'push.default'? If so, then I think, the proposed
change would not affect the working of 'push.default' (if the idea is
implemented in the right way). I am adding tests to be sure about it.
If the user has one set, then the upstream won't be where they push in a
triangular repo workflow.
Pardon me, I am unable to understand what you are trying to say. Could you
please explain a little bit?
Thanks.
From: Philip Oakley <hidden> Date: 2021-12-03 16:46:20
On 03/12/2021 16:03, Abhradeep Chakraborty wrote:
Philip Oakley wrote:
quoted
Can we protect the expectations of a user with a `pushDefault` setting?
Are you talking about 'push.default'? If so, then I think, the proposed
change would not affect the working of 'push.default' (if the idea is
implemented in the right way). I am adding tests to be sure about it.
quoted
If the user has one set, then the upstream won't be where they push in a
triangular repo workflow.
Pardon me, I am unable to understand what you are trying to say. Could you
please explain a little bit?
Thanks.
In my scenario I am tracking various upstream repositories, none of
which I have push permission for. This means I have set up a
`remote.pushDefault` [1] to the remote "my", which is mapped to my
GitHub repo where I can publish work (i.e. push).
So when I push, I am pushing to "my" remote, but when rebasing, the
upstream is not that destination, and in a collaboration environment,
may not even be the place I first forked from (e.g. the distinction
between 'git.git' [git], 'git-for-windows.git' [gfw], and Junio's repo
[gitster], all with the same root). I can then either send PRs (if
acceptable) or send patches (cover letter link to my publish repo).
In the case where a user has set their remote.pushDefault, then it's not
clear that there should be a default at all, though I maybe
misunderstanding the approach here.
Philip
[1]
https://git-scm.com/docs/git-config#Documentation/git-config.txt-remotepushDefault
Can we protect the expectations of a user with a `pushDefault` setting?
If the user has one set, then the upstream won't be where they push in a
triangular repo workflow.
Ohh, sorry! now I understand. You are talking about 'remote.pushDefault'.
I didn't think about it. Checking if the proposed change is affecting it.
Thanks for pointing out!
The changes regarding 'git push -u' in the previous patch version
were hurting the intention of using 'push.default'. This patch version
fixes that.
Argumentless 'git push -u' sets the default remote as '<repository>'.
'<refspec>' depends on the 'push.default' configuration. For
'push.default'=matching, it pushes refs to those branches that it
should and sets them as the upstream of their respective local
branches. For all other values of 'push.default', it uses the
current branch for the refspec.
'<repository>' value depends on 'branch.*.remote' and 'remote.pushDefault'
(if 'branch.*.remote' not found). If none of them are set then it
defaults to 'origin'.
Abhradeep Chakraborty (1):
push: make '-u' have default arguments
Documentation/git-push.txt | 10 ++++
builtin/push.c | 11 +++-
t/t5523-push-upstream.sh | 114 +++++++++++++++++++++++++++++++++++++
3 files changed, 133 insertions(+), 2 deletions(-)
--
2.17.1
"git push -u" (set-upstream) requires where to push to and what
to push. Often people push only the current branch to update
the branch of the same name at the 'origin' repository. For
them, it would be convenient if "git push -u" without repository
or refspec, defaulted to push and set upstream to the branch as
configured by the "push.default" setting, of the remote repository
that is used by default.
Teach "git push -u" not to require repository and refspec. When
the user do not give what repository to push to, or which
branch(es) to push, behave as if the default remote repository
and a refspec (depending on the "push.default" configuration)
are given.
If "push.default"=matching, push all the branches matched on both
remote and local side and set those remote branches as the upstream
of their respective local matched branches. Otherwise, set the
refspec to the refspec for current branch.
Signed-off-by: Abhradeep Chakraborty <redacted>
---
Documentation/git-push.txt | 10 ++++
builtin/push.c | 11 +++-
t/t5523-push-upstream.sh | 114 +++++++++++++++++++++++++++++++++++++
3 files changed, 133 insertions(+), 2 deletions(-)
@@ -375,6 +375,16 @@ Specifying `--no-force-if-includes` disables this behavior. upstream (tracking) reference, used by argument-less linkgit:git-pull[1] and other commands. For more information, see `branch.<name>.merge` in linkgit:git-config[1].+++`-u` can also work with zero arguments( i.e. no `<repository>` and+`<refspec>` are given). In that case, it tries to get the `<repository>`+value from `branch.*.remote` configuration. If not found, it defaults to+`origin`. If `remote.pushDefault` is set then it uses that instead. The+value of `<refspec>` depends on the current `push.default` configuration.+If `push.default` is set to `matching`, all remote branches to which+local branches pushed, will be set as upstream of respective local+branches. For all other values of `push.default`, current branch's+`<refspec>` will be used as the `<refspec>`. --[no-]thin:: These options are passed to linkgit:git-send-pack[1]. A thin transfer
@@ -214,6 +215,8 @@ static void setup_default_push_refspecs(struct remote *remote)return;casePUSH_DEFAULT_NOTHING:+if(is_default_u)+break;die(_("You didn't specify any refspecs to push, and ""push.default is \"nothing\"."));return;
@@ -234,11 +237,15 @@ static void setup_default_push_refspecs(struct remote *remote)casePUSH_DEFAULT_SIMPLE:if(!same_remote)break;+if(is_default_u)+break;if(strcmp(branch->refname,get_upstream_ref(branch,remote->name)))die_push_simple(branch,remote);break;casePUSH_DEFAULT_UPSTREAM:+if(is_default_u)+break;if(!same_remote)die(_("You are pushing to remote '%s', which is not the upstream of\n""your current branch '%s', without telling me what to push\n"
@@ -401,7 +408,7 @@ static int do_push(int flags,if(remote->push.nr){push_refspec=&remote->push;}elseif(!(flags&TRANSPORT_PUSH_MIRROR))-setup_default_push_refspecs(remote);+setup_default_push_refspecs(remote,flags);}errs=0;url_nr=push_url_of_remote(remote,&url);
@@ -60,6 +60,75 @@ test_expect_success 'push -u :topic_2' 'check_configtopic_2upstreamrefs/heads/other2'+default_u_setup(){+gitcheckoutmain+remote=$(gitconfig--getbranch.main.remote)+if[!-z"$remote"];then+gitbranch--unset-upstream+fi+gitconfigpush.default$1+gitconfigremote.pushDefaultupstream+}++test_expect_success'push -u with push.default=simple''+default_u_setupsimple&&+gitpush-u&&+check_configmainupstreamrefs/heads/main&&+gitpush-uupstreammain:other&&+gitpush-u&&+check_configmainupstreamrefs/heads/main+'++test_expect_success'push -u with push.default=current''+default_u_setupcurrent&&+gitpush-u&&+check_configmainupstreamrefs/heads/main&&+gitpush-uupstreammain:other&&+gitpush-u&&+check_configmainupstreamrefs/heads/main+'++test_expect_success'push -u with push.default=upstream''+default_u_setupupstream&&+gitpush-u&&+check_configmainupstreamrefs/heads/main&&+gitpush-uupstreammain:other&&+gitpush-u&&+check_configmainupstreamrefs/heads/main+'++check_empty_config(){+test_expect_code1gitconfig"branch.$1.remote"+test_expect_code1gitconfig"branch.$1.merge"+}++test_expect_success'push -u with push.default=matching''+default_u_setupmatching&&+gitbranchtest_u&&+gitbranchtest_u2&&+gitpushupstreammain:test_u2&&+gitpush-u&&+check_configmainupstreamrefs/heads/main&&+check_configtest_u2upstreamrefs/heads/test_u2&&+check_empty_configtest_u+'++test_expect_success'push -u with push.default=nothing''+default_u_setupnothing&&+gitpush-u&&+check_configmainupstreamrefs/heads/main&&+gitpush-uupstreammain:other&&+gitpush-u&&+check_configmainupstreamrefs/heads/main+'++test_expect_success'push -u --dry-run''+gitcheckoutmain&&+gitpush-uupstreammain:other&&+gitpush-u--dry-run&&+check_configmainupstreamrefs/heads/other+'+ test_expect_success'push -u --all''gitbranchall1&&gitbranchall2&&
@@ -81,6 +150,13 @@ test_expect_success TTY 'progress messages go to tty' 'test_i18ngrep"Writing objects"err'+test_expect_successTTY'progress messages go to tty with default -u''+ensure_fresh_upstream&&++test_terminalgitpush-u>out2>err&&+test_i18ngrep"Writing objects"err+'+ test_expect_success'progress messages do not go to non-tty''ensure_fresh_upstream&&
@@ -89,6 +165,14 @@ test_expect_success 'progress messages do not go to non-tty' 'test_i18ngrep!"Writing objects"err'+test_expect_success'progress messagesdo not go to non-tty (default -u)''+ensure_fresh_upstream&&++# skip progress messages, since stderr is non-tty+gitpush-u>out2>err&&+test_i18ngrep!"Writing objects"err+'+ test_expect_success'progress messages go to non-tty (forced)''ensure_fresh_upstream&&
@@ -97,6 +181,14 @@ test_expect_success 'progress messages go to non-tty (forced)' 'test_i18ngrep"Writing objects"err'+test_expect_success'progress messages go to non-tty with default -u (forced)''+ensure_fresh_upstream&&++# force progress messages to stderr, even though it is non-tty+gitpush-u--progress>out2>err&&+test_i18ngrep"Writing objects"err+'+ test_expect_successTTY'push -q suppresses progress''ensure_fresh_upstream&&
From: Eric Sunshine <hidden> Date: 2021-12-07 22:14:28
On Tue, Dec 7, 2021 at 4:11 PM Abhradeep Chakraborty
[off-list ref] wrote:
[...]
Teach "git push -u" not to require repository and refspec. When
the user do not give what repository to push to, or which
branch(es) to push, behave as if the default remote repository
and a refspec (depending on the "push.default" configuration)
are given.
[...]
Signed-off-by: Abhradeep Chakraborty <redacted>
This is not a proper review... just some superficial comments from
scanning my eye over the patch...
A few issues...
* since callers of this function incorporate it into their &&-chains,
the body of the function itself should probably also have an intact
&&-chain
* use `test` rather than `[`
* `then` goes on its own line
* probably want to use test_config() here rather than raw `git config`
* `! -z` can be written more simply as `-n`
Taking the above into account, gives:
default_u_setup() {
git checkout main &&
remote=$(git config --default '' --get branch.main.remote) &&
if test -n "$remote"
then
git branch --unset-upstream
fi &&
test_config push.default $1 &&
test_config remote.pushDefault upstream
}
The `--default` ensures that `git config` will exit with a success
code which is important now that it's part of the &&-chain.
Alternatively, you could skip the dance of checking for
`branch.main.remote` and just call `git branch --unset-upstream`
unconditionally, but wrap it with test_might_fail() so it can be part
of the &&-chain without worrying about whether that command succeeds
or fails:
default_u_setup() {
git checkout main &&
test_might_fail git branch --unset-upstream &&
test_config push.default $1 &&
test_config remote.pushDefault upstream
}
When a number of tests have nearly identical bodies like this, it is
sometimes clearer and more convenient to turn them into a for-loop
like this:
for i in simple current upstream
do
test_expect_success "push -u with push.default=$i" '
default_u_setup $i &&
git push -u &&
check_config main upstream refs/heads/main &&
git push -u upstream main:other &&
git push -u &&
check_config main upstream refs/heads/main
'
done
As above, because calls to this function are part of the &&-chain in
test bodies, it is important for the &&-chain to be intact in the
function too. It's especially important in this case since this
function is actually checking for specific conditions. As it's
currently written -- with a broken &&-chain -- if the first
test_expect_code() fails, we'll never know about it since that exit
code gets lost; only the exit code from the second test_expect_code()
has any bearing on the overall result of the test.
+test_expect_success 'progress messagesdo not go to non-tty (default -u)' '
The captured stdout in `out` doesn't seem to be used, so it's probably
better to drop that redirection.
+test_expect_success 'progress messages go to non-tty with default -u (forced)' '
+ ensure_fresh_upstream &&
+
+ # force progress messages to stderr, even though it is non-tty
+ git push -u --progress >out 2>err &&
+ test_i18ngrep "Writing objects" err
+'
As above, because calls to this function are part of the &&-chain in
test bodies, it is important for the &&-chain to be intact in the
function too. It's especially important in this case since this
function is actually checking for specific conditions. As it's
currently written -- with a broken &&-chain -- if the first
test_expect_code() fails, we'll never know about it since that exit
code gets lost; only the exit code from the second test_expect_code()
has any bearing on the overall result of the test.
Thanks so much for all the suggestions! I am little beginner in shell
scripting. It would help me a lot!
s/messagesdo/messages do/
oops :|
The captured stdout in `out` doesn't seem to be used, so it's probably
better to drop that redirection.
"git push -u" (set-upstream) requires where to push to and what
to push. Often people push only the current branch to update
the branch of the same name at the 'origin' repository. For
them, it would be convenient if "git push -u" without repository
or refspec, defaulted to push and set upstream to the branch as
configured by the "push.default" setting, of the remote repository
that is used by default.
Teach "git push -u" not to require repository and refspec. When
the user do not give what repository to push to, or which
branch(es) to push, behave as if the default remote repository
and a refspec (depending on the "push.default" configuration)
are given.
If "push.default"=matching, push all the branches matched on both
remote and local side and set those remote branches as the upstream
of their respective local matched branches. Otherwise, set the
refspec to the refspec for current branch.
Signed-off-by: Abhradeep Chakraborty <redacted>
---
Documentation/git-push.txt | 10 +++++
builtin/push.c | 11 ++++-
t/t5523-push-upstream.sh | 87 ++++++++++++++++++++++++++++++++++++++
3 files changed, 106 insertions(+), 2 deletions(-)
@@ -375,6 +375,16 @@ Specifying `--no-force-if-includes` disables this behavior. upstream (tracking) reference, used by argument-less linkgit:git-pull[1] and other commands. For more information, see `branch.<name>.merge` in linkgit:git-config[1].+++`-u` can also work with zero arguments( i.e. no `<repository>` and+`<refspec>` are given). In that case, it tries to get the `<repository>`+value from `branch.*.remote` configuration. If not found, it defaults to+`origin`. If `remote.pushDefault` is set then it uses that instead. The+value of `<refspec>` depends on the current `push.default` configuration.+If `push.default` is set to `matching`, all remote branches to which+local branches pushed, will be set as upstream of respective local+branches. For all other values of `push.default`, current branch's+refspec will be used as the `<refspec>`. --[no-]thin:: These options are passed to linkgit:git-send-pack[1]. A thin transfer
@@ -214,6 +215,8 @@ static void setup_default_push_refspecs(struct remote *remote)return;casePUSH_DEFAULT_NOTHING:+if(is_default_u)+break;die(_("You didn't specify any refspecs to push, and ""push.default is \"nothing\"."));return;
@@ -234,11 +237,15 @@ static void setup_default_push_refspecs(struct remote *remote)casePUSH_DEFAULT_SIMPLE:if(!same_remote)break;+if(is_default_u)+break;if(strcmp(branch->refname,get_upstream_ref(branch,remote->name)))die_push_simple(branch,remote);break;casePUSH_DEFAULT_UPSTREAM:+if(is_default_u)+break;if(!same_remote)die(_("You are pushing to remote '%s', which is not the upstream of\n""your current branch '%s', without telling me what to push\n"
@@ -401,7 +408,7 @@ static int do_push(int flags,if(remote->push.nr){push_refspec=&remote->push;}elseif(!(flags&TRANSPORT_PUSH_MIRROR))-setup_default_push_refspecs(remote);+setup_default_push_refspecs(remote,flags);}errs=0;url_nr=push_url_of_remote(remote,&url);
@@ -60,6 +60,48 @@ test_expect_success 'push -u :topic_2' 'check_configtopic_2upstreamrefs/heads/other2'+default_u_setup(){+gitcheckoutmain&&+test_might_failgitbranch--unset-upstream&&+test_configpush.default$1&&+test_configremote.pushDefaultupstream+}++foriinsimplecurrentupstreamnothing+do+test_expect_success'push -u with push.default=$i''+default_u_setup$i&&+gitpush-u&&+check_configmainupstreamrefs/heads/main&&+gitpush-uupstreammain:other&&+gitpush-u&&+check_configmainupstreamrefs/heads/main+'+done++check_empty_config(){+test_expect_code1gitconfig"branch.$1.remote"&&+test_expect_code1gitconfig"branch.$1.merge"+}++test_expect_success'push -u with push.default=matching''+default_u_setupmatching&&+gitbranchtest_u&&+gitbranchtest_u2&&+gitpushupstreammain:test_u2&&+gitpush-u&&+check_configmainupstreamrefs/heads/main&&+check_configtest_u2upstreamrefs/heads/test_u2&&+check_empty_configtest_u+'++test_expect_success'push -u --dry-run''+gitcheckoutmain&&+gitpush-uupstreammain:other&&+gitpush-u--dry-run&&+check_configmainupstreamrefs/heads/other+'+ test_expect_success'push -u --all''gitbranchall1&&gitbranchall2&&
@@ -81,6 +123,13 @@ test_expect_success TTY 'progress messages go to tty' 'test_i18ngrep"Writing objects"err'+test_expect_successTTY'progress messages go to tty with default -u''+ensure_fresh_upstream&&++test_terminalgitpush-u2>err&&+test_i18ngrep"Writing objects"err+'+ test_expect_success'progress messages do not go to non-tty''ensure_fresh_upstream&&
@@ -89,6 +138,14 @@ test_expect_success 'progress messages do not go to non-tty' 'test_i18ngrep!"Writing objects"err'+test_expect_success'progress messages do not go to non-tty (default -u)''+ensure_fresh_upstream&&++# skip progress messages, since stderr is non-tty+gitpush-u2>err&&+test_i18ngrep!"Writing objects"err+'+ test_expect_success'progress messages go to non-tty (forced)''ensure_fresh_upstream&&
@@ -97,6 +154,14 @@ test_expect_success 'progress messages go to non-tty (forced)' 'test_i18ngrep"Writing objects"err'+test_expect_success'progress messages go to non-tty with default -u (forced)''+ensure_fresh_upstream&&++# force progress messages to stderr, even though it is non-tty+gitpush-u--progress2>err&&+test_i18ngrep"Writing objects"err+'+ test_expect_successTTY'push -q suppresses progress''ensure_fresh_upstream&&
Often developers want to set a remote branch (i.e. upstream branch)
for the local branch according to their `push.default` settings. For
example, beginners often (may be most of the beginners) run
git push -u origin <current_branch_name>
If the `push.default` configuration is set, people may want to set
the upstream to that branch that satisfies the `push.default`
configuration. For example, if the `push.default` is set to
'current', developer may want to do like this -
git push -u <default_repo> <current_branch>
So, it would be great if 'git push -u' (i.e. without <repo> and
<refspec>) by default do this. If `push.default` is not set or has
a value other than 'matching', it would do this -
git push -u <default_repo> <current_branch>
And for `push.default`= 'matching', it would set all the remote
maching branch as upstream of their respective matching local
branches. E.g. if 'branch1' and 'branch2' branches both exist in
the local as well as remote repo, 'git push -u' would set the remote
'branch1' as the upstream of local 'branch1' branch and remote
'branch2' branch would be set as the upstream of local 'branch2'
branch.
Note, 'git push -u' for push.default=matching, already works.
This patch series addresses this.
In v0: argumentless 'git push -u' was blindly passing default remote
name and current branch's name as argv[0] and argv[1] respectively.
This was affecting `push.default` setting.
From v1: The default remote is still used for the <repository> value.
But <refspec> depends on the current push configurations. If
`push.default`='matching', it pushes to the upstream as it should and
sets upstream respectively. For other values of 'push.default', it
pushes to the remote branch with the same name as the current
branch and sets that branch as the upstream.
In v2 and v3: various test cases were added and improved
In current version: tests for 'git push -u' with other options are
added. This includes '-f', '--prune', '-d', '--mirror'.
Abhradeep Chakraborty (1):
push: make 'set-upstream' have dafault arguments
Documentation/git-push.txt | 10 +++
builtin/push.c | 11 +++-
t/t5523-push-upstream.sh | 125 +++++++++++++++++++++++++++++++++++++
3 files changed, 144 insertions(+), 2 deletions(-)
Range-diff against v3:
1: 64655de6ca ! 1: d154c7d1f6 push: make '-u' have default arguments
@@ Metadata
Author: Abhradeep Chakraborty [off-list ref]
## Commit message ##
- push: make '-u' have default arguments
+ push: make 'set-upstream' have dafault arguments
"git push -u" (set-upstream) requires where to push to and what
to push. Often people push only the current branch to update
@@ t/t5523-push-upstream.sh: test_expect_success 'push -u :topic_2' '
+ test_config remote.pushDefault upstream
+}
+
++check_empty_config() {
++ test_expect_code 1 git config "branch.$1.remote" &&
++ test_expect_code 1 git config "branch.$1.merge"
++}
++
+for i in simple current upstream nothing
+do
+ test_expect_success 'push -u with push.default=$i' '
@@ t/t5523-push-upstream.sh: test_expect_success 'push -u :topic_2' '
+ git push -u &&
+ check_config main upstream refs/heads/main
+ '
++
++ test_expect_success 'push -u -f with push.default=$i' '
++ default_u_setup $i &&
++ git push -u -f &&
++ check_config main upstream refs/heads/main
++ '
+done
+
-+check_empty_config() {
-+ test_expect_code 1 git config "branch.$1.remote" &&
-+ test_expect_code 1 git config "branch.$1.merge"
-+}
++for i in simple current upstream nothing matching
++do
++ test_expect_success 'push -u --prune with push.default=$i' '
++ default_u_setup $i &&
++ git push upstream main:test_u215 &&
++ git push -u --prune >out &&
++ check_config main upstream refs/heads/main &&
++ test_i18ngrep "[deleted]" out &&
++ test_i18ngrep ! "Branch '"'"'test_u215'"'"' set up to track" out
++ '
++
++ test_expect_success 'push -u --mirror with push.default=$i' '
++ default_u_setup $i &&
++ test_might_fail git branch mirror1 &&
++ test_might_fail git branch mirror2 &&
++ git push -u --mirror &&
++ check_config main upstream refs/heads/main &&
++ check_config mirror1 upstream refs/heads/mirror1 &&
++ check_config mirror2 upstream refs/heads/mirror2
++ '
++done
+
-+test_expect_success 'push -u with push.default=matching' '
-+ default_u_setup matching &&
-+ git branch test_u &&
-+ git branch test_u2 &&
-+ git push upstream main:test_u2 &&
-+ git push -u &&
-+ check_config main upstream refs/heads/main &&
-+ check_config test_u2 upstream refs/heads/test_u2 &&
-+ check_empty_config test_u
++for i in '' '-f'
++do
++
++ test_expect_success 'push -u $i with push.default=matching' '
++ default_u_setup matching &&
++ test_might_fail git branch test_u &&
++ test_might_fail git branch test_u2 &&
++ git push upstream main:test_u2 &&
++ git push -u $i &&
++ check_config main upstream refs/heads/main &&
++ check_config test_u2 upstream refs/heads/test_u2 &&
++ check_empty_config test_u
++ '
++done
++
++test_expect_success 'push -u -d will fail' '
++ git checkout main &&
++ test_might_fail git branch --unset-upstream &&
++ test_must_fail git push -u -d
+'
+
+test_expect_success 'push -u --dry-run' '
--
2.34.1
"git push -u" (set-upstream) requires where to push to and what
to push. Often people push only the current branch to update
the branch of the same name at the 'origin' repository. For
them, it would be convenient if "git push -u" without repository
or refspec, defaulted to push and set upstream to the branch as
configured by the "push.default" setting, of the remote repository
that is used by default.
Teach "git push -u" not to require repository and refspec. When
the user do not give what repository to push to, or which
branch(es) to push, behave as if the default remote repository
and a refspec (depending on the "push.default" configuration)
are given.
If "push.default"=matching, push all the branches matched on both
remote and local side and set those remote branches as the upstream
of their respective local matched branches. Otherwise, set the
refspec to the refspec for current branch.
Signed-off-by: Abhradeep Chakraborty <redacted>
---
Documentation/git-push.txt | 10 +++
builtin/push.c | 11 +++-
t/t5523-push-upstream.sh | 125 +++++++++++++++++++++++++++++++++++++
3 files changed, 144 insertions(+), 2 deletions(-)
@@ -375,6 +375,16 @@ Specifying `--no-force-if-includes` disables this behavior. upstream (tracking) reference, used by argument-less linkgit:git-pull[1] and other commands. For more information, see `branch.<name>.merge` in linkgit:git-config[1].+++`-u` can also work with zero arguments( i.e. no `<repository>` and+`<refspec>` are given). In that case, it tries to get the `<repository>`+value from `branch.*.remote` configuration. If not found, it defaults to+`origin`. If `remote.pushDefault` is set then it uses that instead. The+value of `<refspec>` depends on the current `push.default` configuration.+If `push.default` is set to `matching`, all remote branches to which+local branches pushed, will be set as upstream of respective local+branches. For all other values of `push.default`, current branch's+refspec will be used as the `<refspec>`. --[no-]thin:: These options are passed to linkgit:git-send-pack[1]. A thin transfer
@@ -214,6 +215,8 @@ static void setup_default_push_refspecs(struct remote *remote)return;casePUSH_DEFAULT_NOTHING:+if(is_default_u)+break;die(_("You didn't specify any refspecs to push, and ""push.default is \"nothing\"."));return;
@@ -234,11 +237,15 @@ static void setup_default_push_refspecs(struct remote *remote)casePUSH_DEFAULT_SIMPLE:if(!same_remote)break;+if(is_default_u)+break;if(strcmp(branch->refname,get_upstream_ref(branch,remote->name)))die_push_simple(branch,remote);break;casePUSH_DEFAULT_UPSTREAM:+if(is_default_u)+break;if(!same_remote)die(_("You are pushing to remote '%s', which is not the upstream of\n""your current branch '%s', without telling me what to push\n"
@@ -401,7 +408,7 @@ static int do_push(int flags,if(remote->push.nr){push_refspec=&remote->push;}elseif(!(flags&TRANSPORT_PUSH_MIRROR))-setup_default_push_refspecs(remote);+setup_default_push_refspecs(remote,flags);}errs=0;url_nr=push_url_of_remote(remote,&url);
@@ -60,6 +60,86 @@ test_expect_success 'push -u :topic_2' 'check_configtopic_2upstreamrefs/heads/other2'+default_u_setup(){+gitcheckoutmain&&+test_might_failgitbranch--unset-upstream&&+test_configpush.default$1&&+test_configremote.pushDefaultupstream+}++check_empty_config(){+test_expect_code1gitconfig"branch.$1.remote"&&+test_expect_code1gitconfig"branch.$1.merge"+}++foriinsimplecurrentupstreamnothing+do+test_expect_success'push -u with push.default=$i''+default_u_setup$i&&+gitpush-u&&+check_configmainupstreamrefs/heads/main&&+gitpush-uupstreammain:other&&+gitpush-u&&+check_configmainupstreamrefs/heads/main+'++test_expect_success'push -u -f with push.default=$i''+default_u_setup$i&&+gitpush-u-f&&+check_configmainupstreamrefs/heads/main+'+done++foriinsimplecurrentupstreamnothingmatching+do+test_expect_success'push -u --prune with push.default=$i''+default_u_setup$i&&+gitpushupstreammain:test_u215&&+gitpush-u--prune>out&&+check_configmainupstreamrefs/heads/main&&+test_i18ngrep"[deleted]"out&&+test_i18ngrep!"Branch '"'"'test_u215'"'"' set up to track"out+'++test_expect_success'push -u --mirror with push.default=$i''+default_u_setup$i&&+test_might_failgitbranchmirror1&&+test_might_failgitbranchmirror2&&+gitpush-u--mirror&&+check_configmainupstreamrefs/heads/main&&+check_configmirror1upstreamrefs/heads/mirror1&&+check_configmirror2upstreamrefs/heads/mirror2+'+done++foriin'''-f'+do++test_expect_success'push -u $i with push.default=matching''+default_u_setupmatching&&+test_might_failgitbranchtest_u&&+test_might_failgitbranchtest_u2&&+gitpushupstreammain:test_u2&&+gitpush-u$i&&+check_configmainupstreamrefs/heads/main&&+check_configtest_u2upstreamrefs/heads/test_u2&&+check_empty_configtest_u+'+done++test_expect_success'push -u -d will fail''+gitcheckoutmain&&+test_might_failgitbranch--unset-upstream&&+test_must_failgitpush-u-d+'++test_expect_success'push -u --dry-run''+gitcheckoutmain&&+gitpush-uupstreammain:other&&+gitpush-u--dry-run&&+check_configmainupstreamrefs/heads/other+'+ test_expect_success'push -u --all''gitbranchall1&&gitbranchall2&&
@@ -81,6 +161,13 @@ test_expect_success TTY 'progress messages go to tty' 'test_i18ngrep"Writing objects"err'+test_expect_successTTY'progress messages go to tty with default -u''+ensure_fresh_upstream&&++test_terminalgitpush-u2>err&&+test_i18ngrep"Writing objects"err+'+ test_expect_success'progress messages do not go to non-tty''ensure_fresh_upstream&&
@@ -89,6 +176,14 @@ test_expect_success 'progress messages do not go to non-tty' 'test_i18ngrep!"Writing objects"err'+test_expect_success'progress messages do not go to non-tty (default -u)''+ensure_fresh_upstream&&++# skip progress messages, since stderr is non-tty+gitpush-u2>err&&+test_i18ngrep!"Writing objects"err+'+ test_expect_success'progress messages go to non-tty (forced)''ensure_fresh_upstream&&
@@ -97,6 +192,14 @@ test_expect_success 'progress messages go to non-tty (forced)' 'test_i18ngrep"Writing objects"err'+test_expect_success'progress messages go to non-tty with default -u (forced)''+ensure_fresh_upstream&&++# force progress messages to stderr, even though it is non-tty+gitpush-u--progress2>err&&+test_i18ngrep"Writing objects"err+'+ test_expect_successTTY'push -q suppresses progress''ensure_fresh_upstream&&
From: Junio C Hamano <hidden> Date: 2022-01-04 03:46:48
Abhradeep Chakraborty [off-list ref] writes:
Teach "git push -u" not to require repository and refspec. When
the user do not give what repository to push to, or which
branch(es) to push, behave as if the default remote repository
and a refspec (depending on the "push.default" configuration)
are given.
That means if the user says push.default==nothing, we should error
out "git push -u" as before, but that is not what the change to
setup_default_push_refspecs() function does, is it?
quoted hunk
-static void setup_default_push_refspecs(struct remote *remote)
+static void setup_default_push_refspecs(struct remote *remote, int flags)
{
struct branch *branch;
const char *dst;
int same_remote;
+ int is_default_u = (flags & TRANSPORT_PUSH_SET_UPSTREAM);
switch (push_default) {
case PUSH_DEFAULT_MATCHING:
@@ -214,6 +215,8 @@ static void setup_default_push_refspecs(struct remote *remote) return; case PUSH_DEFAULT_NOTHING:+ if (is_default_u)+ break; die(_("You didn't specify any refspecs to push, and " "push.default is \"nothing\".")); return;
@@ -234,11 +237,15 @@ static void setup_default_push_refspecs(struct remote *remote) case PUSH_DEFAULT_SIMPLE: if (!same_remote) break;+ if (is_default_u)+ break; if (strcmp(branch->refname, get_upstream_ref(branch, remote->name))) die_push_simple(branch, remote); break; case PUSH_DEFAULT_UPSTREAM:+ if (is_default_u)+ break; if (!same_remote) die(_("You are pushing to remote '%s', which is not the upstream of\n" "your current branch '%s', without telling me what to push\n"
So, I am not sure if many of the above changes are sensible. The
first one certainly does not sound like sensible.
That means if the user says push.default==nothing, we should error
out "git push -u" as before, but that is not what the change to
setup_default_push_refspecs() function does, is it?
Yeah. You're right. The current change does not throw error for
push.default=nothing. Because I thought that for all values of
`push.default` (except matching), 'git push -u' should create a
new branch with the same name as the current local branch. Now, It
seems that I was wrong.
So, I am not sure if many of the above changes are sensible. The
first one certainly does not sound like sensible.
Actually, I didn't think deeply while commiting the changes. Today,
I think about it deeply and I realized the following points.
* if push.default='simple' or unspecified then it should not create
a new branch on the remote. So, my proposed change of 'git push -u'
for push.default='simple' is badly affecting the reason why
push.default='simple' was built for.
* if push.default='nothing', It should throw error if no <refspec> is
provided. Again, my proposed change is hurting it.
* For push.default=upstream, If an upstream is already defined then
'git push -u' should only set that branch as the upstream of the
local branch. This already works in git. But if an upstream is not
provied, it should throw error. So, I am not sure whether 'git push
-u' (with no upstream information) should create a new branch with
the same name or not. What do you think about that?
* For push.default=matching, 'git push -u' should set all the existing
matching branches as upstream of their respective matching local
branches. It also already works. Same for 'push.default'=current also.
So, to put all in a nutshell, I think that the current behaviour of
'git push -u' is okay. It also seems that he/she who built the
setup_default_push_refspecs() was aware of this.
Sorry for the patch request and thanks for reviewing.
Doesn't $i show in the output as-is here? Quote the test title in
double-quotes, while using single-qoutes around the test body.
Yeah. I observed this while testing. But had no idea why this happend
( as I am very beginner in shell scripting). I was waiting for the review
comment for it.
Thanks again for reviewing my patch request.