git-svn: fix cloning of HTTP URLs with '+' in their path
[git/git.git] / branch.c
CommitLineData
e496c003
DB
1#include "cache.h"
2#include "branch.h"
3#include "refs.h"
4#include "remote.h"
5#include "commit.h"
6
7struct tracking {
8 struct refspec spec;
9 char *src;
10 const char *remote;
11 int matches;
12};
13
14static int find_tracked_branch(struct remote *remote, void *priv)
15{
16 struct tracking *tracking = priv;
17
18 if (!remote_find_tracking(remote, &tracking->spec)) {
19 if (++tracking->matches == 1) {
20 tracking->src = tracking->spec.src;
21 tracking->remote = remote->name;
22 } else {
23 free(tracking->spec.src);
24 if (tracking->src) {
25 free(tracking->src);
26 tracking->src = NULL;
27 }
28 }
29 tracking->spec.src = NULL;
30 }
31
32 return 0;
33}
34
35/*
36 * This is called when new_ref is branched off of orig_ref, and tries
37 * to infer the settings for branch.<new_ref>.{remote,merge} from the
38 * config.
39 */
9ed36cfa
JS
40static int setup_tracking(const char *new_ref, const char *orig_ref,
41 enum branch_track track)
e496c003
DB
42{
43 char key[1024];
44 struct tracking tracking;
45
46 if (strlen(new_ref) > 1024 - 7 - 7 - 1)
47 return error("Tracking not set up: name too long: %s",
48 new_ref);
49
50 memset(&tracking, 0, sizeof(tracking));
51 tracking.spec.dst = (char *)orig_ref;
9ed36cfa 52 if (for_each_remote(find_tracked_branch, &tracking))
e496c003
DB
53 return 1;
54
9ed36cfa
JS
55 if (!tracking.matches)
56 switch (track) {
57 case BRANCH_TRACK_ALWAYS:
58 case BRANCH_TRACK_EXPLICIT:
59 break;
60 default:
61 return 1;
62 }
63
e496c003
DB
64 if (tracking.matches > 1)
65 return error("Not tracking: ambiguous information for ref %s",
66 orig_ref);
67
9ed36cfa
JS
68 sprintf(key, "branch.%s.remote", new_ref);
69 git_config_set(key, tracking.remote ? tracking.remote : ".");
70 sprintf(key, "branch.%s.merge", new_ref);
71 git_config_set(key, tracking.src ? tracking.src : orig_ref);
72 free(tracking.src);
73 printf("Branch %s set up to track %s branch %s.\n", new_ref,
74 tracking.remote ? "remote" : "local", orig_ref);
e496c003
DB
75
76 return 0;
77}
78
79void create_branch(const char *head,
80 const char *name, const char *start_name,
9ed36cfa 81 int force, int reflog, enum branch_track track)
e496c003
DB
82{
83 struct ref_lock *lock;
84 struct commit *commit;
85 unsigned char sha1[20];
86 char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20];
87 int forcing = 0;
88
89 snprintf(ref, sizeof ref, "refs/heads/%s", name);
90 if (check_ref_format(ref))
91 die("'%s' is not a valid branch name.", name);
92
93 if (resolve_ref(ref, sha1, 1, NULL)) {
94 if (!force)
95 die("A branch named '%s' already exists.", name);
96 else if (!is_bare_repository() && !strcmp(head, name))
97 die("Cannot force update the current branch.");
98 forcing = 1;
99 }
100
101 real_ref = NULL;
102 if (get_sha1(start_name, sha1))
103 die("Not a valid object name: '%s'.", start_name);
104
105 switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
106 case 0:
107 /* Not branching from any existing branch */
9ed36cfa
JS
108 if (track == BRANCH_TRACK_EXPLICIT)
109 die("Cannot setup tracking information; starting point is not a branch.");
e496c003
DB
110 break;
111 case 1:
112 /* Unique completion -- good */
113 break;
114 default:
115 die("Ambiguous object name: '%s'.", start_name);
116 break;
117 }
118
119 if ((commit = lookup_commit_reference(sha1)) == NULL)
120 die("Not a valid branch point: '%s'.", start_name);
121 hashcpy(sha1, commit->object.sha1);
122
123 lock = lock_any_ref_for_update(ref, NULL, 0);
124 if (!lock)
125 die("Failed to lock ref for update: %s.", strerror(errno));
126
127 if (reflog)
128 log_all_ref_updates = 1;
129
130 if (forcing)
131 snprintf(msg, sizeof msg, "branch: Reset from %s",
132 start_name);
133 else
134 snprintf(msg, sizeof msg, "branch: Created from %s",
135 start_name);
136
e496c003 137 if (real_ref && track)
9ed36cfa 138 setup_tracking(name, real_ref, track);
e496c003
DB
139
140 if (write_ref_sha1(lock, sha1, msg) < 0)
141 die("Failed to write ref: %s.", strerror(errno));
142
9ed36cfa 143 free(real_ref);
e496c003 144}
c369e7b8
DB
145
146void remove_branch_state(void)
147{
148 unlink(git_path("MERGE_HEAD"));
149 unlink(git_path("rr-cache/MERGE_RR"));
150 unlink(git_path("MERGE_MSG"));
151 unlink(git_path("SQUASH_MSG"));
152}