Skip to content

Commit d365a43

Browse files
committed
Merge branch 'fc/push-prune'
* fc/push-prune: push: add '--prune' option remote: refactor code into alloc_delete_ref() remote: reorganize check_pattern_match() remote: use a local variable in match_push_refs() Conflicts: builtin/push.c
2 parents 5419127 + 6ddba5e commit d365a43

File tree

7 files changed

+101
-40
lines changed

7 files changed

+101
-40
lines changed

Documentation/git-push.txt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ SYNOPSIS
1010
--------
1111
[verse]
1212
'git push' [--all | --mirror | --tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
13-
[--repo=<repository>] [-f | --force] [-v | --verbose] [-u | --set-upstream]
13+
[--repo=<repository>] [-f | --force] [--prune] [-v | --verbose] [-u | --set-upstream]
1414
[<repository> [<refspec>...]]
1515

1616
DESCRIPTION
@@ -71,6 +71,14 @@ nor in any Push line of the corresponding remotes file---see below).
7171
Instead of naming each ref to push, specifies that all
7272
refs under `refs/heads/` be pushed.
7373

74+
--prune::
75+
Remove remote branches that don't have a local counterpart. For example
76+
a remote branch `tmp` will be removed if a local branch with the same
77+
name doesn't exist any more. This also respects refspecs, e.g.
78+
`git push --prune remote refs/heads/{asterisk}:refs/tmp/{asterisk}` would
79+
make sure that remote `refs/tmp/foo` will be removed if `refs/heads/foo`
80+
doesn't exist.
81+
7482
--mirror::
7583
Instead of naming each ref to push, specifies that all
7684
refs under `refs/` (which includes but is not

builtin/push.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
261261
OPT_BIT('u', "set-upstream", &flags, "set upstream for git pull/status",
262262
TRANSPORT_PUSH_SET_UPSTREAM),
263263
OPT_BOOL(0, "progress", &progress, "force progress reporting"),
264+
OPT_BIT(0, "prune", &flags, "prune locally removed refs",
265+
TRANSPORT_PUSH_PRUNE),
264266
OPT_END()
265267
};
266268

remote.c

Lines changed: 69 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include "tag.h"
99
#include "string-list.h"
1010

11+
enum map_direction { FROM_SRC, FROM_DST };
12+
1113
static struct refspec s_tag_refspec = {
1214
0,
1315
1,
@@ -978,16 +980,20 @@ static void tail_link_ref(struct ref *ref, struct ref ***tail)
978980
*tail = &ref->next;
979981
}
980982

983+
static struct ref *alloc_delete_ref(void)
984+
{
985+
struct ref *ref = alloc_ref("(delete)");
986+
hashclr(ref->new_sha1);
987+
return ref;
988+
}
989+
981990
static struct ref *try_explicit_object_name(const char *name)
982991
{
983992
unsigned char sha1[20];
984993
struct ref *ref;
985994

986-
if (!*name) {
987-
ref = alloc_ref("(delete)");
988-
hashclr(ref->new_sha1);
989-
return ref;
990-
}
995+
if (!*name)
996+
return alloc_delete_ref();
991997
if (get_sha1(name, sha1))
992998
return NULL;
993999
ref = alloc_ref(name);
@@ -1110,10 +1116,11 @@ static int match_explicit_refs(struct ref *src, struct ref *dst,
11101116
return errs;
11111117
}
11121118

1113-
static const struct refspec *check_pattern_match(const struct refspec *rs,
1114-
int rs_nr,
1115-
const struct ref *src)
1119+
static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref *ref,
1120+
int send_mirror, int direction, const struct refspec **ret_pat)
11161121
{
1122+
const struct refspec *pat;
1123+
char *name;
11171124
int i;
11181125
int matching_refs = -1;
11191126
for (i = 0; i < rs_nr; i++) {
@@ -1123,14 +1130,36 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
11231130
continue;
11241131
}
11251132

1126-
if (rs[i].pattern && match_name_with_pattern(rs[i].src, src->name,
1127-
NULL, NULL))
1128-
return rs + i;
1133+
if (rs[i].pattern) {
1134+
const char *dst_side = rs[i].dst ? rs[i].dst : rs[i].src;
1135+
int match;
1136+
if (direction == FROM_SRC)
1137+
match = match_name_with_pattern(rs[i].src, ref->name, dst_side, &name);
1138+
else
1139+
match = match_name_with_pattern(dst_side, ref->name, rs[i].src, &name);
1140+
if (match) {
1141+
matching_refs = i;
1142+
break;
1143+
}
1144+
}
11291145
}
1130-
if (matching_refs != -1)
1131-
return rs + matching_refs;
1132-
else
1146+
if (matching_refs == -1)
11331147
return NULL;
1148+
1149+
pat = rs + matching_refs;
1150+
if (pat->matching) {
1151+
/*
1152+
* "matching refs"; traditionally we pushed everything
1153+
* including refs outside refs/heads/ hierarchy, but
1154+
* that does not make much sense these days.
1155+
*/
1156+
if (!send_mirror && prefixcmp(ref->name, "refs/heads/"))
1157+
return NULL;
1158+
name = xstrdup(ref->name);
1159+
}
1160+
if (ret_pat)
1161+
*ret_pat = pat;
1162+
return name;
11341163
}
11351164

11361165
static struct ref **tail_ref(struct ref **head)
@@ -1155,9 +1184,10 @@ int match_push_refs(struct ref *src, struct ref **dst,
11551184
struct refspec *rs;
11561185
int send_all = flags & MATCH_REFS_ALL;
11571186
int send_mirror = flags & MATCH_REFS_MIRROR;
1187+
int send_prune = flags & MATCH_REFS_PRUNE;
11581188
int errs;
11591189
static const char *default_refspec[] = { ":", NULL };
1160-
struct ref **dst_tail = tail_ref(dst);
1190+
struct ref *ref, **dst_tail = tail_ref(dst);
11611191

11621192
if (!nr_refspec) {
11631193
nr_refspec = 1;
@@ -1167,39 +1197,23 @@ int match_push_refs(struct ref *src, struct ref **dst,
11671197
errs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec);
11681198

11691199
/* pick the remainder */
1170-
for ( ; src; src = src->next) {
1200+
for (ref = src; ref; ref = ref->next) {
11711201
struct ref *dst_peer;
11721202
const struct refspec *pat = NULL;
11731203
char *dst_name;
1174-
if (src->peer_ref)
1175-
continue;
11761204

1177-
pat = check_pattern_match(rs, nr_refspec, src);
1178-
if (!pat)
1205+
if (ref->peer_ref)
11791206
continue;
11801207

1181-
if (pat->matching) {
1182-
/*
1183-
* "matching refs"; traditionally we pushed everything
1184-
* including refs outside refs/heads/ hierarchy, but
1185-
* that does not make much sense these days.
1186-
*/
1187-
if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
1188-
continue;
1189-
dst_name = xstrdup(src->name);
1208+
dst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_SRC, &pat);
1209+
if (!dst_name)
1210+
continue;
11901211

1191-
} else {
1192-
const char *dst_side = pat->dst ? pat->dst : pat->src;
1193-
if (!match_name_with_pattern(pat->src, src->name,
1194-
dst_side, &dst_name))
1195-
die("Didn't think it matches any more");
1196-
}
11971212
dst_peer = find_ref_by_name(*dst, dst_name);
11981213
if (dst_peer) {
11991214
if (dst_peer->peer_ref)
12001215
/* We're already sending something to this ref. */
12011216
goto free_name;
1202-
12031217
} else {
12041218
if (pat->matching && !(send_all || send_mirror))
12051219
/*
@@ -1211,13 +1225,30 @@ int match_push_refs(struct ref *src, struct ref **dst,
12111225

12121226
/* Create a new one and link it */
12131227
dst_peer = make_linked_ref(dst_name, &dst_tail);
1214-
hashcpy(dst_peer->new_sha1, src->new_sha1);
1228+
hashcpy(dst_peer->new_sha1, ref->new_sha1);
12151229
}
1216-
dst_peer->peer_ref = copy_ref(src);
1230+
dst_peer->peer_ref = copy_ref(ref);
12171231
dst_peer->force = pat->force;
12181232
free_name:
12191233
free(dst_name);
12201234
}
1235+
if (send_prune) {
1236+
/* check for missing refs on the remote */
1237+
for (ref = *dst; ref; ref = ref->next) {
1238+
char *src_name;
1239+
1240+
if (ref->peer_ref)
1241+
/* We're already sending something to this ref. */
1242+
continue;
1243+
1244+
src_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL);
1245+
if (src_name) {
1246+
if (!find_ref_by_name(src, src_name))
1247+
ref->peer_ref = alloc_delete_ref();
1248+
free(src_name);
1249+
}
1250+
}
1251+
}
12211252
if (errs)
12221253
return -1;
12231254
return 0;

remote.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ int branch_merge_matches(struct branch *, int n, const char *);
145145
enum match_refs_flags {
146146
MATCH_REFS_NONE = 0,
147147
MATCH_REFS_ALL = (1 << 0),
148-
MATCH_REFS_MIRROR = (1 << 1)
148+
MATCH_REFS_MIRROR = (1 << 1),
149+
MATCH_REFS_PRUNE = (1 << 2)
149150
};
150151

151152
/* Reporting of tracking info */

t/t5516-fetch-push.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -979,4 +979,20 @@ test_expect_success 'push --porcelain --dry-run rejected' '
979979
test_cmp .git/foo .git/bar
980980
'
981981

982+
test_expect_success 'push --prune' '
983+
mk_test heads/master heads/second heads/foo heads/bar &&
984+
git push --prune testrepo &&
985+
check_push_result $the_commit heads/master &&
986+
check_push_result $the_first_commit heads/second &&
987+
! check_push_result $the_first_commit heads/foo heads/bar
988+
'
989+
990+
test_expect_success 'push --prune refspec' '
991+
mk_test tmp/master tmp/second tmp/foo tmp/bar &&
992+
git push --prune testrepo "refs/heads/*:refs/tmp/*" &&
993+
check_push_result $the_commit tmp/master &&
994+
check_push_result $the_first_commit tmp/second &&
995+
! check_push_result $the_first_commit tmp/foo tmp/bar
996+
'
997+
982998
test_done

transport.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,8 @@ int transport_push(struct transport *transport,
10321032
match_flags |= MATCH_REFS_ALL;
10331033
if (flags & TRANSPORT_PUSH_MIRROR)
10341034
match_flags |= MATCH_REFS_MIRROR;
1035+
if (flags & TRANSPORT_PUSH_PRUNE)
1036+
match_flags |= MATCH_REFS_PRUNE;
10351037

10361038
if (match_push_refs(local_refs, &remote_refs,
10371039
refspec_nr, refspec, match_flags)) {

transport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ struct transport {
102102
#define TRANSPORT_PUSH_PORCELAIN 16
103103
#define TRANSPORT_PUSH_SET_UPSTREAM 32
104104
#define TRANSPORT_RECURSE_SUBMODULES_CHECK 64
105+
#define TRANSPORT_PUSH_PRUNE 128
105106

106107
#define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
107108

0 commit comments

Comments
 (0)