11#include " ../subcommand/push_subcommand.hpp"
22
33#include < iostream>
4+ #include < optional>
5+ #include < unordered_map>
6+ #include < string_view>
47
5- #include < git2/remote .h>
8+ #include < git2.h>
69
10+ #include " ../utils/ansi_code.hpp"
711#include " ../utils/credentials.hpp"
812#include " ../utils/progress.hpp"
913#include " ../wrapper/repository_wrapper.hpp"
@@ -13,8 +17,15 @@ push_subcommand::push_subcommand(const libgit2_object&, CLI::App& app)
1317 auto * sub = app.add_subcommand (" push" , " Update remote refs along with associated objects" );
1418
1519 sub->add_option (" <remote>" , m_remote_name, " The remote to push to" )->default_val (" origin" );
16-
20+ sub-> add_option ( " <branch> " , m_branch_name, " The branch to push " );
1721 sub->add_option (" <refspec>" , m_refspecs, " The refspec(s) to push" );
22+ sub->add_flag (
23+ " --all,--branches" ,
24+ m_branches_flag,
25+ " Push all branches (i.e. refs under " + ansi_code::bold + " refs/heads/" + ansi_code::reset
26+ + " ); cannot be used with other <refspec>."
27+ );
28+
1829
1930 sub->callback (
2031 [this ]()
@@ -24,6 +35,15 @@ push_subcommand::push_subcommand(const libgit2_object&, CLI::App& app)
2435 );
2536}
2637
38+ // TODO: put in common
39+ static std::string oid_to_hex (const git_oid& oid)
40+ {
41+ char oid_str[GIT_OID_SHA1_HEXSIZE + 1 ];
42+ git_oid_fmt (oid_str, &oid);
43+ oid_str[GIT_OID_SHA1_HEXSIZE] = ' \0 ' ;
44+ return std::string (oid_str);
45+ }
46+
2747void push_subcommand::run ()
2848{
2949 auto directory = get_current_git_path ();
@@ -37,25 +57,127 @@ void push_subcommand::run()
3757 push_opts.callbacks .push_transfer_progress = push_transfer_progress;
3858 push_opts.callbacks .push_update_reference = push_update_reference;
3959
40- if (m_refspecs. empty () )
60+ if (m_branches_flag )
4161 {
42- try
62+ auto iter = repo.iterate_branches (GIT_BRANCH_LOCAL);
63+ auto br = iter.next ();
64+ while (br)
4365 {
44- auto head_ref = repo.head ();
45- std::string short_name = head_ref.short_name ();
46- std::string refspec = " refs/heads/" + short_name;
66+ std::string refspec = " refs/heads/" + std::string (br->name ());
4767 m_refspecs.push_back (refspec);
68+ br = iter.next ();
69+ }
70+ }
71+ else if (m_refspecs.empty ())
72+ {
73+ std::string branch;
74+ if (!m_branch_name.empty ())
75+ {
76+ branch = m_branch_name;
4877 }
49- catch (...)
78+ else
5079 {
51- std::cerr << " Could not determine current branch to push." << std::endl;
52- return ;
80+ try
81+ {
82+ auto head_ref = repo.head ();
83+ branch = head_ref.short_name ();
84+ }
85+ catch (...)
86+ {
87+ std::cerr << " Could not determine current branch to push." << std::endl;
88+ return ;
89+ }
5390 }
91+ std::string refspec = " refs/heads/" + branch;
92+ m_refspecs.push_back (refspec);
5493 }
5594 git_strarray_wrapper refspecs_wrapper (m_refspecs);
5695 git_strarray* refspecs_ptr = nullptr ;
5796 refspecs_ptr = refspecs_wrapper;
5897
98+ // Take a snapshot of remote branches to check which ones are new after push
99+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
100+ callbacks.credentials = user_credentials;
101+ credentials_payload creds_payload;
102+ callbacks.payload = &creds_payload;
103+ push_opts.callbacks .payload = &creds_payload;
104+
105+ auto remote_heads = remote.list_heads (&callbacks);
106+
107+ // Map with names of branches and their oids before push
108+ std::unordered_map<std::string, git_oid> remote_heads_map;
109+ for (const auto & h : remote_heads)
110+ {
111+ remote_heads_map.emplace (h.name , h.oid );
112+ }
113+
59114 remote.push (refspecs_ptr, &push_opts);
60- std::cout << " Pushed to " << remote_name << std::endl;
115+
116+ std::cout << " To " << remote.url () << std::endl;
117+ for (const auto & refspec : m_refspecs)
118+ {
119+ std::string_view ref_view (refspec);
120+ std::string_view prefix = " refs/heads/" ;
121+ std::string local_short_name;
122+ if (ref_view.substr (0 , prefix.size ()) == prefix)
123+ {
124+ local_short_name = ref_view.substr (prefix.size ());
125+ }
126+ else
127+ {
128+ local_short_name = refspec;
129+ }
130+
131+ std::optional<std::string> upstream_opt = repo.branch_upstream_name (local_short_name);
132+
133+ std::string remote_branch = local_short_name;
134+ std::string remote_ref = " refs/heads/" + local_short_name;
135+ if (upstream_opt.has_value ())
136+ {
137+ const std::string up_name = upstream_opt.value ();
138+ auto pos = up_name.find (' /' );
139+ if (pos != std::string::npos && pos + 1 < up_name.size ())
140+ {
141+ std::string up_remote = up_name.substr (0 , pos);
142+ std::string up_branch = up_name.substr (pos + 1 );
143+ if (up_remote == remote_name)
144+ {
145+ remote_branch = up_name.substr (pos + 1 );
146+ remote_ref = " refs/heads/" + remote_branch;
147+ }
148+ }
149+ }
150+
151+ auto iter = remote_heads_map.find (remote_ref);
152+ if (iter == remote_heads_map.end ())
153+ {
154+ std::cout << " * [new branch] " << local_short_name << " -> " << remote_branch << std::endl;
155+ continue ;
156+ }
157+
158+ git_oid remote_oid = iter->second ;
159+
160+ std::optional<git_oid> local_oid_opt;
161+ if (auto ref_opt = repo.find_reference_dwim ((" refs/heads/" + local_short_name)))
162+ {
163+ const git_oid* target = ref_opt->target ();
164+ local_oid_opt = *target; // TODO: pas comprenu pourquoi je ne peux pas faire local_oid_opt = ref_opt->target();
165+ }
166+
167+ if (!local_oid_opt)
168+ {
169+ std::cout << " " << local_short_name << " -> " << remote_branch << std::endl;
170+ continue ;
171+ }
172+ git_oid local_oid = local_oid_opt.value ();
173+
174+ if (!git_oid_equal (&remote_oid, &local_oid))
175+ {
176+ std::string old_hex = oid_to_hex (remote_oid);
177+ std::string new_hex = oid_to_hex (local_oid);
178+ // TODO: check order of hex codes
179+ std::cout << " " << old_hex.substr (0 , 7 ) << " .." << new_hex.substr (0 , 7 )
180+ << " " << local_short_name << " -> " << local_short_name << std::endl;
181+ }
182+ }
61183}
0 commit comments