You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RenameBranchCommand.java 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. /*
  2. * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
  3. * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> and others
  4. *
  5. * This program and the accompanying materials are made available under the
  6. * terms of the Eclipse Distribution License v. 1.0 which is available at
  7. * https://www.eclipse.org/org/documents/edl-v10.php.
  8. *
  9. * SPDX-License-Identifier: BSD-3-Clause
  10. */
  11. package org.eclipse.jgit.api;
  12. import java.io.IOException;
  13. import java.text.MessageFormat;
  14. import java.util.Arrays;
  15. import org.eclipse.jgit.api.errors.DetachedHeadException;
  16. import org.eclipse.jgit.api.errors.GitAPIException;
  17. import org.eclipse.jgit.api.errors.InvalidRefNameException;
  18. import org.eclipse.jgit.api.errors.JGitInternalException;
  19. import org.eclipse.jgit.api.errors.NoHeadException;
  20. import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
  21. import org.eclipse.jgit.api.errors.RefNotFoundException;
  22. import org.eclipse.jgit.internal.JGitText;
  23. import org.eclipse.jgit.lib.ConfigConstants;
  24. import org.eclipse.jgit.lib.Constants;
  25. import org.eclipse.jgit.lib.ObjectId;
  26. import org.eclipse.jgit.lib.Ref;
  27. import org.eclipse.jgit.lib.RefRename;
  28. import org.eclipse.jgit.lib.RefUpdate.Result;
  29. import org.eclipse.jgit.lib.Repository;
  30. import org.eclipse.jgit.lib.StoredConfig;
  31. /**
  32. * Used to rename branches.
  33. *
  34. * @see <a
  35. * href="http://www.kernel.org/pub/software/scm/git/docs/git-branch.html"
  36. * >Git documentation about Branch</a>
  37. */
  38. public class RenameBranchCommand extends GitCommand<Ref> {
  39. private String oldName;
  40. private String newName;
  41. /**
  42. * <p>
  43. * Constructor for RenameBranchCommand.
  44. * </p>
  45. *
  46. * @param repo
  47. * the {@link org.eclipse.jgit.lib.Repository}
  48. */
  49. protected RenameBranchCommand(Repository repo) {
  50. super(repo);
  51. }
  52. /** {@inheritDoc} */
  53. @Override
  54. public Ref call() throws GitAPIException, RefNotFoundException, InvalidRefNameException,
  55. RefAlreadyExistsException, DetachedHeadException {
  56. checkCallable();
  57. if (newName == null) {
  58. throw new InvalidRefNameException(MessageFormat.format(JGitText
  59. .get().branchNameInvalid, "<null>")); //$NON-NLS-1$
  60. }
  61. try {
  62. String fullOldName;
  63. String fullNewName;
  64. if (oldName != null) {
  65. // Don't just rely on findRef -- if there are local and remote
  66. // branches with the same name, and oldName is a short name, it
  67. // does not uniquely identify the ref and we might end up
  68. // renaming the wrong branch or finding a tag instead even
  69. // if a unique branch for the name exists!
  70. //
  71. // OldName may be a either a short or a full name.
  72. Ref ref = repo.exactRef(oldName);
  73. if (ref == null) {
  74. ref = repo.exactRef(Constants.R_HEADS + oldName);
  75. Ref ref2 = repo.exactRef(Constants.R_REMOTES + oldName);
  76. if (ref != null && ref2 != null) {
  77. throw new RefNotFoundException(MessageFormat.format(
  78. JGitText.get().renameBranchFailedAmbiguous,
  79. oldName, ref.getName(), ref2.getName()));
  80. } else if (ref == null) {
  81. if (ref2 != null) {
  82. ref = ref2;
  83. } else {
  84. throw new RefNotFoundException(MessageFormat.format(
  85. JGitText.get().refNotResolved, oldName));
  86. }
  87. }
  88. }
  89. fullOldName = ref.getName();
  90. } else {
  91. fullOldName = repo.getFullBranch();
  92. if (fullOldName == null) {
  93. throw new NoHeadException(
  94. JGitText.get().invalidRepositoryStateNoHead);
  95. }
  96. if (ObjectId.isId(fullOldName))
  97. throw new DetachedHeadException();
  98. }
  99. if (fullOldName.startsWith(Constants.R_REMOTES)) {
  100. fullNewName = Constants.R_REMOTES + newName;
  101. } else if (fullOldName.startsWith(Constants.R_HEADS)) {
  102. fullNewName = Constants.R_HEADS + newName;
  103. } else {
  104. throw new RefNotFoundException(MessageFormat.format(
  105. JGitText.get().renameBranchFailedNotABranch,
  106. fullOldName));
  107. }
  108. if (!Repository.isValidRefName(fullNewName)) {
  109. throw new InvalidRefNameException(MessageFormat.format(JGitText
  110. .get().branchNameInvalid, fullNewName));
  111. }
  112. if (repo.exactRef(fullNewName) != null) {
  113. throw new RefAlreadyExistsException(MessageFormat
  114. .format(JGitText.get().refAlreadyExists1, fullNewName));
  115. }
  116. RefRename rename = repo.renameRef(fullOldName, fullNewName);
  117. Result renameResult = rename.rename();
  118. setCallable(false);
  119. if (Result.RENAMED != renameResult) {
  120. throw new JGitInternalException(MessageFormat.format(JGitText
  121. .get().renameBranchUnexpectedResult, renameResult
  122. .name()));
  123. }
  124. if (fullNewName.startsWith(Constants.R_HEADS)) {
  125. String shortOldName = fullOldName.substring(Constants.R_HEADS
  126. .length());
  127. final StoredConfig repoConfig = repo.getConfig();
  128. // Copy all configuration values over to the new branch
  129. for (String name : repoConfig.getNames(
  130. ConfigConstants.CONFIG_BRANCH_SECTION, shortOldName)) {
  131. String[] values = repoConfig.getStringList(
  132. ConfigConstants.CONFIG_BRANCH_SECTION,
  133. shortOldName, name);
  134. if (values.length == 0) {
  135. continue;
  136. }
  137. // Keep any existing values already configured for the
  138. // new branch name
  139. String[] existing = repoConfig.getStringList(
  140. ConfigConstants.CONFIG_BRANCH_SECTION, newName,
  141. name);
  142. if (existing.length > 0) {
  143. String[] newValues = new String[values.length
  144. + existing.length];
  145. System.arraycopy(existing, 0, newValues, 0,
  146. existing.length);
  147. System.arraycopy(values, 0, newValues, existing.length,
  148. values.length);
  149. values = newValues;
  150. }
  151. repoConfig.setStringList(
  152. ConfigConstants.CONFIG_BRANCH_SECTION, newName,
  153. name, Arrays.asList(values));
  154. }
  155. repoConfig.unsetSection(ConfigConstants.CONFIG_BRANCH_SECTION,
  156. shortOldName);
  157. repoConfig.save();
  158. }
  159. Ref resultRef = repo.exactRef(fullNewName);
  160. if (resultRef == null) {
  161. throw new JGitInternalException(
  162. JGitText.get().renameBranchFailedUnknownReason);
  163. }
  164. return resultRef;
  165. } catch (IOException ioe) {
  166. throw new JGitInternalException(ioe.getMessage(), ioe);
  167. }
  168. }
  169. /**
  170. * Sets the new short name of the branch.
  171. * <p>
  172. * The full name is constructed using the prefix of the branch to be renamed
  173. * defined by either {@link #setOldName(String)} or HEAD. If that old branch
  174. * is a local branch, the renamed branch also will be, and if the old branch
  175. * is a remote branch, so will be the renamed branch.
  176. * </p>
  177. *
  178. * @param newName
  179. * the new name
  180. * @return this instance
  181. */
  182. public RenameBranchCommand setNewName(String newName) {
  183. checkCallable();
  184. this.newName = newName;
  185. return this;
  186. }
  187. /**
  188. * Sets the old name of the branch.
  189. * <p>
  190. * {@code oldName} may be a short or a full name. Using a full name is
  191. * recommended to unambiguously identify the branch to be renamed.
  192. * </p>
  193. *
  194. * @param oldName
  195. * the name of the branch to rename; if not set, the currently
  196. * checked out branch (if any) will be renamed
  197. * @return this instance
  198. */
  199. public RenameBranchCommand setOldName(String oldName) {
  200. checkCallable();
  201. this.oldName = oldName;
  202. return this;
  203. }
  204. }