1/* 2 * This file is part of gitg 3 * 4 * Copyright (C) 2012 - Jesse van den Kieboom 5 * 6 * gitg is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * gitg is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with gitg. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20namespace Gitg 21{ 22 23public enum RefType 24{ 25 NONE, 26 BRANCH, 27 REMOTE, 28 TAG, 29 STASH 30} 31 32public enum RefState 33{ 34 NONE, 35 SELECTED, 36 PRELIGHT 37} 38 39/** 40 * Parse ref name into components. 41 * 42 * This class parses a refname and splits it into several components. 43 * 44 */ 45public class ParsedRefName : Object 46{ 47 private string d_shortname; 48 private string d_name; 49 private string d_remote_name; 50 private string d_remote_branch; 51 private string? d_prefix; 52 53 /** 54 * The type of ref. 55 */ 56 public RefType rtype { get; private set; } 57 58 /** 59 * The full name of the ref. 60 */ 61 public string name 62 { 63 owned get { return d_name; } 64 } 65 66 /** 67 * The short name of the ref. This represents the name of the ref 68 * without the information of the type of ref. 69 */ 70 public string shortname 71 { 72 owned get { return d_shortname; } 73 } 74 75 /** 76 * The remote name of the ref (only for remote refs) 77 */ 78 public string? remote_name 79 { 80 owned get { return d_remote_name; } 81 } 82 83 /** 84 * The remote branch name of the ref (only for remote refs) 85 */ 86 public string? remote_branch 87 { 88 owned get { return d_remote_branch; } 89 } 90 91 public ParsedRefName(string name) 92 { 93 parse_name(name); 94 } 95 96 public string? prefix 97 { 98 get { return d_prefix; } 99 } 100 101 private void parse_name(string name) 102 { 103 d_name = name; 104 105 string[] prefixes = { 106 "refs/heads/", 107 "refs/remotes/", 108 "refs/tags/", 109 "refs/stash" 110 }; 111 112 d_shortname = name; 113 d_prefix = null; 114 115 if (d_name == "HEAD") 116 { 117 rtype = RefType.BRANCH; 118 } 119 120 for (var i = 0; i < prefixes.length; ++i) 121 { 122 if (!d_name.has_prefix(prefixes[i])) 123 { 124 continue; 125 } 126 127 d_prefix = prefixes[i]; 128 129 rtype = (RefType)(i + 1); 130 131 if (rtype == RefType.STASH) 132 { 133 d_prefix = "refs/"; 134 d_shortname = "stash"; 135 } 136 else 137 { 138 d_shortname = d_name[d_prefix.length:d_name.length]; 139 } 140 141 if (rtype == RefType.REMOTE) 142 { 143 var pos = d_shortname.index_of_char('/'); 144 145 if (pos != -1) 146 { 147 d_remote_name = d_shortname.substring(0, pos); 148 d_remote_branch = d_shortname.substring(pos + 1); 149 } 150 else 151 { 152 d_remote_name = d_shortname; 153 } 154 } 155 } 156 } 157} 158 159public interface Ref : Ggit.Ref 160{ 161 private static Regex? s_remote_key_regex; 162 163 protected abstract ParsedRefName d_parsed_name { get; set; } 164 protected abstract List<Ref>? d_pushes { get; owned set; } 165 166 public abstract RefState state { get; set; } 167 public abstract bool working { get; set; } 168 169 public ParsedRefName parsed_name 170 { 171 owned get 172 { 173 if (d_parsed_name == null) 174 { 175 d_parsed_name = new ParsedRefName(get_name()); 176 } 177 178 return d_parsed_name; 179 } 180 } 181 182 public abstract new Gitg.Repository get_owner(); 183 184 private void add_push_ref(string spec) 185 { 186 Gitg.Ref rf; 187 188 try 189 { 190 rf = get_owner().lookup_reference(spec); 191 } catch { return; } 192 193 if (d_pushes.find_custom(rf, (a, b) => { 194 return a.get_name().ascii_casecmp(b.get_name()); 195 }) == null) 196 { 197 d_pushes.append(rf); 198 } 199 } 200 201 private void add_branch_configured_push(Ggit.Config cfg) 202 { 203 string remote; 204 string merge; 205 206 try 207 { 208 remote = cfg.get_string(@"branch.$(parsed_name.shortname).remote"); 209 merge = cfg.get_string(@"branch.$(parsed_name.shortname).merge"); 210 } catch { return; } 211 212 var nm = new ParsedRefName(merge); 213 214 add_push_ref(@"refs/remotes/$remote/$(nm.shortname)"); 215 } 216 217 private void add_remote_configured_push(Ggit.Config cfg) 218 { 219 Regex valregex; 220 221 try 222 { 223 valregex = new Regex("^%s:(.*)".printf(Regex.escape_string(get_name()))); 224 225 if (s_remote_key_regex == null) 226 { 227 s_remote_key_regex = new Regex("remote\\.(.*)\\.push"); 228 } 229 230 cfg.match_foreach(s_remote_key_regex, (info, val) => { 231 MatchInfo vinfo; 232 233 if (!valregex.match(val, 0, out vinfo)) 234 { 235 return 0; 236 } 237 238 var rname = info.fetch(1); 239 var pref = vinfo.fetch(1); 240 241 add_push_ref(@"refs/remotes/$rname/$pref"); 242 return 0; 243 }); 244 245 } catch { return; } 246 } 247 248 private void add_branch_same_name_push(Ggit.Config cfg) 249 { 250 string remote; 251 252 try 253 { 254 remote = cfg.get_string(@"branch.$(parsed_name.shortname).remote"); 255 } catch { return; } 256 257 add_push_ref(@"refs/remotes/$remote/$(parsed_name.shortname)"); 258 } 259 260 private void compose_pushes() 261 { 262 d_pushes = new List<Ref>(); 263 264 Ggit.Config cfg; 265 266 try 267 { 268 cfg = get_owner().get_config(); 269 } catch { return; } 270 271 /* The possible refspecs of a local $ref (branch) are resolved in the 272 * following order (duplicates are removed automatically): 273 * 274 * 1) Branch configured remote and merge (git push): 275 * 276 * Remote: branch.<name>.remote 277 * Spec: branch.<name>.merge 278 * 279 * 2) Remote configured matching push refspec: 280 * For each remote.<name>.push matching ${ref.name}:<spec> 281 * 282 * Remote: <name> 283 * Spec: <spec> 284 * 285 * 3) Remote branch with the same name 286 * 287 * Remote: branch.<name>.remote 288 * Spec: ${ref.name} 289 */ 290 291 // Branch configured remote and merge 292 add_branch_configured_push(cfg); 293 294 // Remote configured push spec 295 add_remote_configured_push(cfg); 296 297 // Same name push 298 add_branch_same_name_push(cfg); 299 } 300 301 public List<Ref> pushes 302 { 303 get 304 { 305 if (d_pushes == null) 306 { 307 compose_pushes(); 308 } 309 310 return d_pushes; 311 } 312 } 313} 314 315} 316 317// ex:set ts=4 noet 318