11
11
12
12
struct tracking {
13
13
struct refspec_item spec ;
14
- char * src ;
14
+ struct string_list * srcs ;
15
15
const char * remote ;
16
16
int matches ;
17
17
};
@@ -22,11 +22,11 @@ static int find_tracked_branch(struct remote *remote, void *priv)
22
22
23
23
if (!remote_find_tracking (remote , & tracking -> spec )) {
24
24
if (++ tracking -> matches == 1 ) {
25
- tracking -> src = tracking -> spec .src ;
25
+ string_list_append ( tracking -> srcs , tracking -> spec .src ) ;
26
26
tracking -> remote = remote -> name ;
27
27
} else {
28
28
free (tracking -> spec .src );
29
- FREE_AND_NULL (tracking -> src );
29
+ string_list_clear (tracking -> srcs , 0 );
30
30
}
31
31
tracking -> spec .src = NULL ;
32
32
}
@@ -49,34 +49,64 @@ static int should_setup_rebase(const char *origin)
49
49
return 0 ;
50
50
}
51
51
52
- static const char tracking_advice [] =
53
- N_ ("\n"
54
- "After fixing the error cause you may try to fix up\n"
55
- "the remote tracking information by invoking\n"
56
- "\"git branch --set-upstream-to=%s%s%s\"." );
57
-
58
- int install_branch_config (int flag , const char * local , const char * origin , const char * remote )
52
+ /**
53
+ * Install upstream tracking configuration for a branch; specifically, add
54
+ * `branch.<name>.remote` and `branch.<name>.merge` entries.
55
+ *
56
+ * `flag` contains integer flags for options; currently only
57
+ * BRANCH_CONFIG_VERBOSE is checked.
58
+ *
59
+ * `local` is the name of the branch whose configuration we're installing.
60
+ *
61
+ * `origin` is the name of the remote owning the upstream branches. NULL means
62
+ * the upstream branches are local to this repo.
63
+ *
64
+ * `remotes` is a list of refs that are upstream of local
65
+ */
66
+ static int install_branch_config_multiple_remotes (int flag , const char * local ,
67
+ const char * origin , struct string_list * remotes )
59
68
{
60
69
const char * shortname = NULL ;
61
70
struct strbuf key = STRBUF_INIT ;
71
+ struct string_list_item * item ;
62
72
int rebasing = should_setup_rebase (origin );
63
73
64
- if (skip_prefix (remote , "refs/heads/" , & shortname )
65
- && !strcmp (local , shortname )
66
- && !origin ) {
67
- warning (_ ("Not setting branch %s as its own upstream." ),
68
- local );
69
- return 0 ;
70
- }
74
+ if (!remotes -> nr )
75
+ BUG ("must provide at least one remote for branch config" );
76
+ if (rebasing && remotes -> nr > 1 )
77
+ die (_ ("cannot inherit upstream tracking configuration of "
78
+ "multiple refs when rebasing is requested" ));
79
+
80
+ /*
81
+ * If the new branch is trying to track itself, something has gone
82
+ * wrong. Warn the user and don't proceed any further.
83
+ */
84
+ if (!origin )
85
+ for_each_string_list_item (item , remotes )
86
+ if (skip_prefix (item -> string , "refs/heads/" , & shortname )
87
+ && !strcmp (local , shortname )) {
88
+ warning (_ ("not setting branch '%s' as its own upstream." ),
89
+ local );
90
+ return 0 ;
91
+ }
71
92
72
93
strbuf_addf (& key , "branch.%s.remote" , local );
73
94
if (git_config_set_gently (key .buf , origin ? origin : "." ) < 0 )
74
95
goto out_err ;
75
96
76
97
strbuf_reset (& key );
77
98
strbuf_addf (& key , "branch.%s.merge" , local );
78
- if (git_config_set_gently (key .buf , remote ) < 0 )
99
+ /*
100
+ * We want to overwrite any existing config with all the branches in
101
+ * "remotes". Override any existing config, then write our branches. If
102
+ * more than one is provided, use CONFIG_REGEX_NONE to preserve what
103
+ * we've written so far.
104
+ */
105
+ if (git_config_set_gently (key .buf , NULL ) < 0 )
79
106
goto out_err ;
107
+ for_each_string_list_item (item , remotes )
108
+ if (git_config_set_multivar_gently (key .buf , item -> string , CONFIG_REGEX_NONE , 0 ) < 0 )
109
+ goto out_err ;
80
110
81
111
if (rebasing ) {
82
112
strbuf_reset (& key );
@@ -87,29 +117,40 @@ int install_branch_config(int flag, const char *local, const char *origin, const
87
117
strbuf_release (& key );
88
118
89
119
if (flag & BRANCH_CONFIG_VERBOSE ) {
90
- if (shortname ) {
91
- if (origin )
92
- printf_ln (rebasing ?
93
- _ ("Branch '%s' set up to track remote branch '%s' from '%s' by rebasing." ) :
94
- _ ("Branch '%s' set up to track remote branch '%s' from '%s'." ),
95
- local , shortname , origin );
96
- else
97
- printf_ln (rebasing ?
98
- _ ("Branch '%s' set up to track local branch '%s' by rebasing." ) :
99
- _ ("Branch '%s' set up to track local branch '%s'." ),
100
- local , shortname );
120
+ struct strbuf tmp_ref_name = STRBUF_INIT ;
121
+ struct string_list friendly_ref_names = STRING_LIST_INIT_DUP ;
122
+
123
+ for_each_string_list_item (item , remotes ) {
124
+ shortname = item -> string ;
125
+ skip_prefix (shortname , "refs/heads/" , & shortname );
126
+ if (origin ) {
127
+ strbuf_addf (& tmp_ref_name , "%s/%s" ,
128
+ origin , shortname );
129
+ string_list_append_nodup (
130
+ & friendly_ref_names ,
131
+ strbuf_detach (& tmp_ref_name , NULL ));
132
+ } else {
133
+ string_list_append (
134
+ & friendly_ref_names , shortname );
135
+ }
136
+ }
137
+
138
+ if (remotes -> nr == 1 ) {
139
+ /*
140
+ * Rebasing is only allowed in the case of a single
141
+ * upstream branch.
142
+ */
143
+ printf_ln (rebasing ?
144
+ _ ("branch '%s' set up to track '%s' by rebasing." ) :
145
+ _ ("branch '%s' set up to track '%s'." ),
146
+ local , friendly_ref_names .items [0 ].string );
101
147
} else {
102
- if (origin )
103
- printf_ln (rebasing ?
104
- _ ("Branch '%s' set up to track remote ref '%s' by rebasing." ) :
105
- _ ("Branch '%s' set up to track remote ref '%s'." ),
106
- local , remote );
107
- else
108
- printf_ln (rebasing ?
109
- _ ("Branch '%s' set up to track local ref '%s' by rebasing." ) :
110
- _ ("Branch '%s' set up to track local ref '%s'." ),
111
- local , remote );
148
+ printf_ln (_ ("branch '%s' set up to track:" ), local );
149
+ for_each_string_list_item (item , & friendly_ref_names )
150
+ printf_ln (" %s" , item -> string );
112
151
}
152
+
153
+ string_list_clear (& friendly_ref_names , 0 );
113
154
}
114
155
115
156
return 0 ;
@@ -118,14 +159,64 @@ int install_branch_config(int flag, const char *local, const char *origin, const
118
159
strbuf_release (& key );
119
160
error (_ ("Unable to write upstream branch configuration" ));
120
161
121
- advise (_ (tracking_advice ),
122
- origin ? origin : "" ,
123
- origin ? "/" : "" ,
124
- shortname ? shortname : remote );
162
+ advise (_ ("\nAfter fixing the error cause you may try to fix up\n"
163
+ "the remote tracking information by invoking:" ));
164
+ if (remotes -> nr == 1 )
165
+ advise (" git branch --set-upstream-to=%s%s%s" ,
166
+ origin ? origin : "" ,
167
+ origin ? "/" : "" ,
168
+ remotes -> items [0 ].string );
169
+ else {
170
+ advise (" git config --add branch.\"%s\".remote %s" ,
171
+ local , origin ? origin : "." );
172
+ for_each_string_list_item (item , remotes )
173
+ advise (" git config --add branch.\"%s\".merge %s" ,
174
+ local , item -> string );
175
+ }
125
176
126
177
return -1 ;
127
178
}
128
179
180
+ int install_branch_config (int flag , const char * local , const char * origin ,
181
+ const char * remote )
182
+ {
183
+ int ret ;
184
+ struct string_list remotes = STRING_LIST_INIT_DUP ;
185
+
186
+ string_list_append (& remotes , remote );
187
+ ret = install_branch_config_multiple_remotes (flag , local , origin , & remotes );
188
+ string_list_clear (& remotes , 0 );
189
+ return ret ;
190
+ }
191
+
192
+ static int inherit_tracking (struct tracking * tracking , const char * orig_ref )
193
+ {
194
+ const char * bare_ref ;
195
+ struct branch * branch ;
196
+ int i ;
197
+
198
+ bare_ref = orig_ref ;
199
+ skip_prefix (orig_ref , "refs/heads/" , & bare_ref );
200
+
201
+ branch = branch_get (bare_ref );
202
+ if (!branch -> remote_name ) {
203
+ warning (_ ("asked to inherit tracking from '%s', but no remote is set" ),
204
+ bare_ref );
205
+ return -1 ;
206
+ }
207
+
208
+ if (branch -> merge_nr < 1 || !branch -> merge_name || !branch -> merge_name [0 ]) {
209
+ warning (_ ("asked to inherit tracking from '%s', but no merge configuration is set" ),
210
+ bare_ref );
211
+ return -1 ;
212
+ }
213
+
214
+ tracking -> remote = xstrdup (branch -> remote_name );
215
+ for (i = 0 ; i < branch -> merge_nr ; i ++ )
216
+ string_list_append (tracking -> srcs , branch -> merge_name [i ]);
217
+ return 0 ;
218
+ }
219
+
129
220
/*
130
221
* This is called when new_ref is branched off of orig_ref, and tries
131
222
* to infer the settings for branch.<new_ref>.{remote,merge} from the
@@ -135,18 +226,23 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
135
226
enum branch_track track , int quiet )
136
227
{
137
228
struct tracking tracking ;
229
+ struct string_list tracking_srcs = STRING_LIST_INIT_DUP ;
138
230
int config_flags = quiet ? 0 : BRANCH_CONFIG_VERBOSE ;
139
231
140
232
memset (& tracking , 0 , sizeof (tracking ));
141
233
tracking .spec .dst = (char * )orig_ref ;
142
- if (for_each_remote (find_tracked_branch , & tracking ))
234
+ tracking .srcs = & tracking_srcs ;
235
+ if (track != BRANCH_TRACK_INHERIT )
236
+ for_each_remote (find_tracked_branch , & tracking );
237
+ else if (inherit_tracking (& tracking , orig_ref ))
143
238
return ;
144
239
145
240
if (!tracking .matches )
146
241
switch (track ) {
147
242
case BRANCH_TRACK_ALWAYS :
148
243
case BRANCH_TRACK_EXPLICIT :
149
244
case BRANCH_TRACK_OVERRIDE :
245
+ case BRANCH_TRACK_INHERIT :
150
246
break ;
151
247
default :
152
248
return ;
@@ -156,11 +252,13 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
156
252
die (_ ("Not tracking: ambiguous information for ref %s" ),
157
253
orig_ref );
158
254
159
- if (install_branch_config (config_flags , new_ref , tracking .remote ,
160
- tracking .src ? tracking .src : orig_ref ) < 0 )
255
+ if (tracking .srcs -> nr < 1 )
256
+ string_list_append (tracking .srcs , orig_ref );
257
+ if (install_branch_config_multiple_remotes (config_flags , new_ref ,
258
+ tracking .remote , tracking .srcs ) < 0 )
161
259
exit (-1 );
162
260
163
- free (tracking .src );
261
+ string_list_clear (tracking .srcs , 0 );
164
262
}
165
263
166
264
int read_branch_desc (struct strbuf * buf , const char * branch_name )
0 commit comments