1/*
2* Builtin "git branch"
3*
4* Copyright (c) 2006 Kristian Høgsberg <krh@redhat.com>
5* Based on git-branch.sh by Junio C Hamano.
6*/
78
#include "cache.h"
9#include "refs.h"
10#include "commit.h"
11#include "builtin.h"
1213
static const char builtin_branch_usage[] =
14"git-branch (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | [-r] | [-a]";
1516
17
static const char *head;
18static unsigned char head_sha1[20];
1920
static int in_merge_bases(const unsigned char *sha1,
21struct commit *rev1,
22struct commit *rev2)
23{
24struct commit_list *bases, *b;
25int ret = 0;
2627
bases = get_merge_bases(rev1, rev2, 1);
28for (b = bases; b; b = b->next) {
29if (!hashcmp(sha1, b->item->object.sha1)) {
30ret = 1;
31break;
32}
33}
3435
free_commit_list(bases);
36return ret;
37}
3839
static void delete_branches(int argc, const char **argv, int force)
40{
41struct commit *rev, *head_rev;
42unsigned char sha1[20];
43char *name;
44int i;
4546
head_rev = lookup_commit_reference(head_sha1);
47for (i = 0; i < argc; i++) {
48if (!strcmp(head, argv[i]))
49die("Cannot delete the branch you are currently on.");
5051
name = xstrdup(mkpath("refs/heads/%s", argv[i]));
52if (!resolve_ref(name, sha1, 1, NULL))
53die("Branch '%s' not found.", argv[i]);
5455
rev = lookup_commit_reference(sha1);
56if (!rev || !head_rev)
57die("Couldn't look up commit objects.");
5859
/* This checks whether the merge bases of branch and
60* HEAD contains branch -- which means that the HEAD
61* contains everything in both.
62*/
6364
if (!force &&
65!in_merge_bases(sha1, rev, head_rev)) {
66fprintf(stderr,
67"The branch '%s' is not a strict subset of your current HEAD.\n"
68"If you are sure you want to delete it, run 'git branch -D %s'.\n",
69argv[i], argv[i]);
70exit(1);
71}
7273
if (delete_ref(name, sha1))
74printf("Error deleting branch '%s'\n", argv[i]);
75else
76printf("Deleted branch %s.\n", argv[i]);
7778
free(name);
79}
80}
8182
#define REF_UNKNOWN_TYPE 0x00
83#define REF_LOCAL_BRANCH 0x01
84#define REF_REMOTE_BRANCH 0x02
85#define REF_TAG 0x04
8687
struct ref_item {
88char *name;
89unsigned int kind;
90};
9192
struct ref_list {
93int index, alloc;
94struct ref_item *list;
95int kinds;
96};
9798
static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
99{
100struct ref_list *ref_list = (struct ref_list*)(cb_data);
101struct ref_item *newitem;
102int kind = REF_UNKNOWN_TYPE;
103104
/* Detect kind */
105if (!strncmp(refname, "refs/heads/", 11)) {
106kind = REF_LOCAL_BRANCH;
107refname += 11;
108} else if (!strncmp(refname, "refs/remotes/", 13)) {
109kind = REF_REMOTE_BRANCH;
110refname += 13;
111} else if (!strncmp(refname, "refs/tags/", 10)) {
112kind = REF_TAG;
113refname += 10;
114}
115116
/* Don't add types the caller doesn't want */
117if ((kind & ref_list->kinds) == 0)
118return 0;
119120
/* Resize buffer */
121if (ref_list->index >= ref_list->alloc) {
122ref_list->alloc = alloc_nr(ref_list->alloc);
123ref_list->list = xrealloc(ref_list->list,
124ref_list->alloc * sizeof(struct ref_item));
125}
126127
/* Record the new item */
128newitem = &(ref_list->list[ref_list->index++]);
129newitem->name = xstrdup(refname);
130newitem->kind = kind;
131132
return 0;
133}
134135
static void free_ref_list(struct ref_list *ref_list)
136{
137int i;
138139
for (i = 0; i < ref_list->index; i++)
140free(ref_list->list[i].name);
141free(ref_list->list);
142}
143144
static int ref_cmp(const void *r1, const void *r2)
145{
146struct ref_item *c1 = (struct ref_item *)(r1);
147struct ref_item *c2 = (struct ref_item *)(r2);
148149
if (c1->kind != c2->kind)
150return c1->kind - c2->kind;
151return strcmp(c1->name, c2->name);
152}
153154
static void print_ref_list(int kinds)
155{
156int i;
157char c;
158struct ref_list ref_list;
159160
memset(&ref_list, 0, sizeof(ref_list));
161ref_list.kinds = kinds;
162for_each_ref(append_ref, &ref_list);
163164
qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
165166
for (i = 0; i < ref_list.index; i++) {
167c = ' ';
168if (ref_list.list[i].kind == REF_LOCAL_BRANCH &&
169!strcmp(ref_list.list[i].name, head))
170c = '*';
171172
printf("%c %s\n", c, ref_list.list[i].name);
173}
174175
free_ref_list(&ref_list);
176}
177178
static void create_branch(const char *name, const char *start,
179int force, int reflog)
180{
181struct ref_lock *lock;
182struct commit *commit;
183unsigned char sha1[20];
184char ref[PATH_MAX], msg[PATH_MAX + 20];
185186
snprintf(ref, sizeof ref, "refs/heads/%s", name);
187if (check_ref_format(ref))
188die("'%s' is not a valid branch name.", name);
189190
if (resolve_ref(ref, sha1, 1, NULL)) {
191if (!force)
192die("A branch named '%s' already exists.", name);
193else if (!strcmp(head, name))
194die("Cannot force update the current branch.");
195}
196197
if (get_sha1(start, sha1) ||
198(commit = lookup_commit_reference(sha1)) == NULL)
199die("Not a valid branch point: '%s'.", start);
200hashcpy(sha1, commit->object.sha1);
201202
lock = lock_any_ref_for_update(ref, NULL);
203if (!lock)
204die("Failed to lock ref for update: %s.", strerror(errno));
205206
if (reflog) {
207log_all_ref_updates = 1;
208snprintf(msg, sizeof msg, "branch: Created from %s", start);
209}
210211
if (write_ref_sha1(lock, sha1, msg) < 0)
212die("Failed to write ref: %s.", strerror(errno));
213}
214215
int cmd_branch(int argc, const char **argv, const char *prefix)
216{
217int delete = 0, force_delete = 0, force_create = 0;
218int reflog = 0;
219int kinds = REF_LOCAL_BRANCH;
220int i;
221222
git_config(git_default_config);
223224
for (i = 1; i < argc; i++) {
225const char *arg = argv[i];
226227
if (arg[0] != '-')
228break;
229if (!strcmp(arg, "--")) {
230i++;
231break;
232}
233if (!strcmp(arg, "-d")) {
234delete = 1;
235continue;
236}
237if (!strcmp(arg, "-D")) {
238delete = 1;
239force_delete = 1;
240continue;
241}
242if (!strcmp(arg, "-f")) {
243force_create = 1;
244continue;
245}
246if (!strcmp(arg, "-r")) {
247kinds = REF_REMOTE_BRANCH;
248continue;
249}
250if (!strcmp(arg, "-a")) {
251kinds = REF_REMOTE_BRANCH | REF_LOCAL_BRANCH;
252continue;
253}
254if (!strcmp(arg, "-l")) {
255reflog = 1;
256continue;
257}
258usage(builtin_branch_usage);
259}
260261
head = xstrdup(resolve_ref("HEAD", head_sha1, 0, NULL));
262if (!head)
263die("Failed to resolve HEAD as a valid ref.");
264if (strncmp(head, "refs/heads/", 11))
265die("HEAD not found below refs/heads!");
266head += 11;
267268
if (delete)
269delete_branches(argc - i, argv + i, force_delete);
270else if (i == argc)
271print_ref_list(kinds);
272else if (i == argc - 1)
273create_branch(argv[i], head, force_create, reflog);
274else if (i == argc - 2)
275create_branch(argv[i], argv[i + 1], force_create, reflog);
276else
277usage(builtin_branch_usage);
278279
return 0;
280}