View Javadoc
1   /*
2    * Copyright (C) 2011, GitHub Inc. and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  package org.eclipse.jgit.api;
11  
12  import java.io.IOException;
13  import java.util.ArrayList;
14  import java.util.Collection;
15  import java.util.HashMap;
16  import java.util.Map;
17  
18  import org.eclipse.jgit.api.errors.GitAPIException;
19  import org.eclipse.jgit.api.errors.JGitInternalException;
20  import org.eclipse.jgit.errors.ConfigInvalidException;
21  import org.eclipse.jgit.lib.ConfigConstants;
22  import org.eclipse.jgit.lib.Constants;
23  import org.eclipse.jgit.lib.Ref;
24  import org.eclipse.jgit.lib.Repository;
25  import org.eclipse.jgit.lib.StoredConfig;
26  import org.eclipse.jgit.submodule.SubmoduleWalk;
27  import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
28  
29  /**
30   * A class used to execute a submodule sync command.
31   *
32   * This will set the remote URL in a submodule's repository to the current value
33   * in the .gitmodules file.
34   *
35   * @see <a href=
36   *      "http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html"
37   *      >Git documentation about submodules</a>
38   */
39  public class SubmoduleSyncCommand extends GitCommand<Map<String, String>> {
40  
41  	private final Collection<String> paths;
42  
43  	/**
44  	 * Constructor for SubmoduleSyncCommand.
45  	 *
46  	 * @param repo
47  	 *            a {@link org.eclipse.jgit.lib.Repository} object.
48  	 */
49  	public SubmoduleSyncCommand(Repository repo) {
50  		super(repo);
51  		paths = new ArrayList<>();
52  	}
53  
54  	/**
55  	 * Add repository-relative submodule path to synchronize
56  	 *
57  	 * @param path
58  	 *            (with <code>/</code> as separator)
59  	 * @return this command
60  	 */
61  	public SubmoduleSyncCommand addPath(String path) {
62  		paths.add(path);
63  		return this;
64  	}
65  
66  	/**
67  	 * Get branch that HEAD currently points to
68  	 *
69  	 * @param subRepo
70  	 *            a {@link org.eclipse.jgit.lib.Repository} object.
71  	 * @return shortened branch name, null on failures
72  	 * @throws java.io.IOException
73  	 */
74  	protected String getHeadBranch(Repository subRepo) throws IOException {
75  		Ref head = subRepo.exactRef(Constants.HEAD);
76  		if (head != null && head.isSymbolic()) {
77  			return Repository.shortenRefName(head.getLeaf().getName());
78  		}
79  		return null;
80  	}
81  
82  	/** {@inheritDoc} */
83  	@Override
84  	public Map<String, String> call() throws GitAPIException {
85  		checkCallable();
86  
87  		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(repo)) {
88  			if (!paths.isEmpty())
89  				generator.setFilter(PathFilterGroup.createFromStrings(paths));
90  			Map<String, String> synced = new HashMap<>();
91  			StoredConfig config = repo.getConfig();
92  			while (generator.next()) {
93  				String remoteUrl = generator.getRemoteUrl();
94  				if (remoteUrl == null)
95  					continue;
96  
97  				String path = generator.getPath();
98  				config.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
99  						path, ConfigConstants.CONFIG_KEY_URL, remoteUrl);
100 				synced.put(path, remoteUrl);
101 
102 				try (Repository subRepo = generator.getRepository()) {
103 					if (subRepo == null) {
104 						continue;
105 					}
106 
107 					StoredConfig subConfig;
108 					String branch;
109 
110 					subConfig = subRepo.getConfig();
111 					// Get name of remote associated with current branch and
112 					// fall back to default remote name as last resort
113 					branch = getHeadBranch(subRepo);
114 					String remote = null;
115 					if (branch != null) {
116 						remote = subConfig.getString(
117 								ConfigConstants.CONFIG_BRANCH_SECTION, branch,
118 								ConfigConstants.CONFIG_KEY_REMOTE);
119 					}
120 					if (remote == null) {
121 						remote = Constants.DEFAULT_REMOTE_NAME;
122 					}
123 
124 					subConfig.setString(ConfigConstants.CONFIG_REMOTE_SECTION,
125 							remote, ConfigConstants.CONFIG_KEY_URL, remoteUrl);
126 					subConfig.save();
127 				}
128 			}
129 			if (!synced.isEmpty())
130 				config.save();
131 			return synced;
132 		} catch (IOException | ConfigInvalidException e) {
133 			throw new JGitInternalException(e.getMessage(), e);
134 		}
135 	}
136 }