]>
Commit | Line | Data |
---|---|---|
54b0c1e0 CC |
1 | /* |
2 | * Builtin "git replace" | |
3 | * | |
4 | * Copyright (c) 2008 Christian Couder <chriscool@tuxfamily.org> | |
5 | * | |
6 | * Based on builtin-tag.c by Kristian Høgsberg <krh@redhat.com> | |
7 | * and Carlos Rica <jasampler@gmail.com> that was itself based on | |
8 | * git-tag.sh and mktag.c by Linus Torvalds. | |
9 | */ | |
10 | ||
11 | #include "cache.h" | |
12 | #include "builtin.h" | |
13 | #include "refs.h" | |
14 | #include "parse-options.h" | |
15 | ||
16 | static const char * const git_replace_usage[] = { | |
bebdd271 | 17 | "git replace [-f] <object> <replacement>", |
54b0c1e0 CC |
18 | "git replace -d <object>...", |
19 | "git replace -l [<pattern>]", | |
20 | NULL | |
21 | }; | |
22 | ||
23 | static int show_reference(const char *refname, const unsigned char *sha1, | |
24 | int flag, void *cb_data) | |
25 | { | |
26 | const char *pattern = cb_data; | |
27 | ||
28 | if (!fnmatch(pattern, refname, 0)) | |
29 | printf("%s\n", refname); | |
30 | ||
31 | return 0; | |
32 | } | |
33 | ||
34 | static int list_replace_refs(const char *pattern) | |
35 | { | |
36 | if (pattern == NULL) | |
37 | pattern = "*"; | |
38 | ||
39 | for_each_replace_ref(show_reference, (void *) pattern); | |
40 | ||
41 | return 0; | |
42 | } | |
43 | ||
44 | typedef int (*each_replace_name_fn)(const char *name, const char *ref, | |
45 | const unsigned char *sha1); | |
46 | ||
47 | static int for_each_replace_name(const char **argv, each_replace_name_fn fn) | |
48 | { | |
49 | const char **p; | |
50 | char ref[PATH_MAX]; | |
51 | int had_error = 0; | |
52 | unsigned char sha1[20]; | |
53 | ||
54 | for (p = argv; *p; p++) { | |
55 | if (snprintf(ref, sizeof(ref), "refs/replace/%s", *p) | |
56 | >= sizeof(ref)) { | |
57 | error("replace ref name too long: %.*s...", 50, *p); | |
58 | had_error = 1; | |
59 | continue; | |
60 | } | |
61 | if (!resolve_ref(ref, sha1, 1, NULL)) { | |
62 | error("replace ref '%s' not found.", *p); | |
63 | had_error = 1; | |
64 | continue; | |
65 | } | |
66 | if (fn(*p, ref, sha1)) | |
67 | had_error = 1; | |
68 | } | |
69 | return had_error; | |
70 | } | |
71 | ||
72 | static int delete_replace_ref(const char *name, const char *ref, | |
73 | const unsigned char *sha1) | |
74 | { | |
75 | if (delete_ref(ref, sha1, 0)) | |
76 | return 1; | |
77 | printf("Deleted replace ref '%s'\n", name); | |
78 | return 0; | |
79 | } | |
80 | ||
bebdd271 CC |
81 | static int replace_object(const char *object_ref, const char *replace_ref, |
82 | int force) | |
83 | { | |
84 | unsigned char object[20], prev[20], repl[20]; | |
85 | char ref[PATH_MAX]; | |
86 | struct ref_lock *lock; | |
87 | ||
88 | if (get_sha1(object_ref, object)) | |
89 | die("Failed to resolve '%s' as a valid ref.", object_ref); | |
90 | if (get_sha1(replace_ref, repl)) | |
91 | die("Failed to resolve '%s' as a valid ref.", replace_ref); | |
92 | ||
93 | if (snprintf(ref, sizeof(ref), | |
94 | "refs/replace/%s", | |
95 | sha1_to_hex(object)) > sizeof(ref) - 1) | |
96 | die("replace ref name too long: %.*s...", 50, ref); | |
97 | if (check_ref_format(ref)) | |
98 | die("'%s' is not a valid ref name.", ref); | |
99 | ||
100 | if (!resolve_ref(ref, prev, 1, NULL)) | |
101 | hashclr(prev); | |
102 | else if (!force) | |
103 | die("replace ref '%s' already exists", ref); | |
104 | ||
105 | lock = lock_any_ref_for_update(ref, prev, 0); | |
106 | if (!lock) | |
107 | die("%s: cannot lock the ref", ref); | |
108 | if (write_ref_sha1(lock, repl, NULL) < 0) | |
109 | die("%s: cannot update the ref", ref); | |
110 | ||
111 | return 0; | |
112 | } | |
113 | ||
54b0c1e0 CC |
114 | int cmd_replace(int argc, const char **argv, const char *prefix) |
115 | { | |
bebdd271 | 116 | int list = 0, delete = 0, force = 0; |
54b0c1e0 CC |
117 | struct option options[] = { |
118 | OPT_BOOLEAN('l', NULL, &list, "list replace refs"), | |
119 | OPT_BOOLEAN('d', NULL, &delete, "delete replace refs"), | |
bebdd271 | 120 | OPT_BOOLEAN('f', NULL, &force, "replace the ref if it exists"), |
54b0c1e0 CC |
121 | OPT_END() |
122 | }; | |
123 | ||
451bb210 | 124 | argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0); |
54b0c1e0 CC |
125 | |
126 | if (list && delete) | |
86af2caa CC |
127 | usage_msg_opt("-l and -d cannot be used together", |
128 | git_replace_usage, options); | |
54b0c1e0 | 129 | |
bebdd271 | 130 | if (force && (list || delete)) |
86af2caa CC |
131 | usage_msg_opt("-f cannot be used with -d or -l", |
132 | git_replace_usage, options); | |
bebdd271 CC |
133 | |
134 | /* Delete refs */ | |
54b0c1e0 CC |
135 | if (delete) { |
136 | if (argc < 1) | |
86af2caa CC |
137 | usage_msg_opt("-d needs at least one argument", |
138 | git_replace_usage, options); | |
54b0c1e0 CC |
139 | return for_each_replace_name(argv, delete_replace_ref); |
140 | } | |
141 | ||
bebdd271 CC |
142 | /* Replace object */ |
143 | if (!list && argc) { | |
144 | if (argc != 2) | |
86af2caa CC |
145 | usage_msg_opt("bad number of arguments", |
146 | git_replace_usage, options); | |
bebdd271 CC |
147 | return replace_object(argv[0], argv[1], force); |
148 | } | |
149 | ||
54b0c1e0 CC |
150 | /* List refs, even if "list" is not set */ |
151 | if (argc > 1) | |
86af2caa CC |
152 | usage_msg_opt("only one pattern can be given with -l", |
153 | git_replace_usage, options); | |
bebdd271 | 154 | if (force) |
86af2caa CC |
155 | usage_msg_opt("-f needs some arguments", |
156 | git_replace_usage, options); | |
54b0c1e0 CC |
157 | |
158 | return list_replace_refs(argv[0]); | |
159 | } |