git-branch, git-checkout: autosetup for remote branch tracking
PATCH 1/1 git-branch, git-checkout: autosetup for remote branch tracking
In order to track and build on top of a branch 'topic' you track from your upstream repository, you often would end up doing this sequence:
git checkout -b mytopic origin/topic git config --add branch.mytopic.remote origin git config --add branch.mytopic.merge refs/heads/topic
This would first fork your own 'mytopic' branch from the 'topic' branch you track from the 'origin' repository; then it would set up two configuration variables so that 'git pull' without parameters does the right thing while you are on your own 'mytopic' branch.
This commit adds a --track option to git-branch, so that "git branch --track mytopic origin/topic" performs the latter two actions when creating your 'mytopic' branch.
If the configuration variable branch.autosetupmerge is set to true, you do not have to pass the --track option explicitly; further patches in this series allow setting the variable with a "git remote add" option. The configuration variable is off by default, and there is a --no-track option to countermand it even if the variable is set.
--- Documentation/git-branch.txt | 9 ++ Documentation/git-checkout.txt | 15 ++++ builtin-branch.c | 128 ++++++++++++++++++++++++++++++++++++++--- cache.h | 1 git-checkout.sh | 17 +++-- t/t3200-branch.sh | 65 +++++++++++++++++--- trace.c | 14 +--- 7 files changed, 214 insertions(+), 35 deletions(-)
This is the hopefully final version. I am now looking at all remote configs, not restricting the search to those in refs/remotes/ (and this made the parsing simpler, so no sscanf anymore). I also gave up the per-remote config and I am using instead a global variable.
This version also includes tests and changes to git-checkout. git-remote is no longer affected by the change.
Documentation/git-branch.txt +8 -1
@@ -10,7 +10,7 @@ SYNOPSIS [verse] 'git-branch' [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]] -'git-branch' [-l] [-f] <branchname> [<start-point>] +'git-branch' [--track | --no-track] [-l] [-f] <branchname> [<start-point>] 'git-branch' (-m | -M) [<oldbranch>] <newbranch> 'git-branch' (-d | -D) [-r] <branchname>...
@@ -26,6 +26,13 @@ It will start out with a head equal to the one given as <start-point>. If no <start-point> is given, the branch will be created with a head equal to that of the currently checked out branch. +When a local branch is started off a remote branch, git can setup the +branch so that gitlink:git-pull[1] will appropriately merge from that +remote branch. If this behavior is desired, it is possible to make it +the default using the global `branch.autosetupmerge` configuration +flag. Otherwise, it can be chosen per-branch using the `--track` +and `--no-track` options. + With a '-m' or '-M' option, <oldbranch> will be renamed to <newbranch>. If <oldbranch> had a corresponding reflog, it is renamed to match <newbranch>, and a reflog entry is created to remember the branch
Documentation/git-checkout.txt +13 -2
@@ -8,7 +8,7 @@ git-checkout - Checkout and switch to a branch SYNOPSIS -------- [verse] -'git-checkout' [-q] [-f] [-b <new_branch> [-l]] [-m] [<branch>] +'git-checkout' [-q] [-f] [-b [--track | --no-track] <new_branch> [-l]] [-m] [<branch>] 'git-checkout' [<tree-ish>] <paths>... DESCRIPTION
@@ -18,7 +18,8 @@ When <paths> are not given, this command switches branches by updating the index and working tree to reflect the specified branch, <branch>, and updating HEAD to be <branch> or, if specified, <new_branch>. Using -b will cause <new_branch> to -be created. +be created; in this case you can use the --track or --no-track +options, which will be passed to `git branch`. When <paths> are given, this command does *not* switch branches. It updates the named paths in the working tree from
@@ -45,6 +46,16 @@ OPTIONS by gitlink:git-check-ref-format[1]. Some of these checks may restrict the characters allowed in a branch name. +--track:: + When -b is given and a branch is created off a remote branch, + setup so that git-pull will automatically retrieve data from + the remote branch. + +--no-track:: + When -b is given and a branch is created off a remote branch, + force that git-pull will automatically retrieve data from + the remote branch independent of the configuration settings. + -l:: Create the new branch's ref log. This activates recording of all changes to made the branch ref, enabling use of date
builtin-branch.c +122 -7
196 lines not on this page · raw message
cache.h +1 -0
@@ -480,6 +480,7 @@ extern struct tag *alloc_tag_node(void);extern void alloc_report (void );/* trace.c */ +extern int nfasprintf (char **str,const char *fmt, ...);extern int nfvasprintf (char **str,const char *fmt,va_list va);extern void trace_printf (const char *format, ...);extern void trace_argv_printf (const char **argv,int count,const char *format, ...);
git-checkout.sh +11 -6
@@ -12,6 +12,7 @@ new= new_name= force= branch= +track= newbranch= newbranch_log= merge=
@@ -33,7 +34,10 @@ while [ "$#" != "0" ]; do die "git checkout: we do not like '$newbranch' as a branch name." ;; "-l") - newbranch_log=1 + newbranch_log=-l + ;; + "--track"|"--no-track") + track="$arg" ;; "-f") force=1
@@ -85,6 +89,11 @@ while [ "$#" != "0" ]; do esac done +case "$new_branch,$track" in +,--*) + die "git checkout: --track and --no-track require -b" +esac + case "$force$merge" in 11) die "git checkout: -f and -m are incompatible"
@@ -235,11 +244,7 @@ fi # if [ "$?" -eq 0 ]; then if [ "$newbranch" ]; then - if [ "$newbranch_log" ]; then - mkdir -p $(dirname "$GIT_DIR/logs/refs/heads/$newbranch") - touch "$GIT_DIR/logs/refs/heads/$newbranch" - fi - git-update-ref -m "checkout: Created from $new_name" "refs/heads/$newbranch" $new || exit + git-branch $track $newbranch_log "$newbranch" "$new_name" || exit branch="$newbranch" fi if test -n "$branch"
t/t3200-branch.sh +54 -11
75 lines not on this page · raw message
trace.c +6 -8
@@ -26,14 +26,14 @@#include "quote.h" /* Stolen from "imap-send.c". */ -static int git_vasprintf (char **strp,const char *fmt,va_list ap) +int nfvasprintf (char **strp,const char *fmt,va_list ap) {int len;char tmp[1024 ];if ((len =vsnprintf (tmp,sizeof (tmp), fmt, ap)) <0 || !(*strp =xmalloc (len +1 ))) -return -1 ; +die ("Fatal: Out of memory\n" );if (len >= (int )sizeof (tmp))vsprintf (*strp, fmt, ap);else
@@ -41,13 +41,11 @@ static int git_vasprintf(char **strp, const char *fmt, va_list ap)return len; } -/* Stolen from "imap-send.c". */ -int nfvasprintf (char **str,const char *fmt,va_list va) +int nfasprintf (char **str,const char *fmt, ...) { -int ret =git_vasprintf (str, fmt, va); -if (ret <0 ) -die ("Fatal: Out of memory\n" ); -return ret; +va_list args; +va_start (args, fmt); +return nfvasprintf (str, fmt, args); }/* Get a trace file descriptor from GIT_TRACE env variable. */
Quoted 2 lines · Paolo Bonzini
...
Signed-off-by Paolo Bonzini <[email protected]>
Beautifully done, with even a sign-off ;-).
Quoted 7 lines
Documentation/git-checkout.txt +3
-0@@ -45,6 +46,16 @@ OPTIONS by gitlink:git-check-ref-format[1]. Some of these checks may restrict the characters allowed in a branch name. +--track:: + When -b is given and a branch is created off a remote branch, + setup so that git-pull will automatically retrieve data from
Documentation/git-checkout.txt OPTIONS · line 61
Quoted 10 lines
Documentation/git-checkout.txt +7
-0+ the remote branch. + +--no-track:: + When -b is given and a branch is created off a remote branch, + force that git-pull will automatically retrieve data from + the remote branch independent of the configuration settings. + -l:: Create the new branch's ref log. This activates recording of all changes to made the branch ref, enabling use of date
Earlier I said "checkout -b" vs "checkout -B", but deciding the tracking default with congiguration mechanism and overriding with an additional --track/--no-track as you did here is way superiour. Very nice.
Quoted 8 lines
builtin-branch.c +3
-0@@ -308,14 +313,102 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev) +static int get_remote_branch_name (const char * value) +{ +const char *colon;
builtin-branch.c static void print_ref_list(int kinds, int detached, int verbose, int abbrev) · line 325
Quoted 10 lines
builtin-branch.c +10
-0+const char *end; + +if (*value =='+' ) + value++; + + colon =strchr (value,':' ); + end = value +strlen (value); + +/* Try an exact match first. */ +if (!strcmp (colon +1 , start_ref)) {
Careful. colon may be NULL here, in which case you would want to return with 0 from this function before this strcmp.
You seem to declare variables with "const char *colon" but function args with "const char* value". Are they deliberately done differently? The existing code seems to favor asterisk next to the variable name, not type name.
Other than that, I find this function and get_remote_config() very nicely done.
Quoted 12 lines
builtin-branch.c +12
-0+static void set_branch_defaults (const char *name,const char *real_ref) +{ +char key[1024 ]; +const char *slash =strrchr (real_ref,'/' ); + +if (!slash) +return ; + + start_ref = real_ref; + start_len =strlen (real_ref); + base_len = slash - real_ref; +git_config (get_remote_config);
builtin-branch.c static void print_ref_list(int kinds, int detached, int verbose, int abbrev) · line 392
Quoted 10 lines
builtin-branch.c +10
-0+ +/* Change to != 0 to enable this feature by default. */ +if (config_repo && config_remote) { +if (snprintf (key,sizeof (key),"branch.%s.remote" , name) + >sizeof (key)) +die ("what a long branch name you have!" ); +git_config_set (key, config_remote); + +snprintf (key,sizeof (key),"branch.%s.merge" , name); +git_config_set (key, config_repo);
This is nice, but you might want to have a comment that says why you do not have to check the return value from the second snprintf(). Otherwise, somebody less careful may update this part of the code and swap the order of config_remote and config_repo are set up without thinking in the future.
Quoted 6 lines
trace.c +0
-0@@ -41,13 +41,11 @@ static int git_vasprintf(char **strp, const char *fmt, va_list ap)return len;
trace.c static int git_vasprintf(char **strp, const char *fmt, va_list ap) · line 51
Quoted 10 lines
trace.c +4
-0} +int nfasprintf (char **str,const char *fmt, ...) { +va_list args; +va_start (args, fmt); +return nfvasprintf (str, fmt, args); }/* Get a trace file descriptor from GIT_TRACE env variable. */
It might not matter on common architectures in practice, but va_start() must be matched by a corresponding va_end() in the same function.
PATCH 1/1 git-branch, git-checkout: autosetup for remote branch tracking
In order to track and build on top of a branch 'topic' you track from your upstream repository, you often would end up doing this sequence:
git checkout -b mytopic origin/topic git config --add branch.mytopic.remote origin git config --add branch.mytopic.merge refs/heads/topic
This would first fork your own 'mytopic' branch from the 'topic' branch you track from the 'origin' repository; then it would set up two configuration variables so that 'git pull' without parameters does the right thing while you are on your own 'mytopic' branch.
This commit adds a --track option to git-branch, so that "git branch --track mytopic origin/topic" performs the latter two actions when creating your 'mytopic' branch.
If the configuration variable branch.autosetupmerge is set to true, you do not have to pass the --track option explicitly; further patches in this series allow setting the variable with a "git remote add" option. The configuration variable is off by default, and there is a --no-track option to countermand it even if the variable is set.
--- Documentation/git-branch.txt | 9 ++ Documentation/git-checkout.txt | 15 +++- builtin-branch.c | 137 ++++++++++++++++++++++++++++++++++++++--- cache.h | 1 git-checkout.sh | 17 +++-- t/t3200-branch.sh | 65 ++++++++++++++++--- trace.c | 18 ++--- 7 files changed, 226 insertions(+), 36 deletions(-)
This fixes all the nits you pointed out. :-D
Now, this was an experience to make...
Documentation/git-branch.txt +8 -1
@@ -10,7 +10,7 @@ SYNOPSIS [verse] 'git-branch' [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]] -'git-branch' [-l] [-f] <branchname> [<start-point>] +'git-branch' [--track | --no-track] [-l] [-f] <branchname> [<start-point>] 'git-branch' (-m | -M) [<oldbranch>] <newbranch> 'git-branch' (-d | -D) [-r] <branchname>...
@@ -26,6 +26,13 @@ It will start out with a head equal to the one given as <start-point>. If no <start-point> is given, the branch will be created with a head equal to that of the currently checked out branch. +When a local branch is started off a remote branch, git can setup the +branch so that gitlink:git-pull[1] will appropriately merge from that +remote branch. If this behavior is desired, it is possible to make it +the default using the global `branch.autosetupmerge` configuration +flag. Otherwise, it can be chosen per-branch using the `--track` +and `--no-track` options. + With a '-m' or '-M' option, <oldbranch> will be renamed to <newbranch>. If <oldbranch> had a corresponding reflog, it is renamed to match <newbranch>, and a reflog entry is created to remember the branch
Documentation/git-checkout.txt +13 -2
@@ -8,7 +8,7 @@ git-checkout - Checkout and switch to a branch SYNOPSIS -------- [verse] -'git-checkout' [-q] [-f] [-b <new_branch> [-l]] [-m] [<branch>] +'git-checkout' [-q] [-f] [-b [--track | --no-track] <new_branch> [-l]] [-m] [<branch>] 'git-checkout' [<tree-ish>] <paths>... DESCRIPTION
@@ -18,7 +18,8 @@ When <paths> are not given, this command switches branches by updating the index and working tree to reflect the specified branch, <branch>, and updating HEAD to be <branch> or, if specified, <new_branch>. Using -b will cause <new_branch> to -be created. +be created; in this case you can use the --track or --no-track +options, which will be passed to `git branch`. When <paths> are given, this command does *not* switch branches. It updates the named paths in the working tree from
@@ -45,6 +46,16 @@ OPTIONS by gitlink:git-check-ref-format[1]. Some of these checks may restrict the characters allowed in a branch name. +--track:: + When -b is given and a branch is created off a remote branch, + setup so that git-pull will automatically retrieve data from + the remote branch. + +--no-track:: + When -b is given and a branch is created off a remote branch, + force that git-pull will automatically retrieve data from + the remote branch independent of the configuration settings. + -l:: Create the new branch's ref log. This activates recording of all changes to made the branch ref, enabling use of date
builtin-branch.c +129 -8
206 lines not on this page · raw message
cache.h +1 -0
@@ -480,6 +480,7 @@ extern struct tag *alloc_tag_node(void);extern void alloc_report (void );/* trace.c */ +extern int nfasprintf (char **str,const char *fmt, ...);extern int nfvasprintf (char **str,const char *fmt,va_list va);extern void trace_printf (const char *format, ...);extern void trace_argv_printf (const char **argv,int count,const char *format, ...);
git-checkout.sh +11 -6
@@ -12,6 +12,7 @@ new= new_name= force= branch= +track= newbranch= newbranch_log= merge=
@@ -33,7 +34,10 @@ while [ "$#" != "0" ]; do die "git checkout: we do not like '$newbranch' as a branch name." ;; "-l") - newbranch_log=1 + newbranch_log=-l + ;; + "--track"|"--no-track") + track="$arg" ;; "-f") force=1
@@ -85,6 +89,11 @@ while [ "$#" != "0" ]; do esac done +case "$new_branch,$track" in +,--*) + die "git checkout: --track and --no-track require -b" +esac + case "$force$merge" in 11) die "git checkout: -f and -m are incompatible"
@@ -235,11 +244,7 @@ fi # if [ "$?" -eq 0 ]; then if [ "$newbranch" ]; then - if [ "$newbranch_log" ]; then - mkdir -p $(dirname "$GIT_DIR/logs/refs/heads/$newbranch") - touch "$GIT_DIR/logs/refs/heads/$newbranch" - fi - git-update-ref -m "checkout: Created from $new_name" "refs/heads/$newbranch" $new || exit + git-branch $track $newbranch_log "$newbranch" "$new_name" || exit branch="$newbranch" fi if test -n "$branch"
t/t3200-branch.sh +54 -11
75 lines not on this page · raw message
trace.c +10 -8
@@ -26,14 +26,14 @@#include "quote.h" /* Stolen from "imap-send.c". */ -static int git_vasprintf (char **strp,const char *fmt,va_list ap) +int nfvasprintf (char **strp,const char *fmt,va_list ap) {int len;char tmp[1024 ];if ((len =vsnprintf (tmp,sizeof (tmp), fmt, ap)) <0 || !(*strp =xmalloc (len +1 ))) -return -1 ; +die ("Fatal: Out of memory\n" );if (len >= (int )sizeof (tmp))vsprintf (*strp, fmt, ap);else
@@ -41,13 +41,15 @@ static int git_vasprintf(char **strp, const char *fmt, va_list ap)return len; } -/* Stolen from "imap-send.c". */ -int nfvasprintf (char **str,const char *fmt,va_list va) +int nfasprintf (char **str,const char *fmt, ...) { -int ret =git_vasprintf (str, fmt, va); -if (ret <0 ) -die ("Fatal: Out of memory\n" ); -return ret; +int rc; +va_list args; + +va_start (args, fmt); + rc =nfvasprintf (str, fmt, args); +va_end (args); +return args; }/* Get a trace file descriptor from GIT_TRACE env variable. */
Quoted 2 lines · Paolo Bonzini
This fixes all the nits you pointed out. :-D Now, this was an experience to make...
Thanks.
Will apply after reviewing once more tomorrow, with fix-ups locally if needed. No need to resend.
builtin-branch.c static void print_ref_list(int kinds, int detached, int verbose, int abbrev) · line 331
Quoted 3 lines
builtin-branch.c +3
-0+ + colon =strchr (value,':' ); +if (!colon)
s/^\040{8}/\011/;
Quoted 5 lines
trace.c +0
-0
trace.c static in... · line 50
Quoted 10 lines
trace.c +8
-0@@ -41,13 +41,15 @@ static in... +int nfasprintf (char **str,const char *fmt, ...) { +int rc; +va_list args; + +va_start (args, fmt); + rc =nfvasprintf (str, fmt, args); +va_end (args); +return args; }
s/return args/return rc/;
Quoted 9 lines · Junio C Hamano
Quoted 2 lines · Paolo Bonzini
This fixes all the nits you pointed out. :-D Now, this was an experience to make...
Thanks.
Will apply after reviewing once more tomorrow, with fix-ups locally if needed. No need to resend.
Gaah.
The new create_branch() is totally borked, and I did not notice it while exchanging review e-mails.
The problem is that we have:
if (real_ref == NULL)
die();which is completely bogus. We do "checkout -b temp v2.6.20~15" all the time, and in that case dwim_ref(v2.6.20~15) rightfully would say "nope, that's not a branch tip". We have no right to die there.
I think your new tests should have caught this kind of breakage, but now I notice that there is no such test that makes sure that no auto set-up is made when the original branch is not a remote tracking branch (your new tests are only interested in testing to see if the new feature works, and it does not test that there is no regression to old usage). The breakage was caught by an unrelated test, t3900.
The appended is on top of your "final". This is what I am considering to apply currently.
Grumble....
---
builtin-branch.c +17 -14
@@ -409,7 +409,6 @@ static void set_branch_defaults(const char *name, const char *real_ref) }static void create_branch (const char *name,const char *start_name, -unsigned char *start_sha1,int force,int reflog,int track) {struct ref_lock *lock;
@@ -430,15 +429,22 @@ static void create_branch(const char *name, const char *start_name, forcing =1 ; } -if (start_sha1) { -/* detached HEAD */ -hashcpy (sha1, start_sha1); + real_ref =NULL ; +if (get_sha1 (start_name, sha1)) +die ("Not a valid object name: '%s'." , start_name); + +switch (dwim_ref (start_name,strlen (start_name), sha1, &real_ref)) { +case 0 : +/* Not branching from any existing branch */ real_ref =NULL ; - } -else if (dwim_ref (start_name,strlen (start_name), sha1, &real_ref) >1 ) +break ; +case 1 : +/* Unique completion -- good */ +break ; +default :die ("Ambiguous object name: '%s'." , start_name); -else if (real_ref ==NULL ) -die ("Not a valid object name: '%s'." , start_name); +break ; + }if ((commit =lookup_commit_reference (sha1)) ==NULL )die ("Not a valid branch point: '%s'." , start_name);
@@ -619,12 +625,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)rename_branch (head, argv[i], force_rename); else if (rename && (i == argc - 2)) rename_branch(argv[i], argv[i + 1], force_rename); - else if (i == argc - 1) - create_branch(argv[i], head, head_sha1, force_create, reflog, - track); - else if (i == argc - 2) - create_branch(argv[i], argv[i+1], NULL, force_create, reflog, - track); + else if (i == argc - 1 || i == argc - 2) + create_branch(argv[i], (i == argc - 2) ? argv[i+1] : head, + force_create, reflog, track); elseusage (builtin_branch_usage );
Quoted 9 lines · Junio C Hamano
Gaah.
The new
create_branch()is totally borked, and I did not notice it while exchanging review e-mails.
...
The appended is on top of your "final". This is what I am considering to apply currently.Grumble....
I _like_ this feature. I even think it _might_ make sense to turn it on by default --- I would definitely argue for it if this were before 1.5.0 which we have enough time to give advance warning to users that the defaults and UIs would change, and the only reason I am saying "it _might_" is because it _is_ a change and it is post 1.5.0.
But because I had to say Gaah, I am currently in "discouraged" state. Extra eyeballing from the list, tests and Acks are greatly appreciated on this series from Paolo.
PATCH 1/1 git-branch: rename nor delete should not work when HEAD is detached.
In reply to Junio C HamanoWhile reviewing the latest round of 'branch --track' changes, I noticed that we do not do anything special when renaming or deleting a detached HEAD. We do not have anything to rename nor delete in this case, so instead of attempting to rename/delete refs/heads/HEAD, we should error out.
---
* CC'ed are not guilty parties, but people who might know the code better than others.
builtin-branch.c +2 -0
@@ -483,6 +483,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)die ("Failed to resolve HEAD as a valid ref." );if (!strcmp (head,"HEAD" )) { detached =1 ; +if (rename || delete) +die ("Cannot rename nor delete a detached HEAD" ); }else {if (prefixcmp (head,"refs/heads/" ))