push: add '--prune' option
When pushing groups of refs to a remote, there is no simple way to remove old refs that still exist at the remote that is no longer updated from us. This will allow us to remove such refs from the remote. With this change, running this command $ git push --prune remote refs/heads/*:refs/remotes/laptop/* removes refs/remotes/laptop/foo from the remote if we do not have branch "foo" locally anymore. Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Felipe Contreras committed
Feb 23, 2012 at 00:43 UTC
6ddba5e241ebe484d53e3573c72386f487e25697
7 files changed
+60
-5
Documentation/git-push.txt
+9
-1
@@ -10,7 +10,7 @@ SYNOPSIS
10
--------
11
[verse]
12
'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]
14
[<repository> [<refspec>...]]
15
16
DESCRIPTION
@@ -71,6 +71,14 @@ nor in any Push line of the corresponding remotes file---see below).
71
Instead of naming each ref to push, specifies that all
72
refs under `refs/heads/` be pushed.
73
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
+
82
--mirror::
83
Instead of naming each ref to push, specifies that all
84
refs under `refs/` (which includes but is not
builtin/push.c
+2
@@ -261,6 +261,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
261
OPT_BIT('u', "set-upstream", &flags, "set upstream for git pull/status",
262
TRANSPORT_PUSH_SET_UPSTREAM),
263
OPT_BOOLEAN(0, "progress", &progress, "force progress reporting"),
264
+ OPT_BIT(0, "prune", &flags, "prune locally removed refs",
265
+ TRANSPORT_PUSH_PRUNE),
266
OPT_END()
267
};
268
remote.c
+28
-3
@@ -8,6 +8,8 @@
8
#include "tag.h"
9
#include "string-list.h"
10
11
+enum map_direction { FROM_SRC, FROM_DST };
12
+
13
static struct refspec s_tag_refspec = {
14
0,
15
1,
@@ -1115,7 +1117,7 @@ static int match_explicit_refs(struct ref *src, struct ref *dst,
1117
}
1118
1119
static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref *ref,
1118
- int send_mirror, const struct refspec **ret_pat)
1120
+ int send_mirror, int direction, const struct refspec **ret_pat)
1121
{
1122
const struct refspec *pat;
1123
char *name;
@@ -1130,7 +1132,12 @@ static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref
1132
1133
if (rs[i].pattern) {
1134
const char *dst_side = rs[i].dst ? rs[i].dst : rs[i].src;
1133
- if (match_name_with_pattern(rs[i].src, ref->name, dst_side, &name)) {
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
}
@@ -1177,6 +1184,7 @@ int match_push_refs(struct ref *src, struct ref **dst,
1184
struct refspec *rs;
1185
int send_all = flags & MATCH_REFS_ALL;
1186
int send_mirror = flags & MATCH_REFS_MIRROR;
1187
+ int send_prune = flags & MATCH_REFS_PRUNE;
1188
int errs;
1189
static const char *default_refspec[] = { ":", NULL };
1190
struct ref *ref, **dst_tail = tail_ref(dst);
@@ -1197,7 +1205,7 @@ int match_push_refs(struct ref *src, struct ref **dst,
1205
if (ref->peer_ref)
1206
continue;
1207
1200
- dst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, &pat);
1208
+ dst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_SRC, &pat);
1209
if (!dst_name)
1210
continue;
1211
@@ -1224,6 +1232,23 @@ int match_push_refs(struct ref *src, struct ref **dst,
1232
free_name:
1233
free(dst_name);
1234
}
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
+ }
1252
if (errs)
1253
return -1;
1254
return 0;
remote.h
+2
-1
@@ -145,7 +145,8 @@ int branch_merge_matches(struct branch *, int n, const char *);
145
enum match_refs_flags {
146
MATCH_REFS_NONE = 0,
147
MATCH_REFS_ALL = (1 << 0),
148
- MATCH_REFS_MIRROR = (1 << 1)
148
+ MATCH_REFS_MIRROR = (1 << 1),
149
+ MATCH_REFS_PRUNE = (1 << 2)
150
};
151
152
/* Reporting of tracking info */
t/t5516-fetch-push.sh
+16
@@ -979,4 +979,20 @@ test_expect_success 'push --porcelain --dry-run rejected' '
979
test_cmp .git/foo .git/bar
980
'
981
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
+
998
test_done
transport.c
+2
@@ -1028,6 +1028,8 @@ int transport_push(struct transport *transport,
1028
match_flags |= MATCH_REFS_ALL;
1029
if (flags & TRANSPORT_PUSH_MIRROR)
1030
match_flags |= MATCH_REFS_MIRROR;
1031
+ if (flags & TRANSPORT_PUSH_PRUNE)
1032
+ match_flags |= MATCH_REFS_PRUNE;
1033
1034
if (match_push_refs(local_refs, &remote_refs,
1035
refspec_nr, refspec, match_flags)) {
transport.h
+1
@@ -102,6 +102,7 @@ struct transport {
102
#define TRANSPORT_PUSH_PORCELAIN 16
103
#define TRANSPORT_PUSH_SET_UPSTREAM 32
104
#define TRANSPORT_RECURSE_SUBMODULES_CHECK 64
105
+#define TRANSPORT_PUSH_PRUNE 128
106
107
#define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
108