Skip to content

Commit f88395a

Browse files
author
Junio C Hamano
committed
Renaming push.
This allows git-send-pack to push local refs to a destination repository under different names. Here is the name mapping rules for refs. * If there is no ref mapping on the command line: - if '--all' is specified, it is equivalent to specifying <local> ":" <local> for all the existing local refs on the command line - otherwise, it is equivalent to specifying <ref> ":" <ref> for all the refs that exist on both sides. * <name> is just a shorthand for <name> ":" <name> * <src> ":" <dst> push ref that matches <src> to ref that matches <dst>. - It is an error if <src> does not match exactly one of local refs. - It is an error if <dst> matches more than one remote refs. - If <dst> does not match any remote refs, either - it has to start with "refs/"; <dst> is used as the destination literally in this case. - <src> == <dst> and the ref that matched the <src> must not exist in the set of remote refs; the ref matched <src> locally is used as the name of the destination. For example, - "git-send-pack --all <remote>" works exactly as before; - "git-send-pack <remote> master:upstream" pushes local master to remote ref that matches "upstream". If there is no such ref, it is an error. - "git-send-pack <remote> master:refs/heads/upstream" pushes local master to remote refs/heads/upstream, even when refs/heads/upstream does not exist. - "git-send-pack <remote> master" into an empty remote repository pushes the local ref/heads/master to the remote ref/heads/master. Signed-off-by: Junio C Hamano <junkio@cox.net>
1 parent 8d5afef commit f88395a

File tree

3 files changed

+226
-94
lines changed

3 files changed

+226
-94
lines changed

cache.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,12 +302,15 @@ struct ref {
302302
struct ref *next;
303303
unsigned char old_sha1[20];
304304
unsigned char new_sha1[20];
305+
struct ref *peer_ref; /* when renaming */
305306
char name[0];
306307
};
307308

308309
extern int git_connect(int fd[2], char *url, const char *prog);
309310
extern int finish_connect(pid_t pid);
310311
extern int path_match(const char *path, int nr, char **match);
312+
extern int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
313+
int nr_refspec, char **refspec, int all);
311314
extern int get_ack(int fd, unsigned char *result_sha1);
312315
extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match);
313316

connect.c

Lines changed: 169 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,9 @@ struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **ma
3131
name = buffer + 41;
3232
if (nr_match && !path_match(name, nr_match, match))
3333
continue;
34-
ref = xmalloc(sizeof(*ref) + len - 40);
34+
ref = xcalloc(1, sizeof(*ref) + len - 40);
3535
memcpy(ref->old_sha1, old_sha1, 20);
36-
memset(ref->new_sha1, 0, 20);
3736
memcpy(ref->name, buffer + 41, len - 40);
38-
ref->next = NULL;
3937
*list = ref;
4038
list = &ref->next;
4139
}
@@ -81,6 +79,174 @@ int path_match(const char *path, int nr, char **match)
8179
return 0;
8280
}
8381

82+
struct refspec {
83+
char *src;
84+
char *dst;
85+
};
86+
87+
static struct refspec *parse_ref_spec(int nr_refspec, char **refspec)
88+
{
89+
int i;
90+
struct refspec *rs = xmalloc(sizeof(*rs) * (nr_refspec + 1));
91+
for (i = 0; i < nr_refspec; i++) {
92+
char *sp, *dp, *ep;
93+
sp = refspec[i];
94+
ep = strchr(sp, ':');
95+
if (ep) {
96+
dp = ep + 1;
97+
*ep = 0;
98+
}
99+
else
100+
dp = sp;
101+
rs[i].src = sp;
102+
rs[i].dst = dp;
103+
}
104+
rs[nr_refspec].src = rs[nr_refspec].dst = NULL;
105+
return rs;
106+
}
107+
108+
static int count_refspec_match(const char *pattern,
109+
struct ref *refs,
110+
struct ref **matched_ref)
111+
{
112+
int match;
113+
int patlen = strlen(pattern);
114+
115+
for (match = 0; refs; refs = refs->next) {
116+
char *name = refs->name;
117+
int namelen = strlen(name);
118+
if (namelen < patlen ||
119+
memcmp(name + namelen - patlen, pattern, patlen))
120+
continue;
121+
if (namelen != patlen && name[namelen - patlen - 1] != '/')
122+
continue;
123+
match++;
124+
*matched_ref = refs;
125+
}
126+
return match;
127+
}
128+
129+
static void link_dst_tail(struct ref *ref, struct ref ***tail)
130+
{
131+
**tail = ref;
132+
*tail = &ref->next;
133+
**tail = NULL;
134+
}
135+
136+
static int match_explicit_refs(struct ref *src, struct ref *dst,
137+
struct ref ***dst_tail, struct refspec *rs)
138+
{
139+
int i, errs;
140+
for (i = errs = 0; rs[i].src; i++) {
141+
struct ref *matched_src, *matched_dst;
142+
143+
matched_src = matched_dst = NULL;
144+
switch (count_refspec_match(rs[i].src, src, &matched_src)) {
145+
case 1:
146+
break;
147+
case 0:
148+
errs = 1;
149+
error("src refspec %s does not match any.");
150+
break;
151+
default:
152+
errs = 1;
153+
error("src refspec %s matches more than one.",
154+
rs[i].src);
155+
break;
156+
}
157+
switch (count_refspec_match(rs[i].dst, dst, &matched_dst)) {
158+
case 1:
159+
break;
160+
case 0:
161+
if (!memcmp(rs[i].dst, "refs/", 5)) {
162+
int len = strlen(rs[i].dst) + 1;
163+
matched_dst = xcalloc(1, sizeof(*dst) + len);
164+
memcpy(matched_dst->name, rs[i].dst, len);
165+
link_dst_tail(matched_dst, dst_tail);
166+
}
167+
else if (!strcmp(rs[i].src, rs[i].dst) &&
168+
matched_src) {
169+
/* pushing "master:master" when
170+
* remote does not have master yet.
171+
*/
172+
int len = strlen(matched_src->name);
173+
matched_dst = xcalloc(1, sizeof(*dst) + len);
174+
memcpy(matched_dst->name, matched_src->name,
175+
len);
176+
link_dst_tail(matched_dst, dst_tail);
177+
}
178+
else {
179+
errs = 1;
180+
error("dst refspec %s does not match any "
181+
"existing ref on the remote and does "
182+
"not start with refs/.", rs[i].dst);
183+
}
184+
break;
185+
default:
186+
errs = 1;
187+
error("dst refspec %s matches more than one.",
188+
rs[i].dst);
189+
break;
190+
}
191+
if (errs)
192+
continue;
193+
if (matched_src->peer_ref) {
194+
errs = 1;
195+
error("src ref %s is sent to more than one dst.",
196+
matched_src->name);
197+
}
198+
else
199+
matched_src->peer_ref = matched_dst;
200+
if (matched_dst->peer_ref) {
201+
errs = 1;
202+
error("dst ref %s receives from more than one src.",
203+
matched_dst->name);
204+
}
205+
else
206+
matched_dst->peer_ref = matched_src;
207+
}
208+
return -errs;
209+
}
210+
211+
static struct ref *find_ref_by_name(struct ref *list, const char *name)
212+
{
213+
for ( ; list; list = list->next)
214+
if (!strcmp(list->name, name))
215+
return list;
216+
return NULL;
217+
}
218+
219+
int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
220+
int nr_refspec, char **refspec, int all)
221+
{
222+
struct refspec *rs = parse_ref_spec(nr_refspec, refspec);
223+
224+
if (nr_refspec)
225+
return match_explicit_refs(src, dst, dst_tail, rs);
226+
227+
/* pick the remainder */
228+
for ( ; src; src = src->next) {
229+
struct ref *dst_peer;
230+
if (src->peer_ref)
231+
continue;
232+
dst_peer = find_ref_by_name(dst, src->name);
233+
if (dst_peer && dst_peer->peer_ref)
234+
continue;
235+
if (!dst_peer) {
236+
if (!all)
237+
continue;
238+
/* Create a new one and link it */
239+
int len = strlen(src->name) + 1;
240+
dst_peer = xcalloc(1, sizeof(*dst_peer) + len);
241+
memcpy(dst_peer->name, src->name, len);
242+
memcpy(dst_peer->new_sha1, src->new_sha1, 20);
243+
link_dst_tail(dst_peer, dst_tail);
244+
}
245+
dst_peer->peer_ref = src;
246+
}
247+
return 0;
248+
}
249+
84250
enum protocol {
85251
PROTO_LOCAL = 1,
86252
PROTO_SSH,

send-pack.c

Lines changed: 54 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -104,21 +104,6 @@ static int pack_objects(int fd, struct ref *refs)
104104
return 0;
105105
}
106106

107-
static int read_ref(const char *ref, unsigned char *sha1)
108-
{
109-
int fd, ret;
110-
char buffer[60];
111-
112-
fd = open(git_path("%s", ref), O_RDONLY);
113-
if (fd < 0)
114-
return -1;
115-
ret = -1;
116-
if (read(fd, buffer, sizeof(buffer)) >= 40)
117-
ret = get_sha1_hex(buffer, sha1);
118-
close(fd);
119-
return ret;
120-
}
121-
122107
static int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
123108
{
124109
struct commit *new, *old;
@@ -144,114 +129,92 @@ static int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha
144129
return 0;
145130
}
146131

147-
static int local_ref_nr_match;
148-
static char **local_ref_match;
149-
static struct ref *local_ref_list;
150-
static struct ref **local_last_ref;
132+
static struct ref *local_refs, **local_tail;
133+
static struct ref *remote_refs, **remote_tail;
151134

152-
static int try_to_match(const char *refname, const unsigned char *sha1)
135+
static int one_local_ref(const char *refname, const unsigned char *sha1)
153136
{
154137
struct ref *ref;
155-
int len;
156-
157-
if (!path_match(refname, local_ref_nr_match, local_ref_match)) {
158-
if (!send_all)
159-
return 0;
160-
161-
/* If we have it listed already, skip it */
162-
for (ref = local_ref_list ; ref ; ref = ref->next) {
163-
if (!strcmp(ref->name, refname))
164-
return 0;
165-
}
166-
}
167-
168-
len = strlen(refname)+1;
169-
ref = xmalloc(sizeof(*ref) + len);
170-
memset(ref->old_sha1, 0, 20);
138+
int len = strlen(refname) + 1;
139+
ref = xcalloc(1, sizeof(*ref) + len);
171140
memcpy(ref->new_sha1, sha1, 20);
172141
memcpy(ref->name, refname, len);
173-
ref->next = NULL;
174-
*local_last_ref = ref;
175-
local_last_ref = &ref->next;
142+
*local_tail = ref;
143+
local_tail = &ref->next;
176144
return 0;
177145
}
178146

179-
static int send_pack(int in, int out, int nr_match, char **match)
147+
static void get_local_heads(void)
148+
{
149+
local_tail = &local_refs;
150+
for_each_ref(one_local_ref);
151+
}
152+
153+
static int send_pack(int in, int out, int nr_refspec, char **refspec)
180154
{
181-
struct ref *ref_list, **last_ref;
182155
struct ref *ref;
183156
int new_refs;
184157

185-
/* First we get all heads, whether matching or not.. */
186-
last_ref = get_remote_heads(in, &ref_list, 0, NULL);
158+
/* No funny business with the matcher */
159+
remote_tail = get_remote_heads(in, &remote_refs, 0, NULL);
160+
get_local_heads();
187161

162+
/* match them up */
163+
if (!remote_tail)
164+
remote_tail = &remote_refs;
165+
if (match_refs(local_refs, remote_refs, &remote_tail,
166+
nr_refspec, refspec, send_all))
167+
return -1;
188168
/*
189-
* Go through the refs, see if we want to update
190-
* any of them..
169+
* Finally, tell the other end!
191170
*/
192-
for (ref = ref_list; ref; ref = ref->next) {
193-
unsigned char new_sha1[20];
194-
char *name = ref->name;
195-
196-
if (nr_match && !path_match(name, nr_match, match))
197-
continue;
198-
199-
if (read_ref(name, new_sha1) < 0)
200-
continue;
201-
202-
if (!memcmp(ref->old_sha1, new_sha1, 20)) {
203-
fprintf(stderr, "'%s' unchanged\n", name);
171+
new_refs = 0;
172+
for (ref = remote_refs; ref; ref = ref->next) {
173+
char old_hex[60], *new_hex;
174+
if (!ref->peer_ref)
204175
continue;
176+
if (!is_zero_sha1(ref->old_sha1)) {
177+
if (!has_sha1_file(ref->old_sha1)) {
178+
error("remote '%s' object %s does not "
179+
"exist on local",
180+
ref->name, sha1_to_hex(ref->old_sha1));
181+
continue;
182+
}
183+
if (!ref_newer(ref->peer_ref->new_sha1,
184+
ref->old_sha1)) {
185+
error("remote ref '%s' is not a strict "
186+
"subset of local ref '%s'.", ref->name,
187+
ref->peer_ref->name);
188+
continue;
189+
}
205190
}
206-
207-
if (!has_sha1_file(ref->old_sha1)) {
208-
error("remote '%s' object %s does not exist on local",
209-
name, sha1_to_hex(ref->old_sha1));
191+
if (!memcmp(ref->old_sha1, ref->peer_ref->new_sha1, 20)) {
192+
fprintf(stderr, "'%s': up-to-date\n", ref->name);
210193
continue;
211194
}
212-
213-
if (!ref_newer(new_sha1, ref->old_sha1)) {
214-
error("remote '%s' isn't a strict parent of local", name);
195+
memcpy(ref->new_sha1, ref->peer_ref->new_sha1, 20);
196+
if (is_zero_sha1(ref->new_sha1)) {
197+
error("cannot happen anymore");
215198
continue;
216199
}
217-
218-
/* Ok, mark it for update */
219-
memcpy(ref->new_sha1, new_sha1, 20);
220-
}
221-
222-
/*
223-
* See if we have any refs that the other end didn't have
224-
*/
225-
if (nr_match || send_all) {
226-
local_ref_nr_match = nr_match;
227-
local_ref_match = match;
228-
local_ref_list = ref_list;
229-
local_last_ref = last_ref;
230-
for_each_ref(try_to_match);
231-
}
232-
233-
/*
234-
* Finally, tell the other end!
235-
*/
236-
new_refs = 0;
237-
for (ref = ref_list; ref; ref = ref->next) {
238-
char old_hex[60], *new_hex;
239-
if (is_zero_sha1(ref->new_sha1))
240-
continue;
241200
new_refs++;
242201
strcpy(old_hex, sha1_to_hex(ref->old_sha1));
243202
new_hex = sha1_to_hex(ref->new_sha1);
244203
packet_write(out, "%s %s %s", old_hex, new_hex, ref->name);
245-
fprintf(stderr, "'%s': updating from %s to %s\n", ref->name, old_hex, new_hex);
204+
fprintf(stderr, "updating '%s'", ref->name);
205+
if (strcmp(ref->name, ref->peer_ref->name))
206+
fprintf(stderr, " using '%s'", ref->peer_ref->name);
207+
fprintf(stderr, "\n from %s\n to %s\n", old_hex, new_hex);
246208
}
247-
209+
248210
packet_flush(out);
249211
if (new_refs)
250-
pack_objects(out, ref_list);
212+
pack_objects(out, remote_refs);
251213
close(out);
252214
return 0;
253215
}
254216

217+
255218
int main(int argc, char **argv)
256219
{
257220
int i, nr_heads = 0;

0 commit comments

Comments
 (0)