Mercurial > hgsubversion
comparison push_cmd.py @ 83:6c9b7cf1c5aa
push_cmd: delete empty svn directories, refactor directory creation
| author | Patrick Mezard <pmezard@gmail.com> |
|---|---|
| date | Fri, 14 Nov 2008 16:18:24 -0600 |
| parents | 49b7cbe4c8e3 |
| children | 05a0c4f6060f |
comparison
equal
deleted
inserted
replaced
| 82:71de43e9f614 | 83:6c9b7cf1c5aa |
|---|---|
| 66 outgoing = utility_commands.outgoing_revisions(ui, repo, hge, | 66 outgoing = utility_commands.outgoing_revisions(ui, repo, hge, |
| 67 svn_commit_hashes) | 67 svn_commit_hashes) |
| 68 merc_util._encoding = oldencoding | 68 merc_util._encoding = oldencoding |
| 69 return 0 | 69 return 0 |
| 70 | 70 |
| 71 | 71 def _getdirchanges(svn, branchpath, parentctx, ctx, changedfiles): |
| 72 def _findmissing(dirname, svn, branch_path): | 72 """Compute directories to add or delete when moving from parentctx |
| 73 """Find missing directories in svn. dirname *must* end in a / | 73 to ctx, assuming only 'changedfiles' files changed. |
| 74 | |
| 75 Return (added, deleted) where 'added' is the list of all added | |
| 76 directories and 'deleted' the list of deleted directories. | |
| 77 Intermediate directories are included: if a/b/c is new and requires | |
| 78 the addition of a/b and a, those will be listed too. Intermediate | |
| 79 deleted directories are also listed, but item order of undefined | |
| 80 in either list. | |
| 74 """ | 81 """ |
| 75 assert dirname[-1] == '/' | 82 def exists(svndir): |
| 76 missing = [] | |
| 77 keep_checking = True | |
| 78 # check and see if the dir exists svn-side. | |
| 79 path = dirname | |
| 80 while keep_checking: | |
| 81 try: | 83 try: |
| 82 assert svn.list_dir('%s/%s' % (branch_path, path)) | 84 svn.list_dir('%s/%s' % (branchpath, svndir)) |
| 83 keep_checking = False | 85 return True |
| 84 except core.SubversionException, e: | 86 except core.SubversionException: |
| 85 # dir must not exist | 87 return False |
| 86 missing.append(path[:-1]) | 88 |
| 87 path = '/'.join(path.split('/')[:-2] + ['']) | 89 def finddirs(path): |
| 88 return missing | 90 pos = path.rfind('/') |
| 91 while pos != -1: | |
| 92 yield path[:pos] | |
| 93 pos = path.rfind('/', 0, pos) | |
| 94 | |
| 95 def getctxdirs(ctx, keptdirs): | |
| 96 dirs = {} | |
| 97 for f in ctx.manifest(): | |
| 98 for d in finddirs(f): | |
| 99 if d in dirs: | |
| 100 break | |
| 101 if d in keptdirs: | |
| 102 dirs[d] = 1 | |
| 103 return dirs | |
| 104 | |
| 105 deleted, added = [], [] | |
| 106 if not changedfiles: | |
| 107 return added, deleted | |
| 108 changeddirs = {} | |
| 109 for f in changedfiles: | |
| 110 for d in finddirs(f): | |
| 111 changeddirs[d] = 1 | |
| 112 olddirs = getctxdirs(parentctx, changeddirs) | |
| 113 newdirs = getctxdirs(ctx, changeddirs) | |
| 114 | |
| 115 for d in newdirs: | |
| 116 if d not in olddirs and not exists(d): | |
| 117 added.append(d) | |
| 118 | |
| 119 for d in olddirs: | |
| 120 if d not in newdirs and exists(d): | |
| 121 deleted.append(d) | |
| 122 | |
| 123 return added, deleted | |
| 124 | |
| 89 | 125 |
| 90 def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision): | 126 def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision): |
| 91 """Build and send a commit from Mercurial to Subversion. | 127 """Build and send a commit from Mercurial to Subversion. |
| 92 """ | 128 """ |
| 93 file_data = {} | 129 file_data = {} |
| 97 branch_path = 'trunk' | 133 branch_path = 'trunk' |
| 98 | 134 |
| 99 if parent_branch and parent_branch != 'default': | 135 if parent_branch and parent_branch != 'default': |
| 100 branch_path = 'branches/%s' % parent_branch | 136 branch_path = 'branches/%s' % parent_branch |
| 101 | 137 |
| 102 added_dirs = [] | 138 addeddirs, deleteddirs = _getdirchanges(svn, branch_path, parent, |
| 139 rev_ctx, rev_ctx.files()) | |
| 140 deleteddirs = set(deleteddirs) | |
| 141 | |
| 103 props = {} | 142 props = {} |
| 104 copies = {} | 143 copies = {} |
| 105 for file in rev_ctx.files(): | 144 for file in rev_ctx.files(): |
| 106 new_data = base_data = '' | 145 new_data = base_data = '' |
| 107 action = '' | 146 action = '' |
| 122 copies[file] = renamed[0] | 161 copies[file] = renamed[0] |
| 123 base_data = parent[renamed[0]].data() | 162 base_data = parent[renamed[0]].data() |
| 124 | 163 |
| 125 action = 'add' | 164 action = 'add' |
| 126 dirname = '/'.join(file.split('/')[:-1] + ['']) | 165 dirname = '/'.join(file.split('/')[:-1] + ['']) |
| 127 # check for new directories | |
| 128 if not list(parent.walk(util.PrefixMatch(dirname))): | |
| 129 added_dirs += _findmissing(dirname, svn, branch_path) | |
| 130 else: | 166 else: |
| 131 base_data = parent.filectx(file).data() | 167 base_data = parent.filectx(file).data() |
| 132 if ('x' in parent.filectx(file).flags() | 168 if ('x' in parent.filectx(file).flags() |
| 133 and 'x' not in rev_ctx.filectx(file).flags()): | 169 and 'x' not in rev_ctx.filectx(file).flags()): |
| 134 props.setdefault(file, {})['svn:executable'] = None | 170 props.setdefault(file, {})['svn:executable'] = None |
| 135 if ('l' in parent.filectx(file).flags() | 171 if ('l' in parent.filectx(file).flags() |
| 136 and 'l' not in rev_ctx.filectx(file).flags()): | 172 and 'l' not in rev_ctx.filectx(file).flags()): |
| 137 props.setdefault(file, {})['svn:special'] = None | 173 props.setdefault(file, {})['svn:special'] = None |
| 138 action = 'modify' | 174 action = 'modify' |
| 139 else: | 175 else: |
| 176 pos = file.rfind('/') | |
| 177 if pos >= 0: | |
| 178 if file[:pos] in deleteddirs: | |
| 179 # This file will be removed when its directory is removed | |
| 180 continue | |
| 140 base_data = parent.filectx(file).data() | 181 base_data = parent.filectx(file).data() |
| 141 action = 'delete' | 182 action = 'delete' |
| 142 file_data[file] = base_data, new_data, action | 183 file_data[file] = base_data, new_data, action |
| 143 | 184 |
| 144 # TODO check for directory deletes here | 185 # Now we are done with files, we can prune deleted directories |
| 186 # against themselves: ignore a/b if a/ is already removed | |
| 187 deleteddirs2 = list(deleteddirs) | |
| 188 deleteddirs2.sort() | |
| 189 deleteddirs2.reverse() | |
| 190 for d in deleteddirs2: | |
| 191 pos = d.rfind('/') | |
| 192 if pos >= 0 and d[:pos] in deleteddirs: | |
| 193 deleteddirs.remove(d[:pos]) | |
| 194 | |
| 145 def svnpath(p): | 195 def svnpath(p): |
| 146 return '%s/%s' % (branch_path, p) | 196 return '%s/%s' % (branch_path, p) |
| 147 | 197 |
| 148 newcopies = {} | 198 newcopies = {} |
| 149 for source, dest in copies.iteritems(): | 199 for source, dest in copies.iteritems(): |
| 150 newcopies[svnpath(source)] = (svnpath(dest), base_revision) | 200 newcopies[svnpath(source)] = (svnpath(dest), base_revision) |
| 151 | 201 |
| 152 new_target_files = [svnpath(f) for f in rev_ctx.files()] | 202 new_target_files = [svnpath(f) for f in file_data] |
| 153 for tf, ntf in zip(rev_ctx.files(), new_target_files): | 203 for tf, ntf in zip(file_data, new_target_files): |
| 154 if tf in file_data: | 204 if tf in file_data: |
| 155 file_data[ntf] = file_data[tf] | 205 file_data[ntf] = file_data[tf] |
| 156 if tf in props: | 206 if tf in props: |
| 157 props[ntf] = props[tf] | 207 props[ntf] = props[tf] |
| 158 del props[tf] | 208 del props[tf] |
| 159 if merc_util.binary(file_data[ntf][1]): | 209 if merc_util.binary(file_data[ntf][1]): |
| 160 props.setdefault(ntf, {}).update(props.get(ntf, {})) | 210 props.setdefault(ntf, {}).update(props.get(ntf, {})) |
| 161 props.setdefault(ntf, {})['svn:mime-type'] = 'application/octet-stream' | 211 props.setdefault(ntf, {})['svn:mime-type'] = 'application/octet-stream' |
| 162 del file_data[tf] | 212 del file_data[tf] |
| 163 added_dirs = ['%s/%s' % (branch_path, f) for f in added_dirs] | 213 |
| 164 added_dirs = set(added_dirs) | 214 addeddirs = [svnpath(d) for d in addeddirs] |
| 165 new_target_files += added_dirs | 215 deleteddirs = [svnpath(d) for d in deleteddirs] |
| 216 new_target_files += addeddirs + deleteddirs | |
| 166 try: | 217 try: |
| 167 svn.commit(new_target_files, rev_ctx.description(), file_data, | 218 svn.commit(new_target_files, rev_ctx.description(), file_data, |
| 168 base_revision, set(added_dirs), props, newcopies) | 219 base_revision, set(addeddirs), set(deleteddirs), |
| 220 props, newcopies) | |
| 169 except core.SubversionException, e: | 221 except core.SubversionException, e: |
| 170 if hasattr(e, 'apr_err') and e.apr_err == 160028: | 222 if hasattr(e, 'apr_err') and e.apr_err == 160028: |
| 171 raise merc_util.Abort('Base text was out of date, maybe rebase?') | 223 raise merc_util.Abort('Base text was out of date, maybe rebase?') |
| 172 else: | 224 else: |
| 173 raise | 225 raise |
