2929
3030enum map_direction { FROM_SRC , FROM_DST };
3131
32+ enum {
33+ ENABLE_ADVICE_PULL = (1 << 0 ),
34+ ENABLE_ADVICE_PUSH = (1 << 1 ),
35+ ENABLE_ADVICE_DIVERGENCE = (1 << 2 ),
36+ };
37+
3238struct counted_string {
3339 size_t len ;
3440 const char * s ;
@@ -2234,52 +2240,58 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
22342240 return stat_branch_pair (branch -> refname , base , num_ours , num_theirs , abf );
22352241}
22362242
2237- /*
2238- * Return true when there is anything to report, otherwise false.
2239- */
2240- int format_tracking_info (struct branch * branch , struct strbuf * sb ,
2241- enum ahead_behind_flags abf ,
2242- int show_divergence_advice )
2243+ static char * resolve_compare_branch (struct branch * branch , const char * name )
22432244{
2244- int ours , theirs , sti ;
2245- const char * full_base ;
2246- char * base ;
2247- int upstream_is_gone = 0 ;
2245+ const char * resolved = NULL ;
22482246
2249- sti = stat_tracking_info (branch , & ours , & theirs , & full_base , 0 , abf );
2250- if (sti < 0 ) {
2251- if (!full_base )
2252- return 0 ;
2253- upstream_is_gone = 1 ;
2247+ if (!branch || !name )
2248+ return NULL ;
2249+
2250+ if (!strcasecmp (name , "@{upstream}" )) {
2251+ resolved = branch_get_upstream (branch , NULL );
2252+ } else if (!strcasecmp (name , "@{push}" )) {
2253+ resolved = branch_get_push (branch , NULL );
2254+ } else {
2255+ warning (_ ("ignoring value '%s' for status.compareBranches, "
2256+ "only @{upstream} and @{push} are supported" ),
2257+ name );
2258+ return NULL ;
22542259 }
22552260
2256- base = refs_shorten_unambiguous_ref (get_main_ref_store (the_repository ),
2257- full_base , 0 );
2258- if (upstream_is_gone ) {
2259- strbuf_addf (sb ,
2260- _ ("Your branch is based on '%s', but the upstream is gone.\n" ),
2261- base );
2262- if (advice_enabled (ADVICE_STATUS_HINTS ))
2263- strbuf_addstr (sb ,
2264- _ (" (use \"git branch --unset-upstream\" to fixup)\n" ));
2265- } else if (!sti ) {
2261+ if (resolved )
2262+ return xstrdup (resolved );
2263+ return NULL ;
2264+ }
2265+
2266+ static void format_branch_comparison (struct strbuf * sb ,
2267+ bool up_to_date ,
2268+ int ours , int theirs ,
2269+ const char * branch_name ,
2270+ enum ahead_behind_flags abf ,
2271+ unsigned flags )
2272+ {
2273+ bool use_push_advice = (flags & ENABLE_ADVICE_PUSH );
2274+ bool use_pull_advice = (flags & ENABLE_ADVICE_PULL );
2275+ bool use_divergence_advice = (flags & ENABLE_ADVICE_DIVERGENCE );
2276+
2277+ if (up_to_date ) {
22662278 strbuf_addf (sb ,
22672279 _ ("Your branch is up to date with '%s'.\n" ),
2268- base );
2280+ branch_name );
22692281 } else if (abf == AHEAD_BEHIND_QUICK ) {
22702282 strbuf_addf (sb ,
22712283 _ ("Your branch and '%s' refer to different commits.\n" ),
2272- base );
2273- if (advice_enabled (ADVICE_STATUS_HINTS ))
2284+ branch_name );
2285+ if (use_push_advice && advice_enabled (ADVICE_STATUS_HINTS ))
22742286 strbuf_addf (sb , _ (" (use \"%s\" for details)\n" ),
22752287 "git status --ahead-behind" );
22762288 } else if (!theirs ) {
22772289 strbuf_addf (sb ,
22782290 Q_ ("Your branch is ahead of '%s' by %d commit.\n" ,
22792291 "Your branch is ahead of '%s' by %d commits.\n" ,
22802292 ours ),
2281- base , ours );
2282- if (advice_enabled (ADVICE_STATUS_HINTS ))
2293+ branch_name , ours );
2294+ if (use_push_advice && advice_enabled (ADVICE_STATUS_HINTS ))
22832295 strbuf_addstr (sb ,
22842296 _ (" (use \"git push\" to publish your local commits)\n" ));
22852297 } else if (!ours ) {
@@ -2289,8 +2301,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
22892301 "Your branch is behind '%s' by %d commits, "
22902302 "and can be fast-forwarded.\n" ,
22912303 theirs ),
2292- base , theirs );
2293- if (advice_enabled (ADVICE_STATUS_HINTS ))
2304+ branch_name , theirs );
2305+ if (use_pull_advice && advice_enabled (ADVICE_STATUS_HINTS ))
22942306 strbuf_addstr (sb ,
22952307 _ (" (use \"git pull\" to update your local branch)\n" ));
22962308 } else {
@@ -2302,14 +2314,106 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
23022314 "and have %d and %d different commits each, "
23032315 "respectively.\n" ,
23042316 ours + theirs ),
2305- base , ours , theirs );
2306- if (show_divergence_advice &&
2307- advice_enabled (ADVICE_STATUS_HINTS ))
2317+ branch_name , ours , theirs );
2318+ if (use_divergence_advice && advice_enabled (ADVICE_STATUS_HINTS ))
23082319 strbuf_addstr (sb ,
23092320 _ (" (use \"git pull\" if you want to integrate the remote branch with yours)\n" ));
23102321 }
2311- free (base );
2312- return 1 ;
2322+ }
2323+
2324+ /*
2325+ * Return true when there is anything to report, otherwise false.
2326+ */
2327+ int format_tracking_info (struct branch * branch , struct strbuf * sb ,
2328+ enum ahead_behind_flags abf ,
2329+ int show_divergence_advice )
2330+ {
2331+ char * compare_branches = NULL ;
2332+ struct string_list branches = STRING_LIST_INIT_DUP ;
2333+ struct strset processed_refs = STRSET_INIT ;
2334+ int reported = 0 ;
2335+ size_t i ;
2336+ const char * upstream_ref ;
2337+ const char * push_ref ;
2338+
2339+ repo_config_get_string (the_repository , "status.comparebranches" ,
2340+ & compare_branches );
2341+
2342+ if (compare_branches ) {
2343+ string_list_split (& branches , compare_branches , " " , -1 );
2344+ string_list_remove_empty_items (& branches , 0 );
2345+ } else {
2346+ string_list_append (& branches , "@{upstream}" );
2347+ }
2348+
2349+ upstream_ref = branch_get_upstream (branch , NULL );
2350+ push_ref = branch_get_push (branch , NULL );
2351+
2352+ for (i = 0 ; i < branches .nr ; i ++ ) {
2353+ char * full_ref ;
2354+ char * short_ref ;
2355+ int ours , theirs , cmp ;
2356+ int is_upstream , is_push ;
2357+ unsigned flags = 0 ;
2358+
2359+ full_ref = resolve_compare_branch (branch ,
2360+ branches .items [i ].string );
2361+ if (!full_ref )
2362+ continue ;
2363+
2364+ if (!strset_add (& processed_refs , full_ref )) {
2365+ free (full_ref );
2366+ continue ;
2367+ }
2368+
2369+ short_ref = refs_shorten_unambiguous_ref (
2370+ get_main_ref_store (the_repository ), full_ref , 0 );
2371+
2372+ is_upstream = upstream_ref && !strcmp (full_ref , upstream_ref );
2373+ is_push = push_ref && !strcmp (full_ref , push_ref );
2374+
2375+ if (is_upstream && (!push_ref || !strcmp (upstream_ref , push_ref )))
2376+ is_push = 1 ;
2377+
2378+ cmp = stat_branch_pair (branch -> refname , full_ref ,
2379+ & ours , & theirs , abf );
2380+
2381+ if (cmp < 0 ) {
2382+ if (is_upstream ) {
2383+ strbuf_addf (sb ,
2384+ _ ("Your branch is based on '%s', but the upstream is gone.\n" ),
2385+ short_ref );
2386+ if (advice_enabled (ADVICE_STATUS_HINTS ))
2387+ strbuf_addstr (sb ,
2388+ _ (" (use \"git branch --unset-upstream\" to fixup)\n" ));
2389+ reported = 1 ;
2390+ }
2391+ free (full_ref );
2392+ free (short_ref );
2393+ continue ;
2394+ }
2395+
2396+ if (reported )
2397+ strbuf_addstr (sb , "\n" );
2398+
2399+ if (is_upstream )
2400+ flags |= ENABLE_ADVICE_PULL ;
2401+ if (is_push )
2402+ flags |= ENABLE_ADVICE_PUSH ;
2403+ if (show_divergence_advice && is_upstream )
2404+ flags |= ENABLE_ADVICE_DIVERGENCE ;
2405+ format_branch_comparison (sb , !cmp , ours , theirs , short_ref ,
2406+ abf , flags );
2407+ reported = 1 ;
2408+
2409+ free (full_ref );
2410+ free (short_ref );
2411+ }
2412+
2413+ string_list_clear (& branches , 0 );
2414+ strset_clear (& processed_refs );
2415+ free (compare_branches );
2416+ return reported ;
23132417}
23142418
23152419static int one_local_ref (const struct reference * ref , void * cb_data )
0 commit comments