##// END OF EJS Templates
copyright: update to 2021...
Matt Harbison -
r47142:ed43b6fa stable
parent child Browse files
Show More
@@ -1,27 +1,27 b''
1 Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
1 Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
2 Upstream-Name: mercurial
2 Upstream-Name: mercurial
3 Source: https://www.mercurial-scm.org/
3 Source: https://www.mercurial-scm.org/
4
4
5 Files: *
5 Files: *
6 Copyright: 2005-2020, Matt Mackall <mpm@selenic.com> and others.
6 Copyright: 2005-2021, Matt Mackall <mpm@selenic.com> and others.
7 License: GPL-2+
7 License: GPL-2+
8 This program is free software; you can redistribute it
8 This program is free software; you can redistribute it
9 and/or modify it under the terms of the GNU General Public
9 and/or modify it under the terms of the GNU General Public
10 License as published by the Free Software Foundation; either
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later
11 version 2 of the License, or (at your option) any later
12 version.
12 version.
13 .
13 .
14 This program is distributed in the hope that it will be
14 This program is distributed in the hope that it will be
15 useful, but WITHOUT ANY WARRANTY; without even the implied
15 useful, but WITHOUT ANY WARRANTY; without even the implied
16 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
17 PURPOSE. See the GNU General Public License for more
17 PURPOSE. See the GNU General Public License for more
18 details.
18 details.
19 .
19 .
20 You should have received a copy of the GNU General Public
20 You should have received a copy of the GNU General Public
21 License along with this package; if not, write to the Free
21 License along with this package; if not, write to the Free
22 Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22 Software Foundation, Inc., 51 Franklin St, Fifth Floor,
23 Boston, MA 02110-1301 USA
23 Boston, MA 02110-1301 USA
24 .
24 .
25 On Debian systems, the full text of the GNU General Public
25 On Debian systems, the full text of the GNU General Public
26 License version 2 can be found in the file
26 License version 2 can be found in the file
27 `/usr/share/common-licenses/GPL-2'.
27 `/usr/share/common-licenses/GPL-2'.
@@ -1,82 +1,82 b''
1 ; Script generated by the Inno Setup Script Wizard.
1 ; Script generated by the Inno Setup Script Wizard.
2 ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
2 ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
3
3
4 #ifndef ARCH
4 #ifndef ARCH
5 #define ARCH = "x86"
5 #define ARCH = "x86"
6 #endif
6 #endif
7
7
8 [Setup]
8 [Setup]
9 AppCopyright=Copyright 2005-2020 Matt Mackall and others
9 AppCopyright=Copyright 2005-2021 Matt Mackall and others
10 AppName=Mercurial
10 AppName=Mercurial
11 AppVersion={#VERSION}
11 AppVersion={#VERSION}
12 OutputBaseFilename=Mercurial-{#VERSION}{#SUFFIX}
12 OutputBaseFilename=Mercurial-{#VERSION}{#SUFFIX}
13 #if ARCH == "x64"
13 #if ARCH == "x64"
14 AppVerName=Mercurial {#VERSION} (64-bit)
14 AppVerName=Mercurial {#VERSION} (64-bit)
15 ArchitecturesAllowed=x64
15 ArchitecturesAllowed=x64
16 ArchitecturesInstallIn64BitMode=x64
16 ArchitecturesInstallIn64BitMode=x64
17 #else
17 #else
18 AppVerName=Mercurial {#VERSION}
18 AppVerName=Mercurial {#VERSION}
19 #endif
19 #endif
20 InfoAfterFile=../postinstall.txt
20 InfoAfterFile=../postinstall.txt
21 LicenseFile=Copying.txt
21 LicenseFile=Copying.txt
22 ShowLanguageDialog=yes
22 ShowLanguageDialog=yes
23 AppPublisher=Matt Mackall and others
23 AppPublisher=Matt Mackall and others
24 AppPublisherURL=https://mercurial-scm.org/
24 AppPublisherURL=https://mercurial-scm.org/
25 AppSupportURL=https://mercurial-scm.org/
25 AppSupportURL=https://mercurial-scm.org/
26 AppUpdatesURL=https://mercurial-scm.org/
26 AppUpdatesURL=https://mercurial-scm.org/
27 {{ 'AppID={{4B95A5F1-EF59-4B08-BED8-C891C46121B3}' }}
27 {{ 'AppID={{4B95A5F1-EF59-4B08-BED8-C891C46121B3}' }}
28 AppContact=mercurial@mercurial-scm.org
28 AppContact=mercurial@mercurial-scm.org
29 DefaultDirName={pf}\Mercurial
29 DefaultDirName={pf}\Mercurial
30 SourceDir=stage
30 SourceDir=stage
31 VersionInfoDescription=Mercurial distributed SCM (version {#VERSION})
31 VersionInfoDescription=Mercurial distributed SCM (version {#VERSION})
32 VersionInfoCopyright=Copyright 2005-2020 Matt Mackall and others
32 VersionInfoCopyright=Copyright 2005-2021 Matt Mackall and others
33 VersionInfoCompany=Matt Mackall and others
33 VersionInfoCompany=Matt Mackall and others
34 VersionInfoVersion={#QUAD_VERSION}
34 VersionInfoVersion={#QUAD_VERSION}
35 InternalCompressLevel=max
35 InternalCompressLevel=max
36 SolidCompression=true
36 SolidCompression=true
37 SetupIconFile=../mercurial.ico
37 SetupIconFile=../mercurial.ico
38 AllowNoIcons=true
38 AllowNoIcons=true
39 DefaultGroupName=Mercurial
39 DefaultGroupName=Mercurial
40 PrivilegesRequired=none
40 PrivilegesRequired=none
41 ChangesEnvironment=true
41 ChangesEnvironment=true
42
42
43 [Files]
43 [Files]
44 {% for entry in package_files -%}
44 {% for entry in package_files -%}
45 Source: {{ entry.source }}; DestDir: {{ entry.dest_dir }}
45 Source: {{ entry.source }}; DestDir: {{ entry.dest_dir }}
46 {%- if entry.metadata %}; {{ entry.metadata }}{% endif %}
46 {%- if entry.metadata %}; {{ entry.metadata }}{% endif %}
47 {% endfor %}
47 {% endfor %}
48
48
49 [INI]
49 [INI]
50 Filename: {app}\Mercurial.url; Section: InternetShortcut; Key: URL; String: https://mercurial-scm.org/
50 Filename: {app}\Mercurial.url; Section: InternetShortcut; Key: URL; String: https://mercurial-scm.org/
51
51
52 [UninstallDelete]
52 [UninstallDelete]
53 Type: files; Name: {app}\Mercurial.url
53 Type: files; Name: {app}\Mercurial.url
54 Type: filesandordirs; Name: {app}\defaultrc
54 Type: filesandordirs; Name: {app}\defaultrc
55
55
56 [Icons]
56 [Icons]
57 Name: {group}\Uninstall Mercurial; Filename: {uninstallexe}
57 Name: {group}\Uninstall Mercurial; Filename: {uninstallexe}
58 Name: {group}\Mercurial Command Reference; Filename: {app}\Docs\hg.1.html
58 Name: {group}\Mercurial Command Reference; Filename: {app}\Docs\hg.1.html
59 Name: {group}\Mercurial Configuration Files; Filename: {app}\Docs\hgrc.5.html
59 Name: {group}\Mercurial Configuration Files; Filename: {app}\Docs\hgrc.5.html
60 Name: {group}\Mercurial Ignore Files; Filename: {app}\Docs\hgignore.5.html
60 Name: {group}\Mercurial Ignore Files; Filename: {app}\Docs\hgignore.5.html
61 Name: {group}\Mercurial Web Site; Filename: {app}\Mercurial.url
61 Name: {group}\Mercurial Web Site; Filename: {app}\Mercurial.url
62
62
63 [Tasks]
63 [Tasks]
64 Name: modifypath; Description: Add the installation path to the search path; Flags: unchecked
64 Name: modifypath; Description: Add the installation path to the search path; Flags: unchecked
65
65
66 [Code]
66 [Code]
67 procedure Touch(fn: String);
67 procedure Touch(fn: String);
68 begin
68 begin
69 SaveStringToFile(ExpandConstant(fn), '', False);
69 SaveStringToFile(ExpandConstant(fn), '', False);
70 end;
70 end;
71
71
72 const
72 const
73 ModPathName = 'modifypath';
73 ModPathName = 'modifypath';
74 ModPathType = 'user';
74 ModPathType = 'user';
75
75
76 function ModPathDir(): TArrayOfString;
76 function ModPathDir(): TArrayOfString;
77 begin
77 begin
78 setArrayLength(Result, 1)
78 setArrayLength(Result, 1)
79 Result[0] := ExpandConstant('{app}');
79 Result[0] := ExpandConstant('{app}');
80 end;
80 end;
81
81
82 {% include 'modpath.iss' %}
82 {% include 'modpath.iss' %}
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,161 +1,161 b''
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2 <html>
2 <html>
3 <head>
3 <head>
4 <title>Mercurial for Windows</title>
4 <title>Mercurial for Windows</title>
5 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
5 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
6 <style type="text/css">
6 <style type="text/css">
7 <!--
7 <!--
8 html {
8 html {
9 font-family: sans-serif;
9 font-family: sans-serif;
10 margin: 1em 2em;
10 margin: 1em 2em;
11 }
11 }
12
12
13 p {
13 p {
14 margin-top: 0.5em;
14 margin-top: 0.5em;
15 margin-bottom: 0.5em;
15 margin-bottom: 0.5em;
16 }
16 }
17
17
18 pre {
18 pre {
19 margin: 0.25em 0em;
19 margin: 0.25em 0em;
20 padding: 0.5em;
20 padding: 0.5em;
21 background-color: #EEE;
21 background-color: #EEE;
22 border: thin solid #CCC;
22 border: thin solid #CCC;
23 }
23 }
24
24
25 .indented {
25 .indented {
26 padding-left: 10pt;
26 padding-left: 10pt;
27 }
27 }
28 -->
28 -->
29 </style>
29 </style>
30 </head>
30 </head>
31
31
32 <body>
32 <body>
33 <h1>Mercurial for Windows</h1>
33 <h1>Mercurial for Windows</h1>
34
34
35 <p>Welcome to Mercurial for Windows!</p>
35 <p>Welcome to Mercurial for Windows!</p>
36
36
37 <p>
37 <p>
38 Mercurial is a command-line application. You must run it from
38 Mercurial is a command-line application. You must run it from
39 the Windows command prompt (or if you're hard core, a <a
39 the Windows command prompt (or if you're hard core, a <a
40 href="http://www.mingw.org/">MinGW</a> shell).
40 href="http://www.mingw.org/">MinGW</a> shell).
41 </p>
41 </p>
42
42
43 <p class="indented">
43 <p class="indented">
44 <i>Note: the standard <a href="http://www.mingw.org/">MinGW</a>
44 <i>Note: the standard <a href="http://www.mingw.org/">MinGW</a>
45 msys startup script uses rxvt which has problems setting up
45 msys startup script uses rxvt which has problems setting up
46 standard input and output. Running bash directly works
46 standard input and output. Running bash directly works
47 correctly.</i>
47 correctly.</i>
48 </p>
48 </p>
49
49
50 <p>
50 <p>
51 For documentation, please visit the <a
51 For documentation, please visit the <a
52 href="https://mercurial-scm.org/">Mercurial web site</a>.
52 href="https://mercurial-scm.org/">Mercurial web site</a>.
53 You can also download a free book, <a
53 You can also download a free book, <a
54 href="https://book.mercurial-scm.org/">Mercurial: The Definitive
54 href="https://book.mercurial-scm.org/">Mercurial: The Definitive
55 Guide</a>.
55 Guide</a>.
56 </p>
56 </p>
57
57
58 <p>
58 <p>
59 By default, Mercurial installs to <tt>C:\Program
59 By default, Mercurial installs to <tt>C:\Program
60 Files\Mercurial</tt>. The Mercurial command is called
60 Files\Mercurial</tt>. The Mercurial command is called
61 <tt>hg.exe</tt>.
61 <tt>hg.exe</tt>.
62 </p>
62 </p>
63
63
64 <h1>Testing Mercurial after you've installed it</h1>
64 <h1>Testing Mercurial after you've installed it</h1>
65
65
66 <p>
66 <p>
67 The easiest way to check that Mercurial is installed properly is
67 The easiest way to check that Mercurial is installed properly is
68 to just type the following at the command prompt:
68 to just type the following at the command prompt:
69 </p>
69 </p>
70
70
71 <pre>
71 <pre>
72 hg
72 hg
73 </pre>
73 </pre>
74
74
75 <p>
75 <p>
76 This command should print a useful help message. If it does,
76 This command should print a useful help message. If it does,
77 other Mercurial commands should work fine for you.
77 other Mercurial commands should work fine for you.
78 </p>
78 </p>
79
79
80 <h1>Configuration notes</h1>
80 <h1>Configuration notes</h1>
81 <h4>Default editor</h4>
81 <h4>Default editor</h4>
82 <p>
82 <p>
83 The default editor for commit messages is 'notepad'. You can set
83 The default editor for commit messages is 'notepad'. You can set
84 the <tt>EDITOR</tt> (or <tt>HGEDITOR</tt>) environment variable
84 the <tt>EDITOR</tt> (or <tt>HGEDITOR</tt>) environment variable
85 to specify your preference or set it in <tt>mercurial.ini</tt>:
85 to specify your preference or set it in <tt>mercurial.ini</tt>:
86 </p>
86 </p>
87 <pre>
87 <pre>
88 [ui]
88 [ui]
89 editor = whatever
89 editor = whatever
90 </pre>
90 </pre>
91
91
92 <h4>Configuring a Merge program</h4>
92 <h4>Configuring a Merge program</h4>
93 <p>
93 <p>
94 It should be emphasized that Mercurial by itself doesn't attempt
94 It should be emphasized that Mercurial by itself doesn't attempt
95 to do a Merge at the file level, neither does it make any
95 to do a Merge at the file level, neither does it make any
96 attempt to Resolve the conflicts.
96 attempt to Resolve the conflicts.
97 </p>
97 </p>
98
98
99 <p>
99 <p>
100 By default, Mercurial will use the merge program defined by the
100 By default, Mercurial will use the merge program defined by the
101 <tt>HGMERGE</tt> environment variable, or uses the one defined
101 <tt>HGMERGE</tt> environment variable, or uses the one defined
102 in the <tt>mercurial.ini</tt> file. (see <a
102 in the <tt>mercurial.ini</tt> file. (see <a
103 href="https://mercurial-scm.org/wiki/MergeProgram">MergeProgram</a>
103 href="https://mercurial-scm.org/wiki/MergeProgram">MergeProgram</a>
104 on the Mercurial Wiki for more information)
104 on the Mercurial Wiki for more information)
105 </p>
105 </p>
106
106
107 <h1>Reporting problems</h1>
107 <h1>Reporting problems</h1>
108
108
109 <p>
109 <p>
110 Before you report any problems, please consult the <a
110 Before you report any problems, please consult the <a
111 href="https://mercurial-scm.org/">Mercurial web site</a>
111 href="https://mercurial-scm.org/">Mercurial web site</a>
112 and see if your question is already in our list of <a
112 and see if your question is already in our list of <a
113 href="https://mercurial-scm.org/wiki/FAQ">Frequently
113 href="https://mercurial-scm.org/wiki/FAQ">Frequently
114 Answered Questions</a> (the "FAQ").
114 Answered Questions</a> (the "FAQ").
115 </p>
115 </p>
116
116
117 <p>
117 <p>
118 If you cannot find an answer to your question, please feel free
118 If you cannot find an answer to your question, please feel free
119 to send mail to the Mercurial mailing list, at <a
119 to send mail to the Mercurial mailing list, at <a
120 href="mailto:mercurial@mercurial-scm.org">mercurial@mercurial-scm.org</a>.
120 href="mailto:mercurial@mercurial-scm.org">mercurial@mercurial-scm.org</a>.
121 <b>Remember</b>, the more useful information you include in your
121 <b>Remember</b>, the more useful information you include in your
122 report, the easier it will be for us to help you!
122 report, the easier it will be for us to help you!
123 </p>
123 </p>
124
124
125 <p>
125 <p>
126 If you are IRC-savvy, that's usually the fastest way to get
126 If you are IRC-savvy, that's usually the fastest way to get
127 help. Go to <tt>#mercurial</tt> on <tt>irc.freenode.net</tt>.
127 help. Go to <tt>#mercurial</tt> on <tt>irc.freenode.net</tt>.
128 </p>
128 </p>
129
129
130 <h1>Author and copyright information</h1>
130 <h1>Author and copyright information</h1>
131
131
132 <p>
132 <p>
133 Mercurial was written by <a href="http://www.selenic.com">Matt
133 Mercurial was written by <a href="http://www.selenic.com">Matt
134 Mackall</a>, and is maintained by Matt and a team of volunteers.
134 Mackall</a>, and is maintained by Matt and a team of volunteers.
135 </p>
135 </p>
136
136
137 <p>
137 <p>
138 The Windows installer was written by <a
138 The Windows installer was written by <a
139 href="http://www.serpentine.com/blog">Bryan O'Sullivan</a>.
139 href="http://www.serpentine.com/blog">Bryan O'Sullivan</a>.
140 </p>
140 </p>
141
141
142 <p>
142 <p>
143 Mercurial is Copyright 2005-2020 Matt Mackall and others.
143 Mercurial is Copyright 2005-2021 Matt Mackall and others.
144 </p>
144 </p>
145
145
146 <p>
146 <p>
147 Mercurial is free software; you can redistribute it and/or
147 Mercurial is free software; you can redistribute it and/or
148 modify it under the terms of the <a
148 modify it under the terms of the <a
149 href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt">GNU
149 href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt">GNU
150 General Public License version 2</a> or any later version.
150 General Public License version 2</a> or any later version.
151 </p>
151 </p>
152
152
153 <p>
153 <p>
154 Mercurial is distributed in the hope that it will be useful, but
154 Mercurial is distributed in the hope that it will be useful, but
155 <b>without any warranty</b>; without even the implied warranty
155 <b>without any warranty</b>; without even the implied warranty
156 of <b>merchantability</b> or <b>fitness for a particular
156 of <b>merchantability</b> or <b>fitness for a particular
157 purpose</b>. See the GNU General Public License for more
157 purpose</b>. See the GNU General Public License for more
158 details.
158 details.
159 </p>
159 </p>
160 </body>
160 </body>
161 </html>
161 </html>
@@ -1,7753 +1,7753 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import os
11 import os
12 import re
12 import re
13 import sys
13 import sys
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 hex,
17 hex,
18 nullid,
18 nullid,
19 nullrev,
19 nullrev,
20 short,
20 short,
21 wdirhex,
21 wdirhex,
22 wdirrev,
22 wdirrev,
23 )
23 )
24 from .pycompat import open
24 from .pycompat import open
25 from . import (
25 from . import (
26 archival,
26 archival,
27 bookmarks,
27 bookmarks,
28 bundle2,
28 bundle2,
29 bundlecaches,
29 bundlecaches,
30 changegroup,
30 changegroup,
31 cmdutil,
31 cmdutil,
32 copies,
32 copies,
33 debugcommands as debugcommandsmod,
33 debugcommands as debugcommandsmod,
34 destutil,
34 destutil,
35 dirstateguard,
35 dirstateguard,
36 discovery,
36 discovery,
37 encoding,
37 encoding,
38 error,
38 error,
39 exchange,
39 exchange,
40 extensions,
40 extensions,
41 filemerge,
41 filemerge,
42 formatter,
42 formatter,
43 graphmod,
43 graphmod,
44 grep as grepmod,
44 grep as grepmod,
45 hbisect,
45 hbisect,
46 help,
46 help,
47 hg,
47 hg,
48 logcmdutil,
48 logcmdutil,
49 merge as mergemod,
49 merge as mergemod,
50 mergestate as mergestatemod,
50 mergestate as mergestatemod,
51 narrowspec,
51 narrowspec,
52 obsolete,
52 obsolete,
53 obsutil,
53 obsutil,
54 patch,
54 patch,
55 phases,
55 phases,
56 pycompat,
56 pycompat,
57 rcutil,
57 rcutil,
58 registrar,
58 registrar,
59 requirements,
59 requirements,
60 revsetlang,
60 revsetlang,
61 rewriteutil,
61 rewriteutil,
62 scmutil,
62 scmutil,
63 server,
63 server,
64 shelve as shelvemod,
64 shelve as shelvemod,
65 state as statemod,
65 state as statemod,
66 streamclone,
66 streamclone,
67 tags as tagsmod,
67 tags as tagsmod,
68 ui as uimod,
68 ui as uimod,
69 util,
69 util,
70 verify as verifymod,
70 verify as verifymod,
71 vfs as vfsmod,
71 vfs as vfsmod,
72 wireprotoserver,
72 wireprotoserver,
73 )
73 )
74 from .utils import (
74 from .utils import (
75 dateutil,
75 dateutil,
76 stringutil,
76 stringutil,
77 )
77 )
78
78
79 table = {}
79 table = {}
80 table.update(debugcommandsmod.command._table)
80 table.update(debugcommandsmod.command._table)
81
81
82 command = registrar.command(table)
82 command = registrar.command(table)
83 INTENT_READONLY = registrar.INTENT_READONLY
83 INTENT_READONLY = registrar.INTENT_READONLY
84
84
85 # common command options
85 # common command options
86
86
87 globalopts = [
87 globalopts = [
88 (
88 (
89 b'R',
89 b'R',
90 b'repository',
90 b'repository',
91 b'',
91 b'',
92 _(b'repository root directory or name of overlay bundle file'),
92 _(b'repository root directory or name of overlay bundle file'),
93 _(b'REPO'),
93 _(b'REPO'),
94 ),
94 ),
95 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
95 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
96 (
96 (
97 b'y',
97 b'y',
98 b'noninteractive',
98 b'noninteractive',
99 None,
99 None,
100 _(
100 _(
101 b'do not prompt, automatically pick the first choice for all prompts'
101 b'do not prompt, automatically pick the first choice for all prompts'
102 ),
102 ),
103 ),
103 ),
104 (b'q', b'quiet', None, _(b'suppress output')),
104 (b'q', b'quiet', None, _(b'suppress output')),
105 (b'v', b'verbose', None, _(b'enable additional output')),
105 (b'v', b'verbose', None, _(b'enable additional output')),
106 (
106 (
107 b'',
107 b'',
108 b'color',
108 b'color',
109 b'',
109 b'',
110 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
110 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
111 # and should not be translated
111 # and should not be translated
112 _(b"when to colorize (boolean, always, auto, never, or debug)"),
112 _(b"when to colorize (boolean, always, auto, never, or debug)"),
113 _(b'TYPE'),
113 _(b'TYPE'),
114 ),
114 ),
115 (
115 (
116 b'',
116 b'',
117 b'config',
117 b'config',
118 [],
118 [],
119 _(b'set/override config option (use \'section.name=value\')'),
119 _(b'set/override config option (use \'section.name=value\')'),
120 _(b'CONFIG'),
120 _(b'CONFIG'),
121 ),
121 ),
122 (b'', b'debug', None, _(b'enable debugging output')),
122 (b'', b'debug', None, _(b'enable debugging output')),
123 (b'', b'debugger', None, _(b'start debugger')),
123 (b'', b'debugger', None, _(b'start debugger')),
124 (
124 (
125 b'',
125 b'',
126 b'encoding',
126 b'encoding',
127 encoding.encoding,
127 encoding.encoding,
128 _(b'set the charset encoding'),
128 _(b'set the charset encoding'),
129 _(b'ENCODE'),
129 _(b'ENCODE'),
130 ),
130 ),
131 (
131 (
132 b'',
132 b'',
133 b'encodingmode',
133 b'encodingmode',
134 encoding.encodingmode,
134 encoding.encodingmode,
135 _(b'set the charset encoding mode'),
135 _(b'set the charset encoding mode'),
136 _(b'MODE'),
136 _(b'MODE'),
137 ),
137 ),
138 (b'', b'traceback', None, _(b'always print a traceback on exception')),
138 (b'', b'traceback', None, _(b'always print a traceback on exception')),
139 (b'', b'time', None, _(b'time how long the command takes')),
139 (b'', b'time', None, _(b'time how long the command takes')),
140 (b'', b'profile', None, _(b'print command execution profile')),
140 (b'', b'profile', None, _(b'print command execution profile')),
141 (b'', b'version', None, _(b'output version information and exit')),
141 (b'', b'version', None, _(b'output version information and exit')),
142 (b'h', b'help', None, _(b'display help and exit')),
142 (b'h', b'help', None, _(b'display help and exit')),
143 (b'', b'hidden', False, _(b'consider hidden changesets')),
143 (b'', b'hidden', False, _(b'consider hidden changesets')),
144 (
144 (
145 b'',
145 b'',
146 b'pager',
146 b'pager',
147 b'auto',
147 b'auto',
148 _(b"when to paginate (boolean, always, auto, or never)"),
148 _(b"when to paginate (boolean, always, auto, or never)"),
149 _(b'TYPE'),
149 _(b'TYPE'),
150 ),
150 ),
151 ]
151 ]
152
152
153 dryrunopts = cmdutil.dryrunopts
153 dryrunopts = cmdutil.dryrunopts
154 remoteopts = cmdutil.remoteopts
154 remoteopts = cmdutil.remoteopts
155 walkopts = cmdutil.walkopts
155 walkopts = cmdutil.walkopts
156 commitopts = cmdutil.commitopts
156 commitopts = cmdutil.commitopts
157 commitopts2 = cmdutil.commitopts2
157 commitopts2 = cmdutil.commitopts2
158 commitopts3 = cmdutil.commitopts3
158 commitopts3 = cmdutil.commitopts3
159 formatteropts = cmdutil.formatteropts
159 formatteropts = cmdutil.formatteropts
160 templateopts = cmdutil.templateopts
160 templateopts = cmdutil.templateopts
161 logopts = cmdutil.logopts
161 logopts = cmdutil.logopts
162 diffopts = cmdutil.diffopts
162 diffopts = cmdutil.diffopts
163 diffwsopts = cmdutil.diffwsopts
163 diffwsopts = cmdutil.diffwsopts
164 diffopts2 = cmdutil.diffopts2
164 diffopts2 = cmdutil.diffopts2
165 mergetoolopts = cmdutil.mergetoolopts
165 mergetoolopts = cmdutil.mergetoolopts
166 similarityopts = cmdutil.similarityopts
166 similarityopts = cmdutil.similarityopts
167 subrepoopts = cmdutil.subrepoopts
167 subrepoopts = cmdutil.subrepoopts
168 debugrevlogopts = cmdutil.debugrevlogopts
168 debugrevlogopts = cmdutil.debugrevlogopts
169
169
170 # Commands start here, listed alphabetically
170 # Commands start here, listed alphabetically
171
171
172
172
173 @command(
173 @command(
174 b'abort',
174 b'abort',
175 dryrunopts,
175 dryrunopts,
176 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
176 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
177 helpbasic=True,
177 helpbasic=True,
178 )
178 )
179 def abort(ui, repo, **opts):
179 def abort(ui, repo, **opts):
180 """abort an unfinished operation (EXPERIMENTAL)
180 """abort an unfinished operation (EXPERIMENTAL)
181
181
182 Aborts a multistep operation like graft, histedit, rebase, merge,
182 Aborts a multistep operation like graft, histedit, rebase, merge,
183 and unshelve if they are in an unfinished state.
183 and unshelve if they are in an unfinished state.
184
184
185 use --dry-run/-n to dry run the command.
185 use --dry-run/-n to dry run the command.
186 """
186 """
187 dryrun = opts.get('dry_run')
187 dryrun = opts.get('dry_run')
188 abortstate = cmdutil.getunfinishedstate(repo)
188 abortstate = cmdutil.getunfinishedstate(repo)
189 if not abortstate:
189 if not abortstate:
190 raise error.StateError(_(b'no operation in progress'))
190 raise error.StateError(_(b'no operation in progress'))
191 if not abortstate.abortfunc:
191 if not abortstate.abortfunc:
192 raise error.InputError(
192 raise error.InputError(
193 (
193 (
194 _(b"%s in progress but does not support 'hg abort'")
194 _(b"%s in progress but does not support 'hg abort'")
195 % (abortstate._opname)
195 % (abortstate._opname)
196 ),
196 ),
197 hint=abortstate.hint(),
197 hint=abortstate.hint(),
198 )
198 )
199 if dryrun:
199 if dryrun:
200 ui.status(
200 ui.status(
201 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
201 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
202 )
202 )
203 return
203 return
204 return abortstate.abortfunc(ui, repo)
204 return abortstate.abortfunc(ui, repo)
205
205
206
206
207 @command(
207 @command(
208 b'add',
208 b'add',
209 walkopts + subrepoopts + dryrunopts,
209 walkopts + subrepoopts + dryrunopts,
210 _(b'[OPTION]... [FILE]...'),
210 _(b'[OPTION]... [FILE]...'),
211 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
211 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
212 helpbasic=True,
212 helpbasic=True,
213 inferrepo=True,
213 inferrepo=True,
214 )
214 )
215 def add(ui, repo, *pats, **opts):
215 def add(ui, repo, *pats, **opts):
216 """add the specified files on the next commit
216 """add the specified files on the next commit
217
217
218 Schedule files to be version controlled and added to the
218 Schedule files to be version controlled and added to the
219 repository.
219 repository.
220
220
221 The files will be added to the repository at the next commit. To
221 The files will be added to the repository at the next commit. To
222 undo an add before that, see :hg:`forget`.
222 undo an add before that, see :hg:`forget`.
223
223
224 If no names are given, add all files to the repository (except
224 If no names are given, add all files to the repository (except
225 files matching ``.hgignore``).
225 files matching ``.hgignore``).
226
226
227 .. container:: verbose
227 .. container:: verbose
228
228
229 Examples:
229 Examples:
230
230
231 - New (unknown) files are added
231 - New (unknown) files are added
232 automatically by :hg:`add`::
232 automatically by :hg:`add`::
233
233
234 $ ls
234 $ ls
235 foo.c
235 foo.c
236 $ hg status
236 $ hg status
237 ? foo.c
237 ? foo.c
238 $ hg add
238 $ hg add
239 adding foo.c
239 adding foo.c
240 $ hg status
240 $ hg status
241 A foo.c
241 A foo.c
242
242
243 - Specific files to be added can be specified::
243 - Specific files to be added can be specified::
244
244
245 $ ls
245 $ ls
246 bar.c foo.c
246 bar.c foo.c
247 $ hg status
247 $ hg status
248 ? bar.c
248 ? bar.c
249 ? foo.c
249 ? foo.c
250 $ hg add bar.c
250 $ hg add bar.c
251 $ hg status
251 $ hg status
252 A bar.c
252 A bar.c
253 ? foo.c
253 ? foo.c
254
254
255 Returns 0 if all files are successfully added.
255 Returns 0 if all files are successfully added.
256 """
256 """
257
257
258 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
258 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
259 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
259 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
260 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
260 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
261 return rejected and 1 or 0
261 return rejected and 1 or 0
262
262
263
263
264 @command(
264 @command(
265 b'addremove',
265 b'addremove',
266 similarityopts + subrepoopts + walkopts + dryrunopts,
266 similarityopts + subrepoopts + walkopts + dryrunopts,
267 _(b'[OPTION]... [FILE]...'),
267 _(b'[OPTION]... [FILE]...'),
268 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
268 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
269 inferrepo=True,
269 inferrepo=True,
270 )
270 )
271 def addremove(ui, repo, *pats, **opts):
271 def addremove(ui, repo, *pats, **opts):
272 """add all new files, delete all missing files
272 """add all new files, delete all missing files
273
273
274 Add all new files and remove all missing files from the
274 Add all new files and remove all missing files from the
275 repository.
275 repository.
276
276
277 Unless names are given, new files are ignored if they match any of
277 Unless names are given, new files are ignored if they match any of
278 the patterns in ``.hgignore``. As with add, these changes take
278 the patterns in ``.hgignore``. As with add, these changes take
279 effect at the next commit.
279 effect at the next commit.
280
280
281 Use the -s/--similarity option to detect renamed files. This
281 Use the -s/--similarity option to detect renamed files. This
282 option takes a percentage between 0 (disabled) and 100 (files must
282 option takes a percentage between 0 (disabled) and 100 (files must
283 be identical) as its parameter. With a parameter greater than 0,
283 be identical) as its parameter. With a parameter greater than 0,
284 this compares every removed file with every added file and records
284 this compares every removed file with every added file and records
285 those similar enough as renames. Detecting renamed files this way
285 those similar enough as renames. Detecting renamed files this way
286 can be expensive. After using this option, :hg:`status -C` can be
286 can be expensive. After using this option, :hg:`status -C` can be
287 used to check which files were identified as moved or renamed. If
287 used to check which files were identified as moved or renamed. If
288 not specified, -s/--similarity defaults to 100 and only renames of
288 not specified, -s/--similarity defaults to 100 and only renames of
289 identical files are detected.
289 identical files are detected.
290
290
291 .. container:: verbose
291 .. container:: verbose
292
292
293 Examples:
293 Examples:
294
294
295 - A number of files (bar.c and foo.c) are new,
295 - A number of files (bar.c and foo.c) are new,
296 while foobar.c has been removed (without using :hg:`remove`)
296 while foobar.c has been removed (without using :hg:`remove`)
297 from the repository::
297 from the repository::
298
298
299 $ ls
299 $ ls
300 bar.c foo.c
300 bar.c foo.c
301 $ hg status
301 $ hg status
302 ! foobar.c
302 ! foobar.c
303 ? bar.c
303 ? bar.c
304 ? foo.c
304 ? foo.c
305 $ hg addremove
305 $ hg addremove
306 adding bar.c
306 adding bar.c
307 adding foo.c
307 adding foo.c
308 removing foobar.c
308 removing foobar.c
309 $ hg status
309 $ hg status
310 A bar.c
310 A bar.c
311 A foo.c
311 A foo.c
312 R foobar.c
312 R foobar.c
313
313
314 - A file foobar.c was moved to foo.c without using :hg:`rename`.
314 - A file foobar.c was moved to foo.c without using :hg:`rename`.
315 Afterwards, it was edited slightly::
315 Afterwards, it was edited slightly::
316
316
317 $ ls
317 $ ls
318 foo.c
318 foo.c
319 $ hg status
319 $ hg status
320 ! foobar.c
320 ! foobar.c
321 ? foo.c
321 ? foo.c
322 $ hg addremove --similarity 90
322 $ hg addremove --similarity 90
323 removing foobar.c
323 removing foobar.c
324 adding foo.c
324 adding foo.c
325 recording removal of foobar.c as rename to foo.c (94% similar)
325 recording removal of foobar.c as rename to foo.c (94% similar)
326 $ hg status -C
326 $ hg status -C
327 A foo.c
327 A foo.c
328 foobar.c
328 foobar.c
329 R foobar.c
329 R foobar.c
330
330
331 Returns 0 if all files are successfully added.
331 Returns 0 if all files are successfully added.
332 """
332 """
333 opts = pycompat.byteskwargs(opts)
333 opts = pycompat.byteskwargs(opts)
334 if not opts.get(b'similarity'):
334 if not opts.get(b'similarity'):
335 opts[b'similarity'] = b'100'
335 opts[b'similarity'] = b'100'
336 matcher = scmutil.match(repo[None], pats, opts)
336 matcher = scmutil.match(repo[None], pats, opts)
337 relative = scmutil.anypats(pats, opts)
337 relative = scmutil.anypats(pats, opts)
338 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
338 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
339 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
339 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
340
340
341
341
342 @command(
342 @command(
343 b'annotate|blame',
343 b'annotate|blame',
344 [
344 [
345 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
345 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
346 (
346 (
347 b'',
347 b'',
348 b'follow',
348 b'follow',
349 None,
349 None,
350 _(b'follow copies/renames and list the filename (DEPRECATED)'),
350 _(b'follow copies/renames and list the filename (DEPRECATED)'),
351 ),
351 ),
352 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
352 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
353 (b'a', b'text', None, _(b'treat all files as text')),
353 (b'a', b'text', None, _(b'treat all files as text')),
354 (b'u', b'user', None, _(b'list the author (long with -v)')),
354 (b'u', b'user', None, _(b'list the author (long with -v)')),
355 (b'f', b'file', None, _(b'list the filename')),
355 (b'f', b'file', None, _(b'list the filename')),
356 (b'd', b'date', None, _(b'list the date (short with -q)')),
356 (b'd', b'date', None, _(b'list the date (short with -q)')),
357 (b'n', b'number', None, _(b'list the revision number (default)')),
357 (b'n', b'number', None, _(b'list the revision number (default)')),
358 (b'c', b'changeset', None, _(b'list the changeset')),
358 (b'c', b'changeset', None, _(b'list the changeset')),
359 (
359 (
360 b'l',
360 b'l',
361 b'line-number',
361 b'line-number',
362 None,
362 None,
363 _(b'show line number at the first appearance'),
363 _(b'show line number at the first appearance'),
364 ),
364 ),
365 (
365 (
366 b'',
366 b'',
367 b'skip',
367 b'skip',
368 [],
368 [],
369 _(b'revset to not display (EXPERIMENTAL)'),
369 _(b'revset to not display (EXPERIMENTAL)'),
370 _(b'REV'),
370 _(b'REV'),
371 ),
371 ),
372 ]
372 ]
373 + diffwsopts
373 + diffwsopts
374 + walkopts
374 + walkopts
375 + formatteropts,
375 + formatteropts,
376 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
376 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
377 helpcategory=command.CATEGORY_FILE_CONTENTS,
377 helpcategory=command.CATEGORY_FILE_CONTENTS,
378 helpbasic=True,
378 helpbasic=True,
379 inferrepo=True,
379 inferrepo=True,
380 )
380 )
381 def annotate(ui, repo, *pats, **opts):
381 def annotate(ui, repo, *pats, **opts):
382 """show changeset information by line for each file
382 """show changeset information by line for each file
383
383
384 List changes in files, showing the revision id responsible for
384 List changes in files, showing the revision id responsible for
385 each line.
385 each line.
386
386
387 This command is useful for discovering when a change was made and
387 This command is useful for discovering when a change was made and
388 by whom.
388 by whom.
389
389
390 If you include --file, --user, or --date, the revision number is
390 If you include --file, --user, or --date, the revision number is
391 suppressed unless you also include --number.
391 suppressed unless you also include --number.
392
392
393 Without the -a/--text option, annotate will avoid processing files
393 Without the -a/--text option, annotate will avoid processing files
394 it detects as binary. With -a, annotate will annotate the file
394 it detects as binary. With -a, annotate will annotate the file
395 anyway, although the results will probably be neither useful
395 anyway, although the results will probably be neither useful
396 nor desirable.
396 nor desirable.
397
397
398 .. container:: verbose
398 .. container:: verbose
399
399
400 Template:
400 Template:
401
401
402 The following keywords are supported in addition to the common template
402 The following keywords are supported in addition to the common template
403 keywords and functions. See also :hg:`help templates`.
403 keywords and functions. See also :hg:`help templates`.
404
404
405 :lines: List of lines with annotation data.
405 :lines: List of lines with annotation data.
406 :path: String. Repository-absolute path of the specified file.
406 :path: String. Repository-absolute path of the specified file.
407
407
408 And each entry of ``{lines}`` provides the following sub-keywords in
408 And each entry of ``{lines}`` provides the following sub-keywords in
409 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
409 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
410
410
411 :line: String. Line content.
411 :line: String. Line content.
412 :lineno: Integer. Line number at that revision.
412 :lineno: Integer. Line number at that revision.
413 :path: String. Repository-absolute path of the file at that revision.
413 :path: String. Repository-absolute path of the file at that revision.
414
414
415 See :hg:`help templates.operators` for the list expansion syntax.
415 See :hg:`help templates.operators` for the list expansion syntax.
416
416
417 Returns 0 on success.
417 Returns 0 on success.
418 """
418 """
419 opts = pycompat.byteskwargs(opts)
419 opts = pycompat.byteskwargs(opts)
420 if not pats:
420 if not pats:
421 raise error.InputError(
421 raise error.InputError(
422 _(b'at least one filename or pattern is required')
422 _(b'at least one filename or pattern is required')
423 )
423 )
424
424
425 if opts.get(b'follow'):
425 if opts.get(b'follow'):
426 # --follow is deprecated and now just an alias for -f/--file
426 # --follow is deprecated and now just an alias for -f/--file
427 # to mimic the behavior of Mercurial before version 1.5
427 # to mimic the behavior of Mercurial before version 1.5
428 opts[b'file'] = True
428 opts[b'file'] = True
429
429
430 if (
430 if (
431 not opts.get(b'user')
431 not opts.get(b'user')
432 and not opts.get(b'changeset')
432 and not opts.get(b'changeset')
433 and not opts.get(b'date')
433 and not opts.get(b'date')
434 and not opts.get(b'file')
434 and not opts.get(b'file')
435 ):
435 ):
436 opts[b'number'] = True
436 opts[b'number'] = True
437
437
438 linenumber = opts.get(b'line_number') is not None
438 linenumber = opts.get(b'line_number') is not None
439 if (
439 if (
440 linenumber
440 linenumber
441 and (not opts.get(b'changeset'))
441 and (not opts.get(b'changeset'))
442 and (not opts.get(b'number'))
442 and (not opts.get(b'number'))
443 ):
443 ):
444 raise error.InputError(_(b'at least one of -n/-c is required for -l'))
444 raise error.InputError(_(b'at least one of -n/-c is required for -l'))
445
445
446 rev = opts.get(b'rev')
446 rev = opts.get(b'rev')
447 if rev:
447 if rev:
448 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
448 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
449 ctx = scmutil.revsingle(repo, rev)
449 ctx = scmutil.revsingle(repo, rev)
450
450
451 ui.pager(b'annotate')
451 ui.pager(b'annotate')
452 rootfm = ui.formatter(b'annotate', opts)
452 rootfm = ui.formatter(b'annotate', opts)
453 if ui.debugflag:
453 if ui.debugflag:
454 shorthex = pycompat.identity
454 shorthex = pycompat.identity
455 else:
455 else:
456
456
457 def shorthex(h):
457 def shorthex(h):
458 return h[:12]
458 return h[:12]
459
459
460 if ui.quiet:
460 if ui.quiet:
461 datefunc = dateutil.shortdate
461 datefunc = dateutil.shortdate
462 else:
462 else:
463 datefunc = dateutil.datestr
463 datefunc = dateutil.datestr
464 if ctx.rev() is None:
464 if ctx.rev() is None:
465 if opts.get(b'changeset'):
465 if opts.get(b'changeset'):
466 # omit "+" suffix which is appended to node hex
466 # omit "+" suffix which is appended to node hex
467 def formatrev(rev):
467 def formatrev(rev):
468 if rev == wdirrev:
468 if rev == wdirrev:
469 return b'%d' % ctx.p1().rev()
469 return b'%d' % ctx.p1().rev()
470 else:
470 else:
471 return b'%d' % rev
471 return b'%d' % rev
472
472
473 else:
473 else:
474
474
475 def formatrev(rev):
475 def formatrev(rev):
476 if rev == wdirrev:
476 if rev == wdirrev:
477 return b'%d+' % ctx.p1().rev()
477 return b'%d+' % ctx.p1().rev()
478 else:
478 else:
479 return b'%d ' % rev
479 return b'%d ' % rev
480
480
481 def formathex(h):
481 def formathex(h):
482 if h == wdirhex:
482 if h == wdirhex:
483 return b'%s+' % shorthex(hex(ctx.p1().node()))
483 return b'%s+' % shorthex(hex(ctx.p1().node()))
484 else:
484 else:
485 return b'%s ' % shorthex(h)
485 return b'%s ' % shorthex(h)
486
486
487 else:
487 else:
488 formatrev = b'%d'.__mod__
488 formatrev = b'%d'.__mod__
489 formathex = shorthex
489 formathex = shorthex
490
490
491 opmap = [
491 opmap = [
492 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
492 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
493 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
493 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
494 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
494 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
495 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
495 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
496 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
496 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
497 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
497 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
498 ]
498 ]
499 opnamemap = {
499 opnamemap = {
500 b'rev': b'number',
500 b'rev': b'number',
501 b'node': b'changeset',
501 b'node': b'changeset',
502 b'path': b'file',
502 b'path': b'file',
503 b'lineno': b'line_number',
503 b'lineno': b'line_number',
504 }
504 }
505
505
506 if rootfm.isplain():
506 if rootfm.isplain():
507
507
508 def makefunc(get, fmt):
508 def makefunc(get, fmt):
509 return lambda x: fmt(get(x))
509 return lambda x: fmt(get(x))
510
510
511 else:
511 else:
512
512
513 def makefunc(get, fmt):
513 def makefunc(get, fmt):
514 return get
514 return get
515
515
516 datahint = rootfm.datahint()
516 datahint = rootfm.datahint()
517 funcmap = [
517 funcmap = [
518 (makefunc(get, fmt), sep)
518 (makefunc(get, fmt), sep)
519 for fn, sep, get, fmt in opmap
519 for fn, sep, get, fmt in opmap
520 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
520 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
521 ]
521 ]
522 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
522 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
523 fields = b' '.join(
523 fields = b' '.join(
524 fn
524 fn
525 for fn, sep, get, fmt in opmap
525 for fn, sep, get, fmt in opmap
526 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
526 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
527 )
527 )
528
528
529 def bad(x, y):
529 def bad(x, y):
530 raise error.Abort(b"%s: %s" % (x, y))
530 raise error.Abort(b"%s: %s" % (x, y))
531
531
532 m = scmutil.match(ctx, pats, opts, badfn=bad)
532 m = scmutil.match(ctx, pats, opts, badfn=bad)
533
533
534 follow = not opts.get(b'no_follow')
534 follow = not opts.get(b'no_follow')
535 diffopts = patch.difffeatureopts(
535 diffopts = patch.difffeatureopts(
536 ui, opts, section=b'annotate', whitespace=True
536 ui, opts, section=b'annotate', whitespace=True
537 )
537 )
538 skiprevs = opts.get(b'skip')
538 skiprevs = opts.get(b'skip')
539 if skiprevs:
539 if skiprevs:
540 skiprevs = scmutil.revrange(repo, skiprevs)
540 skiprevs = scmutil.revrange(repo, skiprevs)
541
541
542 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
542 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
543 for abs in ctx.walk(m):
543 for abs in ctx.walk(m):
544 fctx = ctx[abs]
544 fctx = ctx[abs]
545 rootfm.startitem()
545 rootfm.startitem()
546 rootfm.data(path=abs)
546 rootfm.data(path=abs)
547 if not opts.get(b'text') and fctx.isbinary():
547 if not opts.get(b'text') and fctx.isbinary():
548 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
548 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
549 continue
549 continue
550
550
551 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
551 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
552 lines = fctx.annotate(
552 lines = fctx.annotate(
553 follow=follow, skiprevs=skiprevs, diffopts=diffopts
553 follow=follow, skiprevs=skiprevs, diffopts=diffopts
554 )
554 )
555 if not lines:
555 if not lines:
556 fm.end()
556 fm.end()
557 continue
557 continue
558 formats = []
558 formats = []
559 pieces = []
559 pieces = []
560
560
561 for f, sep in funcmap:
561 for f, sep in funcmap:
562 l = [f(n) for n in lines]
562 l = [f(n) for n in lines]
563 if fm.isplain():
563 if fm.isplain():
564 sizes = [encoding.colwidth(x) for x in l]
564 sizes = [encoding.colwidth(x) for x in l]
565 ml = max(sizes)
565 ml = max(sizes)
566 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
566 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
567 else:
567 else:
568 formats.append([b'%s'] * len(l))
568 formats.append([b'%s'] * len(l))
569 pieces.append(l)
569 pieces.append(l)
570
570
571 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
571 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
572 fm.startitem()
572 fm.startitem()
573 fm.context(fctx=n.fctx)
573 fm.context(fctx=n.fctx)
574 fm.write(fields, b"".join(f), *p)
574 fm.write(fields, b"".join(f), *p)
575 if n.skip:
575 if n.skip:
576 fmt = b"* %s"
576 fmt = b"* %s"
577 else:
577 else:
578 fmt = b": %s"
578 fmt = b": %s"
579 fm.write(b'line', fmt, n.text)
579 fm.write(b'line', fmt, n.text)
580
580
581 if not lines[-1].text.endswith(b'\n'):
581 if not lines[-1].text.endswith(b'\n'):
582 fm.plain(b'\n')
582 fm.plain(b'\n')
583 fm.end()
583 fm.end()
584
584
585 rootfm.end()
585 rootfm.end()
586
586
587
587
588 @command(
588 @command(
589 b'archive',
589 b'archive',
590 [
590 [
591 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
591 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
592 (
592 (
593 b'p',
593 b'p',
594 b'prefix',
594 b'prefix',
595 b'',
595 b'',
596 _(b'directory prefix for files in archive'),
596 _(b'directory prefix for files in archive'),
597 _(b'PREFIX'),
597 _(b'PREFIX'),
598 ),
598 ),
599 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
599 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
600 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
600 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
601 ]
601 ]
602 + subrepoopts
602 + subrepoopts
603 + walkopts,
603 + walkopts,
604 _(b'[OPTION]... DEST'),
604 _(b'[OPTION]... DEST'),
605 helpcategory=command.CATEGORY_IMPORT_EXPORT,
605 helpcategory=command.CATEGORY_IMPORT_EXPORT,
606 )
606 )
607 def archive(ui, repo, dest, **opts):
607 def archive(ui, repo, dest, **opts):
608 """create an unversioned archive of a repository revision
608 """create an unversioned archive of a repository revision
609
609
610 By default, the revision used is the parent of the working
610 By default, the revision used is the parent of the working
611 directory; use -r/--rev to specify a different revision.
611 directory; use -r/--rev to specify a different revision.
612
612
613 The archive type is automatically detected based on file
613 The archive type is automatically detected based on file
614 extension (to override, use -t/--type).
614 extension (to override, use -t/--type).
615
615
616 .. container:: verbose
616 .. container:: verbose
617
617
618 Examples:
618 Examples:
619
619
620 - create a zip file containing the 1.0 release::
620 - create a zip file containing the 1.0 release::
621
621
622 hg archive -r 1.0 project-1.0.zip
622 hg archive -r 1.0 project-1.0.zip
623
623
624 - create a tarball excluding .hg files::
624 - create a tarball excluding .hg files::
625
625
626 hg archive project.tar.gz -X ".hg*"
626 hg archive project.tar.gz -X ".hg*"
627
627
628 Valid types are:
628 Valid types are:
629
629
630 :``files``: a directory full of files (default)
630 :``files``: a directory full of files (default)
631 :``tar``: tar archive, uncompressed
631 :``tar``: tar archive, uncompressed
632 :``tbz2``: tar archive, compressed using bzip2
632 :``tbz2``: tar archive, compressed using bzip2
633 :``tgz``: tar archive, compressed using gzip
633 :``tgz``: tar archive, compressed using gzip
634 :``txz``: tar archive, compressed using lzma (only in Python 3)
634 :``txz``: tar archive, compressed using lzma (only in Python 3)
635 :``uzip``: zip archive, uncompressed
635 :``uzip``: zip archive, uncompressed
636 :``zip``: zip archive, compressed using deflate
636 :``zip``: zip archive, compressed using deflate
637
637
638 The exact name of the destination archive or directory is given
638 The exact name of the destination archive or directory is given
639 using a format string; see :hg:`help export` for details.
639 using a format string; see :hg:`help export` for details.
640
640
641 Each member added to an archive file has a directory prefix
641 Each member added to an archive file has a directory prefix
642 prepended. Use -p/--prefix to specify a format string for the
642 prepended. Use -p/--prefix to specify a format string for the
643 prefix. The default is the basename of the archive, with suffixes
643 prefix. The default is the basename of the archive, with suffixes
644 removed.
644 removed.
645
645
646 Returns 0 on success.
646 Returns 0 on success.
647 """
647 """
648
648
649 opts = pycompat.byteskwargs(opts)
649 opts = pycompat.byteskwargs(opts)
650 rev = opts.get(b'rev')
650 rev = opts.get(b'rev')
651 if rev:
651 if rev:
652 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
652 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
653 ctx = scmutil.revsingle(repo, rev)
653 ctx = scmutil.revsingle(repo, rev)
654 if not ctx:
654 if not ctx:
655 raise error.InputError(
655 raise error.InputError(
656 _(b'no working directory: please specify a revision')
656 _(b'no working directory: please specify a revision')
657 )
657 )
658 node = ctx.node()
658 node = ctx.node()
659 dest = cmdutil.makefilename(ctx, dest)
659 dest = cmdutil.makefilename(ctx, dest)
660 if os.path.realpath(dest) == repo.root:
660 if os.path.realpath(dest) == repo.root:
661 raise error.InputError(_(b'repository root cannot be destination'))
661 raise error.InputError(_(b'repository root cannot be destination'))
662
662
663 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
663 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
664 prefix = opts.get(b'prefix')
664 prefix = opts.get(b'prefix')
665
665
666 if dest == b'-':
666 if dest == b'-':
667 if kind == b'files':
667 if kind == b'files':
668 raise error.InputError(_(b'cannot archive plain files to stdout'))
668 raise error.InputError(_(b'cannot archive plain files to stdout'))
669 dest = cmdutil.makefileobj(ctx, dest)
669 dest = cmdutil.makefileobj(ctx, dest)
670 if not prefix:
670 if not prefix:
671 prefix = os.path.basename(repo.root) + b'-%h'
671 prefix = os.path.basename(repo.root) + b'-%h'
672
672
673 prefix = cmdutil.makefilename(ctx, prefix)
673 prefix = cmdutil.makefilename(ctx, prefix)
674 match = scmutil.match(ctx, [], opts)
674 match = scmutil.match(ctx, [], opts)
675 archival.archive(
675 archival.archive(
676 repo,
676 repo,
677 dest,
677 dest,
678 node,
678 node,
679 kind,
679 kind,
680 not opts.get(b'no_decode'),
680 not opts.get(b'no_decode'),
681 match,
681 match,
682 prefix,
682 prefix,
683 subrepos=opts.get(b'subrepos'),
683 subrepos=opts.get(b'subrepos'),
684 )
684 )
685
685
686
686
687 @command(
687 @command(
688 b'backout',
688 b'backout',
689 [
689 [
690 (
690 (
691 b'',
691 b'',
692 b'merge',
692 b'merge',
693 None,
693 None,
694 _(b'merge with old dirstate parent after backout'),
694 _(b'merge with old dirstate parent after backout'),
695 ),
695 ),
696 (
696 (
697 b'',
697 b'',
698 b'commit',
698 b'commit',
699 None,
699 None,
700 _(b'commit if no conflicts were encountered (DEPRECATED)'),
700 _(b'commit if no conflicts were encountered (DEPRECATED)'),
701 ),
701 ),
702 (b'', b'no-commit', None, _(b'do not commit')),
702 (b'', b'no-commit', None, _(b'do not commit')),
703 (
703 (
704 b'',
704 b'',
705 b'parent',
705 b'parent',
706 b'',
706 b'',
707 _(b'parent to choose when backing out merge (DEPRECATED)'),
707 _(b'parent to choose when backing out merge (DEPRECATED)'),
708 _(b'REV'),
708 _(b'REV'),
709 ),
709 ),
710 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
710 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
711 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
711 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
712 ]
712 ]
713 + mergetoolopts
713 + mergetoolopts
714 + walkopts
714 + walkopts
715 + commitopts
715 + commitopts
716 + commitopts2,
716 + commitopts2,
717 _(b'[OPTION]... [-r] REV'),
717 _(b'[OPTION]... [-r] REV'),
718 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
718 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
719 )
719 )
720 def backout(ui, repo, node=None, rev=None, **opts):
720 def backout(ui, repo, node=None, rev=None, **opts):
721 """reverse effect of earlier changeset
721 """reverse effect of earlier changeset
722
722
723 Prepare a new changeset with the effect of REV undone in the
723 Prepare a new changeset with the effect of REV undone in the
724 current working directory. If no conflicts were encountered,
724 current working directory. If no conflicts were encountered,
725 it will be committed immediately.
725 it will be committed immediately.
726
726
727 If REV is the parent of the working directory, then this new changeset
727 If REV is the parent of the working directory, then this new changeset
728 is committed automatically (unless --no-commit is specified).
728 is committed automatically (unless --no-commit is specified).
729
729
730 .. note::
730 .. note::
731
731
732 :hg:`backout` cannot be used to fix either an unwanted or
732 :hg:`backout` cannot be used to fix either an unwanted or
733 incorrect merge.
733 incorrect merge.
734
734
735 .. container:: verbose
735 .. container:: verbose
736
736
737 Examples:
737 Examples:
738
738
739 - Reverse the effect of the parent of the working directory.
739 - Reverse the effect of the parent of the working directory.
740 This backout will be committed immediately::
740 This backout will be committed immediately::
741
741
742 hg backout -r .
742 hg backout -r .
743
743
744 - Reverse the effect of previous bad revision 23::
744 - Reverse the effect of previous bad revision 23::
745
745
746 hg backout -r 23
746 hg backout -r 23
747
747
748 - Reverse the effect of previous bad revision 23 and
748 - Reverse the effect of previous bad revision 23 and
749 leave changes uncommitted::
749 leave changes uncommitted::
750
750
751 hg backout -r 23 --no-commit
751 hg backout -r 23 --no-commit
752 hg commit -m "Backout revision 23"
752 hg commit -m "Backout revision 23"
753
753
754 By default, the pending changeset will have one parent,
754 By default, the pending changeset will have one parent,
755 maintaining a linear history. With --merge, the pending
755 maintaining a linear history. With --merge, the pending
756 changeset will instead have two parents: the old parent of the
756 changeset will instead have two parents: the old parent of the
757 working directory and a new child of REV that simply undoes REV.
757 working directory and a new child of REV that simply undoes REV.
758
758
759 Before version 1.7, the behavior without --merge was equivalent
759 Before version 1.7, the behavior without --merge was equivalent
760 to specifying --merge followed by :hg:`update --clean .` to
760 to specifying --merge followed by :hg:`update --clean .` to
761 cancel the merge and leave the child of REV as a head to be
761 cancel the merge and leave the child of REV as a head to be
762 merged separately.
762 merged separately.
763
763
764 See :hg:`help dates` for a list of formats valid for -d/--date.
764 See :hg:`help dates` for a list of formats valid for -d/--date.
765
765
766 See :hg:`help revert` for a way to restore files to the state
766 See :hg:`help revert` for a way to restore files to the state
767 of another revision.
767 of another revision.
768
768
769 Returns 0 on success, 1 if nothing to backout or there are unresolved
769 Returns 0 on success, 1 if nothing to backout or there are unresolved
770 files.
770 files.
771 """
771 """
772 with repo.wlock(), repo.lock():
772 with repo.wlock(), repo.lock():
773 return _dobackout(ui, repo, node, rev, **opts)
773 return _dobackout(ui, repo, node, rev, **opts)
774
774
775
775
776 def _dobackout(ui, repo, node=None, rev=None, **opts):
776 def _dobackout(ui, repo, node=None, rev=None, **opts):
777 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
777 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
778 opts = pycompat.byteskwargs(opts)
778 opts = pycompat.byteskwargs(opts)
779
779
780 if rev and node:
780 if rev and node:
781 raise error.InputError(_(b"please specify just one revision"))
781 raise error.InputError(_(b"please specify just one revision"))
782
782
783 if not rev:
783 if not rev:
784 rev = node
784 rev = node
785
785
786 if not rev:
786 if not rev:
787 raise error.InputError(_(b"please specify a revision to backout"))
787 raise error.InputError(_(b"please specify a revision to backout"))
788
788
789 date = opts.get(b'date')
789 date = opts.get(b'date')
790 if date:
790 if date:
791 opts[b'date'] = dateutil.parsedate(date)
791 opts[b'date'] = dateutil.parsedate(date)
792
792
793 cmdutil.checkunfinished(repo)
793 cmdutil.checkunfinished(repo)
794 cmdutil.bailifchanged(repo)
794 cmdutil.bailifchanged(repo)
795 ctx = scmutil.revsingle(repo, rev)
795 ctx = scmutil.revsingle(repo, rev)
796 node = ctx.node()
796 node = ctx.node()
797
797
798 op1, op2 = repo.dirstate.parents()
798 op1, op2 = repo.dirstate.parents()
799 if not repo.changelog.isancestor(node, op1):
799 if not repo.changelog.isancestor(node, op1):
800 raise error.InputError(
800 raise error.InputError(
801 _(b'cannot backout change that is not an ancestor')
801 _(b'cannot backout change that is not an ancestor')
802 )
802 )
803
803
804 p1, p2 = repo.changelog.parents(node)
804 p1, p2 = repo.changelog.parents(node)
805 if p1 == nullid:
805 if p1 == nullid:
806 raise error.InputError(_(b'cannot backout a change with no parents'))
806 raise error.InputError(_(b'cannot backout a change with no parents'))
807 if p2 != nullid:
807 if p2 != nullid:
808 if not opts.get(b'parent'):
808 if not opts.get(b'parent'):
809 raise error.InputError(_(b'cannot backout a merge changeset'))
809 raise error.InputError(_(b'cannot backout a merge changeset'))
810 p = repo.lookup(opts[b'parent'])
810 p = repo.lookup(opts[b'parent'])
811 if p not in (p1, p2):
811 if p not in (p1, p2):
812 raise error.InputError(
812 raise error.InputError(
813 _(b'%s is not a parent of %s') % (short(p), short(node))
813 _(b'%s is not a parent of %s') % (short(p), short(node))
814 )
814 )
815 parent = p
815 parent = p
816 else:
816 else:
817 if opts.get(b'parent'):
817 if opts.get(b'parent'):
818 raise error.InputError(
818 raise error.InputError(
819 _(b'cannot use --parent on non-merge changeset')
819 _(b'cannot use --parent on non-merge changeset')
820 )
820 )
821 parent = p1
821 parent = p1
822
822
823 # the backout should appear on the same branch
823 # the backout should appear on the same branch
824 branch = repo.dirstate.branch()
824 branch = repo.dirstate.branch()
825 bheads = repo.branchheads(branch)
825 bheads = repo.branchheads(branch)
826 rctx = scmutil.revsingle(repo, hex(parent))
826 rctx = scmutil.revsingle(repo, hex(parent))
827 if not opts.get(b'merge') and op1 != node:
827 if not opts.get(b'merge') and op1 != node:
828 with dirstateguard.dirstateguard(repo, b'backout'):
828 with dirstateguard.dirstateguard(repo, b'backout'):
829 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
829 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
830 with ui.configoverride(overrides, b'backout'):
830 with ui.configoverride(overrides, b'backout'):
831 stats = mergemod.back_out(ctx, parent=repo[parent])
831 stats = mergemod.back_out(ctx, parent=repo[parent])
832 repo.setparents(op1, op2)
832 repo.setparents(op1, op2)
833 hg._showstats(repo, stats)
833 hg._showstats(repo, stats)
834 if stats.unresolvedcount:
834 if stats.unresolvedcount:
835 repo.ui.status(
835 repo.ui.status(
836 _(b"use 'hg resolve' to retry unresolved file merges\n")
836 _(b"use 'hg resolve' to retry unresolved file merges\n")
837 )
837 )
838 return 1
838 return 1
839 else:
839 else:
840 hg.clean(repo, node, show_stats=False)
840 hg.clean(repo, node, show_stats=False)
841 repo.dirstate.setbranch(branch)
841 repo.dirstate.setbranch(branch)
842 cmdutil.revert(ui, repo, rctx)
842 cmdutil.revert(ui, repo, rctx)
843
843
844 if opts.get(b'no_commit'):
844 if opts.get(b'no_commit'):
845 msg = _(b"changeset %s backed out, don't forget to commit.\n")
845 msg = _(b"changeset %s backed out, don't forget to commit.\n")
846 ui.status(msg % short(node))
846 ui.status(msg % short(node))
847 return 0
847 return 0
848
848
849 def commitfunc(ui, repo, message, match, opts):
849 def commitfunc(ui, repo, message, match, opts):
850 editform = b'backout'
850 editform = b'backout'
851 e = cmdutil.getcommiteditor(
851 e = cmdutil.getcommiteditor(
852 editform=editform, **pycompat.strkwargs(opts)
852 editform=editform, **pycompat.strkwargs(opts)
853 )
853 )
854 if not message:
854 if not message:
855 # we don't translate commit messages
855 # we don't translate commit messages
856 message = b"Backed out changeset %s" % short(node)
856 message = b"Backed out changeset %s" % short(node)
857 e = cmdutil.getcommiteditor(edit=True, editform=editform)
857 e = cmdutil.getcommiteditor(edit=True, editform=editform)
858 return repo.commit(
858 return repo.commit(
859 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
859 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
860 )
860 )
861
861
862 # save to detect changes
862 # save to detect changes
863 tip = repo.changelog.tip()
863 tip = repo.changelog.tip()
864
864
865 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
865 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
866 if not newnode:
866 if not newnode:
867 ui.status(_(b"nothing changed\n"))
867 ui.status(_(b"nothing changed\n"))
868 return 1
868 return 1
869 cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
869 cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
870
870
871 def nice(node):
871 def nice(node):
872 return b'%d:%s' % (repo.changelog.rev(node), short(node))
872 return b'%d:%s' % (repo.changelog.rev(node), short(node))
873
873
874 ui.status(
874 ui.status(
875 _(b'changeset %s backs out changeset %s\n')
875 _(b'changeset %s backs out changeset %s\n')
876 % (nice(newnode), nice(node))
876 % (nice(newnode), nice(node))
877 )
877 )
878 if opts.get(b'merge') and op1 != node:
878 if opts.get(b'merge') and op1 != node:
879 hg.clean(repo, op1, show_stats=False)
879 hg.clean(repo, op1, show_stats=False)
880 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
880 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
881 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
881 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
882 with ui.configoverride(overrides, b'backout'):
882 with ui.configoverride(overrides, b'backout'):
883 return hg.merge(repo[b'tip'])
883 return hg.merge(repo[b'tip'])
884 return 0
884 return 0
885
885
886
886
887 @command(
887 @command(
888 b'bisect',
888 b'bisect',
889 [
889 [
890 (b'r', b'reset', False, _(b'reset bisect state')),
890 (b'r', b'reset', False, _(b'reset bisect state')),
891 (b'g', b'good', False, _(b'mark changeset good')),
891 (b'g', b'good', False, _(b'mark changeset good')),
892 (b'b', b'bad', False, _(b'mark changeset bad')),
892 (b'b', b'bad', False, _(b'mark changeset bad')),
893 (b's', b'skip', False, _(b'skip testing changeset')),
893 (b's', b'skip', False, _(b'skip testing changeset')),
894 (b'e', b'extend', False, _(b'extend the bisect range')),
894 (b'e', b'extend', False, _(b'extend the bisect range')),
895 (
895 (
896 b'c',
896 b'c',
897 b'command',
897 b'command',
898 b'',
898 b'',
899 _(b'use command to check changeset state'),
899 _(b'use command to check changeset state'),
900 _(b'CMD'),
900 _(b'CMD'),
901 ),
901 ),
902 (b'U', b'noupdate', False, _(b'do not update to target')),
902 (b'U', b'noupdate', False, _(b'do not update to target')),
903 ],
903 ],
904 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
904 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
905 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
905 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
906 )
906 )
907 def bisect(
907 def bisect(
908 ui,
908 ui,
909 repo,
909 repo,
910 positional_1=None,
910 positional_1=None,
911 positional_2=None,
911 positional_2=None,
912 command=None,
912 command=None,
913 reset=None,
913 reset=None,
914 good=None,
914 good=None,
915 bad=None,
915 bad=None,
916 skip=None,
916 skip=None,
917 extend=None,
917 extend=None,
918 noupdate=None,
918 noupdate=None,
919 ):
919 ):
920 """subdivision search of changesets
920 """subdivision search of changesets
921
921
922 This command helps to find changesets which introduce problems. To
922 This command helps to find changesets which introduce problems. To
923 use, mark the earliest changeset you know exhibits the problem as
923 use, mark the earliest changeset you know exhibits the problem as
924 bad, then mark the latest changeset which is free from the problem
924 bad, then mark the latest changeset which is free from the problem
925 as good. Bisect will update your working directory to a revision
925 as good. Bisect will update your working directory to a revision
926 for testing (unless the -U/--noupdate option is specified). Once
926 for testing (unless the -U/--noupdate option is specified). Once
927 you have performed tests, mark the working directory as good or
927 you have performed tests, mark the working directory as good or
928 bad, and bisect will either update to another candidate changeset
928 bad, and bisect will either update to another candidate changeset
929 or announce that it has found the bad revision.
929 or announce that it has found the bad revision.
930
930
931 As a shortcut, you can also use the revision argument to mark a
931 As a shortcut, you can also use the revision argument to mark a
932 revision as good or bad without checking it out first.
932 revision as good or bad without checking it out first.
933
933
934 If you supply a command, it will be used for automatic bisection.
934 If you supply a command, it will be used for automatic bisection.
935 The environment variable HG_NODE will contain the ID of the
935 The environment variable HG_NODE will contain the ID of the
936 changeset being tested. The exit status of the command will be
936 changeset being tested. The exit status of the command will be
937 used to mark revisions as good or bad: status 0 means good, 125
937 used to mark revisions as good or bad: status 0 means good, 125
938 means to skip the revision, 127 (command not found) will abort the
938 means to skip the revision, 127 (command not found) will abort the
939 bisection, and any other non-zero exit status means the revision
939 bisection, and any other non-zero exit status means the revision
940 is bad.
940 is bad.
941
941
942 .. container:: verbose
942 .. container:: verbose
943
943
944 Some examples:
944 Some examples:
945
945
946 - start a bisection with known bad revision 34, and good revision 12::
946 - start a bisection with known bad revision 34, and good revision 12::
947
947
948 hg bisect --bad 34
948 hg bisect --bad 34
949 hg bisect --good 12
949 hg bisect --good 12
950
950
951 - advance the current bisection by marking current revision as good or
951 - advance the current bisection by marking current revision as good or
952 bad::
952 bad::
953
953
954 hg bisect --good
954 hg bisect --good
955 hg bisect --bad
955 hg bisect --bad
956
956
957 - mark the current revision, or a known revision, to be skipped (e.g. if
957 - mark the current revision, or a known revision, to be skipped (e.g. if
958 that revision is not usable because of another issue)::
958 that revision is not usable because of another issue)::
959
959
960 hg bisect --skip
960 hg bisect --skip
961 hg bisect --skip 23
961 hg bisect --skip 23
962
962
963 - skip all revisions that do not touch directories ``foo`` or ``bar``::
963 - skip all revisions that do not touch directories ``foo`` or ``bar``::
964
964
965 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
965 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
966
966
967 - forget the current bisection::
967 - forget the current bisection::
968
968
969 hg bisect --reset
969 hg bisect --reset
970
970
971 - use 'make && make tests' to automatically find the first broken
971 - use 'make && make tests' to automatically find the first broken
972 revision::
972 revision::
973
973
974 hg bisect --reset
974 hg bisect --reset
975 hg bisect --bad 34
975 hg bisect --bad 34
976 hg bisect --good 12
976 hg bisect --good 12
977 hg bisect --command "make && make tests"
977 hg bisect --command "make && make tests"
978
978
979 - see all changesets whose states are already known in the current
979 - see all changesets whose states are already known in the current
980 bisection::
980 bisection::
981
981
982 hg log -r "bisect(pruned)"
982 hg log -r "bisect(pruned)"
983
983
984 - see the changeset currently being bisected (especially useful
984 - see the changeset currently being bisected (especially useful
985 if running with -U/--noupdate)::
985 if running with -U/--noupdate)::
986
986
987 hg log -r "bisect(current)"
987 hg log -r "bisect(current)"
988
988
989 - see all changesets that took part in the current bisection::
989 - see all changesets that took part in the current bisection::
990
990
991 hg log -r "bisect(range)"
991 hg log -r "bisect(range)"
992
992
993 - you can even get a nice graph::
993 - you can even get a nice graph::
994
994
995 hg log --graph -r "bisect(range)"
995 hg log --graph -r "bisect(range)"
996
996
997 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
997 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
998
998
999 Returns 0 on success.
999 Returns 0 on success.
1000 """
1000 """
1001 rev = []
1001 rev = []
1002 # backward compatibility
1002 # backward compatibility
1003 if positional_1 in (b"good", b"bad", b"reset", b"init"):
1003 if positional_1 in (b"good", b"bad", b"reset", b"init"):
1004 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1004 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1005 cmd = positional_1
1005 cmd = positional_1
1006 rev.append(positional_2)
1006 rev.append(positional_2)
1007 if cmd == b"good":
1007 if cmd == b"good":
1008 good = True
1008 good = True
1009 elif cmd == b"bad":
1009 elif cmd == b"bad":
1010 bad = True
1010 bad = True
1011 else:
1011 else:
1012 reset = True
1012 reset = True
1013 elif positional_2:
1013 elif positional_2:
1014 raise error.InputError(_(b'incompatible arguments'))
1014 raise error.InputError(_(b'incompatible arguments'))
1015 elif positional_1 is not None:
1015 elif positional_1 is not None:
1016 rev.append(positional_1)
1016 rev.append(positional_1)
1017
1017
1018 incompatibles = {
1018 incompatibles = {
1019 b'--bad': bad,
1019 b'--bad': bad,
1020 b'--command': bool(command),
1020 b'--command': bool(command),
1021 b'--extend': extend,
1021 b'--extend': extend,
1022 b'--good': good,
1022 b'--good': good,
1023 b'--reset': reset,
1023 b'--reset': reset,
1024 b'--skip': skip,
1024 b'--skip': skip,
1025 }
1025 }
1026
1026
1027 enabled = [x for x in incompatibles if incompatibles[x]]
1027 enabled = [x for x in incompatibles if incompatibles[x]]
1028
1028
1029 if len(enabled) > 1:
1029 if len(enabled) > 1:
1030 raise error.InputError(
1030 raise error.InputError(
1031 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1031 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1032 )
1032 )
1033
1033
1034 if reset:
1034 if reset:
1035 hbisect.resetstate(repo)
1035 hbisect.resetstate(repo)
1036 return
1036 return
1037
1037
1038 state = hbisect.load_state(repo)
1038 state = hbisect.load_state(repo)
1039
1039
1040 if rev:
1040 if rev:
1041 nodes = [repo[i].node() for i in scmutil.revrange(repo, rev)]
1041 nodes = [repo[i].node() for i in scmutil.revrange(repo, rev)]
1042 else:
1042 else:
1043 nodes = [repo.lookup(b'.')]
1043 nodes = [repo.lookup(b'.')]
1044
1044
1045 # update state
1045 # update state
1046 if good or bad or skip:
1046 if good or bad or skip:
1047 if good:
1047 if good:
1048 state[b'good'] += nodes
1048 state[b'good'] += nodes
1049 elif bad:
1049 elif bad:
1050 state[b'bad'] += nodes
1050 state[b'bad'] += nodes
1051 elif skip:
1051 elif skip:
1052 state[b'skip'] += nodes
1052 state[b'skip'] += nodes
1053 hbisect.save_state(repo, state)
1053 hbisect.save_state(repo, state)
1054 if not (state[b'good'] and state[b'bad']):
1054 if not (state[b'good'] and state[b'bad']):
1055 return
1055 return
1056
1056
1057 def mayupdate(repo, node, show_stats=True):
1057 def mayupdate(repo, node, show_stats=True):
1058 """common used update sequence"""
1058 """common used update sequence"""
1059 if noupdate:
1059 if noupdate:
1060 return
1060 return
1061 cmdutil.checkunfinished(repo)
1061 cmdutil.checkunfinished(repo)
1062 cmdutil.bailifchanged(repo)
1062 cmdutil.bailifchanged(repo)
1063 return hg.clean(repo, node, show_stats=show_stats)
1063 return hg.clean(repo, node, show_stats=show_stats)
1064
1064
1065 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1065 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1066
1066
1067 if command:
1067 if command:
1068 changesets = 1
1068 changesets = 1
1069 if noupdate:
1069 if noupdate:
1070 try:
1070 try:
1071 node = state[b'current'][0]
1071 node = state[b'current'][0]
1072 except LookupError:
1072 except LookupError:
1073 raise error.StateError(
1073 raise error.StateError(
1074 _(
1074 _(
1075 b'current bisect revision is unknown - '
1075 b'current bisect revision is unknown - '
1076 b'start a new bisect to fix'
1076 b'start a new bisect to fix'
1077 )
1077 )
1078 )
1078 )
1079 else:
1079 else:
1080 node, p2 = repo.dirstate.parents()
1080 node, p2 = repo.dirstate.parents()
1081 if p2 != nullid:
1081 if p2 != nullid:
1082 raise error.StateError(_(b'current bisect revision is a merge'))
1082 raise error.StateError(_(b'current bisect revision is a merge'))
1083 if rev:
1083 if rev:
1084 if not nodes:
1084 if not nodes:
1085 raise error.Abort(_(b'empty revision set'))
1085 raise error.Abort(_(b'empty revision set'))
1086 node = repo[nodes.last()].node()
1086 node = repo[nodes.last()].node()
1087 with hbisect.restore_state(repo, state, node):
1087 with hbisect.restore_state(repo, state, node):
1088 while changesets:
1088 while changesets:
1089 # update state
1089 # update state
1090 state[b'current'] = [node]
1090 state[b'current'] = [node]
1091 hbisect.save_state(repo, state)
1091 hbisect.save_state(repo, state)
1092 status = ui.system(
1092 status = ui.system(
1093 command,
1093 command,
1094 environ={b'HG_NODE': hex(node)},
1094 environ={b'HG_NODE': hex(node)},
1095 blockedtag=b'bisect_check',
1095 blockedtag=b'bisect_check',
1096 )
1096 )
1097 if status == 125:
1097 if status == 125:
1098 transition = b"skip"
1098 transition = b"skip"
1099 elif status == 0:
1099 elif status == 0:
1100 transition = b"good"
1100 transition = b"good"
1101 # status < 0 means process was killed
1101 # status < 0 means process was killed
1102 elif status == 127:
1102 elif status == 127:
1103 raise error.Abort(_(b"failed to execute %s") % command)
1103 raise error.Abort(_(b"failed to execute %s") % command)
1104 elif status < 0:
1104 elif status < 0:
1105 raise error.Abort(_(b"%s killed") % command)
1105 raise error.Abort(_(b"%s killed") % command)
1106 else:
1106 else:
1107 transition = b"bad"
1107 transition = b"bad"
1108 state[transition].append(node)
1108 state[transition].append(node)
1109 ctx = repo[node]
1109 ctx = repo[node]
1110 ui.status(
1110 ui.status(
1111 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1111 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1112 )
1112 )
1113 hbisect.checkstate(state)
1113 hbisect.checkstate(state)
1114 # bisect
1114 # bisect
1115 nodes, changesets, bgood = hbisect.bisect(repo, state)
1115 nodes, changesets, bgood = hbisect.bisect(repo, state)
1116 # update to next check
1116 # update to next check
1117 node = nodes[0]
1117 node = nodes[0]
1118 mayupdate(repo, node, show_stats=False)
1118 mayupdate(repo, node, show_stats=False)
1119 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1119 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1120 return
1120 return
1121
1121
1122 hbisect.checkstate(state)
1122 hbisect.checkstate(state)
1123
1123
1124 # actually bisect
1124 # actually bisect
1125 nodes, changesets, good = hbisect.bisect(repo, state)
1125 nodes, changesets, good = hbisect.bisect(repo, state)
1126 if extend:
1126 if extend:
1127 if not changesets:
1127 if not changesets:
1128 extendnode = hbisect.extendrange(repo, state, nodes, good)
1128 extendnode = hbisect.extendrange(repo, state, nodes, good)
1129 if extendnode is not None:
1129 if extendnode is not None:
1130 ui.write(
1130 ui.write(
1131 _(b"Extending search to changeset %d:%s\n")
1131 _(b"Extending search to changeset %d:%s\n")
1132 % (extendnode.rev(), extendnode)
1132 % (extendnode.rev(), extendnode)
1133 )
1133 )
1134 state[b'current'] = [extendnode.node()]
1134 state[b'current'] = [extendnode.node()]
1135 hbisect.save_state(repo, state)
1135 hbisect.save_state(repo, state)
1136 return mayupdate(repo, extendnode.node())
1136 return mayupdate(repo, extendnode.node())
1137 raise error.StateError(_(b"nothing to extend"))
1137 raise error.StateError(_(b"nothing to extend"))
1138
1138
1139 if changesets == 0:
1139 if changesets == 0:
1140 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1140 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1141 else:
1141 else:
1142 assert len(nodes) == 1 # only a single node can be tested next
1142 assert len(nodes) == 1 # only a single node can be tested next
1143 node = nodes[0]
1143 node = nodes[0]
1144 # compute the approximate number of remaining tests
1144 # compute the approximate number of remaining tests
1145 tests, size = 0, 2
1145 tests, size = 0, 2
1146 while size <= changesets:
1146 while size <= changesets:
1147 tests, size = tests + 1, size * 2
1147 tests, size = tests + 1, size * 2
1148 rev = repo.changelog.rev(node)
1148 rev = repo.changelog.rev(node)
1149 ui.write(
1149 ui.write(
1150 _(
1150 _(
1151 b"Testing changeset %d:%s "
1151 b"Testing changeset %d:%s "
1152 b"(%d changesets remaining, ~%d tests)\n"
1152 b"(%d changesets remaining, ~%d tests)\n"
1153 )
1153 )
1154 % (rev, short(node), changesets, tests)
1154 % (rev, short(node), changesets, tests)
1155 )
1155 )
1156 state[b'current'] = [node]
1156 state[b'current'] = [node]
1157 hbisect.save_state(repo, state)
1157 hbisect.save_state(repo, state)
1158 return mayupdate(repo, node)
1158 return mayupdate(repo, node)
1159
1159
1160
1160
1161 @command(
1161 @command(
1162 b'bookmarks|bookmark',
1162 b'bookmarks|bookmark',
1163 [
1163 [
1164 (b'f', b'force', False, _(b'force')),
1164 (b'f', b'force', False, _(b'force')),
1165 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1165 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1166 (b'd', b'delete', False, _(b'delete a given bookmark')),
1166 (b'd', b'delete', False, _(b'delete a given bookmark')),
1167 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1167 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1168 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1168 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1169 (b'l', b'list', False, _(b'list existing bookmarks')),
1169 (b'l', b'list', False, _(b'list existing bookmarks')),
1170 ]
1170 ]
1171 + formatteropts,
1171 + formatteropts,
1172 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1172 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1173 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1173 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1174 )
1174 )
1175 def bookmark(ui, repo, *names, **opts):
1175 def bookmark(ui, repo, *names, **opts):
1176 """create a new bookmark or list existing bookmarks
1176 """create a new bookmark or list existing bookmarks
1177
1177
1178 Bookmarks are labels on changesets to help track lines of development.
1178 Bookmarks are labels on changesets to help track lines of development.
1179 Bookmarks are unversioned and can be moved, renamed and deleted.
1179 Bookmarks are unversioned and can be moved, renamed and deleted.
1180 Deleting or moving a bookmark has no effect on the associated changesets.
1180 Deleting or moving a bookmark has no effect on the associated changesets.
1181
1181
1182 Creating or updating to a bookmark causes it to be marked as 'active'.
1182 Creating or updating to a bookmark causes it to be marked as 'active'.
1183 The active bookmark is indicated with a '*'.
1183 The active bookmark is indicated with a '*'.
1184 When a commit is made, the active bookmark will advance to the new commit.
1184 When a commit is made, the active bookmark will advance to the new commit.
1185 A plain :hg:`update` will also advance an active bookmark, if possible.
1185 A plain :hg:`update` will also advance an active bookmark, if possible.
1186 Updating away from a bookmark will cause it to be deactivated.
1186 Updating away from a bookmark will cause it to be deactivated.
1187
1187
1188 Bookmarks can be pushed and pulled between repositories (see
1188 Bookmarks can be pushed and pulled between repositories (see
1189 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1189 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1190 diverged, a new 'divergent bookmark' of the form 'name@path' will
1190 diverged, a new 'divergent bookmark' of the form 'name@path' will
1191 be created. Using :hg:`merge` will resolve the divergence.
1191 be created. Using :hg:`merge` will resolve the divergence.
1192
1192
1193 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1193 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1194 the active bookmark's name.
1194 the active bookmark's name.
1195
1195
1196 A bookmark named '@' has the special property that :hg:`clone` will
1196 A bookmark named '@' has the special property that :hg:`clone` will
1197 check it out by default if it exists.
1197 check it out by default if it exists.
1198
1198
1199 .. container:: verbose
1199 .. container:: verbose
1200
1200
1201 Template:
1201 Template:
1202
1202
1203 The following keywords are supported in addition to the common template
1203 The following keywords are supported in addition to the common template
1204 keywords and functions such as ``{bookmark}``. See also
1204 keywords and functions such as ``{bookmark}``. See also
1205 :hg:`help templates`.
1205 :hg:`help templates`.
1206
1206
1207 :active: Boolean. True if the bookmark is active.
1207 :active: Boolean. True if the bookmark is active.
1208
1208
1209 Examples:
1209 Examples:
1210
1210
1211 - create an active bookmark for a new line of development::
1211 - create an active bookmark for a new line of development::
1212
1212
1213 hg book new-feature
1213 hg book new-feature
1214
1214
1215 - create an inactive bookmark as a place marker::
1215 - create an inactive bookmark as a place marker::
1216
1216
1217 hg book -i reviewed
1217 hg book -i reviewed
1218
1218
1219 - create an inactive bookmark on another changeset::
1219 - create an inactive bookmark on another changeset::
1220
1220
1221 hg book -r .^ tested
1221 hg book -r .^ tested
1222
1222
1223 - rename bookmark turkey to dinner::
1223 - rename bookmark turkey to dinner::
1224
1224
1225 hg book -m turkey dinner
1225 hg book -m turkey dinner
1226
1226
1227 - move the '@' bookmark from another branch::
1227 - move the '@' bookmark from another branch::
1228
1228
1229 hg book -f @
1229 hg book -f @
1230
1230
1231 - print only the active bookmark name::
1231 - print only the active bookmark name::
1232
1232
1233 hg book -ql .
1233 hg book -ql .
1234 """
1234 """
1235 opts = pycompat.byteskwargs(opts)
1235 opts = pycompat.byteskwargs(opts)
1236 force = opts.get(b'force')
1236 force = opts.get(b'force')
1237 rev = opts.get(b'rev')
1237 rev = opts.get(b'rev')
1238 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1238 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1239
1239
1240 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1240 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1241 if action:
1241 if action:
1242 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1242 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1243 elif names or rev:
1243 elif names or rev:
1244 action = b'add'
1244 action = b'add'
1245 elif inactive:
1245 elif inactive:
1246 action = b'inactive' # meaning deactivate
1246 action = b'inactive' # meaning deactivate
1247 else:
1247 else:
1248 action = b'list'
1248 action = b'list'
1249
1249
1250 cmdutil.check_incompatible_arguments(
1250 cmdutil.check_incompatible_arguments(
1251 opts, b'inactive', [b'delete', b'list']
1251 opts, b'inactive', [b'delete', b'list']
1252 )
1252 )
1253 if not names and action in {b'add', b'delete'}:
1253 if not names and action in {b'add', b'delete'}:
1254 raise error.InputError(_(b"bookmark name required"))
1254 raise error.InputError(_(b"bookmark name required"))
1255
1255
1256 if action in {b'add', b'delete', b'rename', b'inactive'}:
1256 if action in {b'add', b'delete', b'rename', b'inactive'}:
1257 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1257 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1258 if action == b'delete':
1258 if action == b'delete':
1259 names = pycompat.maplist(repo._bookmarks.expandname, names)
1259 names = pycompat.maplist(repo._bookmarks.expandname, names)
1260 bookmarks.delete(repo, tr, names)
1260 bookmarks.delete(repo, tr, names)
1261 elif action == b'rename':
1261 elif action == b'rename':
1262 if not names:
1262 if not names:
1263 raise error.InputError(_(b"new bookmark name required"))
1263 raise error.InputError(_(b"new bookmark name required"))
1264 elif len(names) > 1:
1264 elif len(names) > 1:
1265 raise error.InputError(
1265 raise error.InputError(
1266 _(b"only one new bookmark name allowed")
1266 _(b"only one new bookmark name allowed")
1267 )
1267 )
1268 oldname = repo._bookmarks.expandname(opts[b'rename'])
1268 oldname = repo._bookmarks.expandname(opts[b'rename'])
1269 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1269 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1270 elif action == b'add':
1270 elif action == b'add':
1271 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1271 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1272 elif action == b'inactive':
1272 elif action == b'inactive':
1273 if len(repo._bookmarks) == 0:
1273 if len(repo._bookmarks) == 0:
1274 ui.status(_(b"no bookmarks set\n"))
1274 ui.status(_(b"no bookmarks set\n"))
1275 elif not repo._activebookmark:
1275 elif not repo._activebookmark:
1276 ui.status(_(b"no active bookmark\n"))
1276 ui.status(_(b"no active bookmark\n"))
1277 else:
1277 else:
1278 bookmarks.deactivate(repo)
1278 bookmarks.deactivate(repo)
1279 elif action == b'list':
1279 elif action == b'list':
1280 names = pycompat.maplist(repo._bookmarks.expandname, names)
1280 names = pycompat.maplist(repo._bookmarks.expandname, names)
1281 with ui.formatter(b'bookmarks', opts) as fm:
1281 with ui.formatter(b'bookmarks', opts) as fm:
1282 bookmarks.printbookmarks(ui, repo, fm, names)
1282 bookmarks.printbookmarks(ui, repo, fm, names)
1283 else:
1283 else:
1284 raise error.ProgrammingError(b'invalid action: %s' % action)
1284 raise error.ProgrammingError(b'invalid action: %s' % action)
1285
1285
1286
1286
1287 @command(
1287 @command(
1288 b'branch',
1288 b'branch',
1289 [
1289 [
1290 (
1290 (
1291 b'f',
1291 b'f',
1292 b'force',
1292 b'force',
1293 None,
1293 None,
1294 _(b'set branch name even if it shadows an existing branch'),
1294 _(b'set branch name even if it shadows an existing branch'),
1295 ),
1295 ),
1296 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1296 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1297 (
1297 (
1298 b'r',
1298 b'r',
1299 b'rev',
1299 b'rev',
1300 [],
1300 [],
1301 _(b'change branches of the given revs (EXPERIMENTAL)'),
1301 _(b'change branches of the given revs (EXPERIMENTAL)'),
1302 ),
1302 ),
1303 ],
1303 ],
1304 _(b'[-fC] [NAME]'),
1304 _(b'[-fC] [NAME]'),
1305 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1305 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1306 )
1306 )
1307 def branch(ui, repo, label=None, **opts):
1307 def branch(ui, repo, label=None, **opts):
1308 """set or show the current branch name
1308 """set or show the current branch name
1309
1309
1310 .. note::
1310 .. note::
1311
1311
1312 Branch names are permanent and global. Use :hg:`bookmark` to create a
1312 Branch names are permanent and global. Use :hg:`bookmark` to create a
1313 light-weight bookmark instead. See :hg:`help glossary` for more
1313 light-weight bookmark instead. See :hg:`help glossary` for more
1314 information about named branches and bookmarks.
1314 information about named branches and bookmarks.
1315
1315
1316 With no argument, show the current branch name. With one argument,
1316 With no argument, show the current branch name. With one argument,
1317 set the working directory branch name (the branch will not exist
1317 set the working directory branch name (the branch will not exist
1318 in the repository until the next commit). Standard practice
1318 in the repository until the next commit). Standard practice
1319 recommends that primary development take place on the 'default'
1319 recommends that primary development take place on the 'default'
1320 branch.
1320 branch.
1321
1321
1322 Unless -f/--force is specified, branch will not let you set a
1322 Unless -f/--force is specified, branch will not let you set a
1323 branch name that already exists.
1323 branch name that already exists.
1324
1324
1325 Use -C/--clean to reset the working directory branch to that of
1325 Use -C/--clean to reset the working directory branch to that of
1326 the parent of the working directory, negating a previous branch
1326 the parent of the working directory, negating a previous branch
1327 change.
1327 change.
1328
1328
1329 Use the command :hg:`update` to switch to an existing branch. Use
1329 Use the command :hg:`update` to switch to an existing branch. Use
1330 :hg:`commit --close-branch` to mark this branch head as closed.
1330 :hg:`commit --close-branch` to mark this branch head as closed.
1331 When all heads of a branch are closed, the branch will be
1331 When all heads of a branch are closed, the branch will be
1332 considered closed.
1332 considered closed.
1333
1333
1334 Returns 0 on success.
1334 Returns 0 on success.
1335 """
1335 """
1336 opts = pycompat.byteskwargs(opts)
1336 opts = pycompat.byteskwargs(opts)
1337 revs = opts.get(b'rev')
1337 revs = opts.get(b'rev')
1338 if label:
1338 if label:
1339 label = label.strip()
1339 label = label.strip()
1340
1340
1341 if not opts.get(b'clean') and not label:
1341 if not opts.get(b'clean') and not label:
1342 if revs:
1342 if revs:
1343 raise error.InputError(
1343 raise error.InputError(
1344 _(b"no branch name specified for the revisions")
1344 _(b"no branch name specified for the revisions")
1345 )
1345 )
1346 ui.write(b"%s\n" % repo.dirstate.branch())
1346 ui.write(b"%s\n" % repo.dirstate.branch())
1347 return
1347 return
1348
1348
1349 with repo.wlock():
1349 with repo.wlock():
1350 if opts.get(b'clean'):
1350 if opts.get(b'clean'):
1351 label = repo[b'.'].branch()
1351 label = repo[b'.'].branch()
1352 repo.dirstate.setbranch(label)
1352 repo.dirstate.setbranch(label)
1353 ui.status(_(b'reset working directory to branch %s\n') % label)
1353 ui.status(_(b'reset working directory to branch %s\n') % label)
1354 elif label:
1354 elif label:
1355
1355
1356 scmutil.checknewlabel(repo, label, b'branch')
1356 scmutil.checknewlabel(repo, label, b'branch')
1357 if revs:
1357 if revs:
1358 return cmdutil.changebranch(ui, repo, revs, label, opts)
1358 return cmdutil.changebranch(ui, repo, revs, label, opts)
1359
1359
1360 if not opts.get(b'force') and label in repo.branchmap():
1360 if not opts.get(b'force') and label in repo.branchmap():
1361 if label not in [p.branch() for p in repo[None].parents()]:
1361 if label not in [p.branch() for p in repo[None].parents()]:
1362 raise error.InputError(
1362 raise error.InputError(
1363 _(b'a branch of the same name already exists'),
1363 _(b'a branch of the same name already exists'),
1364 # i18n: "it" refers to an existing branch
1364 # i18n: "it" refers to an existing branch
1365 hint=_(b"use 'hg update' to switch to it"),
1365 hint=_(b"use 'hg update' to switch to it"),
1366 )
1366 )
1367
1367
1368 repo.dirstate.setbranch(label)
1368 repo.dirstate.setbranch(label)
1369 ui.status(_(b'marked working directory as branch %s\n') % label)
1369 ui.status(_(b'marked working directory as branch %s\n') % label)
1370
1370
1371 # find any open named branches aside from default
1371 # find any open named branches aside from default
1372 for n, h, t, c in repo.branchmap().iterbranches():
1372 for n, h, t, c in repo.branchmap().iterbranches():
1373 if n != b"default" and not c:
1373 if n != b"default" and not c:
1374 return 0
1374 return 0
1375 ui.status(
1375 ui.status(
1376 _(
1376 _(
1377 b'(branches are permanent and global, '
1377 b'(branches are permanent and global, '
1378 b'did you want a bookmark?)\n'
1378 b'did you want a bookmark?)\n'
1379 )
1379 )
1380 )
1380 )
1381
1381
1382
1382
1383 @command(
1383 @command(
1384 b'branches',
1384 b'branches',
1385 [
1385 [
1386 (
1386 (
1387 b'a',
1387 b'a',
1388 b'active',
1388 b'active',
1389 False,
1389 False,
1390 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1390 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1391 ),
1391 ),
1392 (b'c', b'closed', False, _(b'show normal and closed branches')),
1392 (b'c', b'closed', False, _(b'show normal and closed branches')),
1393 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1393 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1394 ]
1394 ]
1395 + formatteropts,
1395 + formatteropts,
1396 _(b'[-c]'),
1396 _(b'[-c]'),
1397 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1397 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1398 intents={INTENT_READONLY},
1398 intents={INTENT_READONLY},
1399 )
1399 )
1400 def branches(ui, repo, active=False, closed=False, **opts):
1400 def branches(ui, repo, active=False, closed=False, **opts):
1401 """list repository named branches
1401 """list repository named branches
1402
1402
1403 List the repository's named branches, indicating which ones are
1403 List the repository's named branches, indicating which ones are
1404 inactive. If -c/--closed is specified, also list branches which have
1404 inactive. If -c/--closed is specified, also list branches which have
1405 been marked closed (see :hg:`commit --close-branch`).
1405 been marked closed (see :hg:`commit --close-branch`).
1406
1406
1407 Use the command :hg:`update` to switch to an existing branch.
1407 Use the command :hg:`update` to switch to an existing branch.
1408
1408
1409 .. container:: verbose
1409 .. container:: verbose
1410
1410
1411 Template:
1411 Template:
1412
1412
1413 The following keywords are supported in addition to the common template
1413 The following keywords are supported in addition to the common template
1414 keywords and functions such as ``{branch}``. See also
1414 keywords and functions such as ``{branch}``. See also
1415 :hg:`help templates`.
1415 :hg:`help templates`.
1416
1416
1417 :active: Boolean. True if the branch is active.
1417 :active: Boolean. True if the branch is active.
1418 :closed: Boolean. True if the branch is closed.
1418 :closed: Boolean. True if the branch is closed.
1419 :current: Boolean. True if it is the current branch.
1419 :current: Boolean. True if it is the current branch.
1420
1420
1421 Returns 0.
1421 Returns 0.
1422 """
1422 """
1423
1423
1424 opts = pycompat.byteskwargs(opts)
1424 opts = pycompat.byteskwargs(opts)
1425 revs = opts.get(b'rev')
1425 revs = opts.get(b'rev')
1426 selectedbranches = None
1426 selectedbranches = None
1427 if revs:
1427 if revs:
1428 revs = scmutil.revrange(repo, revs)
1428 revs = scmutil.revrange(repo, revs)
1429 getbi = repo.revbranchcache().branchinfo
1429 getbi = repo.revbranchcache().branchinfo
1430 selectedbranches = {getbi(r)[0] for r in revs}
1430 selectedbranches = {getbi(r)[0] for r in revs}
1431
1431
1432 ui.pager(b'branches')
1432 ui.pager(b'branches')
1433 fm = ui.formatter(b'branches', opts)
1433 fm = ui.formatter(b'branches', opts)
1434 hexfunc = fm.hexfunc
1434 hexfunc = fm.hexfunc
1435
1435
1436 allheads = set(repo.heads())
1436 allheads = set(repo.heads())
1437 branches = []
1437 branches = []
1438 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1438 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1439 if selectedbranches is not None and tag not in selectedbranches:
1439 if selectedbranches is not None and tag not in selectedbranches:
1440 continue
1440 continue
1441 isactive = False
1441 isactive = False
1442 if not isclosed:
1442 if not isclosed:
1443 openheads = set(repo.branchmap().iteropen(heads))
1443 openheads = set(repo.branchmap().iteropen(heads))
1444 isactive = bool(openheads & allheads)
1444 isactive = bool(openheads & allheads)
1445 branches.append((tag, repo[tip], isactive, not isclosed))
1445 branches.append((tag, repo[tip], isactive, not isclosed))
1446 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1446 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1447
1447
1448 for tag, ctx, isactive, isopen in branches:
1448 for tag, ctx, isactive, isopen in branches:
1449 if active and not isactive:
1449 if active and not isactive:
1450 continue
1450 continue
1451 if isactive:
1451 if isactive:
1452 label = b'branches.active'
1452 label = b'branches.active'
1453 notice = b''
1453 notice = b''
1454 elif not isopen:
1454 elif not isopen:
1455 if not closed:
1455 if not closed:
1456 continue
1456 continue
1457 label = b'branches.closed'
1457 label = b'branches.closed'
1458 notice = _(b' (closed)')
1458 notice = _(b' (closed)')
1459 else:
1459 else:
1460 label = b'branches.inactive'
1460 label = b'branches.inactive'
1461 notice = _(b' (inactive)')
1461 notice = _(b' (inactive)')
1462 current = tag == repo.dirstate.branch()
1462 current = tag == repo.dirstate.branch()
1463 if current:
1463 if current:
1464 label = b'branches.current'
1464 label = b'branches.current'
1465
1465
1466 fm.startitem()
1466 fm.startitem()
1467 fm.write(b'branch', b'%s', tag, label=label)
1467 fm.write(b'branch', b'%s', tag, label=label)
1468 rev = ctx.rev()
1468 rev = ctx.rev()
1469 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1469 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1470 fmt = b' ' * padsize + b' %d:%s'
1470 fmt = b' ' * padsize + b' %d:%s'
1471 fm.condwrite(
1471 fm.condwrite(
1472 not ui.quiet,
1472 not ui.quiet,
1473 b'rev node',
1473 b'rev node',
1474 fmt,
1474 fmt,
1475 rev,
1475 rev,
1476 hexfunc(ctx.node()),
1476 hexfunc(ctx.node()),
1477 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1477 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1478 )
1478 )
1479 fm.context(ctx=ctx)
1479 fm.context(ctx=ctx)
1480 fm.data(active=isactive, closed=not isopen, current=current)
1480 fm.data(active=isactive, closed=not isopen, current=current)
1481 if not ui.quiet:
1481 if not ui.quiet:
1482 fm.plain(notice)
1482 fm.plain(notice)
1483 fm.plain(b'\n')
1483 fm.plain(b'\n')
1484 fm.end()
1484 fm.end()
1485
1485
1486
1486
1487 @command(
1487 @command(
1488 b'bundle',
1488 b'bundle',
1489 [
1489 [
1490 (
1490 (
1491 b'f',
1491 b'f',
1492 b'force',
1492 b'force',
1493 None,
1493 None,
1494 _(b'run even when the destination is unrelated'),
1494 _(b'run even when the destination is unrelated'),
1495 ),
1495 ),
1496 (
1496 (
1497 b'r',
1497 b'r',
1498 b'rev',
1498 b'rev',
1499 [],
1499 [],
1500 _(b'a changeset intended to be added to the destination'),
1500 _(b'a changeset intended to be added to the destination'),
1501 _(b'REV'),
1501 _(b'REV'),
1502 ),
1502 ),
1503 (
1503 (
1504 b'b',
1504 b'b',
1505 b'branch',
1505 b'branch',
1506 [],
1506 [],
1507 _(b'a specific branch you would like to bundle'),
1507 _(b'a specific branch you would like to bundle'),
1508 _(b'BRANCH'),
1508 _(b'BRANCH'),
1509 ),
1509 ),
1510 (
1510 (
1511 b'',
1511 b'',
1512 b'base',
1512 b'base',
1513 [],
1513 [],
1514 _(b'a base changeset assumed to be available at the destination'),
1514 _(b'a base changeset assumed to be available at the destination'),
1515 _(b'REV'),
1515 _(b'REV'),
1516 ),
1516 ),
1517 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1517 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1518 (
1518 (
1519 b't',
1519 b't',
1520 b'type',
1520 b'type',
1521 b'bzip2',
1521 b'bzip2',
1522 _(b'bundle compression type to use'),
1522 _(b'bundle compression type to use'),
1523 _(b'TYPE'),
1523 _(b'TYPE'),
1524 ),
1524 ),
1525 ]
1525 ]
1526 + remoteopts,
1526 + remoteopts,
1527 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1527 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1528 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1528 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1529 )
1529 )
1530 def bundle(ui, repo, fname, dest=None, **opts):
1530 def bundle(ui, repo, fname, dest=None, **opts):
1531 """create a bundle file
1531 """create a bundle file
1532
1532
1533 Generate a bundle file containing data to be transferred to another
1533 Generate a bundle file containing data to be transferred to another
1534 repository.
1534 repository.
1535
1535
1536 To create a bundle containing all changesets, use -a/--all
1536 To create a bundle containing all changesets, use -a/--all
1537 (or --base null). Otherwise, hg assumes the destination will have
1537 (or --base null). Otherwise, hg assumes the destination will have
1538 all the nodes you specify with --base parameters. Otherwise, hg
1538 all the nodes you specify with --base parameters. Otherwise, hg
1539 will assume the repository has all the nodes in destination, or
1539 will assume the repository has all the nodes in destination, or
1540 default-push/default if no destination is specified, where destination
1540 default-push/default if no destination is specified, where destination
1541 is the repository you provide through DEST option.
1541 is the repository you provide through DEST option.
1542
1542
1543 You can change bundle format with the -t/--type option. See
1543 You can change bundle format with the -t/--type option. See
1544 :hg:`help bundlespec` for documentation on this format. By default,
1544 :hg:`help bundlespec` for documentation on this format. By default,
1545 the most appropriate format is used and compression defaults to
1545 the most appropriate format is used and compression defaults to
1546 bzip2.
1546 bzip2.
1547
1547
1548 The bundle file can then be transferred using conventional means
1548 The bundle file can then be transferred using conventional means
1549 and applied to another repository with the unbundle or pull
1549 and applied to another repository with the unbundle or pull
1550 command. This is useful when direct push and pull are not
1550 command. This is useful when direct push and pull are not
1551 available or when exporting an entire repository is undesirable.
1551 available or when exporting an entire repository is undesirable.
1552
1552
1553 Applying bundles preserves all changeset contents including
1553 Applying bundles preserves all changeset contents including
1554 permissions, copy/rename information, and revision history.
1554 permissions, copy/rename information, and revision history.
1555
1555
1556 Returns 0 on success, 1 if no changes found.
1556 Returns 0 on success, 1 if no changes found.
1557 """
1557 """
1558 opts = pycompat.byteskwargs(opts)
1558 opts = pycompat.byteskwargs(opts)
1559 revs = None
1559 revs = None
1560 if b'rev' in opts:
1560 if b'rev' in opts:
1561 revstrings = opts[b'rev']
1561 revstrings = opts[b'rev']
1562 revs = scmutil.revrange(repo, revstrings)
1562 revs = scmutil.revrange(repo, revstrings)
1563 if revstrings and not revs:
1563 if revstrings and not revs:
1564 raise error.InputError(_(b'no commits to bundle'))
1564 raise error.InputError(_(b'no commits to bundle'))
1565
1565
1566 bundletype = opts.get(b'type', b'bzip2').lower()
1566 bundletype = opts.get(b'type', b'bzip2').lower()
1567 try:
1567 try:
1568 bundlespec = bundlecaches.parsebundlespec(
1568 bundlespec = bundlecaches.parsebundlespec(
1569 repo, bundletype, strict=False
1569 repo, bundletype, strict=False
1570 )
1570 )
1571 except error.UnsupportedBundleSpecification as e:
1571 except error.UnsupportedBundleSpecification as e:
1572 raise error.InputError(
1572 raise error.InputError(
1573 pycompat.bytestr(e),
1573 pycompat.bytestr(e),
1574 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1574 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1575 )
1575 )
1576 cgversion = bundlespec.contentopts[b"cg.version"]
1576 cgversion = bundlespec.contentopts[b"cg.version"]
1577
1577
1578 # Packed bundles are a pseudo bundle format for now.
1578 # Packed bundles are a pseudo bundle format for now.
1579 if cgversion == b's1':
1579 if cgversion == b's1':
1580 raise error.InputError(
1580 raise error.InputError(
1581 _(b'packed bundles cannot be produced by "hg bundle"'),
1581 _(b'packed bundles cannot be produced by "hg bundle"'),
1582 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1582 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1583 )
1583 )
1584
1584
1585 if opts.get(b'all'):
1585 if opts.get(b'all'):
1586 if dest:
1586 if dest:
1587 raise error.InputError(
1587 raise error.InputError(
1588 _(b"--all is incompatible with specifying a destination")
1588 _(b"--all is incompatible with specifying a destination")
1589 )
1589 )
1590 if opts.get(b'base'):
1590 if opts.get(b'base'):
1591 ui.warn(_(b"ignoring --base because --all was specified\n"))
1591 ui.warn(_(b"ignoring --base because --all was specified\n"))
1592 base = [nullrev]
1592 base = [nullrev]
1593 else:
1593 else:
1594 base = scmutil.revrange(repo, opts.get(b'base'))
1594 base = scmutil.revrange(repo, opts.get(b'base'))
1595 if cgversion not in changegroup.supportedoutgoingversions(repo):
1595 if cgversion not in changegroup.supportedoutgoingversions(repo):
1596 raise error.Abort(
1596 raise error.Abort(
1597 _(b"repository does not support bundle version %s") % cgversion
1597 _(b"repository does not support bundle version %s") % cgversion
1598 )
1598 )
1599
1599
1600 if base:
1600 if base:
1601 if dest:
1601 if dest:
1602 raise error.InputError(
1602 raise error.InputError(
1603 _(b"--base is incompatible with specifying a destination")
1603 _(b"--base is incompatible with specifying a destination")
1604 )
1604 )
1605 common = [repo[rev].node() for rev in base]
1605 common = [repo[rev].node() for rev in base]
1606 heads = [repo[r].node() for r in revs] if revs else None
1606 heads = [repo[r].node() for r in revs] if revs else None
1607 outgoing = discovery.outgoing(repo, common, heads)
1607 outgoing = discovery.outgoing(repo, common, heads)
1608 else:
1608 else:
1609 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1609 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1610 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1610 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1611 other = hg.peer(repo, opts, dest)
1611 other = hg.peer(repo, opts, dest)
1612 revs = [repo[r].hex() for r in revs]
1612 revs = [repo[r].hex() for r in revs]
1613 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1613 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1614 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1614 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1615 outgoing = discovery.findcommonoutgoing(
1615 outgoing = discovery.findcommonoutgoing(
1616 repo,
1616 repo,
1617 other,
1617 other,
1618 onlyheads=heads,
1618 onlyheads=heads,
1619 force=opts.get(b'force'),
1619 force=opts.get(b'force'),
1620 portable=True,
1620 portable=True,
1621 )
1621 )
1622
1622
1623 if not outgoing.missing:
1623 if not outgoing.missing:
1624 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1624 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1625 return 1
1625 return 1
1626
1626
1627 if cgversion == b'01': # bundle1
1627 if cgversion == b'01': # bundle1
1628 bversion = b'HG10' + bundlespec.wirecompression
1628 bversion = b'HG10' + bundlespec.wirecompression
1629 bcompression = None
1629 bcompression = None
1630 elif cgversion in (b'02', b'03'):
1630 elif cgversion in (b'02', b'03'):
1631 bversion = b'HG20'
1631 bversion = b'HG20'
1632 bcompression = bundlespec.wirecompression
1632 bcompression = bundlespec.wirecompression
1633 else:
1633 else:
1634 raise error.ProgrammingError(
1634 raise error.ProgrammingError(
1635 b'bundle: unexpected changegroup version %s' % cgversion
1635 b'bundle: unexpected changegroup version %s' % cgversion
1636 )
1636 )
1637
1637
1638 # TODO compression options should be derived from bundlespec parsing.
1638 # TODO compression options should be derived from bundlespec parsing.
1639 # This is a temporary hack to allow adjusting bundle compression
1639 # This is a temporary hack to allow adjusting bundle compression
1640 # level without a) formalizing the bundlespec changes to declare it
1640 # level without a) formalizing the bundlespec changes to declare it
1641 # b) introducing a command flag.
1641 # b) introducing a command flag.
1642 compopts = {}
1642 compopts = {}
1643 complevel = ui.configint(
1643 complevel = ui.configint(
1644 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1644 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1645 )
1645 )
1646 if complevel is None:
1646 if complevel is None:
1647 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1647 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1648 if complevel is not None:
1648 if complevel is not None:
1649 compopts[b'level'] = complevel
1649 compopts[b'level'] = complevel
1650
1650
1651 # Bundling of obsmarker and phases is optional as not all clients
1651 # Bundling of obsmarker and phases is optional as not all clients
1652 # support the necessary features.
1652 # support the necessary features.
1653 cfg = ui.configbool
1653 cfg = ui.configbool
1654 contentopts = {
1654 contentopts = {
1655 b'obsolescence': cfg(b'experimental', b'evolution.bundle-obsmarker'),
1655 b'obsolescence': cfg(b'experimental', b'evolution.bundle-obsmarker'),
1656 b'obsolescence-mandatory': cfg(
1656 b'obsolescence-mandatory': cfg(
1657 b'experimental', b'evolution.bundle-obsmarker:mandatory'
1657 b'experimental', b'evolution.bundle-obsmarker:mandatory'
1658 ),
1658 ),
1659 b'phases': cfg(b'experimental', b'bundle-phases'),
1659 b'phases': cfg(b'experimental', b'bundle-phases'),
1660 }
1660 }
1661 bundlespec.contentopts.update(contentopts)
1661 bundlespec.contentopts.update(contentopts)
1662
1662
1663 bundle2.writenewbundle(
1663 bundle2.writenewbundle(
1664 ui,
1664 ui,
1665 repo,
1665 repo,
1666 b'bundle',
1666 b'bundle',
1667 fname,
1667 fname,
1668 bversion,
1668 bversion,
1669 outgoing,
1669 outgoing,
1670 bundlespec.contentopts,
1670 bundlespec.contentopts,
1671 compression=bcompression,
1671 compression=bcompression,
1672 compopts=compopts,
1672 compopts=compopts,
1673 )
1673 )
1674
1674
1675
1675
1676 @command(
1676 @command(
1677 b'cat',
1677 b'cat',
1678 [
1678 [
1679 (
1679 (
1680 b'o',
1680 b'o',
1681 b'output',
1681 b'output',
1682 b'',
1682 b'',
1683 _(b'print output to file with formatted name'),
1683 _(b'print output to file with formatted name'),
1684 _(b'FORMAT'),
1684 _(b'FORMAT'),
1685 ),
1685 ),
1686 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1686 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1687 (b'', b'decode', None, _(b'apply any matching decode filter')),
1687 (b'', b'decode', None, _(b'apply any matching decode filter')),
1688 ]
1688 ]
1689 + walkopts
1689 + walkopts
1690 + formatteropts,
1690 + formatteropts,
1691 _(b'[OPTION]... FILE...'),
1691 _(b'[OPTION]... FILE...'),
1692 helpcategory=command.CATEGORY_FILE_CONTENTS,
1692 helpcategory=command.CATEGORY_FILE_CONTENTS,
1693 inferrepo=True,
1693 inferrepo=True,
1694 intents={INTENT_READONLY},
1694 intents={INTENT_READONLY},
1695 )
1695 )
1696 def cat(ui, repo, file1, *pats, **opts):
1696 def cat(ui, repo, file1, *pats, **opts):
1697 """output the current or given revision of files
1697 """output the current or given revision of files
1698
1698
1699 Print the specified files as they were at the given revision. If
1699 Print the specified files as they were at the given revision. If
1700 no revision is given, the parent of the working directory is used.
1700 no revision is given, the parent of the working directory is used.
1701
1701
1702 Output may be to a file, in which case the name of the file is
1702 Output may be to a file, in which case the name of the file is
1703 given using a template string. See :hg:`help templates`. In addition
1703 given using a template string. See :hg:`help templates`. In addition
1704 to the common template keywords, the following formatting rules are
1704 to the common template keywords, the following formatting rules are
1705 supported:
1705 supported:
1706
1706
1707 :``%%``: literal "%" character
1707 :``%%``: literal "%" character
1708 :``%s``: basename of file being printed
1708 :``%s``: basename of file being printed
1709 :``%d``: dirname of file being printed, or '.' if in repository root
1709 :``%d``: dirname of file being printed, or '.' if in repository root
1710 :``%p``: root-relative path name of file being printed
1710 :``%p``: root-relative path name of file being printed
1711 :``%H``: changeset hash (40 hexadecimal digits)
1711 :``%H``: changeset hash (40 hexadecimal digits)
1712 :``%R``: changeset revision number
1712 :``%R``: changeset revision number
1713 :``%h``: short-form changeset hash (12 hexadecimal digits)
1713 :``%h``: short-form changeset hash (12 hexadecimal digits)
1714 :``%r``: zero-padded changeset revision number
1714 :``%r``: zero-padded changeset revision number
1715 :``%b``: basename of the exporting repository
1715 :``%b``: basename of the exporting repository
1716 :``\\``: literal "\\" character
1716 :``\\``: literal "\\" character
1717
1717
1718 .. container:: verbose
1718 .. container:: verbose
1719
1719
1720 Template:
1720 Template:
1721
1721
1722 The following keywords are supported in addition to the common template
1722 The following keywords are supported in addition to the common template
1723 keywords and functions. See also :hg:`help templates`.
1723 keywords and functions. See also :hg:`help templates`.
1724
1724
1725 :data: String. File content.
1725 :data: String. File content.
1726 :path: String. Repository-absolute path of the file.
1726 :path: String. Repository-absolute path of the file.
1727
1727
1728 Returns 0 on success.
1728 Returns 0 on success.
1729 """
1729 """
1730 opts = pycompat.byteskwargs(opts)
1730 opts = pycompat.byteskwargs(opts)
1731 rev = opts.get(b'rev')
1731 rev = opts.get(b'rev')
1732 if rev:
1732 if rev:
1733 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1733 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1734 ctx = scmutil.revsingle(repo, rev)
1734 ctx = scmutil.revsingle(repo, rev)
1735 m = scmutil.match(ctx, (file1,) + pats, opts)
1735 m = scmutil.match(ctx, (file1,) + pats, opts)
1736 fntemplate = opts.pop(b'output', b'')
1736 fntemplate = opts.pop(b'output', b'')
1737 if cmdutil.isstdiofilename(fntemplate):
1737 if cmdutil.isstdiofilename(fntemplate):
1738 fntemplate = b''
1738 fntemplate = b''
1739
1739
1740 if fntemplate:
1740 if fntemplate:
1741 fm = formatter.nullformatter(ui, b'cat', opts)
1741 fm = formatter.nullformatter(ui, b'cat', opts)
1742 else:
1742 else:
1743 ui.pager(b'cat')
1743 ui.pager(b'cat')
1744 fm = ui.formatter(b'cat', opts)
1744 fm = ui.formatter(b'cat', opts)
1745 with fm:
1745 with fm:
1746 return cmdutil.cat(
1746 return cmdutil.cat(
1747 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1747 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1748 )
1748 )
1749
1749
1750
1750
1751 @command(
1751 @command(
1752 b'clone',
1752 b'clone',
1753 [
1753 [
1754 (
1754 (
1755 b'U',
1755 b'U',
1756 b'noupdate',
1756 b'noupdate',
1757 None,
1757 None,
1758 _(
1758 _(
1759 b'the clone will include an empty working '
1759 b'the clone will include an empty working '
1760 b'directory (only a repository)'
1760 b'directory (only a repository)'
1761 ),
1761 ),
1762 ),
1762 ),
1763 (
1763 (
1764 b'u',
1764 b'u',
1765 b'updaterev',
1765 b'updaterev',
1766 b'',
1766 b'',
1767 _(b'revision, tag, or branch to check out'),
1767 _(b'revision, tag, or branch to check out'),
1768 _(b'REV'),
1768 _(b'REV'),
1769 ),
1769 ),
1770 (
1770 (
1771 b'r',
1771 b'r',
1772 b'rev',
1772 b'rev',
1773 [],
1773 [],
1774 _(
1774 _(
1775 b'do not clone everything, but include this changeset'
1775 b'do not clone everything, but include this changeset'
1776 b' and its ancestors'
1776 b' and its ancestors'
1777 ),
1777 ),
1778 _(b'REV'),
1778 _(b'REV'),
1779 ),
1779 ),
1780 (
1780 (
1781 b'b',
1781 b'b',
1782 b'branch',
1782 b'branch',
1783 [],
1783 [],
1784 _(
1784 _(
1785 b'do not clone everything, but include this branch\'s'
1785 b'do not clone everything, but include this branch\'s'
1786 b' changesets and their ancestors'
1786 b' changesets and their ancestors'
1787 ),
1787 ),
1788 _(b'BRANCH'),
1788 _(b'BRANCH'),
1789 ),
1789 ),
1790 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1790 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1791 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1791 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1792 (b'', b'stream', None, _(b'clone with minimal data processing')),
1792 (b'', b'stream', None, _(b'clone with minimal data processing')),
1793 ]
1793 ]
1794 + remoteopts,
1794 + remoteopts,
1795 _(b'[OPTION]... SOURCE [DEST]'),
1795 _(b'[OPTION]... SOURCE [DEST]'),
1796 helpcategory=command.CATEGORY_REPO_CREATION,
1796 helpcategory=command.CATEGORY_REPO_CREATION,
1797 helpbasic=True,
1797 helpbasic=True,
1798 norepo=True,
1798 norepo=True,
1799 )
1799 )
1800 def clone(ui, source, dest=None, **opts):
1800 def clone(ui, source, dest=None, **opts):
1801 """make a copy of an existing repository
1801 """make a copy of an existing repository
1802
1802
1803 Create a copy of an existing repository in a new directory.
1803 Create a copy of an existing repository in a new directory.
1804
1804
1805 If no destination directory name is specified, it defaults to the
1805 If no destination directory name is specified, it defaults to the
1806 basename of the source.
1806 basename of the source.
1807
1807
1808 The location of the source is added to the new repository's
1808 The location of the source is added to the new repository's
1809 ``.hg/hgrc`` file, as the default to be used for future pulls.
1809 ``.hg/hgrc`` file, as the default to be used for future pulls.
1810
1810
1811 Only local paths and ``ssh://`` URLs are supported as
1811 Only local paths and ``ssh://`` URLs are supported as
1812 destinations. For ``ssh://`` destinations, no working directory or
1812 destinations. For ``ssh://`` destinations, no working directory or
1813 ``.hg/hgrc`` will be created on the remote side.
1813 ``.hg/hgrc`` will be created on the remote side.
1814
1814
1815 If the source repository has a bookmark called '@' set, that
1815 If the source repository has a bookmark called '@' set, that
1816 revision will be checked out in the new repository by default.
1816 revision will be checked out in the new repository by default.
1817
1817
1818 To check out a particular version, use -u/--update, or
1818 To check out a particular version, use -u/--update, or
1819 -U/--noupdate to create a clone with no working directory.
1819 -U/--noupdate to create a clone with no working directory.
1820
1820
1821 To pull only a subset of changesets, specify one or more revisions
1821 To pull only a subset of changesets, specify one or more revisions
1822 identifiers with -r/--rev or branches with -b/--branch. The
1822 identifiers with -r/--rev or branches with -b/--branch. The
1823 resulting clone will contain only the specified changesets and
1823 resulting clone will contain only the specified changesets and
1824 their ancestors. These options (or 'clone src#rev dest') imply
1824 their ancestors. These options (or 'clone src#rev dest') imply
1825 --pull, even for local source repositories.
1825 --pull, even for local source repositories.
1826
1826
1827 In normal clone mode, the remote normalizes repository data into a common
1827 In normal clone mode, the remote normalizes repository data into a common
1828 exchange format and the receiving end translates this data into its local
1828 exchange format and the receiving end translates this data into its local
1829 storage format. --stream activates a different clone mode that essentially
1829 storage format. --stream activates a different clone mode that essentially
1830 copies repository files from the remote with minimal data processing. This
1830 copies repository files from the remote with minimal data processing. This
1831 significantly reduces the CPU cost of a clone both remotely and locally.
1831 significantly reduces the CPU cost of a clone both remotely and locally.
1832 However, it often increases the transferred data size by 30-40%. This can
1832 However, it often increases the transferred data size by 30-40%. This can
1833 result in substantially faster clones where I/O throughput is plentiful,
1833 result in substantially faster clones where I/O throughput is plentiful,
1834 especially for larger repositories. A side-effect of --stream clones is
1834 especially for larger repositories. A side-effect of --stream clones is
1835 that storage settings and requirements on the remote are applied locally:
1835 that storage settings and requirements on the remote are applied locally:
1836 a modern client may inherit legacy or inefficient storage used by the
1836 a modern client may inherit legacy or inefficient storage used by the
1837 remote or a legacy Mercurial client may not be able to clone from a
1837 remote or a legacy Mercurial client may not be able to clone from a
1838 modern Mercurial remote.
1838 modern Mercurial remote.
1839
1839
1840 .. note::
1840 .. note::
1841
1841
1842 Specifying a tag will include the tagged changeset but not the
1842 Specifying a tag will include the tagged changeset but not the
1843 changeset containing the tag.
1843 changeset containing the tag.
1844
1844
1845 .. container:: verbose
1845 .. container:: verbose
1846
1846
1847 For efficiency, hardlinks are used for cloning whenever the
1847 For efficiency, hardlinks are used for cloning whenever the
1848 source and destination are on the same filesystem (note this
1848 source and destination are on the same filesystem (note this
1849 applies only to the repository data, not to the working
1849 applies only to the repository data, not to the working
1850 directory). Some filesystems, such as AFS, implement hardlinking
1850 directory). Some filesystems, such as AFS, implement hardlinking
1851 incorrectly, but do not report errors. In these cases, use the
1851 incorrectly, but do not report errors. In these cases, use the
1852 --pull option to avoid hardlinking.
1852 --pull option to avoid hardlinking.
1853
1853
1854 Mercurial will update the working directory to the first applicable
1854 Mercurial will update the working directory to the first applicable
1855 revision from this list:
1855 revision from this list:
1856
1856
1857 a) null if -U or the source repository has no changesets
1857 a) null if -U or the source repository has no changesets
1858 b) if -u . and the source repository is local, the first parent of
1858 b) if -u . and the source repository is local, the first parent of
1859 the source repository's working directory
1859 the source repository's working directory
1860 c) the changeset specified with -u (if a branch name, this means the
1860 c) the changeset specified with -u (if a branch name, this means the
1861 latest head of that branch)
1861 latest head of that branch)
1862 d) the changeset specified with -r
1862 d) the changeset specified with -r
1863 e) the tipmost head specified with -b
1863 e) the tipmost head specified with -b
1864 f) the tipmost head specified with the url#branch source syntax
1864 f) the tipmost head specified with the url#branch source syntax
1865 g) the revision marked with the '@' bookmark, if present
1865 g) the revision marked with the '@' bookmark, if present
1866 h) the tipmost head of the default branch
1866 h) the tipmost head of the default branch
1867 i) tip
1867 i) tip
1868
1868
1869 When cloning from servers that support it, Mercurial may fetch
1869 When cloning from servers that support it, Mercurial may fetch
1870 pre-generated data from a server-advertised URL or inline from the
1870 pre-generated data from a server-advertised URL or inline from the
1871 same stream. When this is done, hooks operating on incoming changesets
1871 same stream. When this is done, hooks operating on incoming changesets
1872 and changegroups may fire more than once, once for each pre-generated
1872 and changegroups may fire more than once, once for each pre-generated
1873 bundle and as well as for any additional remaining data. In addition,
1873 bundle and as well as for any additional remaining data. In addition,
1874 if an error occurs, the repository may be rolled back to a partial
1874 if an error occurs, the repository may be rolled back to a partial
1875 clone. This behavior may change in future releases.
1875 clone. This behavior may change in future releases.
1876 See :hg:`help -e clonebundles` for more.
1876 See :hg:`help -e clonebundles` for more.
1877
1877
1878 Examples:
1878 Examples:
1879
1879
1880 - clone a remote repository to a new directory named hg/::
1880 - clone a remote repository to a new directory named hg/::
1881
1881
1882 hg clone https://www.mercurial-scm.org/repo/hg/
1882 hg clone https://www.mercurial-scm.org/repo/hg/
1883
1883
1884 - create a lightweight local clone::
1884 - create a lightweight local clone::
1885
1885
1886 hg clone project/ project-feature/
1886 hg clone project/ project-feature/
1887
1887
1888 - clone from an absolute path on an ssh server (note double-slash)::
1888 - clone from an absolute path on an ssh server (note double-slash)::
1889
1889
1890 hg clone ssh://user@server//home/projects/alpha/
1890 hg clone ssh://user@server//home/projects/alpha/
1891
1891
1892 - do a streaming clone while checking out a specified version::
1892 - do a streaming clone while checking out a specified version::
1893
1893
1894 hg clone --stream http://server/repo -u 1.5
1894 hg clone --stream http://server/repo -u 1.5
1895
1895
1896 - create a repository without changesets after a particular revision::
1896 - create a repository without changesets after a particular revision::
1897
1897
1898 hg clone -r 04e544 experimental/ good/
1898 hg clone -r 04e544 experimental/ good/
1899
1899
1900 - clone (and track) a particular named branch::
1900 - clone (and track) a particular named branch::
1901
1901
1902 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1902 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1903
1903
1904 See :hg:`help urls` for details on specifying URLs.
1904 See :hg:`help urls` for details on specifying URLs.
1905
1905
1906 Returns 0 on success.
1906 Returns 0 on success.
1907 """
1907 """
1908 opts = pycompat.byteskwargs(opts)
1908 opts = pycompat.byteskwargs(opts)
1909 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1909 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1910
1910
1911 # --include/--exclude can come from narrow or sparse.
1911 # --include/--exclude can come from narrow or sparse.
1912 includepats, excludepats = None, None
1912 includepats, excludepats = None, None
1913
1913
1914 # hg.clone() differentiates between None and an empty set. So make sure
1914 # hg.clone() differentiates between None and an empty set. So make sure
1915 # patterns are sets if narrow is requested without patterns.
1915 # patterns are sets if narrow is requested without patterns.
1916 if opts.get(b'narrow'):
1916 if opts.get(b'narrow'):
1917 includepats = set()
1917 includepats = set()
1918 excludepats = set()
1918 excludepats = set()
1919
1919
1920 if opts.get(b'include'):
1920 if opts.get(b'include'):
1921 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1921 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1922 if opts.get(b'exclude'):
1922 if opts.get(b'exclude'):
1923 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1923 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1924
1924
1925 r = hg.clone(
1925 r = hg.clone(
1926 ui,
1926 ui,
1927 opts,
1927 opts,
1928 source,
1928 source,
1929 dest,
1929 dest,
1930 pull=opts.get(b'pull'),
1930 pull=opts.get(b'pull'),
1931 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1931 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1932 revs=opts.get(b'rev'),
1932 revs=opts.get(b'rev'),
1933 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1933 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1934 branch=opts.get(b'branch'),
1934 branch=opts.get(b'branch'),
1935 shareopts=opts.get(b'shareopts'),
1935 shareopts=opts.get(b'shareopts'),
1936 storeincludepats=includepats,
1936 storeincludepats=includepats,
1937 storeexcludepats=excludepats,
1937 storeexcludepats=excludepats,
1938 depth=opts.get(b'depth') or None,
1938 depth=opts.get(b'depth') or None,
1939 )
1939 )
1940
1940
1941 return r is None
1941 return r is None
1942
1942
1943
1943
1944 @command(
1944 @command(
1945 b'commit|ci',
1945 b'commit|ci',
1946 [
1946 [
1947 (
1947 (
1948 b'A',
1948 b'A',
1949 b'addremove',
1949 b'addremove',
1950 None,
1950 None,
1951 _(b'mark new/missing files as added/removed before committing'),
1951 _(b'mark new/missing files as added/removed before committing'),
1952 ),
1952 ),
1953 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1953 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1954 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1954 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1955 (b's', b'secret', None, _(b'use the secret phase for committing')),
1955 (b's', b'secret', None, _(b'use the secret phase for committing')),
1956 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1956 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1957 (
1957 (
1958 b'',
1958 b'',
1959 b'force-close-branch',
1959 b'force-close-branch',
1960 None,
1960 None,
1961 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1961 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1962 ),
1962 ),
1963 (b'i', b'interactive', None, _(b'use interactive mode')),
1963 (b'i', b'interactive', None, _(b'use interactive mode')),
1964 ]
1964 ]
1965 + walkopts
1965 + walkopts
1966 + commitopts
1966 + commitopts
1967 + commitopts2
1967 + commitopts2
1968 + subrepoopts,
1968 + subrepoopts,
1969 _(b'[OPTION]... [FILE]...'),
1969 _(b'[OPTION]... [FILE]...'),
1970 helpcategory=command.CATEGORY_COMMITTING,
1970 helpcategory=command.CATEGORY_COMMITTING,
1971 helpbasic=True,
1971 helpbasic=True,
1972 inferrepo=True,
1972 inferrepo=True,
1973 )
1973 )
1974 def commit(ui, repo, *pats, **opts):
1974 def commit(ui, repo, *pats, **opts):
1975 """commit the specified files or all outstanding changes
1975 """commit the specified files or all outstanding changes
1976
1976
1977 Commit changes to the given files into the repository. Unlike a
1977 Commit changes to the given files into the repository. Unlike a
1978 centralized SCM, this operation is a local operation. See
1978 centralized SCM, this operation is a local operation. See
1979 :hg:`push` for a way to actively distribute your changes.
1979 :hg:`push` for a way to actively distribute your changes.
1980
1980
1981 If a list of files is omitted, all changes reported by :hg:`status`
1981 If a list of files is omitted, all changes reported by :hg:`status`
1982 will be committed.
1982 will be committed.
1983
1983
1984 If you are committing the result of a merge, do not provide any
1984 If you are committing the result of a merge, do not provide any
1985 filenames or -I/-X filters.
1985 filenames or -I/-X filters.
1986
1986
1987 If no commit message is specified, Mercurial starts your
1987 If no commit message is specified, Mercurial starts your
1988 configured editor where you can enter a message. In case your
1988 configured editor where you can enter a message. In case your
1989 commit fails, you will find a backup of your message in
1989 commit fails, you will find a backup of your message in
1990 ``.hg/last-message.txt``.
1990 ``.hg/last-message.txt``.
1991
1991
1992 The --close-branch flag can be used to mark the current branch
1992 The --close-branch flag can be used to mark the current branch
1993 head closed. When all heads of a branch are closed, the branch
1993 head closed. When all heads of a branch are closed, the branch
1994 will be considered closed and no longer listed.
1994 will be considered closed and no longer listed.
1995
1995
1996 The --amend flag can be used to amend the parent of the
1996 The --amend flag can be used to amend the parent of the
1997 working directory with a new commit that contains the changes
1997 working directory with a new commit that contains the changes
1998 in the parent in addition to those currently reported by :hg:`status`,
1998 in the parent in addition to those currently reported by :hg:`status`,
1999 if there are any. The old commit is stored in a backup bundle in
1999 if there are any. The old commit is stored in a backup bundle in
2000 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2000 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2001 on how to restore it).
2001 on how to restore it).
2002
2002
2003 Message, user and date are taken from the amended commit unless
2003 Message, user and date are taken from the amended commit unless
2004 specified. When a message isn't specified on the command line,
2004 specified. When a message isn't specified on the command line,
2005 the editor will open with the message of the amended commit.
2005 the editor will open with the message of the amended commit.
2006
2006
2007 It is not possible to amend public changesets (see :hg:`help phases`)
2007 It is not possible to amend public changesets (see :hg:`help phases`)
2008 or changesets that have children.
2008 or changesets that have children.
2009
2009
2010 See :hg:`help dates` for a list of formats valid for -d/--date.
2010 See :hg:`help dates` for a list of formats valid for -d/--date.
2011
2011
2012 Returns 0 on success, 1 if nothing changed.
2012 Returns 0 on success, 1 if nothing changed.
2013
2013
2014 .. container:: verbose
2014 .. container:: verbose
2015
2015
2016 Examples:
2016 Examples:
2017
2017
2018 - commit all files ending in .py::
2018 - commit all files ending in .py::
2019
2019
2020 hg commit --include "set:**.py"
2020 hg commit --include "set:**.py"
2021
2021
2022 - commit all non-binary files::
2022 - commit all non-binary files::
2023
2023
2024 hg commit --exclude "set:binary()"
2024 hg commit --exclude "set:binary()"
2025
2025
2026 - amend the current commit and set the date to now::
2026 - amend the current commit and set the date to now::
2027
2027
2028 hg commit --amend --date now
2028 hg commit --amend --date now
2029 """
2029 """
2030 with repo.wlock(), repo.lock():
2030 with repo.wlock(), repo.lock():
2031 return _docommit(ui, repo, *pats, **opts)
2031 return _docommit(ui, repo, *pats, **opts)
2032
2032
2033
2033
2034 def _docommit(ui, repo, *pats, **opts):
2034 def _docommit(ui, repo, *pats, **opts):
2035 if opts.get('interactive'):
2035 if opts.get('interactive'):
2036 opts.pop('interactive')
2036 opts.pop('interactive')
2037 ret = cmdutil.dorecord(
2037 ret = cmdutil.dorecord(
2038 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2038 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2039 )
2039 )
2040 # ret can be 0 (no changes to record) or the value returned by
2040 # ret can be 0 (no changes to record) or the value returned by
2041 # commit(), 1 if nothing changed or None on success.
2041 # commit(), 1 if nothing changed or None on success.
2042 return 1 if ret == 0 else ret
2042 return 1 if ret == 0 else ret
2043
2043
2044 opts = pycompat.byteskwargs(opts)
2044 opts = pycompat.byteskwargs(opts)
2045 if opts.get(b'subrepos'):
2045 if opts.get(b'subrepos'):
2046 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'amend'])
2046 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'amend'])
2047 # Let --subrepos on the command line override config setting.
2047 # Let --subrepos on the command line override config setting.
2048 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2048 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2049
2049
2050 cmdutil.checkunfinished(repo, commit=True)
2050 cmdutil.checkunfinished(repo, commit=True)
2051
2051
2052 branch = repo[None].branch()
2052 branch = repo[None].branch()
2053 bheads = repo.branchheads(branch)
2053 bheads = repo.branchheads(branch)
2054 tip = repo.changelog.tip()
2054 tip = repo.changelog.tip()
2055
2055
2056 extra = {}
2056 extra = {}
2057 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2057 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2058 extra[b'close'] = b'1'
2058 extra[b'close'] = b'1'
2059
2059
2060 if repo[b'.'].closesbranch():
2060 if repo[b'.'].closesbranch():
2061 raise error.InputError(
2061 raise error.InputError(
2062 _(b'current revision is already a branch closing head')
2062 _(b'current revision is already a branch closing head')
2063 )
2063 )
2064 elif not bheads:
2064 elif not bheads:
2065 raise error.InputError(
2065 raise error.InputError(
2066 _(b'branch "%s" has no heads to close') % branch
2066 _(b'branch "%s" has no heads to close') % branch
2067 )
2067 )
2068 elif (
2068 elif (
2069 branch == repo[b'.'].branch()
2069 branch == repo[b'.'].branch()
2070 and repo[b'.'].node() not in bheads
2070 and repo[b'.'].node() not in bheads
2071 and not opts.get(b'force_close_branch')
2071 and not opts.get(b'force_close_branch')
2072 ):
2072 ):
2073 hint = _(
2073 hint = _(
2074 b'use --force-close-branch to close branch from a non-head'
2074 b'use --force-close-branch to close branch from a non-head'
2075 b' changeset'
2075 b' changeset'
2076 )
2076 )
2077 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2077 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2078 elif opts.get(b'amend'):
2078 elif opts.get(b'amend'):
2079 if (
2079 if (
2080 repo[b'.'].p1().branch() != branch
2080 repo[b'.'].p1().branch() != branch
2081 and repo[b'.'].p2().branch() != branch
2081 and repo[b'.'].p2().branch() != branch
2082 ):
2082 ):
2083 raise error.InputError(_(b'can only close branch heads'))
2083 raise error.InputError(_(b'can only close branch heads'))
2084
2084
2085 if opts.get(b'amend'):
2085 if opts.get(b'amend'):
2086 if ui.configbool(b'ui', b'commitsubrepos'):
2086 if ui.configbool(b'ui', b'commitsubrepos'):
2087 raise error.InputError(
2087 raise error.InputError(
2088 _(b'cannot amend with ui.commitsubrepos enabled')
2088 _(b'cannot amend with ui.commitsubrepos enabled')
2089 )
2089 )
2090
2090
2091 old = repo[b'.']
2091 old = repo[b'.']
2092 rewriteutil.precheck(repo, [old.rev()], b'amend')
2092 rewriteutil.precheck(repo, [old.rev()], b'amend')
2093
2093
2094 # Currently histedit gets confused if an amend happens while histedit
2094 # Currently histedit gets confused if an amend happens while histedit
2095 # is in progress. Since we have a checkunfinished command, we are
2095 # is in progress. Since we have a checkunfinished command, we are
2096 # temporarily honoring it.
2096 # temporarily honoring it.
2097 #
2097 #
2098 # Note: eventually this guard will be removed. Please do not expect
2098 # Note: eventually this guard will be removed. Please do not expect
2099 # this behavior to remain.
2099 # this behavior to remain.
2100 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2100 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2101 cmdutil.checkunfinished(repo)
2101 cmdutil.checkunfinished(repo)
2102
2102
2103 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2103 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2104 if node == old.node():
2104 if node == old.node():
2105 ui.status(_(b"nothing changed\n"))
2105 ui.status(_(b"nothing changed\n"))
2106 return 1
2106 return 1
2107 else:
2107 else:
2108
2108
2109 def commitfunc(ui, repo, message, match, opts):
2109 def commitfunc(ui, repo, message, match, opts):
2110 overrides = {}
2110 overrides = {}
2111 if opts.get(b'secret'):
2111 if opts.get(b'secret'):
2112 overrides[(b'phases', b'new-commit')] = b'secret'
2112 overrides[(b'phases', b'new-commit')] = b'secret'
2113
2113
2114 baseui = repo.baseui
2114 baseui = repo.baseui
2115 with baseui.configoverride(overrides, b'commit'):
2115 with baseui.configoverride(overrides, b'commit'):
2116 with ui.configoverride(overrides, b'commit'):
2116 with ui.configoverride(overrides, b'commit'):
2117 editform = cmdutil.mergeeditform(
2117 editform = cmdutil.mergeeditform(
2118 repo[None], b'commit.normal'
2118 repo[None], b'commit.normal'
2119 )
2119 )
2120 editor = cmdutil.getcommiteditor(
2120 editor = cmdutil.getcommiteditor(
2121 editform=editform, **pycompat.strkwargs(opts)
2121 editform=editform, **pycompat.strkwargs(opts)
2122 )
2122 )
2123 return repo.commit(
2123 return repo.commit(
2124 message,
2124 message,
2125 opts.get(b'user'),
2125 opts.get(b'user'),
2126 opts.get(b'date'),
2126 opts.get(b'date'),
2127 match,
2127 match,
2128 editor=editor,
2128 editor=editor,
2129 extra=extra,
2129 extra=extra,
2130 )
2130 )
2131
2131
2132 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2132 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2133
2133
2134 if not node:
2134 if not node:
2135 stat = cmdutil.postcommitstatus(repo, pats, opts)
2135 stat = cmdutil.postcommitstatus(repo, pats, opts)
2136 if stat.deleted:
2136 if stat.deleted:
2137 ui.status(
2137 ui.status(
2138 _(
2138 _(
2139 b"nothing changed (%d missing files, see "
2139 b"nothing changed (%d missing files, see "
2140 b"'hg status')\n"
2140 b"'hg status')\n"
2141 )
2141 )
2142 % len(stat.deleted)
2142 % len(stat.deleted)
2143 )
2143 )
2144 else:
2144 else:
2145 ui.status(_(b"nothing changed\n"))
2145 ui.status(_(b"nothing changed\n"))
2146 return 1
2146 return 1
2147
2147
2148 cmdutil.commitstatus(repo, node, branch, bheads, tip, opts)
2148 cmdutil.commitstatus(repo, node, branch, bheads, tip, opts)
2149
2149
2150 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2150 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2151 status(
2151 status(
2152 ui,
2152 ui,
2153 repo,
2153 repo,
2154 modified=True,
2154 modified=True,
2155 added=True,
2155 added=True,
2156 removed=True,
2156 removed=True,
2157 deleted=True,
2157 deleted=True,
2158 unknown=True,
2158 unknown=True,
2159 subrepos=opts.get(b'subrepos'),
2159 subrepos=opts.get(b'subrepos'),
2160 )
2160 )
2161
2161
2162
2162
2163 @command(
2163 @command(
2164 b'config|showconfig|debugconfig',
2164 b'config|showconfig|debugconfig',
2165 [
2165 [
2166 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2166 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2167 (b'e', b'edit', None, _(b'edit user config')),
2167 (b'e', b'edit', None, _(b'edit user config')),
2168 (b'l', b'local', None, _(b'edit repository config')),
2168 (b'l', b'local', None, _(b'edit repository config')),
2169 (
2169 (
2170 b'',
2170 b'',
2171 b'shared',
2171 b'shared',
2172 None,
2172 None,
2173 _(b'edit shared source repository config (EXPERIMENTAL)'),
2173 _(b'edit shared source repository config (EXPERIMENTAL)'),
2174 ),
2174 ),
2175 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2175 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2176 (b'g', b'global', None, _(b'edit global config')),
2176 (b'g', b'global', None, _(b'edit global config')),
2177 ]
2177 ]
2178 + formatteropts,
2178 + formatteropts,
2179 _(b'[-u] [NAME]...'),
2179 _(b'[-u] [NAME]...'),
2180 helpcategory=command.CATEGORY_HELP,
2180 helpcategory=command.CATEGORY_HELP,
2181 optionalrepo=True,
2181 optionalrepo=True,
2182 intents={INTENT_READONLY},
2182 intents={INTENT_READONLY},
2183 )
2183 )
2184 def config(ui, repo, *values, **opts):
2184 def config(ui, repo, *values, **opts):
2185 """show combined config settings from all hgrc files
2185 """show combined config settings from all hgrc files
2186
2186
2187 With no arguments, print names and values of all config items.
2187 With no arguments, print names and values of all config items.
2188
2188
2189 With one argument of the form section.name, print just the value
2189 With one argument of the form section.name, print just the value
2190 of that config item.
2190 of that config item.
2191
2191
2192 With multiple arguments, print names and values of all config
2192 With multiple arguments, print names and values of all config
2193 items with matching section names or section.names.
2193 items with matching section names or section.names.
2194
2194
2195 With --edit, start an editor on the user-level config file. With
2195 With --edit, start an editor on the user-level config file. With
2196 --global, edit the system-wide config file. With --local, edit the
2196 --global, edit the system-wide config file. With --local, edit the
2197 repository-level config file.
2197 repository-level config file.
2198
2198
2199 With --debug, the source (filename and line number) is printed
2199 With --debug, the source (filename and line number) is printed
2200 for each config item.
2200 for each config item.
2201
2201
2202 See :hg:`help config` for more information about config files.
2202 See :hg:`help config` for more information about config files.
2203
2203
2204 .. container:: verbose
2204 .. container:: verbose
2205
2205
2206 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2206 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2207 This file is not shared across shares when in share-safe mode.
2207 This file is not shared across shares when in share-safe mode.
2208
2208
2209 Template:
2209 Template:
2210
2210
2211 The following keywords are supported. See also :hg:`help templates`.
2211 The following keywords are supported. See also :hg:`help templates`.
2212
2212
2213 :name: String. Config name.
2213 :name: String. Config name.
2214 :source: String. Filename and line number where the item is defined.
2214 :source: String. Filename and line number where the item is defined.
2215 :value: String. Config value.
2215 :value: String. Config value.
2216
2216
2217 The --shared flag can be used to edit the config file of shared source
2217 The --shared flag can be used to edit the config file of shared source
2218 repository. It only works when you have shared using the experimental
2218 repository. It only works when you have shared using the experimental
2219 share safe feature.
2219 share safe feature.
2220
2220
2221 Returns 0 on success, 1 if NAME does not exist.
2221 Returns 0 on success, 1 if NAME does not exist.
2222
2222
2223 """
2223 """
2224
2224
2225 opts = pycompat.byteskwargs(opts)
2225 opts = pycompat.byteskwargs(opts)
2226 editopts = (b'edit', b'local', b'global', b'shared', b'non_shared')
2226 editopts = (b'edit', b'local', b'global', b'shared', b'non_shared')
2227 if any(opts.get(o) for o in editopts):
2227 if any(opts.get(o) for o in editopts):
2228 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2228 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2229 if opts.get(b'local'):
2229 if opts.get(b'local'):
2230 if not repo:
2230 if not repo:
2231 raise error.InputError(
2231 raise error.InputError(
2232 _(b"can't use --local outside a repository")
2232 _(b"can't use --local outside a repository")
2233 )
2233 )
2234 paths = [repo.vfs.join(b'hgrc')]
2234 paths = [repo.vfs.join(b'hgrc')]
2235 elif opts.get(b'global'):
2235 elif opts.get(b'global'):
2236 paths = rcutil.systemrcpath()
2236 paths = rcutil.systemrcpath()
2237 elif opts.get(b'shared'):
2237 elif opts.get(b'shared'):
2238 if not repo.shared():
2238 if not repo.shared():
2239 raise error.InputError(
2239 raise error.InputError(
2240 _(b"repository is not shared; can't use --shared")
2240 _(b"repository is not shared; can't use --shared")
2241 )
2241 )
2242 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2242 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2243 raise error.InputError(
2243 raise error.InputError(
2244 _(
2244 _(
2245 b"share safe feature not enabled; "
2245 b"share safe feature not enabled; "
2246 b"unable to edit shared source repository config"
2246 b"unable to edit shared source repository config"
2247 )
2247 )
2248 )
2248 )
2249 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2249 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2250 elif opts.get(b'non_shared'):
2250 elif opts.get(b'non_shared'):
2251 paths = [repo.vfs.join(b'hgrc-not-shared')]
2251 paths = [repo.vfs.join(b'hgrc-not-shared')]
2252 else:
2252 else:
2253 paths = rcutil.userrcpath()
2253 paths = rcutil.userrcpath()
2254
2254
2255 for f in paths:
2255 for f in paths:
2256 if os.path.exists(f):
2256 if os.path.exists(f):
2257 break
2257 break
2258 else:
2258 else:
2259 if opts.get(b'global'):
2259 if opts.get(b'global'):
2260 samplehgrc = uimod.samplehgrcs[b'global']
2260 samplehgrc = uimod.samplehgrcs[b'global']
2261 elif opts.get(b'local'):
2261 elif opts.get(b'local'):
2262 samplehgrc = uimod.samplehgrcs[b'local']
2262 samplehgrc = uimod.samplehgrcs[b'local']
2263 else:
2263 else:
2264 samplehgrc = uimod.samplehgrcs[b'user']
2264 samplehgrc = uimod.samplehgrcs[b'user']
2265
2265
2266 f = paths[0]
2266 f = paths[0]
2267 fp = open(f, b"wb")
2267 fp = open(f, b"wb")
2268 fp.write(util.tonativeeol(samplehgrc))
2268 fp.write(util.tonativeeol(samplehgrc))
2269 fp.close()
2269 fp.close()
2270
2270
2271 editor = ui.geteditor()
2271 editor = ui.geteditor()
2272 ui.system(
2272 ui.system(
2273 b"%s \"%s\"" % (editor, f),
2273 b"%s \"%s\"" % (editor, f),
2274 onerr=error.InputError,
2274 onerr=error.InputError,
2275 errprefix=_(b"edit failed"),
2275 errprefix=_(b"edit failed"),
2276 blockedtag=b'config_edit',
2276 blockedtag=b'config_edit',
2277 )
2277 )
2278 return
2278 return
2279 ui.pager(b'config')
2279 ui.pager(b'config')
2280 fm = ui.formatter(b'config', opts)
2280 fm = ui.formatter(b'config', opts)
2281 for t, f in rcutil.rccomponents():
2281 for t, f in rcutil.rccomponents():
2282 if t == b'path':
2282 if t == b'path':
2283 ui.debug(b'read config from: %s\n' % f)
2283 ui.debug(b'read config from: %s\n' % f)
2284 elif t == b'resource':
2284 elif t == b'resource':
2285 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2285 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2286 elif t == b'items':
2286 elif t == b'items':
2287 # Don't print anything for 'items'.
2287 # Don't print anything for 'items'.
2288 pass
2288 pass
2289 else:
2289 else:
2290 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2290 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2291 untrusted = bool(opts.get(b'untrusted'))
2291 untrusted = bool(opts.get(b'untrusted'))
2292
2292
2293 selsections = selentries = []
2293 selsections = selentries = []
2294 if values:
2294 if values:
2295 selsections = [v for v in values if b'.' not in v]
2295 selsections = [v for v in values if b'.' not in v]
2296 selentries = [v for v in values if b'.' in v]
2296 selentries = [v for v in values if b'.' in v]
2297 uniquesel = len(selentries) == 1 and not selsections
2297 uniquesel = len(selentries) == 1 and not selsections
2298 selsections = set(selsections)
2298 selsections = set(selsections)
2299 selentries = set(selentries)
2299 selentries = set(selentries)
2300
2300
2301 matched = False
2301 matched = False
2302 for section, name, value in ui.walkconfig(untrusted=untrusted):
2302 for section, name, value in ui.walkconfig(untrusted=untrusted):
2303 source = ui.configsource(section, name, untrusted)
2303 source = ui.configsource(section, name, untrusted)
2304 value = pycompat.bytestr(value)
2304 value = pycompat.bytestr(value)
2305 defaultvalue = ui.configdefault(section, name)
2305 defaultvalue = ui.configdefault(section, name)
2306 if fm.isplain():
2306 if fm.isplain():
2307 source = source or b'none'
2307 source = source or b'none'
2308 value = value.replace(b'\n', b'\\n')
2308 value = value.replace(b'\n', b'\\n')
2309 entryname = section + b'.' + name
2309 entryname = section + b'.' + name
2310 if values and not (section in selsections or entryname in selentries):
2310 if values and not (section in selsections or entryname in selentries):
2311 continue
2311 continue
2312 fm.startitem()
2312 fm.startitem()
2313 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2313 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2314 if uniquesel:
2314 if uniquesel:
2315 fm.data(name=entryname)
2315 fm.data(name=entryname)
2316 fm.write(b'value', b'%s\n', value)
2316 fm.write(b'value', b'%s\n', value)
2317 else:
2317 else:
2318 fm.write(b'name value', b'%s=%s\n', entryname, value)
2318 fm.write(b'name value', b'%s=%s\n', entryname, value)
2319 if formatter.isprintable(defaultvalue):
2319 if formatter.isprintable(defaultvalue):
2320 fm.data(defaultvalue=defaultvalue)
2320 fm.data(defaultvalue=defaultvalue)
2321 elif isinstance(defaultvalue, list) and all(
2321 elif isinstance(defaultvalue, list) and all(
2322 formatter.isprintable(e) for e in defaultvalue
2322 formatter.isprintable(e) for e in defaultvalue
2323 ):
2323 ):
2324 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2324 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2325 # TODO: no idea how to process unsupported defaultvalue types
2325 # TODO: no idea how to process unsupported defaultvalue types
2326 matched = True
2326 matched = True
2327 fm.end()
2327 fm.end()
2328 if matched:
2328 if matched:
2329 return 0
2329 return 0
2330 return 1
2330 return 1
2331
2331
2332
2332
2333 @command(
2333 @command(
2334 b'continue',
2334 b'continue',
2335 dryrunopts,
2335 dryrunopts,
2336 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2336 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2337 helpbasic=True,
2337 helpbasic=True,
2338 )
2338 )
2339 def continuecmd(ui, repo, **opts):
2339 def continuecmd(ui, repo, **opts):
2340 """resumes an interrupted operation (EXPERIMENTAL)
2340 """resumes an interrupted operation (EXPERIMENTAL)
2341
2341
2342 Finishes a multistep operation like graft, histedit, rebase, merge,
2342 Finishes a multistep operation like graft, histedit, rebase, merge,
2343 and unshelve if they are in an interrupted state.
2343 and unshelve if they are in an interrupted state.
2344
2344
2345 use --dry-run/-n to dry run the command.
2345 use --dry-run/-n to dry run the command.
2346 """
2346 """
2347 dryrun = opts.get('dry_run')
2347 dryrun = opts.get('dry_run')
2348 contstate = cmdutil.getunfinishedstate(repo)
2348 contstate = cmdutil.getunfinishedstate(repo)
2349 if not contstate:
2349 if not contstate:
2350 raise error.StateError(_(b'no operation in progress'))
2350 raise error.StateError(_(b'no operation in progress'))
2351 if not contstate.continuefunc:
2351 if not contstate.continuefunc:
2352 raise error.StateError(
2352 raise error.StateError(
2353 (
2353 (
2354 _(b"%s in progress but does not support 'hg continue'")
2354 _(b"%s in progress but does not support 'hg continue'")
2355 % (contstate._opname)
2355 % (contstate._opname)
2356 ),
2356 ),
2357 hint=contstate.continuemsg(),
2357 hint=contstate.continuemsg(),
2358 )
2358 )
2359 if dryrun:
2359 if dryrun:
2360 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2360 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2361 return
2361 return
2362 return contstate.continuefunc(ui, repo)
2362 return contstate.continuefunc(ui, repo)
2363
2363
2364
2364
2365 @command(
2365 @command(
2366 b'copy|cp',
2366 b'copy|cp',
2367 [
2367 [
2368 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2368 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2369 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2369 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2370 (
2370 (
2371 b'',
2371 b'',
2372 b'at-rev',
2372 b'at-rev',
2373 b'',
2373 b'',
2374 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2374 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2375 _(b'REV'),
2375 _(b'REV'),
2376 ),
2376 ),
2377 (
2377 (
2378 b'f',
2378 b'f',
2379 b'force',
2379 b'force',
2380 None,
2380 None,
2381 _(b'forcibly copy over an existing managed file'),
2381 _(b'forcibly copy over an existing managed file'),
2382 ),
2382 ),
2383 ]
2383 ]
2384 + walkopts
2384 + walkopts
2385 + dryrunopts,
2385 + dryrunopts,
2386 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2386 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2387 helpcategory=command.CATEGORY_FILE_CONTENTS,
2387 helpcategory=command.CATEGORY_FILE_CONTENTS,
2388 )
2388 )
2389 def copy(ui, repo, *pats, **opts):
2389 def copy(ui, repo, *pats, **opts):
2390 """mark files as copied for the next commit
2390 """mark files as copied for the next commit
2391
2391
2392 Mark dest as having copies of source files. If dest is a
2392 Mark dest as having copies of source files. If dest is a
2393 directory, copies are put in that directory. If dest is a file,
2393 directory, copies are put in that directory. If dest is a file,
2394 the source must be a single file.
2394 the source must be a single file.
2395
2395
2396 By default, this command copies the contents of files as they
2396 By default, this command copies the contents of files as they
2397 exist in the working directory. If invoked with -A/--after, the
2397 exist in the working directory. If invoked with -A/--after, the
2398 operation is recorded, but no copying is performed.
2398 operation is recorded, but no copying is performed.
2399
2399
2400 To undo marking a destination file as copied, use --forget. With that
2400 To undo marking a destination file as copied, use --forget. With that
2401 option, all given (positional) arguments are unmarked as copies. The
2401 option, all given (positional) arguments are unmarked as copies. The
2402 destination file(s) will be left in place (still tracked).
2402 destination file(s) will be left in place (still tracked).
2403
2403
2404 This command takes effect with the next commit by default.
2404 This command takes effect with the next commit by default.
2405
2405
2406 Returns 0 on success, 1 if errors are encountered.
2406 Returns 0 on success, 1 if errors are encountered.
2407 """
2407 """
2408 opts = pycompat.byteskwargs(opts)
2408 opts = pycompat.byteskwargs(opts)
2409 with repo.wlock():
2409 with repo.wlock():
2410 return cmdutil.copy(ui, repo, pats, opts)
2410 return cmdutil.copy(ui, repo, pats, opts)
2411
2411
2412
2412
2413 @command(
2413 @command(
2414 b'debugcommands',
2414 b'debugcommands',
2415 [],
2415 [],
2416 _(b'[COMMAND]'),
2416 _(b'[COMMAND]'),
2417 helpcategory=command.CATEGORY_HELP,
2417 helpcategory=command.CATEGORY_HELP,
2418 norepo=True,
2418 norepo=True,
2419 )
2419 )
2420 def debugcommands(ui, cmd=b'', *args):
2420 def debugcommands(ui, cmd=b'', *args):
2421 """list all available commands and options"""
2421 """list all available commands and options"""
2422 for cmd, vals in sorted(pycompat.iteritems(table)):
2422 for cmd, vals in sorted(pycompat.iteritems(table)):
2423 cmd = cmd.split(b'|')[0]
2423 cmd = cmd.split(b'|')[0]
2424 opts = b', '.join([i[1] for i in vals[1]])
2424 opts = b', '.join([i[1] for i in vals[1]])
2425 ui.write(b'%s: %s\n' % (cmd, opts))
2425 ui.write(b'%s: %s\n' % (cmd, opts))
2426
2426
2427
2427
2428 @command(
2428 @command(
2429 b'debugcomplete',
2429 b'debugcomplete',
2430 [(b'o', b'options', None, _(b'show the command options'))],
2430 [(b'o', b'options', None, _(b'show the command options'))],
2431 _(b'[-o] CMD'),
2431 _(b'[-o] CMD'),
2432 helpcategory=command.CATEGORY_HELP,
2432 helpcategory=command.CATEGORY_HELP,
2433 norepo=True,
2433 norepo=True,
2434 )
2434 )
2435 def debugcomplete(ui, cmd=b'', **opts):
2435 def debugcomplete(ui, cmd=b'', **opts):
2436 """returns the completion list associated with the given command"""
2436 """returns the completion list associated with the given command"""
2437
2437
2438 if opts.get('options'):
2438 if opts.get('options'):
2439 options = []
2439 options = []
2440 otables = [globalopts]
2440 otables = [globalopts]
2441 if cmd:
2441 if cmd:
2442 aliases, entry = cmdutil.findcmd(cmd, table, False)
2442 aliases, entry = cmdutil.findcmd(cmd, table, False)
2443 otables.append(entry[1])
2443 otables.append(entry[1])
2444 for t in otables:
2444 for t in otables:
2445 for o in t:
2445 for o in t:
2446 if b"(DEPRECATED)" in o[3]:
2446 if b"(DEPRECATED)" in o[3]:
2447 continue
2447 continue
2448 if o[0]:
2448 if o[0]:
2449 options.append(b'-%s' % o[0])
2449 options.append(b'-%s' % o[0])
2450 options.append(b'--%s' % o[1])
2450 options.append(b'--%s' % o[1])
2451 ui.write(b"%s\n" % b"\n".join(options))
2451 ui.write(b"%s\n" % b"\n".join(options))
2452 return
2452 return
2453
2453
2454 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2454 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2455 if ui.verbose:
2455 if ui.verbose:
2456 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2456 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2457 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2457 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2458
2458
2459
2459
2460 @command(
2460 @command(
2461 b'diff',
2461 b'diff',
2462 [
2462 [
2463 (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2463 (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2464 (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2464 (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2465 (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2465 (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2466 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2466 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2467 ]
2467 ]
2468 + diffopts
2468 + diffopts
2469 + diffopts2
2469 + diffopts2
2470 + walkopts
2470 + walkopts
2471 + subrepoopts,
2471 + subrepoopts,
2472 _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2472 _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2473 helpcategory=command.CATEGORY_FILE_CONTENTS,
2473 helpcategory=command.CATEGORY_FILE_CONTENTS,
2474 helpbasic=True,
2474 helpbasic=True,
2475 inferrepo=True,
2475 inferrepo=True,
2476 intents={INTENT_READONLY},
2476 intents={INTENT_READONLY},
2477 )
2477 )
2478 def diff(ui, repo, *pats, **opts):
2478 def diff(ui, repo, *pats, **opts):
2479 """diff repository (or selected files)
2479 """diff repository (or selected files)
2480
2480
2481 Show differences between revisions for the specified files.
2481 Show differences between revisions for the specified files.
2482
2482
2483 Differences between files are shown using the unified diff format.
2483 Differences between files are shown using the unified diff format.
2484
2484
2485 .. note::
2485 .. note::
2486
2486
2487 :hg:`diff` may generate unexpected results for merges, as it will
2487 :hg:`diff` may generate unexpected results for merges, as it will
2488 default to comparing against the working directory's first
2488 default to comparing against the working directory's first
2489 parent changeset if no revisions are specified.
2489 parent changeset if no revisions are specified.
2490
2490
2491 By default, the working directory files are compared to its first parent. To
2491 By default, the working directory files are compared to its first parent. To
2492 see the differences from another revision, use --from. To see the difference
2492 see the differences from another revision, use --from. To see the difference
2493 to another revision, use --to. For example, :hg:`diff --from .^` will show
2493 to another revision, use --to. For example, :hg:`diff --from .^` will show
2494 the differences from the working copy's grandparent to the working copy,
2494 the differences from the working copy's grandparent to the working copy,
2495 :hg:`diff --to .` will show the diff from the working copy to its parent
2495 :hg:`diff --to .` will show the diff from the working copy to its parent
2496 (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2496 (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2497 show the diff between those two revisions.
2497 show the diff between those two revisions.
2498
2498
2499 Alternatively you can specify -c/--change with a revision to see the changes
2499 Alternatively you can specify -c/--change with a revision to see the changes
2500 in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2500 in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2501 equivalent to :hg:`diff --from 42^ --to 42`)
2501 equivalent to :hg:`diff --from 42^ --to 42`)
2502
2502
2503 Without the -a/--text option, diff will avoid generating diffs of
2503 Without the -a/--text option, diff will avoid generating diffs of
2504 files it detects as binary. With -a, diff will generate a diff
2504 files it detects as binary. With -a, diff will generate a diff
2505 anyway, probably with undesirable results.
2505 anyway, probably with undesirable results.
2506
2506
2507 Use the -g/--git option to generate diffs in the git extended diff
2507 Use the -g/--git option to generate diffs in the git extended diff
2508 format. For more information, read :hg:`help diffs`.
2508 format. For more information, read :hg:`help diffs`.
2509
2509
2510 .. container:: verbose
2510 .. container:: verbose
2511
2511
2512 Examples:
2512 Examples:
2513
2513
2514 - compare a file in the current working directory to its parent::
2514 - compare a file in the current working directory to its parent::
2515
2515
2516 hg diff foo.c
2516 hg diff foo.c
2517
2517
2518 - compare two historical versions of a directory, with rename info::
2518 - compare two historical versions of a directory, with rename info::
2519
2519
2520 hg diff --git --from 1.0 --to 1.2 lib/
2520 hg diff --git --from 1.0 --to 1.2 lib/
2521
2521
2522 - get change stats relative to the last change on some date::
2522 - get change stats relative to the last change on some date::
2523
2523
2524 hg diff --stat --from "date('may 2')"
2524 hg diff --stat --from "date('may 2')"
2525
2525
2526 - diff all newly-added files that contain a keyword::
2526 - diff all newly-added files that contain a keyword::
2527
2527
2528 hg diff "set:added() and grep(GNU)"
2528 hg diff "set:added() and grep(GNU)"
2529
2529
2530 - compare a revision and its parents::
2530 - compare a revision and its parents::
2531
2531
2532 hg diff -c 9353 # compare against first parent
2532 hg diff -c 9353 # compare against first parent
2533 hg diff --from 9353^ --to 9353 # same using revset syntax
2533 hg diff --from 9353^ --to 9353 # same using revset syntax
2534 hg diff --from 9353^2 --to 9353 # compare against the second parent
2534 hg diff --from 9353^2 --to 9353 # compare against the second parent
2535
2535
2536 Returns 0 on success.
2536 Returns 0 on success.
2537 """
2537 """
2538
2538
2539 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2539 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2540 opts = pycompat.byteskwargs(opts)
2540 opts = pycompat.byteskwargs(opts)
2541 revs = opts.get(b'rev')
2541 revs = opts.get(b'rev')
2542 change = opts.get(b'change')
2542 change = opts.get(b'change')
2543 from_rev = opts.get(b'from')
2543 from_rev = opts.get(b'from')
2544 to_rev = opts.get(b'to')
2544 to_rev = opts.get(b'to')
2545 stat = opts.get(b'stat')
2545 stat = opts.get(b'stat')
2546 reverse = opts.get(b'reverse')
2546 reverse = opts.get(b'reverse')
2547
2547
2548 cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2548 cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2549 cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2549 cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2550 if change:
2550 if change:
2551 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2551 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2552 ctx2 = scmutil.revsingle(repo, change, None)
2552 ctx2 = scmutil.revsingle(repo, change, None)
2553 ctx1 = ctx2.p1()
2553 ctx1 = ctx2.p1()
2554 elif from_rev or to_rev:
2554 elif from_rev or to_rev:
2555 repo = scmutil.unhidehashlikerevs(
2555 repo = scmutil.unhidehashlikerevs(
2556 repo, [from_rev] + [to_rev], b'nowarn'
2556 repo, [from_rev] + [to_rev], b'nowarn'
2557 )
2557 )
2558 ctx1 = scmutil.revsingle(repo, from_rev, None)
2558 ctx1 = scmutil.revsingle(repo, from_rev, None)
2559 ctx2 = scmutil.revsingle(repo, to_rev, None)
2559 ctx2 = scmutil.revsingle(repo, to_rev, None)
2560 else:
2560 else:
2561 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2561 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2562 ctx1, ctx2 = scmutil.revpair(repo, revs)
2562 ctx1, ctx2 = scmutil.revpair(repo, revs)
2563
2563
2564 if reverse:
2564 if reverse:
2565 ctxleft = ctx2
2565 ctxleft = ctx2
2566 ctxright = ctx1
2566 ctxright = ctx1
2567 else:
2567 else:
2568 ctxleft = ctx1
2568 ctxleft = ctx1
2569 ctxright = ctx2
2569 ctxright = ctx2
2570
2570
2571 diffopts = patch.diffallopts(ui, opts)
2571 diffopts = patch.diffallopts(ui, opts)
2572 m = scmutil.match(ctx2, pats, opts)
2572 m = scmutil.match(ctx2, pats, opts)
2573 m = repo.narrowmatch(m)
2573 m = repo.narrowmatch(m)
2574 ui.pager(b'diff')
2574 ui.pager(b'diff')
2575 logcmdutil.diffordiffstat(
2575 logcmdutil.diffordiffstat(
2576 ui,
2576 ui,
2577 repo,
2577 repo,
2578 diffopts,
2578 diffopts,
2579 ctxleft,
2579 ctxleft,
2580 ctxright,
2580 ctxright,
2581 m,
2581 m,
2582 stat=stat,
2582 stat=stat,
2583 listsubrepos=opts.get(b'subrepos'),
2583 listsubrepos=opts.get(b'subrepos'),
2584 root=opts.get(b'root'),
2584 root=opts.get(b'root'),
2585 )
2585 )
2586
2586
2587
2587
2588 @command(
2588 @command(
2589 b'export',
2589 b'export',
2590 [
2590 [
2591 (
2591 (
2592 b'B',
2592 b'B',
2593 b'bookmark',
2593 b'bookmark',
2594 b'',
2594 b'',
2595 _(b'export changes only reachable by given bookmark'),
2595 _(b'export changes only reachable by given bookmark'),
2596 _(b'BOOKMARK'),
2596 _(b'BOOKMARK'),
2597 ),
2597 ),
2598 (
2598 (
2599 b'o',
2599 b'o',
2600 b'output',
2600 b'output',
2601 b'',
2601 b'',
2602 _(b'print output to file with formatted name'),
2602 _(b'print output to file with formatted name'),
2603 _(b'FORMAT'),
2603 _(b'FORMAT'),
2604 ),
2604 ),
2605 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2605 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2606 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2606 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2607 ]
2607 ]
2608 + diffopts
2608 + diffopts
2609 + formatteropts,
2609 + formatteropts,
2610 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2610 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2611 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2611 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2612 helpbasic=True,
2612 helpbasic=True,
2613 intents={INTENT_READONLY},
2613 intents={INTENT_READONLY},
2614 )
2614 )
2615 def export(ui, repo, *changesets, **opts):
2615 def export(ui, repo, *changesets, **opts):
2616 """dump the header and diffs for one or more changesets
2616 """dump the header and diffs for one or more changesets
2617
2617
2618 Print the changeset header and diffs for one or more revisions.
2618 Print the changeset header and diffs for one or more revisions.
2619 If no revision is given, the parent of the working directory is used.
2619 If no revision is given, the parent of the working directory is used.
2620
2620
2621 The information shown in the changeset header is: author, date,
2621 The information shown in the changeset header is: author, date,
2622 branch name (if non-default), changeset hash, parent(s) and commit
2622 branch name (if non-default), changeset hash, parent(s) and commit
2623 comment.
2623 comment.
2624
2624
2625 .. note::
2625 .. note::
2626
2626
2627 :hg:`export` may generate unexpected diff output for merge
2627 :hg:`export` may generate unexpected diff output for merge
2628 changesets, as it will compare the merge changeset against its
2628 changesets, as it will compare the merge changeset against its
2629 first parent only.
2629 first parent only.
2630
2630
2631 Output may be to a file, in which case the name of the file is
2631 Output may be to a file, in which case the name of the file is
2632 given using a template string. See :hg:`help templates`. In addition
2632 given using a template string. See :hg:`help templates`. In addition
2633 to the common template keywords, the following formatting rules are
2633 to the common template keywords, the following formatting rules are
2634 supported:
2634 supported:
2635
2635
2636 :``%%``: literal "%" character
2636 :``%%``: literal "%" character
2637 :``%H``: changeset hash (40 hexadecimal digits)
2637 :``%H``: changeset hash (40 hexadecimal digits)
2638 :``%N``: number of patches being generated
2638 :``%N``: number of patches being generated
2639 :``%R``: changeset revision number
2639 :``%R``: changeset revision number
2640 :``%b``: basename of the exporting repository
2640 :``%b``: basename of the exporting repository
2641 :``%h``: short-form changeset hash (12 hexadecimal digits)
2641 :``%h``: short-form changeset hash (12 hexadecimal digits)
2642 :``%m``: first line of the commit message (only alphanumeric characters)
2642 :``%m``: first line of the commit message (only alphanumeric characters)
2643 :``%n``: zero-padded sequence number, starting at 1
2643 :``%n``: zero-padded sequence number, starting at 1
2644 :``%r``: zero-padded changeset revision number
2644 :``%r``: zero-padded changeset revision number
2645 :``\\``: literal "\\" character
2645 :``\\``: literal "\\" character
2646
2646
2647 Without the -a/--text option, export will avoid generating diffs
2647 Without the -a/--text option, export will avoid generating diffs
2648 of files it detects as binary. With -a, export will generate a
2648 of files it detects as binary. With -a, export will generate a
2649 diff anyway, probably with undesirable results.
2649 diff anyway, probably with undesirable results.
2650
2650
2651 With -B/--bookmark changesets reachable by the given bookmark are
2651 With -B/--bookmark changesets reachable by the given bookmark are
2652 selected.
2652 selected.
2653
2653
2654 Use the -g/--git option to generate diffs in the git extended diff
2654 Use the -g/--git option to generate diffs in the git extended diff
2655 format. See :hg:`help diffs` for more information.
2655 format. See :hg:`help diffs` for more information.
2656
2656
2657 With the --switch-parent option, the diff will be against the
2657 With the --switch-parent option, the diff will be against the
2658 second parent. It can be useful to review a merge.
2658 second parent. It can be useful to review a merge.
2659
2659
2660 .. container:: verbose
2660 .. container:: verbose
2661
2661
2662 Template:
2662 Template:
2663
2663
2664 The following keywords are supported in addition to the common template
2664 The following keywords are supported in addition to the common template
2665 keywords and functions. See also :hg:`help templates`.
2665 keywords and functions. See also :hg:`help templates`.
2666
2666
2667 :diff: String. Diff content.
2667 :diff: String. Diff content.
2668 :parents: List of strings. Parent nodes of the changeset.
2668 :parents: List of strings. Parent nodes of the changeset.
2669
2669
2670 Examples:
2670 Examples:
2671
2671
2672 - use export and import to transplant a bugfix to the current
2672 - use export and import to transplant a bugfix to the current
2673 branch::
2673 branch::
2674
2674
2675 hg export -r 9353 | hg import -
2675 hg export -r 9353 | hg import -
2676
2676
2677 - export all the changesets between two revisions to a file with
2677 - export all the changesets between two revisions to a file with
2678 rename information::
2678 rename information::
2679
2679
2680 hg export --git -r 123:150 > changes.txt
2680 hg export --git -r 123:150 > changes.txt
2681
2681
2682 - split outgoing changes into a series of patches with
2682 - split outgoing changes into a series of patches with
2683 descriptive names::
2683 descriptive names::
2684
2684
2685 hg export -r "outgoing()" -o "%n-%m.patch"
2685 hg export -r "outgoing()" -o "%n-%m.patch"
2686
2686
2687 Returns 0 on success.
2687 Returns 0 on success.
2688 """
2688 """
2689 opts = pycompat.byteskwargs(opts)
2689 opts = pycompat.byteskwargs(opts)
2690 bookmark = opts.get(b'bookmark')
2690 bookmark = opts.get(b'bookmark')
2691 changesets += tuple(opts.get(b'rev', []))
2691 changesets += tuple(opts.get(b'rev', []))
2692
2692
2693 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2693 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2694
2694
2695 if bookmark:
2695 if bookmark:
2696 if bookmark not in repo._bookmarks:
2696 if bookmark not in repo._bookmarks:
2697 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2697 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2698
2698
2699 revs = scmutil.bookmarkrevs(repo, bookmark)
2699 revs = scmutil.bookmarkrevs(repo, bookmark)
2700 else:
2700 else:
2701 if not changesets:
2701 if not changesets:
2702 changesets = [b'.']
2702 changesets = [b'.']
2703
2703
2704 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2704 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2705 revs = scmutil.revrange(repo, changesets)
2705 revs = scmutil.revrange(repo, changesets)
2706
2706
2707 if not revs:
2707 if not revs:
2708 raise error.InputError(_(b"export requires at least one changeset"))
2708 raise error.InputError(_(b"export requires at least one changeset"))
2709 if len(revs) > 1:
2709 if len(revs) > 1:
2710 ui.note(_(b'exporting patches:\n'))
2710 ui.note(_(b'exporting patches:\n'))
2711 else:
2711 else:
2712 ui.note(_(b'exporting patch:\n'))
2712 ui.note(_(b'exporting patch:\n'))
2713
2713
2714 fntemplate = opts.get(b'output')
2714 fntemplate = opts.get(b'output')
2715 if cmdutil.isstdiofilename(fntemplate):
2715 if cmdutil.isstdiofilename(fntemplate):
2716 fntemplate = b''
2716 fntemplate = b''
2717
2717
2718 if fntemplate:
2718 if fntemplate:
2719 fm = formatter.nullformatter(ui, b'export', opts)
2719 fm = formatter.nullformatter(ui, b'export', opts)
2720 else:
2720 else:
2721 ui.pager(b'export')
2721 ui.pager(b'export')
2722 fm = ui.formatter(b'export', opts)
2722 fm = ui.formatter(b'export', opts)
2723 with fm:
2723 with fm:
2724 cmdutil.export(
2724 cmdutil.export(
2725 repo,
2725 repo,
2726 revs,
2726 revs,
2727 fm,
2727 fm,
2728 fntemplate=fntemplate,
2728 fntemplate=fntemplate,
2729 switch_parent=opts.get(b'switch_parent'),
2729 switch_parent=opts.get(b'switch_parent'),
2730 opts=patch.diffallopts(ui, opts),
2730 opts=patch.diffallopts(ui, opts),
2731 )
2731 )
2732
2732
2733
2733
2734 @command(
2734 @command(
2735 b'files',
2735 b'files',
2736 [
2736 [
2737 (
2737 (
2738 b'r',
2738 b'r',
2739 b'rev',
2739 b'rev',
2740 b'',
2740 b'',
2741 _(b'search the repository as it is in REV'),
2741 _(b'search the repository as it is in REV'),
2742 _(b'REV'),
2742 _(b'REV'),
2743 ),
2743 ),
2744 (
2744 (
2745 b'0',
2745 b'0',
2746 b'print0',
2746 b'print0',
2747 None,
2747 None,
2748 _(b'end filenames with NUL, for use with xargs'),
2748 _(b'end filenames with NUL, for use with xargs'),
2749 ),
2749 ),
2750 ]
2750 ]
2751 + walkopts
2751 + walkopts
2752 + formatteropts
2752 + formatteropts
2753 + subrepoopts,
2753 + subrepoopts,
2754 _(b'[OPTION]... [FILE]...'),
2754 _(b'[OPTION]... [FILE]...'),
2755 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2755 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2756 intents={INTENT_READONLY},
2756 intents={INTENT_READONLY},
2757 )
2757 )
2758 def files(ui, repo, *pats, **opts):
2758 def files(ui, repo, *pats, **opts):
2759 """list tracked files
2759 """list tracked files
2760
2760
2761 Print files under Mercurial control in the working directory or
2761 Print files under Mercurial control in the working directory or
2762 specified revision for given files (excluding removed files).
2762 specified revision for given files (excluding removed files).
2763 Files can be specified as filenames or filesets.
2763 Files can be specified as filenames or filesets.
2764
2764
2765 If no files are given to match, this command prints the names
2765 If no files are given to match, this command prints the names
2766 of all files under Mercurial control.
2766 of all files under Mercurial control.
2767
2767
2768 .. container:: verbose
2768 .. container:: verbose
2769
2769
2770 Template:
2770 Template:
2771
2771
2772 The following keywords are supported in addition to the common template
2772 The following keywords are supported in addition to the common template
2773 keywords and functions. See also :hg:`help templates`.
2773 keywords and functions. See also :hg:`help templates`.
2774
2774
2775 :flags: String. Character denoting file's symlink and executable bits.
2775 :flags: String. Character denoting file's symlink and executable bits.
2776 :path: String. Repository-absolute path of the file.
2776 :path: String. Repository-absolute path of the file.
2777 :size: Integer. Size of the file in bytes.
2777 :size: Integer. Size of the file in bytes.
2778
2778
2779 Examples:
2779 Examples:
2780
2780
2781 - list all files under the current directory::
2781 - list all files under the current directory::
2782
2782
2783 hg files .
2783 hg files .
2784
2784
2785 - shows sizes and flags for current revision::
2785 - shows sizes and flags for current revision::
2786
2786
2787 hg files -vr .
2787 hg files -vr .
2788
2788
2789 - list all files named README::
2789 - list all files named README::
2790
2790
2791 hg files -I "**/README"
2791 hg files -I "**/README"
2792
2792
2793 - list all binary files::
2793 - list all binary files::
2794
2794
2795 hg files "set:binary()"
2795 hg files "set:binary()"
2796
2796
2797 - find files containing a regular expression::
2797 - find files containing a regular expression::
2798
2798
2799 hg files "set:grep('bob')"
2799 hg files "set:grep('bob')"
2800
2800
2801 - search tracked file contents with xargs and grep::
2801 - search tracked file contents with xargs and grep::
2802
2802
2803 hg files -0 | xargs -0 grep foo
2803 hg files -0 | xargs -0 grep foo
2804
2804
2805 See :hg:`help patterns` and :hg:`help filesets` for more information
2805 See :hg:`help patterns` and :hg:`help filesets` for more information
2806 on specifying file patterns.
2806 on specifying file patterns.
2807
2807
2808 Returns 0 if a match is found, 1 otherwise.
2808 Returns 0 if a match is found, 1 otherwise.
2809
2809
2810 """
2810 """
2811
2811
2812 opts = pycompat.byteskwargs(opts)
2812 opts = pycompat.byteskwargs(opts)
2813 rev = opts.get(b'rev')
2813 rev = opts.get(b'rev')
2814 if rev:
2814 if rev:
2815 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2815 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2816 ctx = scmutil.revsingle(repo, rev, None)
2816 ctx = scmutil.revsingle(repo, rev, None)
2817
2817
2818 end = b'\n'
2818 end = b'\n'
2819 if opts.get(b'print0'):
2819 if opts.get(b'print0'):
2820 end = b'\0'
2820 end = b'\0'
2821 fmt = b'%s' + end
2821 fmt = b'%s' + end
2822
2822
2823 m = scmutil.match(ctx, pats, opts)
2823 m = scmutil.match(ctx, pats, opts)
2824 ui.pager(b'files')
2824 ui.pager(b'files')
2825 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2825 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2826 with ui.formatter(b'files', opts) as fm:
2826 with ui.formatter(b'files', opts) as fm:
2827 return cmdutil.files(
2827 return cmdutil.files(
2828 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2828 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2829 )
2829 )
2830
2830
2831
2831
2832 @command(
2832 @command(
2833 b'forget',
2833 b'forget',
2834 [
2834 [
2835 (b'i', b'interactive', None, _(b'use interactive mode')),
2835 (b'i', b'interactive', None, _(b'use interactive mode')),
2836 ]
2836 ]
2837 + walkopts
2837 + walkopts
2838 + dryrunopts,
2838 + dryrunopts,
2839 _(b'[OPTION]... FILE...'),
2839 _(b'[OPTION]... FILE...'),
2840 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2840 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2841 helpbasic=True,
2841 helpbasic=True,
2842 inferrepo=True,
2842 inferrepo=True,
2843 )
2843 )
2844 def forget(ui, repo, *pats, **opts):
2844 def forget(ui, repo, *pats, **opts):
2845 """forget the specified files on the next commit
2845 """forget the specified files on the next commit
2846
2846
2847 Mark the specified files so they will no longer be tracked
2847 Mark the specified files so they will no longer be tracked
2848 after the next commit.
2848 after the next commit.
2849
2849
2850 This only removes files from the current branch, not from the
2850 This only removes files from the current branch, not from the
2851 entire project history, and it does not delete them from the
2851 entire project history, and it does not delete them from the
2852 working directory.
2852 working directory.
2853
2853
2854 To delete the file from the working directory, see :hg:`remove`.
2854 To delete the file from the working directory, see :hg:`remove`.
2855
2855
2856 To undo a forget before the next commit, see :hg:`add`.
2856 To undo a forget before the next commit, see :hg:`add`.
2857
2857
2858 .. container:: verbose
2858 .. container:: verbose
2859
2859
2860 Examples:
2860 Examples:
2861
2861
2862 - forget newly-added binary files::
2862 - forget newly-added binary files::
2863
2863
2864 hg forget "set:added() and binary()"
2864 hg forget "set:added() and binary()"
2865
2865
2866 - forget files that would be excluded by .hgignore::
2866 - forget files that would be excluded by .hgignore::
2867
2867
2868 hg forget "set:hgignore()"
2868 hg forget "set:hgignore()"
2869
2869
2870 Returns 0 on success.
2870 Returns 0 on success.
2871 """
2871 """
2872
2872
2873 opts = pycompat.byteskwargs(opts)
2873 opts = pycompat.byteskwargs(opts)
2874 if not pats:
2874 if not pats:
2875 raise error.InputError(_(b'no files specified'))
2875 raise error.InputError(_(b'no files specified'))
2876
2876
2877 m = scmutil.match(repo[None], pats, opts)
2877 m = scmutil.match(repo[None], pats, opts)
2878 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2878 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2879 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2879 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2880 rejected = cmdutil.forget(
2880 rejected = cmdutil.forget(
2881 ui,
2881 ui,
2882 repo,
2882 repo,
2883 m,
2883 m,
2884 prefix=b"",
2884 prefix=b"",
2885 uipathfn=uipathfn,
2885 uipathfn=uipathfn,
2886 explicitonly=False,
2886 explicitonly=False,
2887 dryrun=dryrun,
2887 dryrun=dryrun,
2888 interactive=interactive,
2888 interactive=interactive,
2889 )[0]
2889 )[0]
2890 return rejected and 1 or 0
2890 return rejected and 1 or 0
2891
2891
2892
2892
2893 @command(
2893 @command(
2894 b'graft',
2894 b'graft',
2895 [
2895 [
2896 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2896 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2897 (
2897 (
2898 b'',
2898 b'',
2899 b'base',
2899 b'base',
2900 b'',
2900 b'',
2901 _(b'base revision when doing the graft merge (ADVANCED)'),
2901 _(b'base revision when doing the graft merge (ADVANCED)'),
2902 _(b'REV'),
2902 _(b'REV'),
2903 ),
2903 ),
2904 (b'c', b'continue', False, _(b'resume interrupted graft')),
2904 (b'c', b'continue', False, _(b'resume interrupted graft')),
2905 (b'', b'stop', False, _(b'stop interrupted graft')),
2905 (b'', b'stop', False, _(b'stop interrupted graft')),
2906 (b'', b'abort', False, _(b'abort interrupted graft')),
2906 (b'', b'abort', False, _(b'abort interrupted graft')),
2907 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2907 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2908 (b'', b'log', None, _(b'append graft info to log message')),
2908 (b'', b'log', None, _(b'append graft info to log message')),
2909 (
2909 (
2910 b'',
2910 b'',
2911 b'no-commit',
2911 b'no-commit',
2912 None,
2912 None,
2913 _(b"don't commit, just apply the changes in working directory"),
2913 _(b"don't commit, just apply the changes in working directory"),
2914 ),
2914 ),
2915 (b'f', b'force', False, _(b'force graft')),
2915 (b'f', b'force', False, _(b'force graft')),
2916 (
2916 (
2917 b'D',
2917 b'D',
2918 b'currentdate',
2918 b'currentdate',
2919 False,
2919 False,
2920 _(b'record the current date as commit date'),
2920 _(b'record the current date as commit date'),
2921 ),
2921 ),
2922 (
2922 (
2923 b'U',
2923 b'U',
2924 b'currentuser',
2924 b'currentuser',
2925 False,
2925 False,
2926 _(b'record the current user as committer'),
2926 _(b'record the current user as committer'),
2927 ),
2927 ),
2928 ]
2928 ]
2929 + commitopts2
2929 + commitopts2
2930 + mergetoolopts
2930 + mergetoolopts
2931 + dryrunopts,
2931 + dryrunopts,
2932 _(b'[OPTION]... [-r REV]... REV...'),
2932 _(b'[OPTION]... [-r REV]... REV...'),
2933 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2933 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2934 )
2934 )
2935 def graft(ui, repo, *revs, **opts):
2935 def graft(ui, repo, *revs, **opts):
2936 """copy changes from other branches onto the current branch
2936 """copy changes from other branches onto the current branch
2937
2937
2938 This command uses Mercurial's merge logic to copy individual
2938 This command uses Mercurial's merge logic to copy individual
2939 changes from other branches without merging branches in the
2939 changes from other branches without merging branches in the
2940 history graph. This is sometimes known as 'backporting' or
2940 history graph. This is sometimes known as 'backporting' or
2941 'cherry-picking'. By default, graft will copy user, date, and
2941 'cherry-picking'. By default, graft will copy user, date, and
2942 description from the source changesets.
2942 description from the source changesets.
2943
2943
2944 Changesets that are ancestors of the current revision, that have
2944 Changesets that are ancestors of the current revision, that have
2945 already been grafted, or that are merges will be skipped.
2945 already been grafted, or that are merges will be skipped.
2946
2946
2947 If --log is specified, log messages will have a comment appended
2947 If --log is specified, log messages will have a comment appended
2948 of the form::
2948 of the form::
2949
2949
2950 (grafted from CHANGESETHASH)
2950 (grafted from CHANGESETHASH)
2951
2951
2952 If --force is specified, revisions will be grafted even if they
2952 If --force is specified, revisions will be grafted even if they
2953 are already ancestors of, or have been grafted to, the destination.
2953 are already ancestors of, or have been grafted to, the destination.
2954 This is useful when the revisions have since been backed out.
2954 This is useful when the revisions have since been backed out.
2955
2955
2956 If a graft merge results in conflicts, the graft process is
2956 If a graft merge results in conflicts, the graft process is
2957 interrupted so that the current merge can be manually resolved.
2957 interrupted so that the current merge can be manually resolved.
2958 Once all conflicts are addressed, the graft process can be
2958 Once all conflicts are addressed, the graft process can be
2959 continued with the -c/--continue option.
2959 continued with the -c/--continue option.
2960
2960
2961 The -c/--continue option reapplies all the earlier options.
2961 The -c/--continue option reapplies all the earlier options.
2962
2962
2963 .. container:: verbose
2963 .. container:: verbose
2964
2964
2965 The --base option exposes more of how graft internally uses merge with a
2965 The --base option exposes more of how graft internally uses merge with a
2966 custom base revision. --base can be used to specify another ancestor than
2966 custom base revision. --base can be used to specify another ancestor than
2967 the first and only parent.
2967 the first and only parent.
2968
2968
2969 The command::
2969 The command::
2970
2970
2971 hg graft -r 345 --base 234
2971 hg graft -r 345 --base 234
2972
2972
2973 is thus pretty much the same as::
2973 is thus pretty much the same as::
2974
2974
2975 hg diff --from 234 --to 345 | hg import
2975 hg diff --from 234 --to 345 | hg import
2976
2976
2977 but using merge to resolve conflicts and track moved files.
2977 but using merge to resolve conflicts and track moved files.
2978
2978
2979 The result of a merge can thus be backported as a single commit by
2979 The result of a merge can thus be backported as a single commit by
2980 specifying one of the merge parents as base, and thus effectively
2980 specifying one of the merge parents as base, and thus effectively
2981 grafting the changes from the other side.
2981 grafting the changes from the other side.
2982
2982
2983 It is also possible to collapse multiple changesets and clean up history
2983 It is also possible to collapse multiple changesets and clean up history
2984 by specifying another ancestor as base, much like rebase --collapse
2984 by specifying another ancestor as base, much like rebase --collapse
2985 --keep.
2985 --keep.
2986
2986
2987 The commit message can be tweaked after the fact using commit --amend .
2987 The commit message can be tweaked after the fact using commit --amend .
2988
2988
2989 For using non-ancestors as the base to backout changes, see the backout
2989 For using non-ancestors as the base to backout changes, see the backout
2990 command and the hidden --parent option.
2990 command and the hidden --parent option.
2991
2991
2992 .. container:: verbose
2992 .. container:: verbose
2993
2993
2994 Examples:
2994 Examples:
2995
2995
2996 - copy a single change to the stable branch and edit its description::
2996 - copy a single change to the stable branch and edit its description::
2997
2997
2998 hg update stable
2998 hg update stable
2999 hg graft --edit 9393
2999 hg graft --edit 9393
3000
3000
3001 - graft a range of changesets with one exception, updating dates::
3001 - graft a range of changesets with one exception, updating dates::
3002
3002
3003 hg graft -D "2085::2093 and not 2091"
3003 hg graft -D "2085::2093 and not 2091"
3004
3004
3005 - continue a graft after resolving conflicts::
3005 - continue a graft after resolving conflicts::
3006
3006
3007 hg graft -c
3007 hg graft -c
3008
3008
3009 - show the source of a grafted changeset::
3009 - show the source of a grafted changeset::
3010
3010
3011 hg log --debug -r .
3011 hg log --debug -r .
3012
3012
3013 - show revisions sorted by date::
3013 - show revisions sorted by date::
3014
3014
3015 hg log -r "sort(all(), date)"
3015 hg log -r "sort(all(), date)"
3016
3016
3017 - backport the result of a merge as a single commit::
3017 - backport the result of a merge as a single commit::
3018
3018
3019 hg graft -r 123 --base 123^
3019 hg graft -r 123 --base 123^
3020
3020
3021 - land a feature branch as one changeset::
3021 - land a feature branch as one changeset::
3022
3022
3023 hg up -cr default
3023 hg up -cr default
3024 hg graft -r featureX --base "ancestor('featureX', 'default')"
3024 hg graft -r featureX --base "ancestor('featureX', 'default')"
3025
3025
3026 See :hg:`help revisions` for more about specifying revisions.
3026 See :hg:`help revisions` for more about specifying revisions.
3027
3027
3028 Returns 0 on successful completion, 1 if there are unresolved files.
3028 Returns 0 on successful completion, 1 if there are unresolved files.
3029 """
3029 """
3030 with repo.wlock():
3030 with repo.wlock():
3031 return _dograft(ui, repo, *revs, **opts)
3031 return _dograft(ui, repo, *revs, **opts)
3032
3032
3033
3033
3034 def _dograft(ui, repo, *revs, **opts):
3034 def _dograft(ui, repo, *revs, **opts):
3035 opts = pycompat.byteskwargs(opts)
3035 opts = pycompat.byteskwargs(opts)
3036 if revs and opts.get(b'rev'):
3036 if revs and opts.get(b'rev'):
3037 ui.warn(
3037 ui.warn(
3038 _(
3038 _(
3039 b'warning: inconsistent use of --rev might give unexpected '
3039 b'warning: inconsistent use of --rev might give unexpected '
3040 b'revision ordering!\n'
3040 b'revision ordering!\n'
3041 )
3041 )
3042 )
3042 )
3043
3043
3044 revs = list(revs)
3044 revs = list(revs)
3045 revs.extend(opts.get(b'rev'))
3045 revs.extend(opts.get(b'rev'))
3046 # a dict of data to be stored in state file
3046 # a dict of data to be stored in state file
3047 statedata = {}
3047 statedata = {}
3048 # list of new nodes created by ongoing graft
3048 # list of new nodes created by ongoing graft
3049 statedata[b'newnodes'] = []
3049 statedata[b'newnodes'] = []
3050
3050
3051 cmdutil.resolvecommitoptions(ui, opts)
3051 cmdutil.resolvecommitoptions(ui, opts)
3052
3052
3053 editor = cmdutil.getcommiteditor(
3053 editor = cmdutil.getcommiteditor(
3054 editform=b'graft', **pycompat.strkwargs(opts)
3054 editform=b'graft', **pycompat.strkwargs(opts)
3055 )
3055 )
3056
3056
3057 cmdutil.check_at_most_one_arg(opts, b'abort', b'stop', b'continue')
3057 cmdutil.check_at_most_one_arg(opts, b'abort', b'stop', b'continue')
3058
3058
3059 cont = False
3059 cont = False
3060 if opts.get(b'no_commit'):
3060 if opts.get(b'no_commit'):
3061 cmdutil.check_incompatible_arguments(
3061 cmdutil.check_incompatible_arguments(
3062 opts,
3062 opts,
3063 b'no_commit',
3063 b'no_commit',
3064 [b'edit', b'currentuser', b'currentdate', b'log'],
3064 [b'edit', b'currentuser', b'currentdate', b'log'],
3065 )
3065 )
3066
3066
3067 graftstate = statemod.cmdstate(repo, b'graftstate')
3067 graftstate = statemod.cmdstate(repo, b'graftstate')
3068
3068
3069 if opts.get(b'stop'):
3069 if opts.get(b'stop'):
3070 cmdutil.check_incompatible_arguments(
3070 cmdutil.check_incompatible_arguments(
3071 opts,
3071 opts,
3072 b'stop',
3072 b'stop',
3073 [
3073 [
3074 b'edit',
3074 b'edit',
3075 b'log',
3075 b'log',
3076 b'user',
3076 b'user',
3077 b'date',
3077 b'date',
3078 b'currentdate',
3078 b'currentdate',
3079 b'currentuser',
3079 b'currentuser',
3080 b'rev',
3080 b'rev',
3081 ],
3081 ],
3082 )
3082 )
3083 return _stopgraft(ui, repo, graftstate)
3083 return _stopgraft(ui, repo, graftstate)
3084 elif opts.get(b'abort'):
3084 elif opts.get(b'abort'):
3085 cmdutil.check_incompatible_arguments(
3085 cmdutil.check_incompatible_arguments(
3086 opts,
3086 opts,
3087 b'abort',
3087 b'abort',
3088 [
3088 [
3089 b'edit',
3089 b'edit',
3090 b'log',
3090 b'log',
3091 b'user',
3091 b'user',
3092 b'date',
3092 b'date',
3093 b'currentdate',
3093 b'currentdate',
3094 b'currentuser',
3094 b'currentuser',
3095 b'rev',
3095 b'rev',
3096 ],
3096 ],
3097 )
3097 )
3098 return cmdutil.abortgraft(ui, repo, graftstate)
3098 return cmdutil.abortgraft(ui, repo, graftstate)
3099 elif opts.get(b'continue'):
3099 elif opts.get(b'continue'):
3100 cont = True
3100 cont = True
3101 if revs:
3101 if revs:
3102 raise error.InputError(_(b"can't specify --continue and revisions"))
3102 raise error.InputError(_(b"can't specify --continue and revisions"))
3103 # read in unfinished revisions
3103 # read in unfinished revisions
3104 if graftstate.exists():
3104 if graftstate.exists():
3105 statedata = cmdutil.readgraftstate(repo, graftstate)
3105 statedata = cmdutil.readgraftstate(repo, graftstate)
3106 if statedata.get(b'date'):
3106 if statedata.get(b'date'):
3107 opts[b'date'] = statedata[b'date']
3107 opts[b'date'] = statedata[b'date']
3108 if statedata.get(b'user'):
3108 if statedata.get(b'user'):
3109 opts[b'user'] = statedata[b'user']
3109 opts[b'user'] = statedata[b'user']
3110 if statedata.get(b'log'):
3110 if statedata.get(b'log'):
3111 opts[b'log'] = True
3111 opts[b'log'] = True
3112 if statedata.get(b'no_commit'):
3112 if statedata.get(b'no_commit'):
3113 opts[b'no_commit'] = statedata.get(b'no_commit')
3113 opts[b'no_commit'] = statedata.get(b'no_commit')
3114 if statedata.get(b'base'):
3114 if statedata.get(b'base'):
3115 opts[b'base'] = statedata.get(b'base')
3115 opts[b'base'] = statedata.get(b'base')
3116 nodes = statedata[b'nodes']
3116 nodes = statedata[b'nodes']
3117 revs = [repo[node].rev() for node in nodes]
3117 revs = [repo[node].rev() for node in nodes]
3118 else:
3118 else:
3119 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3119 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3120 else:
3120 else:
3121 if not revs:
3121 if not revs:
3122 raise error.InputError(_(b'no revisions specified'))
3122 raise error.InputError(_(b'no revisions specified'))
3123 cmdutil.checkunfinished(repo)
3123 cmdutil.checkunfinished(repo)
3124 cmdutil.bailifchanged(repo)
3124 cmdutil.bailifchanged(repo)
3125 revs = scmutil.revrange(repo, revs)
3125 revs = scmutil.revrange(repo, revs)
3126
3126
3127 skipped = set()
3127 skipped = set()
3128 basectx = None
3128 basectx = None
3129 if opts.get(b'base'):
3129 if opts.get(b'base'):
3130 basectx = scmutil.revsingle(repo, opts[b'base'], None)
3130 basectx = scmutil.revsingle(repo, opts[b'base'], None)
3131 if basectx is None:
3131 if basectx is None:
3132 # check for merges
3132 # check for merges
3133 for rev in repo.revs(b'%ld and merge()', revs):
3133 for rev in repo.revs(b'%ld and merge()', revs):
3134 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3134 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3135 skipped.add(rev)
3135 skipped.add(rev)
3136 revs = [r for r in revs if r not in skipped]
3136 revs = [r for r in revs if r not in skipped]
3137 if not revs:
3137 if not revs:
3138 return -1
3138 return -1
3139 if basectx is not None and len(revs) != 1:
3139 if basectx is not None and len(revs) != 1:
3140 raise error.InputError(_(b'only one revision allowed with --base '))
3140 raise error.InputError(_(b'only one revision allowed with --base '))
3141
3141
3142 # Don't check in the --continue case, in effect retaining --force across
3142 # Don't check in the --continue case, in effect retaining --force across
3143 # --continues. That's because without --force, any revisions we decided to
3143 # --continues. That's because without --force, any revisions we decided to
3144 # skip would have been filtered out here, so they wouldn't have made their
3144 # skip would have been filtered out here, so they wouldn't have made their
3145 # way to the graftstate. With --force, any revisions we would have otherwise
3145 # way to the graftstate. With --force, any revisions we would have otherwise
3146 # skipped would not have been filtered out, and if they hadn't been applied
3146 # skipped would not have been filtered out, and if they hadn't been applied
3147 # already, they'd have been in the graftstate.
3147 # already, they'd have been in the graftstate.
3148 if not (cont or opts.get(b'force')) and basectx is None:
3148 if not (cont or opts.get(b'force')) and basectx is None:
3149 # check for ancestors of dest branch
3149 # check for ancestors of dest branch
3150 ancestors = repo.revs(b'%ld & (::.)', revs)
3150 ancestors = repo.revs(b'%ld & (::.)', revs)
3151 for rev in ancestors:
3151 for rev in ancestors:
3152 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3152 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3153
3153
3154 revs = [r for r in revs if r not in ancestors]
3154 revs = [r for r in revs if r not in ancestors]
3155
3155
3156 if not revs:
3156 if not revs:
3157 return -1
3157 return -1
3158
3158
3159 # analyze revs for earlier grafts
3159 # analyze revs for earlier grafts
3160 ids = {}
3160 ids = {}
3161 for ctx in repo.set(b"%ld", revs):
3161 for ctx in repo.set(b"%ld", revs):
3162 ids[ctx.hex()] = ctx.rev()
3162 ids[ctx.hex()] = ctx.rev()
3163 n = ctx.extra().get(b'source')
3163 n = ctx.extra().get(b'source')
3164 if n:
3164 if n:
3165 ids[n] = ctx.rev()
3165 ids[n] = ctx.rev()
3166
3166
3167 # check ancestors for earlier grafts
3167 # check ancestors for earlier grafts
3168 ui.debug(b'scanning for duplicate grafts\n')
3168 ui.debug(b'scanning for duplicate grafts\n')
3169
3169
3170 # The only changesets we can be sure doesn't contain grafts of any
3170 # The only changesets we can be sure doesn't contain grafts of any
3171 # revs, are the ones that are common ancestors of *all* revs:
3171 # revs, are the ones that are common ancestors of *all* revs:
3172 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3172 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3173 ctx = repo[rev]
3173 ctx = repo[rev]
3174 n = ctx.extra().get(b'source')
3174 n = ctx.extra().get(b'source')
3175 if n in ids:
3175 if n in ids:
3176 try:
3176 try:
3177 r = repo[n].rev()
3177 r = repo[n].rev()
3178 except error.RepoLookupError:
3178 except error.RepoLookupError:
3179 r = None
3179 r = None
3180 if r in revs:
3180 if r in revs:
3181 ui.warn(
3181 ui.warn(
3182 _(
3182 _(
3183 b'skipping revision %d:%s '
3183 b'skipping revision %d:%s '
3184 b'(already grafted to %d:%s)\n'
3184 b'(already grafted to %d:%s)\n'
3185 )
3185 )
3186 % (r, repo[r], rev, ctx)
3186 % (r, repo[r], rev, ctx)
3187 )
3187 )
3188 revs.remove(r)
3188 revs.remove(r)
3189 elif ids[n] in revs:
3189 elif ids[n] in revs:
3190 if r is None:
3190 if r is None:
3191 ui.warn(
3191 ui.warn(
3192 _(
3192 _(
3193 b'skipping already grafted revision %d:%s '
3193 b'skipping already grafted revision %d:%s '
3194 b'(%d:%s also has unknown origin %s)\n'
3194 b'(%d:%s also has unknown origin %s)\n'
3195 )
3195 )
3196 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3196 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3197 )
3197 )
3198 else:
3198 else:
3199 ui.warn(
3199 ui.warn(
3200 _(
3200 _(
3201 b'skipping already grafted revision %d:%s '
3201 b'skipping already grafted revision %d:%s '
3202 b'(%d:%s also has origin %d:%s)\n'
3202 b'(%d:%s also has origin %d:%s)\n'
3203 )
3203 )
3204 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3204 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3205 )
3205 )
3206 revs.remove(ids[n])
3206 revs.remove(ids[n])
3207 elif ctx.hex() in ids:
3207 elif ctx.hex() in ids:
3208 r = ids[ctx.hex()]
3208 r = ids[ctx.hex()]
3209 if r in revs:
3209 if r in revs:
3210 ui.warn(
3210 ui.warn(
3211 _(
3211 _(
3212 b'skipping already grafted revision %d:%s '
3212 b'skipping already grafted revision %d:%s '
3213 b'(was grafted from %d:%s)\n'
3213 b'(was grafted from %d:%s)\n'
3214 )
3214 )
3215 % (r, repo[r], rev, ctx)
3215 % (r, repo[r], rev, ctx)
3216 )
3216 )
3217 revs.remove(r)
3217 revs.remove(r)
3218 if not revs:
3218 if not revs:
3219 return -1
3219 return -1
3220
3220
3221 if opts.get(b'no_commit'):
3221 if opts.get(b'no_commit'):
3222 statedata[b'no_commit'] = True
3222 statedata[b'no_commit'] = True
3223 if opts.get(b'base'):
3223 if opts.get(b'base'):
3224 statedata[b'base'] = opts[b'base']
3224 statedata[b'base'] = opts[b'base']
3225 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3225 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3226 desc = b'%d:%s "%s"' % (
3226 desc = b'%d:%s "%s"' % (
3227 ctx.rev(),
3227 ctx.rev(),
3228 ctx,
3228 ctx,
3229 ctx.description().split(b'\n', 1)[0],
3229 ctx.description().split(b'\n', 1)[0],
3230 )
3230 )
3231 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3231 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3232 if names:
3232 if names:
3233 desc += b' (%s)' % b' '.join(names)
3233 desc += b' (%s)' % b' '.join(names)
3234 ui.status(_(b'grafting %s\n') % desc)
3234 ui.status(_(b'grafting %s\n') % desc)
3235 if opts.get(b'dry_run'):
3235 if opts.get(b'dry_run'):
3236 continue
3236 continue
3237
3237
3238 source = ctx.extra().get(b'source')
3238 source = ctx.extra().get(b'source')
3239 extra = {}
3239 extra = {}
3240 if source:
3240 if source:
3241 extra[b'source'] = source
3241 extra[b'source'] = source
3242 extra[b'intermediate-source'] = ctx.hex()
3242 extra[b'intermediate-source'] = ctx.hex()
3243 else:
3243 else:
3244 extra[b'source'] = ctx.hex()
3244 extra[b'source'] = ctx.hex()
3245 user = ctx.user()
3245 user = ctx.user()
3246 if opts.get(b'user'):
3246 if opts.get(b'user'):
3247 user = opts[b'user']
3247 user = opts[b'user']
3248 statedata[b'user'] = user
3248 statedata[b'user'] = user
3249 date = ctx.date()
3249 date = ctx.date()
3250 if opts.get(b'date'):
3250 if opts.get(b'date'):
3251 date = opts[b'date']
3251 date = opts[b'date']
3252 statedata[b'date'] = date
3252 statedata[b'date'] = date
3253 message = ctx.description()
3253 message = ctx.description()
3254 if opts.get(b'log'):
3254 if opts.get(b'log'):
3255 message += b'\n(grafted from %s)' % ctx.hex()
3255 message += b'\n(grafted from %s)' % ctx.hex()
3256 statedata[b'log'] = True
3256 statedata[b'log'] = True
3257
3257
3258 # we don't merge the first commit when continuing
3258 # we don't merge the first commit when continuing
3259 if not cont:
3259 if not cont:
3260 # perform the graft merge with p1(rev) as 'ancestor'
3260 # perform the graft merge with p1(rev) as 'ancestor'
3261 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3261 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3262 base = ctx.p1() if basectx is None else basectx
3262 base = ctx.p1() if basectx is None else basectx
3263 with ui.configoverride(overrides, b'graft'):
3263 with ui.configoverride(overrides, b'graft'):
3264 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3264 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3265 # report any conflicts
3265 # report any conflicts
3266 if stats.unresolvedcount > 0:
3266 if stats.unresolvedcount > 0:
3267 # write out state for --continue
3267 # write out state for --continue
3268 nodes = [repo[rev].hex() for rev in revs[pos:]]
3268 nodes = [repo[rev].hex() for rev in revs[pos:]]
3269 statedata[b'nodes'] = nodes
3269 statedata[b'nodes'] = nodes
3270 stateversion = 1
3270 stateversion = 1
3271 graftstate.save(stateversion, statedata)
3271 graftstate.save(stateversion, statedata)
3272 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3272 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3273 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3273 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3274 return 1
3274 return 1
3275 else:
3275 else:
3276 cont = False
3276 cont = False
3277
3277
3278 # commit if --no-commit is false
3278 # commit if --no-commit is false
3279 if not opts.get(b'no_commit'):
3279 if not opts.get(b'no_commit'):
3280 node = repo.commit(
3280 node = repo.commit(
3281 text=message, user=user, date=date, extra=extra, editor=editor
3281 text=message, user=user, date=date, extra=extra, editor=editor
3282 )
3282 )
3283 if node is None:
3283 if node is None:
3284 ui.warn(
3284 ui.warn(
3285 _(b'note: graft of %d:%s created no changes to commit\n')
3285 _(b'note: graft of %d:%s created no changes to commit\n')
3286 % (ctx.rev(), ctx)
3286 % (ctx.rev(), ctx)
3287 )
3287 )
3288 # checking that newnodes exist because old state files won't have it
3288 # checking that newnodes exist because old state files won't have it
3289 elif statedata.get(b'newnodes') is not None:
3289 elif statedata.get(b'newnodes') is not None:
3290 statedata[b'newnodes'].append(node)
3290 statedata[b'newnodes'].append(node)
3291
3291
3292 # remove state when we complete successfully
3292 # remove state when we complete successfully
3293 if not opts.get(b'dry_run'):
3293 if not opts.get(b'dry_run'):
3294 graftstate.delete()
3294 graftstate.delete()
3295
3295
3296 return 0
3296 return 0
3297
3297
3298
3298
3299 def _stopgraft(ui, repo, graftstate):
3299 def _stopgraft(ui, repo, graftstate):
3300 """stop the interrupted graft"""
3300 """stop the interrupted graft"""
3301 if not graftstate.exists():
3301 if not graftstate.exists():
3302 raise error.StateError(_(b"no interrupted graft found"))
3302 raise error.StateError(_(b"no interrupted graft found"))
3303 pctx = repo[b'.']
3303 pctx = repo[b'.']
3304 mergemod.clean_update(pctx)
3304 mergemod.clean_update(pctx)
3305 graftstate.delete()
3305 graftstate.delete()
3306 ui.status(_(b"stopped the interrupted graft\n"))
3306 ui.status(_(b"stopped the interrupted graft\n"))
3307 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3307 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3308 return 0
3308 return 0
3309
3309
3310
3310
3311 statemod.addunfinished(
3311 statemod.addunfinished(
3312 b'graft',
3312 b'graft',
3313 fname=b'graftstate',
3313 fname=b'graftstate',
3314 clearable=True,
3314 clearable=True,
3315 stopflag=True,
3315 stopflag=True,
3316 continueflag=True,
3316 continueflag=True,
3317 abortfunc=cmdutil.hgabortgraft,
3317 abortfunc=cmdutil.hgabortgraft,
3318 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3318 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3319 )
3319 )
3320
3320
3321
3321
3322 @command(
3322 @command(
3323 b'grep',
3323 b'grep',
3324 [
3324 [
3325 (b'0', b'print0', None, _(b'end fields with NUL')),
3325 (b'0', b'print0', None, _(b'end fields with NUL')),
3326 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3326 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3327 (
3327 (
3328 b'',
3328 b'',
3329 b'diff',
3329 b'diff',
3330 None,
3330 None,
3331 _(
3331 _(
3332 b'search revision differences for when the pattern was added '
3332 b'search revision differences for when the pattern was added '
3333 b'or removed'
3333 b'or removed'
3334 ),
3334 ),
3335 ),
3335 ),
3336 (b'a', b'text', None, _(b'treat all files as text')),
3336 (b'a', b'text', None, _(b'treat all files as text')),
3337 (
3337 (
3338 b'f',
3338 b'f',
3339 b'follow',
3339 b'follow',
3340 None,
3340 None,
3341 _(
3341 _(
3342 b'follow changeset history,'
3342 b'follow changeset history,'
3343 b' or file history across copies and renames'
3343 b' or file history across copies and renames'
3344 ),
3344 ),
3345 ),
3345 ),
3346 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3346 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3347 (
3347 (
3348 b'l',
3348 b'l',
3349 b'files-with-matches',
3349 b'files-with-matches',
3350 None,
3350 None,
3351 _(b'print only filenames and revisions that match'),
3351 _(b'print only filenames and revisions that match'),
3352 ),
3352 ),
3353 (b'n', b'line-number', None, _(b'print matching line numbers')),
3353 (b'n', b'line-number', None, _(b'print matching line numbers')),
3354 (
3354 (
3355 b'r',
3355 b'r',
3356 b'rev',
3356 b'rev',
3357 [],
3357 [],
3358 _(b'search files changed within revision range'),
3358 _(b'search files changed within revision range'),
3359 _(b'REV'),
3359 _(b'REV'),
3360 ),
3360 ),
3361 (
3361 (
3362 b'',
3362 b'',
3363 b'all-files',
3363 b'all-files',
3364 None,
3364 None,
3365 _(
3365 _(
3366 b'include all files in the changeset while grepping (DEPRECATED)'
3366 b'include all files in the changeset while grepping (DEPRECATED)'
3367 ),
3367 ),
3368 ),
3368 ),
3369 (b'u', b'user', None, _(b'list the author (long with -v)')),
3369 (b'u', b'user', None, _(b'list the author (long with -v)')),
3370 (b'd', b'date', None, _(b'list the date (short with -q)')),
3370 (b'd', b'date', None, _(b'list the date (short with -q)')),
3371 ]
3371 ]
3372 + formatteropts
3372 + formatteropts
3373 + walkopts,
3373 + walkopts,
3374 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3374 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3375 helpcategory=command.CATEGORY_FILE_CONTENTS,
3375 helpcategory=command.CATEGORY_FILE_CONTENTS,
3376 inferrepo=True,
3376 inferrepo=True,
3377 intents={INTENT_READONLY},
3377 intents={INTENT_READONLY},
3378 )
3378 )
3379 def grep(ui, repo, pattern, *pats, **opts):
3379 def grep(ui, repo, pattern, *pats, **opts):
3380 """search for a pattern in specified files
3380 """search for a pattern in specified files
3381
3381
3382 Search the working directory or revision history for a regular
3382 Search the working directory or revision history for a regular
3383 expression in the specified files for the entire repository.
3383 expression in the specified files for the entire repository.
3384
3384
3385 By default, grep searches the repository files in the working
3385 By default, grep searches the repository files in the working
3386 directory and prints the files where it finds a match. To specify
3386 directory and prints the files where it finds a match. To specify
3387 historical revisions instead of the working directory, use the
3387 historical revisions instead of the working directory, use the
3388 --rev flag.
3388 --rev flag.
3389
3389
3390 To search instead historical revision differences that contains a
3390 To search instead historical revision differences that contains a
3391 change in match status ("-" for a match that becomes a non-match,
3391 change in match status ("-" for a match that becomes a non-match,
3392 or "+" for a non-match that becomes a match), use the --diff flag.
3392 or "+" for a non-match that becomes a match), use the --diff flag.
3393
3393
3394 PATTERN can be any Python (roughly Perl-compatible) regular
3394 PATTERN can be any Python (roughly Perl-compatible) regular
3395 expression.
3395 expression.
3396
3396
3397 If no FILEs are specified and the --rev flag isn't supplied, all
3397 If no FILEs are specified and the --rev flag isn't supplied, all
3398 files in the working directory are searched. When using the --rev
3398 files in the working directory are searched. When using the --rev
3399 flag and specifying FILEs, use the --follow argument to also
3399 flag and specifying FILEs, use the --follow argument to also
3400 follow the specified FILEs across renames and copies.
3400 follow the specified FILEs across renames and copies.
3401
3401
3402 .. container:: verbose
3402 .. container:: verbose
3403
3403
3404 Template:
3404 Template:
3405
3405
3406 The following keywords are supported in addition to the common template
3406 The following keywords are supported in addition to the common template
3407 keywords and functions. See also :hg:`help templates`.
3407 keywords and functions. See also :hg:`help templates`.
3408
3408
3409 :change: String. Character denoting insertion ``+`` or removal ``-``.
3409 :change: String. Character denoting insertion ``+`` or removal ``-``.
3410 Available if ``--diff`` is specified.
3410 Available if ``--diff`` is specified.
3411 :lineno: Integer. Line number of the match.
3411 :lineno: Integer. Line number of the match.
3412 :path: String. Repository-absolute path of the file.
3412 :path: String. Repository-absolute path of the file.
3413 :texts: List of text chunks.
3413 :texts: List of text chunks.
3414
3414
3415 And each entry of ``{texts}`` provides the following sub-keywords.
3415 And each entry of ``{texts}`` provides the following sub-keywords.
3416
3416
3417 :matched: Boolean. True if the chunk matches the specified pattern.
3417 :matched: Boolean. True if the chunk matches the specified pattern.
3418 :text: String. Chunk content.
3418 :text: String. Chunk content.
3419
3419
3420 See :hg:`help templates.operators` for the list expansion syntax.
3420 See :hg:`help templates.operators` for the list expansion syntax.
3421
3421
3422 Returns 0 if a match is found, 1 otherwise.
3422 Returns 0 if a match is found, 1 otherwise.
3423
3423
3424 """
3424 """
3425 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3425 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3426 opts = pycompat.byteskwargs(opts)
3426 opts = pycompat.byteskwargs(opts)
3427 diff = opts.get(b'all') or opts.get(b'diff')
3427 diff = opts.get(b'all') or opts.get(b'diff')
3428 follow = opts.get(b'follow')
3428 follow = opts.get(b'follow')
3429 if opts.get(b'all_files') is None and not diff:
3429 if opts.get(b'all_files') is None and not diff:
3430 opts[b'all_files'] = True
3430 opts[b'all_files'] = True
3431 plaingrep = (
3431 plaingrep = (
3432 opts.get(b'all_files')
3432 opts.get(b'all_files')
3433 and not opts.get(b'rev')
3433 and not opts.get(b'rev')
3434 and not opts.get(b'follow')
3434 and not opts.get(b'follow')
3435 )
3435 )
3436 all_files = opts.get(b'all_files')
3436 all_files = opts.get(b'all_files')
3437 if plaingrep:
3437 if plaingrep:
3438 opts[b'rev'] = [b'wdir()']
3438 opts[b'rev'] = [b'wdir()']
3439
3439
3440 reflags = re.M
3440 reflags = re.M
3441 if opts.get(b'ignore_case'):
3441 if opts.get(b'ignore_case'):
3442 reflags |= re.I
3442 reflags |= re.I
3443 try:
3443 try:
3444 regexp = util.re.compile(pattern, reflags)
3444 regexp = util.re.compile(pattern, reflags)
3445 except re.error as inst:
3445 except re.error as inst:
3446 ui.warn(
3446 ui.warn(
3447 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3447 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3448 )
3448 )
3449 return 1
3449 return 1
3450 sep, eol = b':', b'\n'
3450 sep, eol = b':', b'\n'
3451 if opts.get(b'print0'):
3451 if opts.get(b'print0'):
3452 sep = eol = b'\0'
3452 sep = eol = b'\0'
3453
3453
3454 searcher = grepmod.grepsearcher(
3454 searcher = grepmod.grepsearcher(
3455 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3455 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3456 )
3456 )
3457
3457
3458 getfile = searcher._getfile
3458 getfile = searcher._getfile
3459
3459
3460 uipathfn = scmutil.getuipathfn(repo)
3460 uipathfn = scmutil.getuipathfn(repo)
3461
3461
3462 def display(fm, fn, ctx, pstates, states):
3462 def display(fm, fn, ctx, pstates, states):
3463 rev = scmutil.intrev(ctx)
3463 rev = scmutil.intrev(ctx)
3464 if fm.isplain():
3464 if fm.isplain():
3465 formatuser = ui.shortuser
3465 formatuser = ui.shortuser
3466 else:
3466 else:
3467 formatuser = pycompat.bytestr
3467 formatuser = pycompat.bytestr
3468 if ui.quiet:
3468 if ui.quiet:
3469 datefmt = b'%Y-%m-%d'
3469 datefmt = b'%Y-%m-%d'
3470 else:
3470 else:
3471 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3471 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3472 found = False
3472 found = False
3473
3473
3474 @util.cachefunc
3474 @util.cachefunc
3475 def binary():
3475 def binary():
3476 flog = getfile(fn)
3476 flog = getfile(fn)
3477 try:
3477 try:
3478 return stringutil.binary(flog.read(ctx.filenode(fn)))
3478 return stringutil.binary(flog.read(ctx.filenode(fn)))
3479 except error.WdirUnsupported:
3479 except error.WdirUnsupported:
3480 return ctx[fn].isbinary()
3480 return ctx[fn].isbinary()
3481
3481
3482 fieldnamemap = {b'linenumber': b'lineno'}
3482 fieldnamemap = {b'linenumber': b'lineno'}
3483 if diff:
3483 if diff:
3484 iter = grepmod.difflinestates(pstates, states)
3484 iter = grepmod.difflinestates(pstates, states)
3485 else:
3485 else:
3486 iter = [(b'', l) for l in states]
3486 iter = [(b'', l) for l in states]
3487 for change, l in iter:
3487 for change, l in iter:
3488 fm.startitem()
3488 fm.startitem()
3489 fm.context(ctx=ctx)
3489 fm.context(ctx=ctx)
3490 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3490 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3491 fm.plain(uipathfn(fn), label=b'grep.filename')
3491 fm.plain(uipathfn(fn), label=b'grep.filename')
3492
3492
3493 cols = [
3493 cols = [
3494 (b'rev', b'%d', rev, not plaingrep, b''),
3494 (b'rev', b'%d', rev, not plaingrep, b''),
3495 (
3495 (
3496 b'linenumber',
3496 b'linenumber',
3497 b'%d',
3497 b'%d',
3498 l.linenum,
3498 l.linenum,
3499 opts.get(b'line_number'),
3499 opts.get(b'line_number'),
3500 b'',
3500 b'',
3501 ),
3501 ),
3502 ]
3502 ]
3503 if diff:
3503 if diff:
3504 cols.append(
3504 cols.append(
3505 (
3505 (
3506 b'change',
3506 b'change',
3507 b'%s',
3507 b'%s',
3508 change,
3508 change,
3509 True,
3509 True,
3510 b'grep.inserted '
3510 b'grep.inserted '
3511 if change == b'+'
3511 if change == b'+'
3512 else b'grep.deleted ',
3512 else b'grep.deleted ',
3513 )
3513 )
3514 )
3514 )
3515 cols.extend(
3515 cols.extend(
3516 [
3516 [
3517 (
3517 (
3518 b'user',
3518 b'user',
3519 b'%s',
3519 b'%s',
3520 formatuser(ctx.user()),
3520 formatuser(ctx.user()),
3521 opts.get(b'user'),
3521 opts.get(b'user'),
3522 b'',
3522 b'',
3523 ),
3523 ),
3524 (
3524 (
3525 b'date',
3525 b'date',
3526 b'%s',
3526 b'%s',
3527 fm.formatdate(ctx.date(), datefmt),
3527 fm.formatdate(ctx.date(), datefmt),
3528 opts.get(b'date'),
3528 opts.get(b'date'),
3529 b'',
3529 b'',
3530 ),
3530 ),
3531 ]
3531 ]
3532 )
3532 )
3533 for name, fmt, data, cond, extra_label in cols:
3533 for name, fmt, data, cond, extra_label in cols:
3534 if cond:
3534 if cond:
3535 fm.plain(sep, label=b'grep.sep')
3535 fm.plain(sep, label=b'grep.sep')
3536 field = fieldnamemap.get(name, name)
3536 field = fieldnamemap.get(name, name)
3537 label = extra_label + (b'grep.%s' % name)
3537 label = extra_label + (b'grep.%s' % name)
3538 fm.condwrite(cond, field, fmt, data, label=label)
3538 fm.condwrite(cond, field, fmt, data, label=label)
3539 if not opts.get(b'files_with_matches'):
3539 if not opts.get(b'files_with_matches'):
3540 fm.plain(sep, label=b'grep.sep')
3540 fm.plain(sep, label=b'grep.sep')
3541 if not opts.get(b'text') and binary():
3541 if not opts.get(b'text') and binary():
3542 fm.plain(_(b" Binary file matches"))
3542 fm.plain(_(b" Binary file matches"))
3543 else:
3543 else:
3544 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3544 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3545 fm.plain(eol)
3545 fm.plain(eol)
3546 found = True
3546 found = True
3547 if opts.get(b'files_with_matches'):
3547 if opts.get(b'files_with_matches'):
3548 break
3548 break
3549 return found
3549 return found
3550
3550
3551 def displaymatches(fm, l):
3551 def displaymatches(fm, l):
3552 p = 0
3552 p = 0
3553 for s, e in l.findpos(regexp):
3553 for s, e in l.findpos(regexp):
3554 if p < s:
3554 if p < s:
3555 fm.startitem()
3555 fm.startitem()
3556 fm.write(b'text', b'%s', l.line[p:s])
3556 fm.write(b'text', b'%s', l.line[p:s])
3557 fm.data(matched=False)
3557 fm.data(matched=False)
3558 fm.startitem()
3558 fm.startitem()
3559 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3559 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3560 fm.data(matched=True)
3560 fm.data(matched=True)
3561 p = e
3561 p = e
3562 if p < len(l.line):
3562 if p < len(l.line):
3563 fm.startitem()
3563 fm.startitem()
3564 fm.write(b'text', b'%s', l.line[p:])
3564 fm.write(b'text', b'%s', l.line[p:])
3565 fm.data(matched=False)
3565 fm.data(matched=False)
3566 fm.end()
3566 fm.end()
3567
3567
3568 found = False
3568 found = False
3569
3569
3570 wopts = logcmdutil.walkopts(
3570 wopts = logcmdutil.walkopts(
3571 pats=pats,
3571 pats=pats,
3572 opts=opts,
3572 opts=opts,
3573 revspec=opts[b'rev'],
3573 revspec=opts[b'rev'],
3574 include_pats=opts[b'include'],
3574 include_pats=opts[b'include'],
3575 exclude_pats=opts[b'exclude'],
3575 exclude_pats=opts[b'exclude'],
3576 follow=follow,
3576 follow=follow,
3577 force_changelog_traversal=all_files,
3577 force_changelog_traversal=all_files,
3578 filter_revisions_by_pats=not all_files,
3578 filter_revisions_by_pats=not all_files,
3579 )
3579 )
3580 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3580 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3581
3581
3582 ui.pager(b'grep')
3582 ui.pager(b'grep')
3583 fm = ui.formatter(b'grep', opts)
3583 fm = ui.formatter(b'grep', opts)
3584 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3584 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3585 r = display(fm, fn, ctx, pstates, states)
3585 r = display(fm, fn, ctx, pstates, states)
3586 found = found or r
3586 found = found or r
3587 if r and not diff and not all_files:
3587 if r and not diff and not all_files:
3588 searcher.skipfile(fn, ctx.rev())
3588 searcher.skipfile(fn, ctx.rev())
3589 fm.end()
3589 fm.end()
3590
3590
3591 return not found
3591 return not found
3592
3592
3593
3593
3594 @command(
3594 @command(
3595 b'heads',
3595 b'heads',
3596 [
3596 [
3597 (
3597 (
3598 b'r',
3598 b'r',
3599 b'rev',
3599 b'rev',
3600 b'',
3600 b'',
3601 _(b'show only heads which are descendants of STARTREV'),
3601 _(b'show only heads which are descendants of STARTREV'),
3602 _(b'STARTREV'),
3602 _(b'STARTREV'),
3603 ),
3603 ),
3604 (b't', b'topo', False, _(b'show topological heads only')),
3604 (b't', b'topo', False, _(b'show topological heads only')),
3605 (
3605 (
3606 b'a',
3606 b'a',
3607 b'active',
3607 b'active',
3608 False,
3608 False,
3609 _(b'show active branchheads only (DEPRECATED)'),
3609 _(b'show active branchheads only (DEPRECATED)'),
3610 ),
3610 ),
3611 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3611 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3612 ]
3612 ]
3613 + templateopts,
3613 + templateopts,
3614 _(b'[-ct] [-r STARTREV] [REV]...'),
3614 _(b'[-ct] [-r STARTREV] [REV]...'),
3615 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3615 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3616 intents={INTENT_READONLY},
3616 intents={INTENT_READONLY},
3617 )
3617 )
3618 def heads(ui, repo, *branchrevs, **opts):
3618 def heads(ui, repo, *branchrevs, **opts):
3619 """show branch heads
3619 """show branch heads
3620
3620
3621 With no arguments, show all open branch heads in the repository.
3621 With no arguments, show all open branch heads in the repository.
3622 Branch heads are changesets that have no descendants on the
3622 Branch heads are changesets that have no descendants on the
3623 same branch. They are where development generally takes place and
3623 same branch. They are where development generally takes place and
3624 are the usual targets for update and merge operations.
3624 are the usual targets for update and merge operations.
3625
3625
3626 If one or more REVs are given, only open branch heads on the
3626 If one or more REVs are given, only open branch heads on the
3627 branches associated with the specified changesets are shown. This
3627 branches associated with the specified changesets are shown. This
3628 means that you can use :hg:`heads .` to see the heads on the
3628 means that you can use :hg:`heads .` to see the heads on the
3629 currently checked-out branch.
3629 currently checked-out branch.
3630
3630
3631 If -c/--closed is specified, also show branch heads marked closed
3631 If -c/--closed is specified, also show branch heads marked closed
3632 (see :hg:`commit --close-branch`).
3632 (see :hg:`commit --close-branch`).
3633
3633
3634 If STARTREV is specified, only those heads that are descendants of
3634 If STARTREV is specified, only those heads that are descendants of
3635 STARTREV will be displayed.
3635 STARTREV will be displayed.
3636
3636
3637 If -t/--topo is specified, named branch mechanics will be ignored and only
3637 If -t/--topo is specified, named branch mechanics will be ignored and only
3638 topological heads (changesets with no children) will be shown.
3638 topological heads (changesets with no children) will be shown.
3639
3639
3640 Returns 0 if matching heads are found, 1 if not.
3640 Returns 0 if matching heads are found, 1 if not.
3641 """
3641 """
3642
3642
3643 opts = pycompat.byteskwargs(opts)
3643 opts = pycompat.byteskwargs(opts)
3644 start = None
3644 start = None
3645 rev = opts.get(b'rev')
3645 rev = opts.get(b'rev')
3646 if rev:
3646 if rev:
3647 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3647 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3648 start = scmutil.revsingle(repo, rev, None).node()
3648 start = scmutil.revsingle(repo, rev, None).node()
3649
3649
3650 if opts.get(b'topo'):
3650 if opts.get(b'topo'):
3651 heads = [repo[h] for h in repo.heads(start)]
3651 heads = [repo[h] for h in repo.heads(start)]
3652 else:
3652 else:
3653 heads = []
3653 heads = []
3654 for branch in repo.branchmap():
3654 for branch in repo.branchmap():
3655 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3655 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3656 heads = [repo[h] for h in heads]
3656 heads = [repo[h] for h in heads]
3657
3657
3658 if branchrevs:
3658 if branchrevs:
3659 branches = {
3659 branches = {
3660 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3660 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3661 }
3661 }
3662 heads = [h for h in heads if h.branch() in branches]
3662 heads = [h for h in heads if h.branch() in branches]
3663
3663
3664 if opts.get(b'active') and branchrevs:
3664 if opts.get(b'active') and branchrevs:
3665 dagheads = repo.heads(start)
3665 dagheads = repo.heads(start)
3666 heads = [h for h in heads if h.node() in dagheads]
3666 heads = [h for h in heads if h.node() in dagheads]
3667
3667
3668 if branchrevs:
3668 if branchrevs:
3669 haveheads = {h.branch() for h in heads}
3669 haveheads = {h.branch() for h in heads}
3670 if branches - haveheads:
3670 if branches - haveheads:
3671 headless = b', '.join(b for b in branches - haveheads)
3671 headless = b', '.join(b for b in branches - haveheads)
3672 msg = _(b'no open branch heads found on branches %s')
3672 msg = _(b'no open branch heads found on branches %s')
3673 if opts.get(b'rev'):
3673 if opts.get(b'rev'):
3674 msg += _(b' (started at %s)') % opts[b'rev']
3674 msg += _(b' (started at %s)') % opts[b'rev']
3675 ui.warn((msg + b'\n') % headless)
3675 ui.warn((msg + b'\n') % headless)
3676
3676
3677 if not heads:
3677 if not heads:
3678 return 1
3678 return 1
3679
3679
3680 ui.pager(b'heads')
3680 ui.pager(b'heads')
3681 heads = sorted(heads, key=lambda x: -(x.rev()))
3681 heads = sorted(heads, key=lambda x: -(x.rev()))
3682 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3682 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3683 for ctx in heads:
3683 for ctx in heads:
3684 displayer.show(ctx)
3684 displayer.show(ctx)
3685 displayer.close()
3685 displayer.close()
3686
3686
3687
3687
3688 @command(
3688 @command(
3689 b'help',
3689 b'help',
3690 [
3690 [
3691 (b'e', b'extension', None, _(b'show only help for extensions')),
3691 (b'e', b'extension', None, _(b'show only help for extensions')),
3692 (b'c', b'command', None, _(b'show only help for commands')),
3692 (b'c', b'command', None, _(b'show only help for commands')),
3693 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3693 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3694 (
3694 (
3695 b's',
3695 b's',
3696 b'system',
3696 b'system',
3697 [],
3697 [],
3698 _(b'show help for specific platform(s)'),
3698 _(b'show help for specific platform(s)'),
3699 _(b'PLATFORM'),
3699 _(b'PLATFORM'),
3700 ),
3700 ),
3701 ],
3701 ],
3702 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3702 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3703 helpcategory=command.CATEGORY_HELP,
3703 helpcategory=command.CATEGORY_HELP,
3704 norepo=True,
3704 norepo=True,
3705 intents={INTENT_READONLY},
3705 intents={INTENT_READONLY},
3706 )
3706 )
3707 def help_(ui, name=None, **opts):
3707 def help_(ui, name=None, **opts):
3708 """show help for a given topic or a help overview
3708 """show help for a given topic or a help overview
3709
3709
3710 With no arguments, print a list of commands with short help messages.
3710 With no arguments, print a list of commands with short help messages.
3711
3711
3712 Given a topic, extension, or command name, print help for that
3712 Given a topic, extension, or command name, print help for that
3713 topic.
3713 topic.
3714
3714
3715 Returns 0 if successful.
3715 Returns 0 if successful.
3716 """
3716 """
3717
3717
3718 keep = opts.get('system') or []
3718 keep = opts.get('system') or []
3719 if len(keep) == 0:
3719 if len(keep) == 0:
3720 if pycompat.sysplatform.startswith(b'win'):
3720 if pycompat.sysplatform.startswith(b'win'):
3721 keep.append(b'windows')
3721 keep.append(b'windows')
3722 elif pycompat.sysplatform == b'OpenVMS':
3722 elif pycompat.sysplatform == b'OpenVMS':
3723 keep.append(b'vms')
3723 keep.append(b'vms')
3724 elif pycompat.sysplatform == b'plan9':
3724 elif pycompat.sysplatform == b'plan9':
3725 keep.append(b'plan9')
3725 keep.append(b'plan9')
3726 else:
3726 else:
3727 keep.append(b'unix')
3727 keep.append(b'unix')
3728 keep.append(pycompat.sysplatform.lower())
3728 keep.append(pycompat.sysplatform.lower())
3729 if ui.verbose:
3729 if ui.verbose:
3730 keep.append(b'verbose')
3730 keep.append(b'verbose')
3731
3731
3732 commands = sys.modules[__name__]
3732 commands = sys.modules[__name__]
3733 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3733 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3734 ui.pager(b'help')
3734 ui.pager(b'help')
3735 ui.write(formatted)
3735 ui.write(formatted)
3736
3736
3737
3737
3738 @command(
3738 @command(
3739 b'identify|id',
3739 b'identify|id',
3740 [
3740 [
3741 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3741 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3742 (b'n', b'num', None, _(b'show local revision number')),
3742 (b'n', b'num', None, _(b'show local revision number')),
3743 (b'i', b'id', None, _(b'show global revision id')),
3743 (b'i', b'id', None, _(b'show global revision id')),
3744 (b'b', b'branch', None, _(b'show branch')),
3744 (b'b', b'branch', None, _(b'show branch')),
3745 (b't', b'tags', None, _(b'show tags')),
3745 (b't', b'tags', None, _(b'show tags')),
3746 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3746 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3747 ]
3747 ]
3748 + remoteopts
3748 + remoteopts
3749 + formatteropts,
3749 + formatteropts,
3750 _(b'[-nibtB] [-r REV] [SOURCE]'),
3750 _(b'[-nibtB] [-r REV] [SOURCE]'),
3751 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3751 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3752 optionalrepo=True,
3752 optionalrepo=True,
3753 intents={INTENT_READONLY},
3753 intents={INTENT_READONLY},
3754 )
3754 )
3755 def identify(
3755 def identify(
3756 ui,
3756 ui,
3757 repo,
3757 repo,
3758 source=None,
3758 source=None,
3759 rev=None,
3759 rev=None,
3760 num=None,
3760 num=None,
3761 id=None,
3761 id=None,
3762 branch=None,
3762 branch=None,
3763 tags=None,
3763 tags=None,
3764 bookmarks=None,
3764 bookmarks=None,
3765 **opts
3765 **opts
3766 ):
3766 ):
3767 """identify the working directory or specified revision
3767 """identify the working directory or specified revision
3768
3768
3769 Print a summary identifying the repository state at REV using one or
3769 Print a summary identifying the repository state at REV using one or
3770 two parent hash identifiers, followed by a "+" if the working
3770 two parent hash identifiers, followed by a "+" if the working
3771 directory has uncommitted changes, the branch name (if not default),
3771 directory has uncommitted changes, the branch name (if not default),
3772 a list of tags, and a list of bookmarks.
3772 a list of tags, and a list of bookmarks.
3773
3773
3774 When REV is not given, print a summary of the current state of the
3774 When REV is not given, print a summary of the current state of the
3775 repository including the working directory. Specify -r. to get information
3775 repository including the working directory. Specify -r. to get information
3776 of the working directory parent without scanning uncommitted changes.
3776 of the working directory parent without scanning uncommitted changes.
3777
3777
3778 Specifying a path to a repository root or Mercurial bundle will
3778 Specifying a path to a repository root or Mercurial bundle will
3779 cause lookup to operate on that repository/bundle.
3779 cause lookup to operate on that repository/bundle.
3780
3780
3781 .. container:: verbose
3781 .. container:: verbose
3782
3782
3783 Template:
3783 Template:
3784
3784
3785 The following keywords are supported in addition to the common template
3785 The following keywords are supported in addition to the common template
3786 keywords and functions. See also :hg:`help templates`.
3786 keywords and functions. See also :hg:`help templates`.
3787
3787
3788 :dirty: String. Character ``+`` denoting if the working directory has
3788 :dirty: String. Character ``+`` denoting if the working directory has
3789 uncommitted changes.
3789 uncommitted changes.
3790 :id: String. One or two nodes, optionally followed by ``+``.
3790 :id: String. One or two nodes, optionally followed by ``+``.
3791 :parents: List of strings. Parent nodes of the changeset.
3791 :parents: List of strings. Parent nodes of the changeset.
3792
3792
3793 Examples:
3793 Examples:
3794
3794
3795 - generate a build identifier for the working directory::
3795 - generate a build identifier for the working directory::
3796
3796
3797 hg id --id > build-id.dat
3797 hg id --id > build-id.dat
3798
3798
3799 - find the revision corresponding to a tag::
3799 - find the revision corresponding to a tag::
3800
3800
3801 hg id -n -r 1.3
3801 hg id -n -r 1.3
3802
3802
3803 - check the most recent revision of a remote repository::
3803 - check the most recent revision of a remote repository::
3804
3804
3805 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3805 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3806
3806
3807 See :hg:`log` for generating more information about specific revisions,
3807 See :hg:`log` for generating more information about specific revisions,
3808 including full hash identifiers.
3808 including full hash identifiers.
3809
3809
3810 Returns 0 if successful.
3810 Returns 0 if successful.
3811 """
3811 """
3812
3812
3813 opts = pycompat.byteskwargs(opts)
3813 opts = pycompat.byteskwargs(opts)
3814 if not repo and not source:
3814 if not repo and not source:
3815 raise error.InputError(
3815 raise error.InputError(
3816 _(b"there is no Mercurial repository here (.hg not found)")
3816 _(b"there is no Mercurial repository here (.hg not found)")
3817 )
3817 )
3818
3818
3819 default = not (num or id or branch or tags or bookmarks)
3819 default = not (num or id or branch or tags or bookmarks)
3820 output = []
3820 output = []
3821 revs = []
3821 revs = []
3822
3822
3823 if source:
3823 if source:
3824 source, branches = hg.parseurl(ui.expandpath(source))
3824 source, branches = hg.parseurl(ui.expandpath(source))
3825 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3825 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3826 repo = peer.local()
3826 repo = peer.local()
3827 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3827 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3828
3828
3829 fm = ui.formatter(b'identify', opts)
3829 fm = ui.formatter(b'identify', opts)
3830 fm.startitem()
3830 fm.startitem()
3831
3831
3832 if not repo:
3832 if not repo:
3833 if num or branch or tags:
3833 if num or branch or tags:
3834 raise error.InputError(
3834 raise error.InputError(
3835 _(b"can't query remote revision number, branch, or tags")
3835 _(b"can't query remote revision number, branch, or tags")
3836 )
3836 )
3837 if not rev and revs:
3837 if not rev and revs:
3838 rev = revs[0]
3838 rev = revs[0]
3839 if not rev:
3839 if not rev:
3840 rev = b"tip"
3840 rev = b"tip"
3841
3841
3842 remoterev = peer.lookup(rev)
3842 remoterev = peer.lookup(rev)
3843 hexrev = fm.hexfunc(remoterev)
3843 hexrev = fm.hexfunc(remoterev)
3844 if default or id:
3844 if default or id:
3845 output = [hexrev]
3845 output = [hexrev]
3846 fm.data(id=hexrev)
3846 fm.data(id=hexrev)
3847
3847
3848 @util.cachefunc
3848 @util.cachefunc
3849 def getbms():
3849 def getbms():
3850 bms = []
3850 bms = []
3851
3851
3852 if b'bookmarks' in peer.listkeys(b'namespaces'):
3852 if b'bookmarks' in peer.listkeys(b'namespaces'):
3853 hexremoterev = hex(remoterev)
3853 hexremoterev = hex(remoterev)
3854 bms = [
3854 bms = [
3855 bm
3855 bm
3856 for bm, bmr in pycompat.iteritems(
3856 for bm, bmr in pycompat.iteritems(
3857 peer.listkeys(b'bookmarks')
3857 peer.listkeys(b'bookmarks')
3858 )
3858 )
3859 if bmr == hexremoterev
3859 if bmr == hexremoterev
3860 ]
3860 ]
3861
3861
3862 return sorted(bms)
3862 return sorted(bms)
3863
3863
3864 if fm.isplain():
3864 if fm.isplain():
3865 if bookmarks:
3865 if bookmarks:
3866 output.extend(getbms())
3866 output.extend(getbms())
3867 elif default and not ui.quiet:
3867 elif default and not ui.quiet:
3868 # multiple bookmarks for a single parent separated by '/'
3868 # multiple bookmarks for a single parent separated by '/'
3869 bm = b'/'.join(getbms())
3869 bm = b'/'.join(getbms())
3870 if bm:
3870 if bm:
3871 output.append(bm)
3871 output.append(bm)
3872 else:
3872 else:
3873 fm.data(node=hex(remoterev))
3873 fm.data(node=hex(remoterev))
3874 if bookmarks or b'bookmarks' in fm.datahint():
3874 if bookmarks or b'bookmarks' in fm.datahint():
3875 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3875 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3876 else:
3876 else:
3877 if rev:
3877 if rev:
3878 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3878 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3879 ctx = scmutil.revsingle(repo, rev, None)
3879 ctx = scmutil.revsingle(repo, rev, None)
3880
3880
3881 if ctx.rev() is None:
3881 if ctx.rev() is None:
3882 ctx = repo[None]
3882 ctx = repo[None]
3883 parents = ctx.parents()
3883 parents = ctx.parents()
3884 taglist = []
3884 taglist = []
3885 for p in parents:
3885 for p in parents:
3886 taglist.extend(p.tags())
3886 taglist.extend(p.tags())
3887
3887
3888 dirty = b""
3888 dirty = b""
3889 if ctx.dirty(missing=True, merge=False, branch=False):
3889 if ctx.dirty(missing=True, merge=False, branch=False):
3890 dirty = b'+'
3890 dirty = b'+'
3891 fm.data(dirty=dirty)
3891 fm.data(dirty=dirty)
3892
3892
3893 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3893 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3894 if default or id:
3894 if default or id:
3895 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3895 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3896 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3896 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3897
3897
3898 if num:
3898 if num:
3899 numoutput = [b"%d" % p.rev() for p in parents]
3899 numoutput = [b"%d" % p.rev() for p in parents]
3900 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3900 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3901
3901
3902 fm.data(
3902 fm.data(
3903 parents=fm.formatlist(
3903 parents=fm.formatlist(
3904 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3904 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3905 )
3905 )
3906 )
3906 )
3907 else:
3907 else:
3908 hexoutput = fm.hexfunc(ctx.node())
3908 hexoutput = fm.hexfunc(ctx.node())
3909 if default or id:
3909 if default or id:
3910 output = [hexoutput]
3910 output = [hexoutput]
3911 fm.data(id=hexoutput)
3911 fm.data(id=hexoutput)
3912
3912
3913 if num:
3913 if num:
3914 output.append(pycompat.bytestr(ctx.rev()))
3914 output.append(pycompat.bytestr(ctx.rev()))
3915 taglist = ctx.tags()
3915 taglist = ctx.tags()
3916
3916
3917 if default and not ui.quiet:
3917 if default and not ui.quiet:
3918 b = ctx.branch()
3918 b = ctx.branch()
3919 if b != b'default':
3919 if b != b'default':
3920 output.append(b"(%s)" % b)
3920 output.append(b"(%s)" % b)
3921
3921
3922 # multiple tags for a single parent separated by '/'
3922 # multiple tags for a single parent separated by '/'
3923 t = b'/'.join(taglist)
3923 t = b'/'.join(taglist)
3924 if t:
3924 if t:
3925 output.append(t)
3925 output.append(t)
3926
3926
3927 # multiple bookmarks for a single parent separated by '/'
3927 # multiple bookmarks for a single parent separated by '/'
3928 bm = b'/'.join(ctx.bookmarks())
3928 bm = b'/'.join(ctx.bookmarks())
3929 if bm:
3929 if bm:
3930 output.append(bm)
3930 output.append(bm)
3931 else:
3931 else:
3932 if branch:
3932 if branch:
3933 output.append(ctx.branch())
3933 output.append(ctx.branch())
3934
3934
3935 if tags:
3935 if tags:
3936 output.extend(taglist)
3936 output.extend(taglist)
3937
3937
3938 if bookmarks:
3938 if bookmarks:
3939 output.extend(ctx.bookmarks())
3939 output.extend(ctx.bookmarks())
3940
3940
3941 fm.data(node=ctx.hex())
3941 fm.data(node=ctx.hex())
3942 fm.data(branch=ctx.branch())
3942 fm.data(branch=ctx.branch())
3943 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
3943 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
3944 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
3944 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
3945 fm.context(ctx=ctx)
3945 fm.context(ctx=ctx)
3946
3946
3947 fm.plain(b"%s\n" % b' '.join(output))
3947 fm.plain(b"%s\n" % b' '.join(output))
3948 fm.end()
3948 fm.end()
3949
3949
3950
3950
3951 @command(
3951 @command(
3952 b'import|patch',
3952 b'import|patch',
3953 [
3953 [
3954 (
3954 (
3955 b'p',
3955 b'p',
3956 b'strip',
3956 b'strip',
3957 1,
3957 1,
3958 _(
3958 _(
3959 b'directory strip option for patch. This has the same '
3959 b'directory strip option for patch. This has the same '
3960 b'meaning as the corresponding patch option'
3960 b'meaning as the corresponding patch option'
3961 ),
3961 ),
3962 _(b'NUM'),
3962 _(b'NUM'),
3963 ),
3963 ),
3964 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
3964 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
3965 (b'', b'secret', None, _(b'use the secret phase for committing')),
3965 (b'', b'secret', None, _(b'use the secret phase for committing')),
3966 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
3966 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
3967 (
3967 (
3968 b'f',
3968 b'f',
3969 b'force',
3969 b'force',
3970 None,
3970 None,
3971 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
3971 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
3972 ),
3972 ),
3973 (
3973 (
3974 b'',
3974 b'',
3975 b'no-commit',
3975 b'no-commit',
3976 None,
3976 None,
3977 _(b"don't commit, just update the working directory"),
3977 _(b"don't commit, just update the working directory"),
3978 ),
3978 ),
3979 (
3979 (
3980 b'',
3980 b'',
3981 b'bypass',
3981 b'bypass',
3982 None,
3982 None,
3983 _(b"apply patch without touching the working directory"),
3983 _(b"apply patch without touching the working directory"),
3984 ),
3984 ),
3985 (b'', b'partial', None, _(b'commit even if some hunks fail')),
3985 (b'', b'partial', None, _(b'commit even if some hunks fail')),
3986 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
3986 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
3987 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
3987 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
3988 (
3988 (
3989 b'',
3989 b'',
3990 b'import-branch',
3990 b'import-branch',
3991 None,
3991 None,
3992 _(b'use any branch information in patch (implied by --exact)'),
3992 _(b'use any branch information in patch (implied by --exact)'),
3993 ),
3993 ),
3994 ]
3994 ]
3995 + commitopts
3995 + commitopts
3996 + commitopts2
3996 + commitopts2
3997 + similarityopts,
3997 + similarityopts,
3998 _(b'[OPTION]... PATCH...'),
3998 _(b'[OPTION]... PATCH...'),
3999 helpcategory=command.CATEGORY_IMPORT_EXPORT,
3999 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4000 )
4000 )
4001 def import_(ui, repo, patch1=None, *patches, **opts):
4001 def import_(ui, repo, patch1=None, *patches, **opts):
4002 """import an ordered set of patches
4002 """import an ordered set of patches
4003
4003
4004 Import a list of patches and commit them individually (unless
4004 Import a list of patches and commit them individually (unless
4005 --no-commit is specified).
4005 --no-commit is specified).
4006
4006
4007 To read a patch from standard input (stdin), use "-" as the patch
4007 To read a patch from standard input (stdin), use "-" as the patch
4008 name. If a URL is specified, the patch will be downloaded from
4008 name. If a URL is specified, the patch will be downloaded from
4009 there.
4009 there.
4010
4010
4011 Import first applies changes to the working directory (unless
4011 Import first applies changes to the working directory (unless
4012 --bypass is specified), import will abort if there are outstanding
4012 --bypass is specified), import will abort if there are outstanding
4013 changes.
4013 changes.
4014
4014
4015 Use --bypass to apply and commit patches directly to the
4015 Use --bypass to apply and commit patches directly to the
4016 repository, without affecting the working directory. Without
4016 repository, without affecting the working directory. Without
4017 --exact, patches will be applied on top of the working directory
4017 --exact, patches will be applied on top of the working directory
4018 parent revision.
4018 parent revision.
4019
4019
4020 You can import a patch straight from a mail message. Even patches
4020 You can import a patch straight from a mail message. Even patches
4021 as attachments work (to use the body part, it must have type
4021 as attachments work (to use the body part, it must have type
4022 text/plain or text/x-patch). From and Subject headers of email
4022 text/plain or text/x-patch). From and Subject headers of email
4023 message are used as default committer and commit message. All
4023 message are used as default committer and commit message. All
4024 text/plain body parts before first diff are added to the commit
4024 text/plain body parts before first diff are added to the commit
4025 message.
4025 message.
4026
4026
4027 If the imported patch was generated by :hg:`export`, user and
4027 If the imported patch was generated by :hg:`export`, user and
4028 description from patch override values from message headers and
4028 description from patch override values from message headers and
4029 body. Values given on command line with -m/--message and -u/--user
4029 body. Values given on command line with -m/--message and -u/--user
4030 override these.
4030 override these.
4031
4031
4032 If --exact is specified, import will set the working directory to
4032 If --exact is specified, import will set the working directory to
4033 the parent of each patch before applying it, and will abort if the
4033 the parent of each patch before applying it, and will abort if the
4034 resulting changeset has a different ID than the one recorded in
4034 resulting changeset has a different ID than the one recorded in
4035 the patch. This will guard against various ways that portable
4035 the patch. This will guard against various ways that portable
4036 patch formats and mail systems might fail to transfer Mercurial
4036 patch formats and mail systems might fail to transfer Mercurial
4037 data or metadata. See :hg:`bundle` for lossless transmission.
4037 data or metadata. See :hg:`bundle` for lossless transmission.
4038
4038
4039 Use --partial to ensure a changeset will be created from the patch
4039 Use --partial to ensure a changeset will be created from the patch
4040 even if some hunks fail to apply. Hunks that fail to apply will be
4040 even if some hunks fail to apply. Hunks that fail to apply will be
4041 written to a <target-file>.rej file. Conflicts can then be resolved
4041 written to a <target-file>.rej file. Conflicts can then be resolved
4042 by hand before :hg:`commit --amend` is run to update the created
4042 by hand before :hg:`commit --amend` is run to update the created
4043 changeset. This flag exists to let people import patches that
4043 changeset. This flag exists to let people import patches that
4044 partially apply without losing the associated metadata (author,
4044 partially apply without losing the associated metadata (author,
4045 date, description, ...).
4045 date, description, ...).
4046
4046
4047 .. note::
4047 .. note::
4048
4048
4049 When no hunks apply cleanly, :hg:`import --partial` will create
4049 When no hunks apply cleanly, :hg:`import --partial` will create
4050 an empty changeset, importing only the patch metadata.
4050 an empty changeset, importing only the patch metadata.
4051
4051
4052 With -s/--similarity, hg will attempt to discover renames and
4052 With -s/--similarity, hg will attempt to discover renames and
4053 copies in the patch in the same way as :hg:`addremove`.
4053 copies in the patch in the same way as :hg:`addremove`.
4054
4054
4055 It is possible to use external patch programs to perform the patch
4055 It is possible to use external patch programs to perform the patch
4056 by setting the ``ui.patch`` configuration option. For the default
4056 by setting the ``ui.patch`` configuration option. For the default
4057 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4057 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4058 See :hg:`help config` for more information about configuration
4058 See :hg:`help config` for more information about configuration
4059 files and how to use these options.
4059 files and how to use these options.
4060
4060
4061 See :hg:`help dates` for a list of formats valid for -d/--date.
4061 See :hg:`help dates` for a list of formats valid for -d/--date.
4062
4062
4063 .. container:: verbose
4063 .. container:: verbose
4064
4064
4065 Examples:
4065 Examples:
4066
4066
4067 - import a traditional patch from a website and detect renames::
4067 - import a traditional patch from a website and detect renames::
4068
4068
4069 hg import -s 80 http://example.com/bugfix.patch
4069 hg import -s 80 http://example.com/bugfix.patch
4070
4070
4071 - import a changeset from an hgweb server::
4071 - import a changeset from an hgweb server::
4072
4072
4073 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4073 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4074
4074
4075 - import all the patches in an Unix-style mbox::
4075 - import all the patches in an Unix-style mbox::
4076
4076
4077 hg import incoming-patches.mbox
4077 hg import incoming-patches.mbox
4078
4078
4079 - import patches from stdin::
4079 - import patches from stdin::
4080
4080
4081 hg import -
4081 hg import -
4082
4082
4083 - attempt to exactly restore an exported changeset (not always
4083 - attempt to exactly restore an exported changeset (not always
4084 possible)::
4084 possible)::
4085
4085
4086 hg import --exact proposed-fix.patch
4086 hg import --exact proposed-fix.patch
4087
4087
4088 - use an external tool to apply a patch which is too fuzzy for
4088 - use an external tool to apply a patch which is too fuzzy for
4089 the default internal tool.
4089 the default internal tool.
4090
4090
4091 hg import --config ui.patch="patch --merge" fuzzy.patch
4091 hg import --config ui.patch="patch --merge" fuzzy.patch
4092
4092
4093 - change the default fuzzing from 2 to a less strict 7
4093 - change the default fuzzing from 2 to a less strict 7
4094
4094
4095 hg import --config ui.fuzz=7 fuzz.patch
4095 hg import --config ui.fuzz=7 fuzz.patch
4096
4096
4097 Returns 0 on success, 1 on partial success (see --partial).
4097 Returns 0 on success, 1 on partial success (see --partial).
4098 """
4098 """
4099
4099
4100 cmdutil.check_incompatible_arguments(
4100 cmdutil.check_incompatible_arguments(
4101 opts, 'no_commit', ['bypass', 'secret']
4101 opts, 'no_commit', ['bypass', 'secret']
4102 )
4102 )
4103 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4103 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4104 opts = pycompat.byteskwargs(opts)
4104 opts = pycompat.byteskwargs(opts)
4105 if not patch1:
4105 if not patch1:
4106 raise error.InputError(_(b'need at least one patch to import'))
4106 raise error.InputError(_(b'need at least one patch to import'))
4107
4107
4108 patches = (patch1,) + patches
4108 patches = (patch1,) + patches
4109
4109
4110 date = opts.get(b'date')
4110 date = opts.get(b'date')
4111 if date:
4111 if date:
4112 opts[b'date'] = dateutil.parsedate(date)
4112 opts[b'date'] = dateutil.parsedate(date)
4113
4113
4114 exact = opts.get(b'exact')
4114 exact = opts.get(b'exact')
4115 update = not opts.get(b'bypass')
4115 update = not opts.get(b'bypass')
4116 try:
4116 try:
4117 sim = float(opts.get(b'similarity') or 0)
4117 sim = float(opts.get(b'similarity') or 0)
4118 except ValueError:
4118 except ValueError:
4119 raise error.InputError(_(b'similarity must be a number'))
4119 raise error.InputError(_(b'similarity must be a number'))
4120 if sim < 0 or sim > 100:
4120 if sim < 0 or sim > 100:
4121 raise error.InputError(_(b'similarity must be between 0 and 100'))
4121 raise error.InputError(_(b'similarity must be between 0 and 100'))
4122 if sim and not update:
4122 if sim and not update:
4123 raise error.InputError(_(b'cannot use --similarity with --bypass'))
4123 raise error.InputError(_(b'cannot use --similarity with --bypass'))
4124
4124
4125 base = opts[b"base"]
4125 base = opts[b"base"]
4126 msgs = []
4126 msgs = []
4127 ret = 0
4127 ret = 0
4128
4128
4129 with repo.wlock():
4129 with repo.wlock():
4130 if update:
4130 if update:
4131 cmdutil.checkunfinished(repo)
4131 cmdutil.checkunfinished(repo)
4132 if exact or not opts.get(b'force'):
4132 if exact or not opts.get(b'force'):
4133 cmdutil.bailifchanged(repo)
4133 cmdutil.bailifchanged(repo)
4134
4134
4135 if not opts.get(b'no_commit'):
4135 if not opts.get(b'no_commit'):
4136 lock = repo.lock
4136 lock = repo.lock
4137 tr = lambda: repo.transaction(b'import')
4137 tr = lambda: repo.transaction(b'import')
4138 dsguard = util.nullcontextmanager
4138 dsguard = util.nullcontextmanager
4139 else:
4139 else:
4140 lock = util.nullcontextmanager
4140 lock = util.nullcontextmanager
4141 tr = util.nullcontextmanager
4141 tr = util.nullcontextmanager
4142 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4142 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4143 with lock(), tr(), dsguard():
4143 with lock(), tr(), dsguard():
4144 parents = repo[None].parents()
4144 parents = repo[None].parents()
4145 for patchurl in patches:
4145 for patchurl in patches:
4146 if patchurl == b'-':
4146 if patchurl == b'-':
4147 ui.status(_(b'applying patch from stdin\n'))
4147 ui.status(_(b'applying patch from stdin\n'))
4148 patchfile = ui.fin
4148 patchfile = ui.fin
4149 patchurl = b'stdin' # for error message
4149 patchurl = b'stdin' # for error message
4150 else:
4150 else:
4151 patchurl = os.path.join(base, patchurl)
4151 patchurl = os.path.join(base, patchurl)
4152 ui.status(_(b'applying %s\n') % patchurl)
4152 ui.status(_(b'applying %s\n') % patchurl)
4153 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4153 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4154
4154
4155 haspatch = False
4155 haspatch = False
4156 for hunk in patch.split(patchfile):
4156 for hunk in patch.split(patchfile):
4157 with patch.extract(ui, hunk) as patchdata:
4157 with patch.extract(ui, hunk) as patchdata:
4158 msg, node, rej = cmdutil.tryimportone(
4158 msg, node, rej = cmdutil.tryimportone(
4159 ui, repo, patchdata, parents, opts, msgs, hg.clean
4159 ui, repo, patchdata, parents, opts, msgs, hg.clean
4160 )
4160 )
4161 if msg:
4161 if msg:
4162 haspatch = True
4162 haspatch = True
4163 ui.note(msg + b'\n')
4163 ui.note(msg + b'\n')
4164 if update or exact:
4164 if update or exact:
4165 parents = repo[None].parents()
4165 parents = repo[None].parents()
4166 else:
4166 else:
4167 parents = [repo[node]]
4167 parents = [repo[node]]
4168 if rej:
4168 if rej:
4169 ui.write_err(_(b"patch applied partially\n"))
4169 ui.write_err(_(b"patch applied partially\n"))
4170 ui.write_err(
4170 ui.write_err(
4171 _(
4171 _(
4172 b"(fix the .rej files and run "
4172 b"(fix the .rej files and run "
4173 b"`hg commit --amend`)\n"
4173 b"`hg commit --amend`)\n"
4174 )
4174 )
4175 )
4175 )
4176 ret = 1
4176 ret = 1
4177 break
4177 break
4178
4178
4179 if not haspatch:
4179 if not haspatch:
4180 raise error.InputError(_(b'%s: no diffs found') % patchurl)
4180 raise error.InputError(_(b'%s: no diffs found') % patchurl)
4181
4181
4182 if msgs:
4182 if msgs:
4183 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4183 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4184 return ret
4184 return ret
4185
4185
4186
4186
4187 @command(
4187 @command(
4188 b'incoming|in',
4188 b'incoming|in',
4189 [
4189 [
4190 (
4190 (
4191 b'f',
4191 b'f',
4192 b'force',
4192 b'force',
4193 None,
4193 None,
4194 _(b'run even if remote repository is unrelated'),
4194 _(b'run even if remote repository is unrelated'),
4195 ),
4195 ),
4196 (b'n', b'newest-first', None, _(b'show newest record first')),
4196 (b'n', b'newest-first', None, _(b'show newest record first')),
4197 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4197 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4198 (
4198 (
4199 b'r',
4199 b'r',
4200 b'rev',
4200 b'rev',
4201 [],
4201 [],
4202 _(b'a remote changeset intended to be added'),
4202 _(b'a remote changeset intended to be added'),
4203 _(b'REV'),
4203 _(b'REV'),
4204 ),
4204 ),
4205 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4205 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4206 (
4206 (
4207 b'b',
4207 b'b',
4208 b'branch',
4208 b'branch',
4209 [],
4209 [],
4210 _(b'a specific branch you would like to pull'),
4210 _(b'a specific branch you would like to pull'),
4211 _(b'BRANCH'),
4211 _(b'BRANCH'),
4212 ),
4212 ),
4213 ]
4213 ]
4214 + logopts
4214 + logopts
4215 + remoteopts
4215 + remoteopts
4216 + subrepoopts,
4216 + subrepoopts,
4217 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4217 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4218 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4218 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4219 )
4219 )
4220 def incoming(ui, repo, source=b"default", **opts):
4220 def incoming(ui, repo, source=b"default", **opts):
4221 """show new changesets found in source
4221 """show new changesets found in source
4222
4222
4223 Show new changesets found in the specified path/URL or the default
4223 Show new changesets found in the specified path/URL or the default
4224 pull location. These are the changesets that would have been pulled
4224 pull location. These are the changesets that would have been pulled
4225 by :hg:`pull` at the time you issued this command.
4225 by :hg:`pull` at the time you issued this command.
4226
4226
4227 See pull for valid source format details.
4227 See pull for valid source format details.
4228
4228
4229 .. container:: verbose
4229 .. container:: verbose
4230
4230
4231 With -B/--bookmarks, the result of bookmark comparison between
4231 With -B/--bookmarks, the result of bookmark comparison between
4232 local and remote repositories is displayed. With -v/--verbose,
4232 local and remote repositories is displayed. With -v/--verbose,
4233 status is also displayed for each bookmark like below::
4233 status is also displayed for each bookmark like below::
4234
4234
4235 BM1 01234567890a added
4235 BM1 01234567890a added
4236 BM2 1234567890ab advanced
4236 BM2 1234567890ab advanced
4237 BM3 234567890abc diverged
4237 BM3 234567890abc diverged
4238 BM4 34567890abcd changed
4238 BM4 34567890abcd changed
4239
4239
4240 The action taken locally when pulling depends on the
4240 The action taken locally when pulling depends on the
4241 status of each bookmark:
4241 status of each bookmark:
4242
4242
4243 :``added``: pull will create it
4243 :``added``: pull will create it
4244 :``advanced``: pull will update it
4244 :``advanced``: pull will update it
4245 :``diverged``: pull will create a divergent bookmark
4245 :``diverged``: pull will create a divergent bookmark
4246 :``changed``: result depends on remote changesets
4246 :``changed``: result depends on remote changesets
4247
4247
4248 From the point of view of pulling behavior, bookmark
4248 From the point of view of pulling behavior, bookmark
4249 existing only in the remote repository are treated as ``added``,
4249 existing only in the remote repository are treated as ``added``,
4250 even if it is in fact locally deleted.
4250 even if it is in fact locally deleted.
4251
4251
4252 .. container:: verbose
4252 .. container:: verbose
4253
4253
4254 For remote repository, using --bundle avoids downloading the
4254 For remote repository, using --bundle avoids downloading the
4255 changesets twice if the incoming is followed by a pull.
4255 changesets twice if the incoming is followed by a pull.
4256
4256
4257 Examples:
4257 Examples:
4258
4258
4259 - show incoming changes with patches and full description::
4259 - show incoming changes with patches and full description::
4260
4260
4261 hg incoming -vp
4261 hg incoming -vp
4262
4262
4263 - show incoming changes excluding merges, store a bundle::
4263 - show incoming changes excluding merges, store a bundle::
4264
4264
4265 hg in -vpM --bundle incoming.hg
4265 hg in -vpM --bundle incoming.hg
4266 hg pull incoming.hg
4266 hg pull incoming.hg
4267
4267
4268 - briefly list changes inside a bundle::
4268 - briefly list changes inside a bundle::
4269
4269
4270 hg in changes.hg -T "{desc|firstline}\\n"
4270 hg in changes.hg -T "{desc|firstline}\\n"
4271
4271
4272 Returns 0 if there are incoming changes, 1 otherwise.
4272 Returns 0 if there are incoming changes, 1 otherwise.
4273 """
4273 """
4274 opts = pycompat.byteskwargs(opts)
4274 opts = pycompat.byteskwargs(opts)
4275 if opts.get(b'graph'):
4275 if opts.get(b'graph'):
4276 logcmdutil.checkunsupportedgraphflags([], opts)
4276 logcmdutil.checkunsupportedgraphflags([], opts)
4277
4277
4278 def display(other, chlist, displayer):
4278 def display(other, chlist, displayer):
4279 revdag = logcmdutil.graphrevs(other, chlist, opts)
4279 revdag = logcmdutil.graphrevs(other, chlist, opts)
4280 logcmdutil.displaygraph(
4280 logcmdutil.displaygraph(
4281 ui, repo, revdag, displayer, graphmod.asciiedges
4281 ui, repo, revdag, displayer, graphmod.asciiedges
4282 )
4282 )
4283
4283
4284 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4284 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4285 return 0
4285 return 0
4286
4286
4287 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4287 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4288
4288
4289 if opts.get(b'bookmarks'):
4289 if opts.get(b'bookmarks'):
4290 source, branches = hg.parseurl(
4290 source, branches = hg.parseurl(
4291 ui.expandpath(source), opts.get(b'branch')
4291 ui.expandpath(source), opts.get(b'branch')
4292 )
4292 )
4293 other = hg.peer(repo, opts, source)
4293 other = hg.peer(repo, opts, source)
4294 if b'bookmarks' not in other.listkeys(b'namespaces'):
4294 if b'bookmarks' not in other.listkeys(b'namespaces'):
4295 ui.warn(_(b"remote doesn't support bookmarks\n"))
4295 ui.warn(_(b"remote doesn't support bookmarks\n"))
4296 return 0
4296 return 0
4297 ui.pager(b'incoming')
4297 ui.pager(b'incoming')
4298 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4298 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4299 return bookmarks.incoming(ui, repo, other)
4299 return bookmarks.incoming(ui, repo, other)
4300
4300
4301 repo._subtoppath = ui.expandpath(source)
4301 repo._subtoppath = ui.expandpath(source)
4302 try:
4302 try:
4303 return hg.incoming(ui, repo, source, opts)
4303 return hg.incoming(ui, repo, source, opts)
4304 finally:
4304 finally:
4305 del repo._subtoppath
4305 del repo._subtoppath
4306
4306
4307
4307
4308 @command(
4308 @command(
4309 b'init',
4309 b'init',
4310 remoteopts,
4310 remoteopts,
4311 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4311 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4312 helpcategory=command.CATEGORY_REPO_CREATION,
4312 helpcategory=command.CATEGORY_REPO_CREATION,
4313 helpbasic=True,
4313 helpbasic=True,
4314 norepo=True,
4314 norepo=True,
4315 )
4315 )
4316 def init(ui, dest=b".", **opts):
4316 def init(ui, dest=b".", **opts):
4317 """create a new repository in the given directory
4317 """create a new repository in the given directory
4318
4318
4319 Initialize a new repository in the given directory. If the given
4319 Initialize a new repository in the given directory. If the given
4320 directory does not exist, it will be created.
4320 directory does not exist, it will be created.
4321
4321
4322 If no directory is given, the current directory is used.
4322 If no directory is given, the current directory is used.
4323
4323
4324 It is possible to specify an ``ssh://`` URL as the destination.
4324 It is possible to specify an ``ssh://`` URL as the destination.
4325 See :hg:`help urls` for more information.
4325 See :hg:`help urls` for more information.
4326
4326
4327 Returns 0 on success.
4327 Returns 0 on success.
4328 """
4328 """
4329 opts = pycompat.byteskwargs(opts)
4329 opts = pycompat.byteskwargs(opts)
4330 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4330 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4331
4331
4332
4332
4333 @command(
4333 @command(
4334 b'locate',
4334 b'locate',
4335 [
4335 [
4336 (
4336 (
4337 b'r',
4337 b'r',
4338 b'rev',
4338 b'rev',
4339 b'',
4339 b'',
4340 _(b'search the repository as it is in REV'),
4340 _(b'search the repository as it is in REV'),
4341 _(b'REV'),
4341 _(b'REV'),
4342 ),
4342 ),
4343 (
4343 (
4344 b'0',
4344 b'0',
4345 b'print0',
4345 b'print0',
4346 None,
4346 None,
4347 _(b'end filenames with NUL, for use with xargs'),
4347 _(b'end filenames with NUL, for use with xargs'),
4348 ),
4348 ),
4349 (
4349 (
4350 b'f',
4350 b'f',
4351 b'fullpath',
4351 b'fullpath',
4352 None,
4352 None,
4353 _(b'print complete paths from the filesystem root'),
4353 _(b'print complete paths from the filesystem root'),
4354 ),
4354 ),
4355 ]
4355 ]
4356 + walkopts,
4356 + walkopts,
4357 _(b'[OPTION]... [PATTERN]...'),
4357 _(b'[OPTION]... [PATTERN]...'),
4358 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4358 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4359 )
4359 )
4360 def locate(ui, repo, *pats, **opts):
4360 def locate(ui, repo, *pats, **opts):
4361 """locate files matching specific patterns (DEPRECATED)
4361 """locate files matching specific patterns (DEPRECATED)
4362
4362
4363 Print files under Mercurial control in the working directory whose
4363 Print files under Mercurial control in the working directory whose
4364 names match the given patterns.
4364 names match the given patterns.
4365
4365
4366 By default, this command searches all directories in the working
4366 By default, this command searches all directories in the working
4367 directory. To search just the current directory and its
4367 directory. To search just the current directory and its
4368 subdirectories, use "--include .".
4368 subdirectories, use "--include .".
4369
4369
4370 If no patterns are given to match, this command prints the names
4370 If no patterns are given to match, this command prints the names
4371 of all files under Mercurial control in the working directory.
4371 of all files under Mercurial control in the working directory.
4372
4372
4373 If you want to feed the output of this command into the "xargs"
4373 If you want to feed the output of this command into the "xargs"
4374 command, use the -0 option to both this command and "xargs". This
4374 command, use the -0 option to both this command and "xargs". This
4375 will avoid the problem of "xargs" treating single filenames that
4375 will avoid the problem of "xargs" treating single filenames that
4376 contain whitespace as multiple filenames.
4376 contain whitespace as multiple filenames.
4377
4377
4378 See :hg:`help files` for a more versatile command.
4378 See :hg:`help files` for a more versatile command.
4379
4379
4380 Returns 0 if a match is found, 1 otherwise.
4380 Returns 0 if a match is found, 1 otherwise.
4381 """
4381 """
4382 opts = pycompat.byteskwargs(opts)
4382 opts = pycompat.byteskwargs(opts)
4383 if opts.get(b'print0'):
4383 if opts.get(b'print0'):
4384 end = b'\0'
4384 end = b'\0'
4385 else:
4385 else:
4386 end = b'\n'
4386 end = b'\n'
4387 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4387 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4388
4388
4389 ret = 1
4389 ret = 1
4390 m = scmutil.match(
4390 m = scmutil.match(
4391 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4391 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4392 )
4392 )
4393
4393
4394 ui.pager(b'locate')
4394 ui.pager(b'locate')
4395 if ctx.rev() is None:
4395 if ctx.rev() is None:
4396 # When run on the working copy, "locate" includes removed files, so
4396 # When run on the working copy, "locate" includes removed files, so
4397 # we get the list of files from the dirstate.
4397 # we get the list of files from the dirstate.
4398 filesgen = sorted(repo.dirstate.matches(m))
4398 filesgen = sorted(repo.dirstate.matches(m))
4399 else:
4399 else:
4400 filesgen = ctx.matches(m)
4400 filesgen = ctx.matches(m)
4401 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4401 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4402 for abs in filesgen:
4402 for abs in filesgen:
4403 if opts.get(b'fullpath'):
4403 if opts.get(b'fullpath'):
4404 ui.write(repo.wjoin(abs), end)
4404 ui.write(repo.wjoin(abs), end)
4405 else:
4405 else:
4406 ui.write(uipathfn(abs), end)
4406 ui.write(uipathfn(abs), end)
4407 ret = 0
4407 ret = 0
4408
4408
4409 return ret
4409 return ret
4410
4410
4411
4411
4412 @command(
4412 @command(
4413 b'log|history',
4413 b'log|history',
4414 [
4414 [
4415 (
4415 (
4416 b'f',
4416 b'f',
4417 b'follow',
4417 b'follow',
4418 None,
4418 None,
4419 _(
4419 _(
4420 b'follow changeset history, or file history across copies and renames'
4420 b'follow changeset history, or file history across copies and renames'
4421 ),
4421 ),
4422 ),
4422 ),
4423 (
4423 (
4424 b'',
4424 b'',
4425 b'follow-first',
4425 b'follow-first',
4426 None,
4426 None,
4427 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4427 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4428 ),
4428 ),
4429 (
4429 (
4430 b'd',
4430 b'd',
4431 b'date',
4431 b'date',
4432 b'',
4432 b'',
4433 _(b'show revisions matching date spec'),
4433 _(b'show revisions matching date spec'),
4434 _(b'DATE'),
4434 _(b'DATE'),
4435 ),
4435 ),
4436 (b'C', b'copies', None, _(b'show copied files')),
4436 (b'C', b'copies', None, _(b'show copied files')),
4437 (
4437 (
4438 b'k',
4438 b'k',
4439 b'keyword',
4439 b'keyword',
4440 [],
4440 [],
4441 _(b'do case-insensitive search for a given text'),
4441 _(b'do case-insensitive search for a given text'),
4442 _(b'TEXT'),
4442 _(b'TEXT'),
4443 ),
4443 ),
4444 (
4444 (
4445 b'r',
4445 b'r',
4446 b'rev',
4446 b'rev',
4447 [],
4447 [],
4448 _(b'revisions to select or follow from'),
4448 _(b'revisions to select or follow from'),
4449 _(b'REV'),
4449 _(b'REV'),
4450 ),
4450 ),
4451 (
4451 (
4452 b'L',
4452 b'L',
4453 b'line-range',
4453 b'line-range',
4454 [],
4454 [],
4455 _(b'follow line range of specified file (EXPERIMENTAL)'),
4455 _(b'follow line range of specified file (EXPERIMENTAL)'),
4456 _(b'FILE,RANGE'),
4456 _(b'FILE,RANGE'),
4457 ),
4457 ),
4458 (
4458 (
4459 b'',
4459 b'',
4460 b'removed',
4460 b'removed',
4461 None,
4461 None,
4462 _(b'include revisions where files were removed'),
4462 _(b'include revisions where files were removed'),
4463 ),
4463 ),
4464 (
4464 (
4465 b'm',
4465 b'm',
4466 b'only-merges',
4466 b'only-merges',
4467 None,
4467 None,
4468 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4468 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4469 ),
4469 ),
4470 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4470 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4471 (
4471 (
4472 b'',
4472 b'',
4473 b'only-branch',
4473 b'only-branch',
4474 [],
4474 [],
4475 _(
4475 _(
4476 b'show only changesets within the given named branch (DEPRECATED)'
4476 b'show only changesets within the given named branch (DEPRECATED)'
4477 ),
4477 ),
4478 _(b'BRANCH'),
4478 _(b'BRANCH'),
4479 ),
4479 ),
4480 (
4480 (
4481 b'b',
4481 b'b',
4482 b'branch',
4482 b'branch',
4483 [],
4483 [],
4484 _(b'show changesets within the given named branch'),
4484 _(b'show changesets within the given named branch'),
4485 _(b'BRANCH'),
4485 _(b'BRANCH'),
4486 ),
4486 ),
4487 (
4487 (
4488 b'B',
4488 b'B',
4489 b'bookmark',
4489 b'bookmark',
4490 [],
4490 [],
4491 _(b"show changesets within the given bookmark"),
4491 _(b"show changesets within the given bookmark"),
4492 _(b'BOOKMARK'),
4492 _(b'BOOKMARK'),
4493 ),
4493 ),
4494 (
4494 (
4495 b'P',
4495 b'P',
4496 b'prune',
4496 b'prune',
4497 [],
4497 [],
4498 _(b'do not display revision or any of its ancestors'),
4498 _(b'do not display revision or any of its ancestors'),
4499 _(b'REV'),
4499 _(b'REV'),
4500 ),
4500 ),
4501 ]
4501 ]
4502 + logopts
4502 + logopts
4503 + walkopts,
4503 + walkopts,
4504 _(b'[OPTION]... [FILE]'),
4504 _(b'[OPTION]... [FILE]'),
4505 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4505 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4506 helpbasic=True,
4506 helpbasic=True,
4507 inferrepo=True,
4507 inferrepo=True,
4508 intents={INTENT_READONLY},
4508 intents={INTENT_READONLY},
4509 )
4509 )
4510 def log(ui, repo, *pats, **opts):
4510 def log(ui, repo, *pats, **opts):
4511 """show revision history of entire repository or files
4511 """show revision history of entire repository or files
4512
4512
4513 Print the revision history of the specified files or the entire
4513 Print the revision history of the specified files or the entire
4514 project.
4514 project.
4515
4515
4516 If no revision range is specified, the default is ``tip:0`` unless
4516 If no revision range is specified, the default is ``tip:0`` unless
4517 --follow is set.
4517 --follow is set.
4518
4518
4519 File history is shown without following rename or copy history of
4519 File history is shown without following rename or copy history of
4520 files. Use -f/--follow with a filename to follow history across
4520 files. Use -f/--follow with a filename to follow history across
4521 renames and copies. --follow without a filename will only show
4521 renames and copies. --follow without a filename will only show
4522 ancestors of the starting revisions. The starting revisions can be
4522 ancestors of the starting revisions. The starting revisions can be
4523 specified by -r/--rev, which default to the working directory parent.
4523 specified by -r/--rev, which default to the working directory parent.
4524
4524
4525 By default this command prints revision number and changeset id,
4525 By default this command prints revision number and changeset id,
4526 tags, non-trivial parents, user, date and time, and a summary for
4526 tags, non-trivial parents, user, date and time, and a summary for
4527 each commit. When the -v/--verbose switch is used, the list of
4527 each commit. When the -v/--verbose switch is used, the list of
4528 changed files and full commit message are shown.
4528 changed files and full commit message are shown.
4529
4529
4530 With --graph the revisions are shown as an ASCII art DAG with the most
4530 With --graph the revisions are shown as an ASCII art DAG with the most
4531 recent changeset at the top.
4531 recent changeset at the top.
4532 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4532 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4533 involved in an unresolved merge conflict, '_' closes a branch,
4533 involved in an unresolved merge conflict, '_' closes a branch,
4534 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4534 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4535 changeset from the lines below is a parent of the 'o' merge on the same
4535 changeset from the lines below is a parent of the 'o' merge on the same
4536 line.
4536 line.
4537 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4537 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4538 of a '|' indicates one or more revisions in a path are omitted.
4538 of a '|' indicates one or more revisions in a path are omitted.
4539
4539
4540 .. container:: verbose
4540 .. container:: verbose
4541
4541
4542 Use -L/--line-range FILE,M:N options to follow the history of lines
4542 Use -L/--line-range FILE,M:N options to follow the history of lines
4543 from M to N in FILE. With -p/--patch only diff hunks affecting
4543 from M to N in FILE. With -p/--patch only diff hunks affecting
4544 specified line range will be shown. This option requires --follow;
4544 specified line range will be shown. This option requires --follow;
4545 it can be specified multiple times. Currently, this option is not
4545 it can be specified multiple times. Currently, this option is not
4546 compatible with --graph. This option is experimental.
4546 compatible with --graph. This option is experimental.
4547
4547
4548 .. note::
4548 .. note::
4549
4549
4550 :hg:`log --patch` may generate unexpected diff output for merge
4550 :hg:`log --patch` may generate unexpected diff output for merge
4551 changesets, as it will only compare the merge changeset against
4551 changesets, as it will only compare the merge changeset against
4552 its first parent. Also, only files different from BOTH parents
4552 its first parent. Also, only files different from BOTH parents
4553 will appear in files:.
4553 will appear in files:.
4554
4554
4555 .. note::
4555 .. note::
4556
4556
4557 For performance reasons, :hg:`log FILE` may omit duplicate changes
4557 For performance reasons, :hg:`log FILE` may omit duplicate changes
4558 made on branches and will not show removals or mode changes. To
4558 made on branches and will not show removals or mode changes. To
4559 see all such changes, use the --removed switch.
4559 see all such changes, use the --removed switch.
4560
4560
4561 .. container:: verbose
4561 .. container:: verbose
4562
4562
4563 .. note::
4563 .. note::
4564
4564
4565 The history resulting from -L/--line-range options depends on diff
4565 The history resulting from -L/--line-range options depends on diff
4566 options; for instance if white-spaces are ignored, respective changes
4566 options; for instance if white-spaces are ignored, respective changes
4567 with only white-spaces in specified line range will not be listed.
4567 with only white-spaces in specified line range will not be listed.
4568
4568
4569 .. container:: verbose
4569 .. container:: verbose
4570
4570
4571 Some examples:
4571 Some examples:
4572
4572
4573 - changesets with full descriptions and file lists::
4573 - changesets with full descriptions and file lists::
4574
4574
4575 hg log -v
4575 hg log -v
4576
4576
4577 - changesets ancestral to the working directory::
4577 - changesets ancestral to the working directory::
4578
4578
4579 hg log -f
4579 hg log -f
4580
4580
4581 - last 10 commits on the current branch::
4581 - last 10 commits on the current branch::
4582
4582
4583 hg log -l 10 -b .
4583 hg log -l 10 -b .
4584
4584
4585 - changesets showing all modifications of a file, including removals::
4585 - changesets showing all modifications of a file, including removals::
4586
4586
4587 hg log --removed file.c
4587 hg log --removed file.c
4588
4588
4589 - all changesets that touch a directory, with diffs, excluding merges::
4589 - all changesets that touch a directory, with diffs, excluding merges::
4590
4590
4591 hg log -Mp lib/
4591 hg log -Mp lib/
4592
4592
4593 - all revision numbers that match a keyword::
4593 - all revision numbers that match a keyword::
4594
4594
4595 hg log -k bug --template "{rev}\\n"
4595 hg log -k bug --template "{rev}\\n"
4596
4596
4597 - the full hash identifier of the working directory parent::
4597 - the full hash identifier of the working directory parent::
4598
4598
4599 hg log -r . --template "{node}\\n"
4599 hg log -r . --template "{node}\\n"
4600
4600
4601 - list available log templates::
4601 - list available log templates::
4602
4602
4603 hg log -T list
4603 hg log -T list
4604
4604
4605 - check if a given changeset is included in a tagged release::
4605 - check if a given changeset is included in a tagged release::
4606
4606
4607 hg log -r "a21ccf and ancestor(1.9)"
4607 hg log -r "a21ccf and ancestor(1.9)"
4608
4608
4609 - find all changesets by some user in a date range::
4609 - find all changesets by some user in a date range::
4610
4610
4611 hg log -k alice -d "may 2008 to jul 2008"
4611 hg log -k alice -d "may 2008 to jul 2008"
4612
4612
4613 - summary of all changesets after the last tag::
4613 - summary of all changesets after the last tag::
4614
4614
4615 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4615 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4616
4616
4617 - changesets touching lines 13 to 23 for file.c::
4617 - changesets touching lines 13 to 23 for file.c::
4618
4618
4619 hg log -L file.c,13:23
4619 hg log -L file.c,13:23
4620
4620
4621 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4621 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4622 main.c with patch::
4622 main.c with patch::
4623
4623
4624 hg log -L file.c,13:23 -L main.c,2:6 -p
4624 hg log -L file.c,13:23 -L main.c,2:6 -p
4625
4625
4626 See :hg:`help dates` for a list of formats valid for -d/--date.
4626 See :hg:`help dates` for a list of formats valid for -d/--date.
4627
4627
4628 See :hg:`help revisions` for more about specifying and ordering
4628 See :hg:`help revisions` for more about specifying and ordering
4629 revisions.
4629 revisions.
4630
4630
4631 See :hg:`help templates` for more about pre-packaged styles and
4631 See :hg:`help templates` for more about pre-packaged styles and
4632 specifying custom templates. The default template used by the log
4632 specifying custom templates. The default template used by the log
4633 command can be customized via the ``command-templates.log`` configuration
4633 command can be customized via the ``command-templates.log`` configuration
4634 setting.
4634 setting.
4635
4635
4636 Returns 0 on success.
4636 Returns 0 on success.
4637
4637
4638 """
4638 """
4639 opts = pycompat.byteskwargs(opts)
4639 opts = pycompat.byteskwargs(opts)
4640 linerange = opts.get(b'line_range')
4640 linerange = opts.get(b'line_range')
4641
4641
4642 if linerange and not opts.get(b'follow'):
4642 if linerange and not opts.get(b'follow'):
4643 raise error.InputError(_(b'--line-range requires --follow'))
4643 raise error.InputError(_(b'--line-range requires --follow'))
4644
4644
4645 if linerange and pats:
4645 if linerange and pats:
4646 # TODO: take pats as patterns with no line-range filter
4646 # TODO: take pats as patterns with no line-range filter
4647 raise error.InputError(
4647 raise error.InputError(
4648 _(b'FILE arguments are not compatible with --line-range option')
4648 _(b'FILE arguments are not compatible with --line-range option')
4649 )
4649 )
4650
4650
4651 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4651 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4652 walk_opts = logcmdutil.parseopts(ui, pats, opts)
4652 walk_opts = logcmdutil.parseopts(ui, pats, opts)
4653 revs, differ = logcmdutil.getrevs(repo, walk_opts)
4653 revs, differ = logcmdutil.getrevs(repo, walk_opts)
4654 if linerange:
4654 if linerange:
4655 # TODO: should follow file history from logcmdutil._initialrevs(),
4655 # TODO: should follow file history from logcmdutil._initialrevs(),
4656 # then filter the result by logcmdutil._makerevset() and --limit
4656 # then filter the result by logcmdutil._makerevset() and --limit
4657 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4657 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4658
4658
4659 getcopies = None
4659 getcopies = None
4660 if opts.get(b'copies'):
4660 if opts.get(b'copies'):
4661 endrev = None
4661 endrev = None
4662 if revs:
4662 if revs:
4663 endrev = revs.max() + 1
4663 endrev = revs.max() + 1
4664 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4664 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4665
4665
4666 ui.pager(b'log')
4666 ui.pager(b'log')
4667 displayer = logcmdutil.changesetdisplayer(
4667 displayer = logcmdutil.changesetdisplayer(
4668 ui, repo, opts, differ, buffered=True
4668 ui, repo, opts, differ, buffered=True
4669 )
4669 )
4670 if opts.get(b'graph'):
4670 if opts.get(b'graph'):
4671 displayfn = logcmdutil.displaygraphrevs
4671 displayfn = logcmdutil.displaygraphrevs
4672 else:
4672 else:
4673 displayfn = logcmdutil.displayrevs
4673 displayfn = logcmdutil.displayrevs
4674 displayfn(ui, repo, revs, displayer, getcopies)
4674 displayfn(ui, repo, revs, displayer, getcopies)
4675
4675
4676
4676
4677 @command(
4677 @command(
4678 b'manifest',
4678 b'manifest',
4679 [
4679 [
4680 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4680 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4681 (b'', b'all', False, _(b"list files from all revisions")),
4681 (b'', b'all', False, _(b"list files from all revisions")),
4682 ]
4682 ]
4683 + formatteropts,
4683 + formatteropts,
4684 _(b'[-r REV]'),
4684 _(b'[-r REV]'),
4685 helpcategory=command.CATEGORY_MAINTENANCE,
4685 helpcategory=command.CATEGORY_MAINTENANCE,
4686 intents={INTENT_READONLY},
4686 intents={INTENT_READONLY},
4687 )
4687 )
4688 def manifest(ui, repo, node=None, rev=None, **opts):
4688 def manifest(ui, repo, node=None, rev=None, **opts):
4689 """output the current or given revision of the project manifest
4689 """output the current or given revision of the project manifest
4690
4690
4691 Print a list of version controlled files for the given revision.
4691 Print a list of version controlled files for the given revision.
4692 If no revision is given, the first parent of the working directory
4692 If no revision is given, the first parent of the working directory
4693 is used, or the null revision if no revision is checked out.
4693 is used, or the null revision if no revision is checked out.
4694
4694
4695 With -v, print file permissions, symlink and executable bits.
4695 With -v, print file permissions, symlink and executable bits.
4696 With --debug, print file revision hashes.
4696 With --debug, print file revision hashes.
4697
4697
4698 If option --all is specified, the list of all files from all revisions
4698 If option --all is specified, the list of all files from all revisions
4699 is printed. This includes deleted and renamed files.
4699 is printed. This includes deleted and renamed files.
4700
4700
4701 Returns 0 on success.
4701 Returns 0 on success.
4702 """
4702 """
4703 opts = pycompat.byteskwargs(opts)
4703 opts = pycompat.byteskwargs(opts)
4704 fm = ui.formatter(b'manifest', opts)
4704 fm = ui.formatter(b'manifest', opts)
4705
4705
4706 if opts.get(b'all'):
4706 if opts.get(b'all'):
4707 if rev or node:
4707 if rev or node:
4708 raise error.InputError(_(b"can't specify a revision with --all"))
4708 raise error.InputError(_(b"can't specify a revision with --all"))
4709
4709
4710 res = set()
4710 res = set()
4711 for rev in repo:
4711 for rev in repo:
4712 ctx = repo[rev]
4712 ctx = repo[rev]
4713 res |= set(ctx.files())
4713 res |= set(ctx.files())
4714
4714
4715 ui.pager(b'manifest')
4715 ui.pager(b'manifest')
4716 for f in sorted(res):
4716 for f in sorted(res):
4717 fm.startitem()
4717 fm.startitem()
4718 fm.write(b"path", b'%s\n', f)
4718 fm.write(b"path", b'%s\n', f)
4719 fm.end()
4719 fm.end()
4720 return
4720 return
4721
4721
4722 if rev and node:
4722 if rev and node:
4723 raise error.InputError(_(b"please specify just one revision"))
4723 raise error.InputError(_(b"please specify just one revision"))
4724
4724
4725 if not node:
4725 if not node:
4726 node = rev
4726 node = rev
4727
4727
4728 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4728 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4729 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4729 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4730 if node:
4730 if node:
4731 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4731 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4732 ctx = scmutil.revsingle(repo, node)
4732 ctx = scmutil.revsingle(repo, node)
4733 mf = ctx.manifest()
4733 mf = ctx.manifest()
4734 ui.pager(b'manifest')
4734 ui.pager(b'manifest')
4735 for f in ctx:
4735 for f in ctx:
4736 fm.startitem()
4736 fm.startitem()
4737 fm.context(ctx=ctx)
4737 fm.context(ctx=ctx)
4738 fl = ctx[f].flags()
4738 fl = ctx[f].flags()
4739 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4739 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4740 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4740 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4741 fm.write(b'path', b'%s\n', f)
4741 fm.write(b'path', b'%s\n', f)
4742 fm.end()
4742 fm.end()
4743
4743
4744
4744
4745 @command(
4745 @command(
4746 b'merge',
4746 b'merge',
4747 [
4747 [
4748 (
4748 (
4749 b'f',
4749 b'f',
4750 b'force',
4750 b'force',
4751 None,
4751 None,
4752 _(b'force a merge including outstanding changes (DEPRECATED)'),
4752 _(b'force a merge including outstanding changes (DEPRECATED)'),
4753 ),
4753 ),
4754 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4754 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4755 (
4755 (
4756 b'P',
4756 b'P',
4757 b'preview',
4757 b'preview',
4758 None,
4758 None,
4759 _(b'review revisions to merge (no merge is performed)'),
4759 _(b'review revisions to merge (no merge is performed)'),
4760 ),
4760 ),
4761 (b'', b'abort', None, _(b'abort the ongoing merge')),
4761 (b'', b'abort', None, _(b'abort the ongoing merge')),
4762 ]
4762 ]
4763 + mergetoolopts,
4763 + mergetoolopts,
4764 _(b'[-P] [[-r] REV]'),
4764 _(b'[-P] [[-r] REV]'),
4765 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4765 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4766 helpbasic=True,
4766 helpbasic=True,
4767 )
4767 )
4768 def merge(ui, repo, node=None, **opts):
4768 def merge(ui, repo, node=None, **opts):
4769 """merge another revision into working directory
4769 """merge another revision into working directory
4770
4770
4771 The current working directory is updated with all changes made in
4771 The current working directory is updated with all changes made in
4772 the requested revision since the last common predecessor revision.
4772 the requested revision since the last common predecessor revision.
4773
4773
4774 Files that changed between either parent are marked as changed for
4774 Files that changed between either parent are marked as changed for
4775 the next commit and a commit must be performed before any further
4775 the next commit and a commit must be performed before any further
4776 updates to the repository are allowed. The next commit will have
4776 updates to the repository are allowed. The next commit will have
4777 two parents.
4777 two parents.
4778
4778
4779 ``--tool`` can be used to specify the merge tool used for file
4779 ``--tool`` can be used to specify the merge tool used for file
4780 merges. It overrides the HGMERGE environment variable and your
4780 merges. It overrides the HGMERGE environment variable and your
4781 configuration files. See :hg:`help merge-tools` for options.
4781 configuration files. See :hg:`help merge-tools` for options.
4782
4782
4783 If no revision is specified, the working directory's parent is a
4783 If no revision is specified, the working directory's parent is a
4784 head revision, and the current branch contains exactly one other
4784 head revision, and the current branch contains exactly one other
4785 head, the other head is merged with by default. Otherwise, an
4785 head, the other head is merged with by default. Otherwise, an
4786 explicit revision with which to merge must be provided.
4786 explicit revision with which to merge must be provided.
4787
4787
4788 See :hg:`help resolve` for information on handling file conflicts.
4788 See :hg:`help resolve` for information on handling file conflicts.
4789
4789
4790 To undo an uncommitted merge, use :hg:`merge --abort` which
4790 To undo an uncommitted merge, use :hg:`merge --abort` which
4791 will check out a clean copy of the original merge parent, losing
4791 will check out a clean copy of the original merge parent, losing
4792 all changes.
4792 all changes.
4793
4793
4794 Returns 0 on success, 1 if there are unresolved files.
4794 Returns 0 on success, 1 if there are unresolved files.
4795 """
4795 """
4796
4796
4797 opts = pycompat.byteskwargs(opts)
4797 opts = pycompat.byteskwargs(opts)
4798 abort = opts.get(b'abort')
4798 abort = opts.get(b'abort')
4799 if abort and repo.dirstate.p2() == nullid:
4799 if abort and repo.dirstate.p2() == nullid:
4800 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4800 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4801 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4801 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4802 if abort:
4802 if abort:
4803 state = cmdutil.getunfinishedstate(repo)
4803 state = cmdutil.getunfinishedstate(repo)
4804 if state and state._opname != b'merge':
4804 if state and state._opname != b'merge':
4805 raise error.StateError(
4805 raise error.StateError(
4806 _(b'cannot abort merge with %s in progress') % (state._opname),
4806 _(b'cannot abort merge with %s in progress') % (state._opname),
4807 hint=state.hint(),
4807 hint=state.hint(),
4808 )
4808 )
4809 if node:
4809 if node:
4810 raise error.InputError(_(b"cannot specify a node with --abort"))
4810 raise error.InputError(_(b"cannot specify a node with --abort"))
4811 return hg.abortmerge(repo.ui, repo)
4811 return hg.abortmerge(repo.ui, repo)
4812
4812
4813 if opts.get(b'rev') and node:
4813 if opts.get(b'rev') and node:
4814 raise error.InputError(_(b"please specify just one revision"))
4814 raise error.InputError(_(b"please specify just one revision"))
4815 if not node:
4815 if not node:
4816 node = opts.get(b'rev')
4816 node = opts.get(b'rev')
4817
4817
4818 if node:
4818 if node:
4819 ctx = scmutil.revsingle(repo, node)
4819 ctx = scmutil.revsingle(repo, node)
4820 else:
4820 else:
4821 if ui.configbool(b'commands', b'merge.require-rev'):
4821 if ui.configbool(b'commands', b'merge.require-rev'):
4822 raise error.InputError(
4822 raise error.InputError(
4823 _(
4823 _(
4824 b'configuration requires specifying revision to merge '
4824 b'configuration requires specifying revision to merge '
4825 b'with'
4825 b'with'
4826 )
4826 )
4827 )
4827 )
4828 ctx = repo[destutil.destmerge(repo)]
4828 ctx = repo[destutil.destmerge(repo)]
4829
4829
4830 if ctx.node() is None:
4830 if ctx.node() is None:
4831 raise error.InputError(
4831 raise error.InputError(
4832 _(b'merging with the working copy has no effect')
4832 _(b'merging with the working copy has no effect')
4833 )
4833 )
4834
4834
4835 if opts.get(b'preview'):
4835 if opts.get(b'preview'):
4836 # find nodes that are ancestors of p2 but not of p1
4836 # find nodes that are ancestors of p2 but not of p1
4837 p1 = repo[b'.'].node()
4837 p1 = repo[b'.'].node()
4838 p2 = ctx.node()
4838 p2 = ctx.node()
4839 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4839 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4840
4840
4841 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4841 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4842 for node in nodes:
4842 for node in nodes:
4843 displayer.show(repo[node])
4843 displayer.show(repo[node])
4844 displayer.close()
4844 displayer.close()
4845 return 0
4845 return 0
4846
4846
4847 # ui.forcemerge is an internal variable, do not document
4847 # ui.forcemerge is an internal variable, do not document
4848 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4848 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4849 with ui.configoverride(overrides, b'merge'):
4849 with ui.configoverride(overrides, b'merge'):
4850 force = opts.get(b'force')
4850 force = opts.get(b'force')
4851 labels = [b'working copy', b'merge rev']
4851 labels = [b'working copy', b'merge rev']
4852 return hg.merge(ctx, force=force, labels=labels)
4852 return hg.merge(ctx, force=force, labels=labels)
4853
4853
4854
4854
4855 statemod.addunfinished(
4855 statemod.addunfinished(
4856 b'merge',
4856 b'merge',
4857 fname=None,
4857 fname=None,
4858 clearable=True,
4858 clearable=True,
4859 allowcommit=True,
4859 allowcommit=True,
4860 cmdmsg=_(b'outstanding uncommitted merge'),
4860 cmdmsg=_(b'outstanding uncommitted merge'),
4861 abortfunc=hg.abortmerge,
4861 abortfunc=hg.abortmerge,
4862 statushint=_(
4862 statushint=_(
4863 b'To continue: hg commit\nTo abort: hg merge --abort'
4863 b'To continue: hg commit\nTo abort: hg merge --abort'
4864 ),
4864 ),
4865 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4865 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4866 )
4866 )
4867
4867
4868
4868
4869 @command(
4869 @command(
4870 b'outgoing|out',
4870 b'outgoing|out',
4871 [
4871 [
4872 (
4872 (
4873 b'f',
4873 b'f',
4874 b'force',
4874 b'force',
4875 None,
4875 None,
4876 _(b'run even when the destination is unrelated'),
4876 _(b'run even when the destination is unrelated'),
4877 ),
4877 ),
4878 (
4878 (
4879 b'r',
4879 b'r',
4880 b'rev',
4880 b'rev',
4881 [],
4881 [],
4882 _(b'a changeset intended to be included in the destination'),
4882 _(b'a changeset intended to be included in the destination'),
4883 _(b'REV'),
4883 _(b'REV'),
4884 ),
4884 ),
4885 (b'n', b'newest-first', None, _(b'show newest record first')),
4885 (b'n', b'newest-first', None, _(b'show newest record first')),
4886 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4886 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4887 (
4887 (
4888 b'b',
4888 b'b',
4889 b'branch',
4889 b'branch',
4890 [],
4890 [],
4891 _(b'a specific branch you would like to push'),
4891 _(b'a specific branch you would like to push'),
4892 _(b'BRANCH'),
4892 _(b'BRANCH'),
4893 ),
4893 ),
4894 ]
4894 ]
4895 + logopts
4895 + logopts
4896 + remoteopts
4896 + remoteopts
4897 + subrepoopts,
4897 + subrepoopts,
4898 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4898 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4899 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4899 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4900 )
4900 )
4901 def outgoing(ui, repo, dest=None, **opts):
4901 def outgoing(ui, repo, dest=None, **opts):
4902 """show changesets not found in the destination
4902 """show changesets not found in the destination
4903
4903
4904 Show changesets not found in the specified destination repository
4904 Show changesets not found in the specified destination repository
4905 or the default push location. These are the changesets that would
4905 or the default push location. These are the changesets that would
4906 be pushed if a push was requested.
4906 be pushed if a push was requested.
4907
4907
4908 See pull for details of valid destination formats.
4908 See pull for details of valid destination formats.
4909
4909
4910 .. container:: verbose
4910 .. container:: verbose
4911
4911
4912 With -B/--bookmarks, the result of bookmark comparison between
4912 With -B/--bookmarks, the result of bookmark comparison between
4913 local and remote repositories is displayed. With -v/--verbose,
4913 local and remote repositories is displayed. With -v/--verbose,
4914 status is also displayed for each bookmark like below::
4914 status is also displayed for each bookmark like below::
4915
4915
4916 BM1 01234567890a added
4916 BM1 01234567890a added
4917 BM2 deleted
4917 BM2 deleted
4918 BM3 234567890abc advanced
4918 BM3 234567890abc advanced
4919 BM4 34567890abcd diverged
4919 BM4 34567890abcd diverged
4920 BM5 4567890abcde changed
4920 BM5 4567890abcde changed
4921
4921
4922 The action taken when pushing depends on the
4922 The action taken when pushing depends on the
4923 status of each bookmark:
4923 status of each bookmark:
4924
4924
4925 :``added``: push with ``-B`` will create it
4925 :``added``: push with ``-B`` will create it
4926 :``deleted``: push with ``-B`` will delete it
4926 :``deleted``: push with ``-B`` will delete it
4927 :``advanced``: push will update it
4927 :``advanced``: push will update it
4928 :``diverged``: push with ``-B`` will update it
4928 :``diverged``: push with ``-B`` will update it
4929 :``changed``: push with ``-B`` will update it
4929 :``changed``: push with ``-B`` will update it
4930
4930
4931 From the point of view of pushing behavior, bookmarks
4931 From the point of view of pushing behavior, bookmarks
4932 existing only in the remote repository are treated as
4932 existing only in the remote repository are treated as
4933 ``deleted``, even if it is in fact added remotely.
4933 ``deleted``, even if it is in fact added remotely.
4934
4934
4935 Returns 0 if there are outgoing changes, 1 otherwise.
4935 Returns 0 if there are outgoing changes, 1 otherwise.
4936 """
4936 """
4937 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4937 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4938 # style URLs, so don't overwrite dest.
4938 # style URLs, so don't overwrite dest.
4939 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
4939 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
4940 if not path:
4940 if not path:
4941 raise error.ConfigError(
4941 raise error.ConfigError(
4942 _(b'default repository not configured!'),
4942 _(b'default repository not configured!'),
4943 hint=_(b"see 'hg help config.paths'"),
4943 hint=_(b"see 'hg help config.paths'"),
4944 )
4944 )
4945
4945
4946 opts = pycompat.byteskwargs(opts)
4946 opts = pycompat.byteskwargs(opts)
4947 if opts.get(b'graph'):
4947 if opts.get(b'graph'):
4948 logcmdutil.checkunsupportedgraphflags([], opts)
4948 logcmdutil.checkunsupportedgraphflags([], opts)
4949 o, other = hg._outgoing(ui, repo, dest, opts)
4949 o, other = hg._outgoing(ui, repo, dest, opts)
4950 if not o:
4950 if not o:
4951 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4951 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4952 return
4952 return
4953
4953
4954 revdag = logcmdutil.graphrevs(repo, o, opts)
4954 revdag = logcmdutil.graphrevs(repo, o, opts)
4955 ui.pager(b'outgoing')
4955 ui.pager(b'outgoing')
4956 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4956 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4957 logcmdutil.displaygraph(
4957 logcmdutil.displaygraph(
4958 ui, repo, revdag, displayer, graphmod.asciiedges
4958 ui, repo, revdag, displayer, graphmod.asciiedges
4959 )
4959 )
4960 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4960 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4961 return 0
4961 return 0
4962
4962
4963 if opts.get(b'bookmarks'):
4963 if opts.get(b'bookmarks'):
4964 dest = path.pushloc or path.loc
4964 dest = path.pushloc or path.loc
4965 other = hg.peer(repo, opts, dest)
4965 other = hg.peer(repo, opts, dest)
4966 if b'bookmarks' not in other.listkeys(b'namespaces'):
4966 if b'bookmarks' not in other.listkeys(b'namespaces'):
4967 ui.warn(_(b"remote doesn't support bookmarks\n"))
4967 ui.warn(_(b"remote doesn't support bookmarks\n"))
4968 return 0
4968 return 0
4969 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
4969 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
4970 ui.pager(b'outgoing')
4970 ui.pager(b'outgoing')
4971 return bookmarks.outgoing(ui, repo, other)
4971 return bookmarks.outgoing(ui, repo, other)
4972
4972
4973 repo._subtoppath = path.pushloc or path.loc
4973 repo._subtoppath = path.pushloc or path.loc
4974 try:
4974 try:
4975 return hg.outgoing(ui, repo, dest, opts)
4975 return hg.outgoing(ui, repo, dest, opts)
4976 finally:
4976 finally:
4977 del repo._subtoppath
4977 del repo._subtoppath
4978
4978
4979
4979
4980 @command(
4980 @command(
4981 b'parents',
4981 b'parents',
4982 [
4982 [
4983 (
4983 (
4984 b'r',
4984 b'r',
4985 b'rev',
4985 b'rev',
4986 b'',
4986 b'',
4987 _(b'show parents of the specified revision'),
4987 _(b'show parents of the specified revision'),
4988 _(b'REV'),
4988 _(b'REV'),
4989 ),
4989 ),
4990 ]
4990 ]
4991 + templateopts,
4991 + templateopts,
4992 _(b'[-r REV] [FILE]'),
4992 _(b'[-r REV] [FILE]'),
4993 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4993 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4994 inferrepo=True,
4994 inferrepo=True,
4995 )
4995 )
4996 def parents(ui, repo, file_=None, **opts):
4996 def parents(ui, repo, file_=None, **opts):
4997 """show the parents of the working directory or revision (DEPRECATED)
4997 """show the parents of the working directory or revision (DEPRECATED)
4998
4998
4999 Print the working directory's parent revisions. If a revision is
4999 Print the working directory's parent revisions. If a revision is
5000 given via -r/--rev, the parent of that revision will be printed.
5000 given via -r/--rev, the parent of that revision will be printed.
5001 If a file argument is given, the revision in which the file was
5001 If a file argument is given, the revision in which the file was
5002 last changed (before the working directory revision or the
5002 last changed (before the working directory revision or the
5003 argument to --rev if given) is printed.
5003 argument to --rev if given) is printed.
5004
5004
5005 This command is equivalent to::
5005 This command is equivalent to::
5006
5006
5007 hg log -r "p1()+p2()" or
5007 hg log -r "p1()+p2()" or
5008 hg log -r "p1(REV)+p2(REV)" or
5008 hg log -r "p1(REV)+p2(REV)" or
5009 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5009 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5010 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5010 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5011
5011
5012 See :hg:`summary` and :hg:`help revsets` for related information.
5012 See :hg:`summary` and :hg:`help revsets` for related information.
5013
5013
5014 Returns 0 on success.
5014 Returns 0 on success.
5015 """
5015 """
5016
5016
5017 opts = pycompat.byteskwargs(opts)
5017 opts = pycompat.byteskwargs(opts)
5018 rev = opts.get(b'rev')
5018 rev = opts.get(b'rev')
5019 if rev:
5019 if rev:
5020 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5020 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5021 ctx = scmutil.revsingle(repo, rev, None)
5021 ctx = scmutil.revsingle(repo, rev, None)
5022
5022
5023 if file_:
5023 if file_:
5024 m = scmutil.match(ctx, (file_,), opts)
5024 m = scmutil.match(ctx, (file_,), opts)
5025 if m.anypats() or len(m.files()) != 1:
5025 if m.anypats() or len(m.files()) != 1:
5026 raise error.InputError(_(b'can only specify an explicit filename'))
5026 raise error.InputError(_(b'can only specify an explicit filename'))
5027 file_ = m.files()[0]
5027 file_ = m.files()[0]
5028 filenodes = []
5028 filenodes = []
5029 for cp in ctx.parents():
5029 for cp in ctx.parents():
5030 if not cp:
5030 if not cp:
5031 continue
5031 continue
5032 try:
5032 try:
5033 filenodes.append(cp.filenode(file_))
5033 filenodes.append(cp.filenode(file_))
5034 except error.LookupError:
5034 except error.LookupError:
5035 pass
5035 pass
5036 if not filenodes:
5036 if not filenodes:
5037 raise error.InputError(_(b"'%s' not found in manifest") % file_)
5037 raise error.InputError(_(b"'%s' not found in manifest") % file_)
5038 p = []
5038 p = []
5039 for fn in filenodes:
5039 for fn in filenodes:
5040 fctx = repo.filectx(file_, fileid=fn)
5040 fctx = repo.filectx(file_, fileid=fn)
5041 p.append(fctx.node())
5041 p.append(fctx.node())
5042 else:
5042 else:
5043 p = [cp.node() for cp in ctx.parents()]
5043 p = [cp.node() for cp in ctx.parents()]
5044
5044
5045 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5045 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5046 for n in p:
5046 for n in p:
5047 if n != nullid:
5047 if n != nullid:
5048 displayer.show(repo[n])
5048 displayer.show(repo[n])
5049 displayer.close()
5049 displayer.close()
5050
5050
5051
5051
5052 @command(
5052 @command(
5053 b'paths',
5053 b'paths',
5054 formatteropts,
5054 formatteropts,
5055 _(b'[NAME]'),
5055 _(b'[NAME]'),
5056 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5056 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5057 optionalrepo=True,
5057 optionalrepo=True,
5058 intents={INTENT_READONLY},
5058 intents={INTENT_READONLY},
5059 )
5059 )
5060 def paths(ui, repo, search=None, **opts):
5060 def paths(ui, repo, search=None, **opts):
5061 """show aliases for remote repositories
5061 """show aliases for remote repositories
5062
5062
5063 Show definition of symbolic path name NAME. If no name is given,
5063 Show definition of symbolic path name NAME. If no name is given,
5064 show definition of all available names.
5064 show definition of all available names.
5065
5065
5066 Option -q/--quiet suppresses all output when searching for NAME
5066 Option -q/--quiet suppresses all output when searching for NAME
5067 and shows only the path names when listing all definitions.
5067 and shows only the path names when listing all definitions.
5068
5068
5069 Path names are defined in the [paths] section of your
5069 Path names are defined in the [paths] section of your
5070 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5070 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5071 repository, ``.hg/hgrc`` is used, too.
5071 repository, ``.hg/hgrc`` is used, too.
5072
5072
5073 The path names ``default`` and ``default-push`` have a special
5073 The path names ``default`` and ``default-push`` have a special
5074 meaning. When performing a push or pull operation, they are used
5074 meaning. When performing a push or pull operation, they are used
5075 as fallbacks if no location is specified on the command-line.
5075 as fallbacks if no location is specified on the command-line.
5076 When ``default-push`` is set, it will be used for push and
5076 When ``default-push`` is set, it will be used for push and
5077 ``default`` will be used for pull; otherwise ``default`` is used
5077 ``default`` will be used for pull; otherwise ``default`` is used
5078 as the fallback for both. When cloning a repository, the clone
5078 as the fallback for both. When cloning a repository, the clone
5079 source is written as ``default`` in ``.hg/hgrc``.
5079 source is written as ``default`` in ``.hg/hgrc``.
5080
5080
5081 .. note::
5081 .. note::
5082
5082
5083 ``default`` and ``default-push`` apply to all inbound (e.g.
5083 ``default`` and ``default-push`` apply to all inbound (e.g.
5084 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5084 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5085 and :hg:`bundle`) operations.
5085 and :hg:`bundle`) operations.
5086
5086
5087 See :hg:`help urls` for more information.
5087 See :hg:`help urls` for more information.
5088
5088
5089 .. container:: verbose
5089 .. container:: verbose
5090
5090
5091 Template:
5091 Template:
5092
5092
5093 The following keywords are supported. See also :hg:`help templates`.
5093 The following keywords are supported. See also :hg:`help templates`.
5094
5094
5095 :name: String. Symbolic name of the path alias.
5095 :name: String. Symbolic name of the path alias.
5096 :pushurl: String. URL for push operations.
5096 :pushurl: String. URL for push operations.
5097 :url: String. URL or directory path for the other operations.
5097 :url: String. URL or directory path for the other operations.
5098
5098
5099 Returns 0 on success.
5099 Returns 0 on success.
5100 """
5100 """
5101
5101
5102 opts = pycompat.byteskwargs(opts)
5102 opts = pycompat.byteskwargs(opts)
5103 ui.pager(b'paths')
5103 ui.pager(b'paths')
5104 if search:
5104 if search:
5105 pathitems = [
5105 pathitems = [
5106 (name, path)
5106 (name, path)
5107 for name, path in pycompat.iteritems(ui.paths)
5107 for name, path in pycompat.iteritems(ui.paths)
5108 if name == search
5108 if name == search
5109 ]
5109 ]
5110 else:
5110 else:
5111 pathitems = sorted(pycompat.iteritems(ui.paths))
5111 pathitems = sorted(pycompat.iteritems(ui.paths))
5112
5112
5113 fm = ui.formatter(b'paths', opts)
5113 fm = ui.formatter(b'paths', opts)
5114 if fm.isplain():
5114 if fm.isplain():
5115 hidepassword = util.hidepassword
5115 hidepassword = util.hidepassword
5116 else:
5116 else:
5117 hidepassword = bytes
5117 hidepassword = bytes
5118 if ui.quiet:
5118 if ui.quiet:
5119 namefmt = b'%s\n'
5119 namefmt = b'%s\n'
5120 else:
5120 else:
5121 namefmt = b'%s = '
5121 namefmt = b'%s = '
5122 showsubopts = not search and not ui.quiet
5122 showsubopts = not search and not ui.quiet
5123
5123
5124 for name, path in pathitems:
5124 for name, path in pathitems:
5125 fm.startitem()
5125 fm.startitem()
5126 fm.condwrite(not search, b'name', namefmt, name)
5126 fm.condwrite(not search, b'name', namefmt, name)
5127 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5127 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5128 for subopt, value in sorted(path.suboptions.items()):
5128 for subopt, value in sorted(path.suboptions.items()):
5129 assert subopt not in (b'name', b'url')
5129 assert subopt not in (b'name', b'url')
5130 if showsubopts:
5130 if showsubopts:
5131 fm.plain(b'%s:%s = ' % (name, subopt))
5131 fm.plain(b'%s:%s = ' % (name, subopt))
5132 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5132 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5133
5133
5134 fm.end()
5134 fm.end()
5135
5135
5136 if search and not pathitems:
5136 if search and not pathitems:
5137 if not ui.quiet:
5137 if not ui.quiet:
5138 ui.warn(_(b"not found!\n"))
5138 ui.warn(_(b"not found!\n"))
5139 return 1
5139 return 1
5140 else:
5140 else:
5141 return 0
5141 return 0
5142
5142
5143
5143
5144 @command(
5144 @command(
5145 b'phase',
5145 b'phase',
5146 [
5146 [
5147 (b'p', b'public', False, _(b'set changeset phase to public')),
5147 (b'p', b'public', False, _(b'set changeset phase to public')),
5148 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5148 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5149 (b's', b'secret', False, _(b'set changeset phase to secret')),
5149 (b's', b'secret', False, _(b'set changeset phase to secret')),
5150 (b'f', b'force', False, _(b'allow to move boundary backward')),
5150 (b'f', b'force', False, _(b'allow to move boundary backward')),
5151 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5151 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5152 ],
5152 ],
5153 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5153 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5154 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5154 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5155 )
5155 )
5156 def phase(ui, repo, *revs, **opts):
5156 def phase(ui, repo, *revs, **opts):
5157 """set or show the current phase name
5157 """set or show the current phase name
5158
5158
5159 With no argument, show the phase name of the current revision(s).
5159 With no argument, show the phase name of the current revision(s).
5160
5160
5161 With one of -p/--public, -d/--draft or -s/--secret, change the
5161 With one of -p/--public, -d/--draft or -s/--secret, change the
5162 phase value of the specified revisions.
5162 phase value of the specified revisions.
5163
5163
5164 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5164 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5165 lower phase to a higher phase. Phases are ordered as follows::
5165 lower phase to a higher phase. Phases are ordered as follows::
5166
5166
5167 public < draft < secret
5167 public < draft < secret
5168
5168
5169 Returns 0 on success, 1 if some phases could not be changed.
5169 Returns 0 on success, 1 if some phases could not be changed.
5170
5170
5171 (For more information about the phases concept, see :hg:`help phases`.)
5171 (For more information about the phases concept, see :hg:`help phases`.)
5172 """
5172 """
5173 opts = pycompat.byteskwargs(opts)
5173 opts = pycompat.byteskwargs(opts)
5174 # search for a unique phase argument
5174 # search for a unique phase argument
5175 targetphase = None
5175 targetphase = None
5176 for idx, name in enumerate(phases.cmdphasenames):
5176 for idx, name in enumerate(phases.cmdphasenames):
5177 if opts[name]:
5177 if opts[name]:
5178 if targetphase is not None:
5178 if targetphase is not None:
5179 raise error.InputError(_(b'only one phase can be specified'))
5179 raise error.InputError(_(b'only one phase can be specified'))
5180 targetphase = idx
5180 targetphase = idx
5181
5181
5182 # look for specified revision
5182 # look for specified revision
5183 revs = list(revs)
5183 revs = list(revs)
5184 revs.extend(opts[b'rev'])
5184 revs.extend(opts[b'rev'])
5185 if not revs:
5185 if not revs:
5186 # display both parents as the second parent phase can influence
5186 # display both parents as the second parent phase can influence
5187 # the phase of a merge commit
5187 # the phase of a merge commit
5188 revs = [c.rev() for c in repo[None].parents()]
5188 revs = [c.rev() for c in repo[None].parents()]
5189
5189
5190 revs = scmutil.revrange(repo, revs)
5190 revs = scmutil.revrange(repo, revs)
5191
5191
5192 ret = 0
5192 ret = 0
5193 if targetphase is None:
5193 if targetphase is None:
5194 # display
5194 # display
5195 for r in revs:
5195 for r in revs:
5196 ctx = repo[r]
5196 ctx = repo[r]
5197 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5197 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5198 else:
5198 else:
5199 with repo.lock(), repo.transaction(b"phase") as tr:
5199 with repo.lock(), repo.transaction(b"phase") as tr:
5200 # set phase
5200 # set phase
5201 if not revs:
5201 if not revs:
5202 raise error.InputError(_(b'empty revision set'))
5202 raise error.InputError(_(b'empty revision set'))
5203 nodes = [repo[r].node() for r in revs]
5203 nodes = [repo[r].node() for r in revs]
5204 # moving revision from public to draft may hide them
5204 # moving revision from public to draft may hide them
5205 # We have to check result on an unfiltered repository
5205 # We have to check result on an unfiltered repository
5206 unfi = repo.unfiltered()
5206 unfi = repo.unfiltered()
5207 getphase = unfi._phasecache.phase
5207 getphase = unfi._phasecache.phase
5208 olddata = [getphase(unfi, r) for r in unfi]
5208 olddata = [getphase(unfi, r) for r in unfi]
5209 phases.advanceboundary(repo, tr, targetphase, nodes)
5209 phases.advanceboundary(repo, tr, targetphase, nodes)
5210 if opts[b'force']:
5210 if opts[b'force']:
5211 phases.retractboundary(repo, tr, targetphase, nodes)
5211 phases.retractboundary(repo, tr, targetphase, nodes)
5212 getphase = unfi._phasecache.phase
5212 getphase = unfi._phasecache.phase
5213 newdata = [getphase(unfi, r) for r in unfi]
5213 newdata = [getphase(unfi, r) for r in unfi]
5214 changes = sum(newdata[r] != olddata[r] for r in unfi)
5214 changes = sum(newdata[r] != olddata[r] for r in unfi)
5215 cl = unfi.changelog
5215 cl = unfi.changelog
5216 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5216 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5217 if rejected:
5217 if rejected:
5218 ui.warn(
5218 ui.warn(
5219 _(
5219 _(
5220 b'cannot move %i changesets to a higher '
5220 b'cannot move %i changesets to a higher '
5221 b'phase, use --force\n'
5221 b'phase, use --force\n'
5222 )
5222 )
5223 % len(rejected)
5223 % len(rejected)
5224 )
5224 )
5225 ret = 1
5225 ret = 1
5226 if changes:
5226 if changes:
5227 msg = _(b'phase changed for %i changesets\n') % changes
5227 msg = _(b'phase changed for %i changesets\n') % changes
5228 if ret:
5228 if ret:
5229 ui.status(msg)
5229 ui.status(msg)
5230 else:
5230 else:
5231 ui.note(msg)
5231 ui.note(msg)
5232 else:
5232 else:
5233 ui.warn(_(b'no phases changed\n'))
5233 ui.warn(_(b'no phases changed\n'))
5234 return ret
5234 return ret
5235
5235
5236
5236
5237 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5237 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5238 """Run after a changegroup has been added via pull/unbundle
5238 """Run after a changegroup has been added via pull/unbundle
5239
5239
5240 This takes arguments below:
5240 This takes arguments below:
5241
5241
5242 :modheads: change of heads by pull/unbundle
5242 :modheads: change of heads by pull/unbundle
5243 :optupdate: updating working directory is needed or not
5243 :optupdate: updating working directory is needed or not
5244 :checkout: update destination revision (or None to default destination)
5244 :checkout: update destination revision (or None to default destination)
5245 :brev: a name, which might be a bookmark to be activated after updating
5245 :brev: a name, which might be a bookmark to be activated after updating
5246 """
5246 """
5247 if modheads == 0:
5247 if modheads == 0:
5248 return
5248 return
5249 if optupdate:
5249 if optupdate:
5250 try:
5250 try:
5251 return hg.updatetotally(ui, repo, checkout, brev)
5251 return hg.updatetotally(ui, repo, checkout, brev)
5252 except error.UpdateAbort as inst:
5252 except error.UpdateAbort as inst:
5253 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5253 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5254 hint = inst.hint
5254 hint = inst.hint
5255 raise error.UpdateAbort(msg, hint=hint)
5255 raise error.UpdateAbort(msg, hint=hint)
5256 if modheads is not None and modheads > 1:
5256 if modheads is not None and modheads > 1:
5257 currentbranchheads = len(repo.branchheads())
5257 currentbranchheads = len(repo.branchheads())
5258 if currentbranchheads == modheads:
5258 if currentbranchheads == modheads:
5259 ui.status(
5259 ui.status(
5260 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5260 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5261 )
5261 )
5262 elif currentbranchheads > 1:
5262 elif currentbranchheads > 1:
5263 ui.status(
5263 ui.status(
5264 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5264 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5265 )
5265 )
5266 else:
5266 else:
5267 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5267 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5268 elif not ui.configbool(b'commands', b'update.requiredest'):
5268 elif not ui.configbool(b'commands', b'update.requiredest'):
5269 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5269 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5270
5270
5271
5271
5272 @command(
5272 @command(
5273 b'pull',
5273 b'pull',
5274 [
5274 [
5275 (
5275 (
5276 b'u',
5276 b'u',
5277 b'update',
5277 b'update',
5278 None,
5278 None,
5279 _(b'update to new branch head if new descendants were pulled'),
5279 _(b'update to new branch head if new descendants were pulled'),
5280 ),
5280 ),
5281 (
5281 (
5282 b'f',
5282 b'f',
5283 b'force',
5283 b'force',
5284 None,
5284 None,
5285 _(b'run even when remote repository is unrelated'),
5285 _(b'run even when remote repository is unrelated'),
5286 ),
5286 ),
5287 (
5287 (
5288 b'',
5288 b'',
5289 b'confirm',
5289 b'confirm',
5290 None,
5290 None,
5291 _(b'confirm pull before applying changes'),
5291 _(b'confirm pull before applying changes'),
5292 ),
5292 ),
5293 (
5293 (
5294 b'r',
5294 b'r',
5295 b'rev',
5295 b'rev',
5296 [],
5296 [],
5297 _(b'a remote changeset intended to be added'),
5297 _(b'a remote changeset intended to be added'),
5298 _(b'REV'),
5298 _(b'REV'),
5299 ),
5299 ),
5300 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5300 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5301 (
5301 (
5302 b'b',
5302 b'b',
5303 b'branch',
5303 b'branch',
5304 [],
5304 [],
5305 _(b'a specific branch you would like to pull'),
5305 _(b'a specific branch you would like to pull'),
5306 _(b'BRANCH'),
5306 _(b'BRANCH'),
5307 ),
5307 ),
5308 ]
5308 ]
5309 + remoteopts,
5309 + remoteopts,
5310 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5310 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5311 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5311 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5312 helpbasic=True,
5312 helpbasic=True,
5313 )
5313 )
5314 def pull(ui, repo, source=b"default", **opts):
5314 def pull(ui, repo, source=b"default", **opts):
5315 """pull changes from the specified source
5315 """pull changes from the specified source
5316
5316
5317 Pull changes from a remote repository to a local one.
5317 Pull changes from a remote repository to a local one.
5318
5318
5319 This finds all changes from the repository at the specified path
5319 This finds all changes from the repository at the specified path
5320 or URL and adds them to a local repository (the current one unless
5320 or URL and adds them to a local repository (the current one unless
5321 -R is specified). By default, this does not update the copy of the
5321 -R is specified). By default, this does not update the copy of the
5322 project in the working directory.
5322 project in the working directory.
5323
5323
5324 When cloning from servers that support it, Mercurial may fetch
5324 When cloning from servers that support it, Mercurial may fetch
5325 pre-generated data. When this is done, hooks operating on incoming
5325 pre-generated data. When this is done, hooks operating on incoming
5326 changesets and changegroups may fire more than once, once for each
5326 changesets and changegroups may fire more than once, once for each
5327 pre-generated bundle and as well as for any additional remaining
5327 pre-generated bundle and as well as for any additional remaining
5328 data. See :hg:`help -e clonebundles` for more.
5328 data. See :hg:`help -e clonebundles` for more.
5329
5329
5330 Use :hg:`incoming` if you want to see what would have been added
5330 Use :hg:`incoming` if you want to see what would have been added
5331 by a pull at the time you issued this command. If you then decide
5331 by a pull at the time you issued this command. If you then decide
5332 to add those changes to the repository, you should use :hg:`pull
5332 to add those changes to the repository, you should use :hg:`pull
5333 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5333 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5334
5334
5335 If SOURCE is omitted, the 'default' path will be used.
5335 If SOURCE is omitted, the 'default' path will be used.
5336 See :hg:`help urls` for more information.
5336 See :hg:`help urls` for more information.
5337
5337
5338 Specifying bookmark as ``.`` is equivalent to specifying the active
5338 Specifying bookmark as ``.`` is equivalent to specifying the active
5339 bookmark's name.
5339 bookmark's name.
5340
5340
5341 Returns 0 on success, 1 if an update had unresolved files.
5341 Returns 0 on success, 1 if an update had unresolved files.
5342 """
5342 """
5343
5343
5344 opts = pycompat.byteskwargs(opts)
5344 opts = pycompat.byteskwargs(opts)
5345 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5345 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5346 b'update'
5346 b'update'
5347 ):
5347 ):
5348 msg = _(b'update destination required by configuration')
5348 msg = _(b'update destination required by configuration')
5349 hint = _(b'use hg pull followed by hg update DEST')
5349 hint = _(b'use hg pull followed by hg update DEST')
5350 raise error.InputError(msg, hint=hint)
5350 raise error.InputError(msg, hint=hint)
5351
5351
5352 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5352 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5353 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5353 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5354 ui.flush()
5354 ui.flush()
5355 other = hg.peer(repo, opts, source)
5355 other = hg.peer(repo, opts, source)
5356 try:
5356 try:
5357 revs, checkout = hg.addbranchrevs(
5357 revs, checkout = hg.addbranchrevs(
5358 repo, other, branches, opts.get(b'rev')
5358 repo, other, branches, opts.get(b'rev')
5359 )
5359 )
5360
5360
5361 pullopargs = {}
5361 pullopargs = {}
5362
5362
5363 nodes = None
5363 nodes = None
5364 if opts.get(b'bookmark') or revs:
5364 if opts.get(b'bookmark') or revs:
5365 # The list of bookmark used here is the same used to actually update
5365 # The list of bookmark used here is the same used to actually update
5366 # the bookmark names, to avoid the race from issue 4689 and we do
5366 # the bookmark names, to avoid the race from issue 4689 and we do
5367 # all lookup and bookmark queries in one go so they see the same
5367 # all lookup and bookmark queries in one go so they see the same
5368 # version of the server state (issue 4700).
5368 # version of the server state (issue 4700).
5369 nodes = []
5369 nodes = []
5370 fnodes = []
5370 fnodes = []
5371 revs = revs or []
5371 revs = revs or []
5372 if revs and not other.capable(b'lookup'):
5372 if revs and not other.capable(b'lookup'):
5373 err = _(
5373 err = _(
5374 b"other repository doesn't support revision lookup, "
5374 b"other repository doesn't support revision lookup, "
5375 b"so a rev cannot be specified."
5375 b"so a rev cannot be specified."
5376 )
5376 )
5377 raise error.Abort(err)
5377 raise error.Abort(err)
5378 with other.commandexecutor() as e:
5378 with other.commandexecutor() as e:
5379 fremotebookmarks = e.callcommand(
5379 fremotebookmarks = e.callcommand(
5380 b'listkeys', {b'namespace': b'bookmarks'}
5380 b'listkeys', {b'namespace': b'bookmarks'}
5381 )
5381 )
5382 for r in revs:
5382 for r in revs:
5383 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5383 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5384 remotebookmarks = fremotebookmarks.result()
5384 remotebookmarks = fremotebookmarks.result()
5385 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5385 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5386 pullopargs[b'remotebookmarks'] = remotebookmarks
5386 pullopargs[b'remotebookmarks'] = remotebookmarks
5387 for b in opts.get(b'bookmark', []):
5387 for b in opts.get(b'bookmark', []):
5388 b = repo._bookmarks.expandname(b)
5388 b = repo._bookmarks.expandname(b)
5389 if b not in remotebookmarks:
5389 if b not in remotebookmarks:
5390 raise error.InputError(
5390 raise error.InputError(
5391 _(b'remote bookmark %s not found!') % b
5391 _(b'remote bookmark %s not found!') % b
5392 )
5392 )
5393 nodes.append(remotebookmarks[b])
5393 nodes.append(remotebookmarks[b])
5394 for i, rev in enumerate(revs):
5394 for i, rev in enumerate(revs):
5395 node = fnodes[i].result()
5395 node = fnodes[i].result()
5396 nodes.append(node)
5396 nodes.append(node)
5397 if rev == checkout:
5397 if rev == checkout:
5398 checkout = node
5398 checkout = node
5399
5399
5400 wlock = util.nullcontextmanager()
5400 wlock = util.nullcontextmanager()
5401 if opts.get(b'update'):
5401 if opts.get(b'update'):
5402 wlock = repo.wlock()
5402 wlock = repo.wlock()
5403 with wlock:
5403 with wlock:
5404 pullopargs.update(opts.get(b'opargs', {}))
5404 pullopargs.update(opts.get(b'opargs', {}))
5405 modheads = exchange.pull(
5405 modheads = exchange.pull(
5406 repo,
5406 repo,
5407 other,
5407 other,
5408 heads=nodes,
5408 heads=nodes,
5409 force=opts.get(b'force'),
5409 force=opts.get(b'force'),
5410 bookmarks=opts.get(b'bookmark', ()),
5410 bookmarks=opts.get(b'bookmark', ()),
5411 opargs=pullopargs,
5411 opargs=pullopargs,
5412 confirm=opts.get(b'confirm'),
5412 confirm=opts.get(b'confirm'),
5413 ).cgresult
5413 ).cgresult
5414
5414
5415 # brev is a name, which might be a bookmark to be activated at
5415 # brev is a name, which might be a bookmark to be activated at
5416 # the end of the update. In other words, it is an explicit
5416 # the end of the update. In other words, it is an explicit
5417 # destination of the update
5417 # destination of the update
5418 brev = None
5418 brev = None
5419
5419
5420 if checkout:
5420 if checkout:
5421 checkout = repo.unfiltered().changelog.rev(checkout)
5421 checkout = repo.unfiltered().changelog.rev(checkout)
5422
5422
5423 # order below depends on implementation of
5423 # order below depends on implementation of
5424 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5424 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5425 # because 'checkout' is determined without it.
5425 # because 'checkout' is determined without it.
5426 if opts.get(b'rev'):
5426 if opts.get(b'rev'):
5427 brev = opts[b'rev'][0]
5427 brev = opts[b'rev'][0]
5428 elif opts.get(b'branch'):
5428 elif opts.get(b'branch'):
5429 brev = opts[b'branch'][0]
5429 brev = opts[b'branch'][0]
5430 else:
5430 else:
5431 brev = branches[0]
5431 brev = branches[0]
5432 repo._subtoppath = source
5432 repo._subtoppath = source
5433 try:
5433 try:
5434 ret = postincoming(
5434 ret = postincoming(
5435 ui, repo, modheads, opts.get(b'update'), checkout, brev
5435 ui, repo, modheads, opts.get(b'update'), checkout, brev
5436 )
5436 )
5437 except error.FilteredRepoLookupError as exc:
5437 except error.FilteredRepoLookupError as exc:
5438 msg = _(b'cannot update to target: %s') % exc.args[0]
5438 msg = _(b'cannot update to target: %s') % exc.args[0]
5439 exc.args = (msg,) + exc.args[1:]
5439 exc.args = (msg,) + exc.args[1:]
5440 raise
5440 raise
5441 finally:
5441 finally:
5442 del repo._subtoppath
5442 del repo._subtoppath
5443
5443
5444 finally:
5444 finally:
5445 other.close()
5445 other.close()
5446 return ret
5446 return ret
5447
5447
5448
5448
5449 @command(
5449 @command(
5450 b'push',
5450 b'push',
5451 [
5451 [
5452 (b'f', b'force', None, _(b'force push')),
5452 (b'f', b'force', None, _(b'force push')),
5453 (
5453 (
5454 b'r',
5454 b'r',
5455 b'rev',
5455 b'rev',
5456 [],
5456 [],
5457 _(b'a changeset intended to be included in the destination'),
5457 _(b'a changeset intended to be included in the destination'),
5458 _(b'REV'),
5458 _(b'REV'),
5459 ),
5459 ),
5460 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5460 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5461 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5461 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5462 (
5462 (
5463 b'b',
5463 b'b',
5464 b'branch',
5464 b'branch',
5465 [],
5465 [],
5466 _(b'a specific branch you would like to push'),
5466 _(b'a specific branch you would like to push'),
5467 _(b'BRANCH'),
5467 _(b'BRANCH'),
5468 ),
5468 ),
5469 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5469 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5470 (
5470 (
5471 b'',
5471 b'',
5472 b'pushvars',
5472 b'pushvars',
5473 [],
5473 [],
5474 _(b'variables that can be sent to server (ADVANCED)'),
5474 _(b'variables that can be sent to server (ADVANCED)'),
5475 ),
5475 ),
5476 (
5476 (
5477 b'',
5477 b'',
5478 b'publish',
5478 b'publish',
5479 False,
5479 False,
5480 _(b'push the changeset as public (EXPERIMENTAL)'),
5480 _(b'push the changeset as public (EXPERIMENTAL)'),
5481 ),
5481 ),
5482 ]
5482 ]
5483 + remoteopts,
5483 + remoteopts,
5484 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5484 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5485 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5485 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5486 helpbasic=True,
5486 helpbasic=True,
5487 )
5487 )
5488 def push(ui, repo, dest=None, **opts):
5488 def push(ui, repo, dest=None, **opts):
5489 """push changes to the specified destination
5489 """push changes to the specified destination
5490
5490
5491 Push changesets from the local repository to the specified
5491 Push changesets from the local repository to the specified
5492 destination.
5492 destination.
5493
5493
5494 This operation is symmetrical to pull: it is identical to a pull
5494 This operation is symmetrical to pull: it is identical to a pull
5495 in the destination repository from the current one.
5495 in the destination repository from the current one.
5496
5496
5497 By default, push will not allow creation of new heads at the
5497 By default, push will not allow creation of new heads at the
5498 destination, since multiple heads would make it unclear which head
5498 destination, since multiple heads would make it unclear which head
5499 to use. In this situation, it is recommended to pull and merge
5499 to use. In this situation, it is recommended to pull and merge
5500 before pushing.
5500 before pushing.
5501
5501
5502 Use --new-branch if you want to allow push to create a new named
5502 Use --new-branch if you want to allow push to create a new named
5503 branch that is not present at the destination. This allows you to
5503 branch that is not present at the destination. This allows you to
5504 only create a new branch without forcing other changes.
5504 only create a new branch without forcing other changes.
5505
5505
5506 .. note::
5506 .. note::
5507
5507
5508 Extra care should be taken with the -f/--force option,
5508 Extra care should be taken with the -f/--force option,
5509 which will push all new heads on all branches, an action which will
5509 which will push all new heads on all branches, an action which will
5510 almost always cause confusion for collaborators.
5510 almost always cause confusion for collaborators.
5511
5511
5512 If -r/--rev is used, the specified revision and all its ancestors
5512 If -r/--rev is used, the specified revision and all its ancestors
5513 will be pushed to the remote repository.
5513 will be pushed to the remote repository.
5514
5514
5515 If -B/--bookmark is used, the specified bookmarked revision, its
5515 If -B/--bookmark is used, the specified bookmarked revision, its
5516 ancestors, and the bookmark will be pushed to the remote
5516 ancestors, and the bookmark will be pushed to the remote
5517 repository. Specifying ``.`` is equivalent to specifying the active
5517 repository. Specifying ``.`` is equivalent to specifying the active
5518 bookmark's name. Use the --all-bookmarks option for pushing all
5518 bookmark's name. Use the --all-bookmarks option for pushing all
5519 current bookmarks.
5519 current bookmarks.
5520
5520
5521 Please see :hg:`help urls` for important details about ``ssh://``
5521 Please see :hg:`help urls` for important details about ``ssh://``
5522 URLs. If DESTINATION is omitted, a default path will be used.
5522 URLs. If DESTINATION is omitted, a default path will be used.
5523
5523
5524 .. container:: verbose
5524 .. container:: verbose
5525
5525
5526 The --pushvars option sends strings to the server that become
5526 The --pushvars option sends strings to the server that become
5527 environment variables prepended with ``HG_USERVAR_``. For example,
5527 environment variables prepended with ``HG_USERVAR_``. For example,
5528 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5528 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5529 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5529 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5530
5530
5531 pushvars can provide for user-overridable hooks as well as set debug
5531 pushvars can provide for user-overridable hooks as well as set debug
5532 levels. One example is having a hook that blocks commits containing
5532 levels. One example is having a hook that blocks commits containing
5533 conflict markers, but enables the user to override the hook if the file
5533 conflict markers, but enables the user to override the hook if the file
5534 is using conflict markers for testing purposes or the file format has
5534 is using conflict markers for testing purposes or the file format has
5535 strings that look like conflict markers.
5535 strings that look like conflict markers.
5536
5536
5537 By default, servers will ignore `--pushvars`. To enable it add the
5537 By default, servers will ignore `--pushvars`. To enable it add the
5538 following to your configuration file::
5538 following to your configuration file::
5539
5539
5540 [push]
5540 [push]
5541 pushvars.server = true
5541 pushvars.server = true
5542
5542
5543 Returns 0 if push was successful, 1 if nothing to push.
5543 Returns 0 if push was successful, 1 if nothing to push.
5544 """
5544 """
5545
5545
5546 opts = pycompat.byteskwargs(opts)
5546 opts = pycompat.byteskwargs(opts)
5547
5547
5548 if opts.get(b'all_bookmarks'):
5548 if opts.get(b'all_bookmarks'):
5549 cmdutil.check_incompatible_arguments(
5549 cmdutil.check_incompatible_arguments(
5550 opts,
5550 opts,
5551 b'all_bookmarks',
5551 b'all_bookmarks',
5552 [b'bookmark', b'rev'],
5552 [b'bookmark', b'rev'],
5553 )
5553 )
5554 opts[b'bookmark'] = list(repo._bookmarks)
5554 opts[b'bookmark'] = list(repo._bookmarks)
5555
5555
5556 if opts.get(b'bookmark'):
5556 if opts.get(b'bookmark'):
5557 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5557 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5558 for b in opts[b'bookmark']:
5558 for b in opts[b'bookmark']:
5559 # translate -B options to -r so changesets get pushed
5559 # translate -B options to -r so changesets get pushed
5560 b = repo._bookmarks.expandname(b)
5560 b = repo._bookmarks.expandname(b)
5561 if b in repo._bookmarks:
5561 if b in repo._bookmarks:
5562 opts.setdefault(b'rev', []).append(b)
5562 opts.setdefault(b'rev', []).append(b)
5563 else:
5563 else:
5564 # if we try to push a deleted bookmark, translate it to null
5564 # if we try to push a deleted bookmark, translate it to null
5565 # this lets simultaneous -r, -b options continue working
5565 # this lets simultaneous -r, -b options continue working
5566 opts.setdefault(b'rev', []).append(b"null")
5566 opts.setdefault(b'rev', []).append(b"null")
5567
5567
5568 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5568 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5569 if not path:
5569 if not path:
5570 raise error.ConfigError(
5570 raise error.ConfigError(
5571 _(b'default repository not configured!'),
5571 _(b'default repository not configured!'),
5572 hint=_(b"see 'hg help config.paths'"),
5572 hint=_(b"see 'hg help config.paths'"),
5573 )
5573 )
5574 dest = path.pushloc or path.loc
5574 dest = path.pushloc or path.loc
5575 branches = (path.branch, opts.get(b'branch') or [])
5575 branches = (path.branch, opts.get(b'branch') or [])
5576 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5576 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5577 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5577 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5578 other = hg.peer(repo, opts, dest)
5578 other = hg.peer(repo, opts, dest)
5579
5579
5580 if revs:
5580 if revs:
5581 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5581 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5582 if not revs:
5582 if not revs:
5583 raise error.InputError(
5583 raise error.InputError(
5584 _(b"specified revisions evaluate to an empty set"),
5584 _(b"specified revisions evaluate to an empty set"),
5585 hint=_(b"use different revision arguments"),
5585 hint=_(b"use different revision arguments"),
5586 )
5586 )
5587 elif path.pushrev:
5587 elif path.pushrev:
5588 # It doesn't make any sense to specify ancestor revisions. So limit
5588 # It doesn't make any sense to specify ancestor revisions. So limit
5589 # to DAG heads to make discovery simpler.
5589 # to DAG heads to make discovery simpler.
5590 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5590 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5591 revs = scmutil.revrange(repo, [expr])
5591 revs = scmutil.revrange(repo, [expr])
5592 revs = [repo[rev].node() for rev in revs]
5592 revs = [repo[rev].node() for rev in revs]
5593 if not revs:
5593 if not revs:
5594 raise error.InputError(
5594 raise error.InputError(
5595 _(b'default push revset for path evaluates to an empty set')
5595 _(b'default push revset for path evaluates to an empty set')
5596 )
5596 )
5597 elif ui.configbool(b'commands', b'push.require-revs'):
5597 elif ui.configbool(b'commands', b'push.require-revs'):
5598 raise error.InputError(
5598 raise error.InputError(
5599 _(b'no revisions specified to push'),
5599 _(b'no revisions specified to push'),
5600 hint=_(b'did you mean "hg push -r ."?'),
5600 hint=_(b'did you mean "hg push -r ."?'),
5601 )
5601 )
5602
5602
5603 repo._subtoppath = dest
5603 repo._subtoppath = dest
5604 try:
5604 try:
5605 # push subrepos depth-first for coherent ordering
5605 # push subrepos depth-first for coherent ordering
5606 c = repo[b'.']
5606 c = repo[b'.']
5607 subs = c.substate # only repos that are committed
5607 subs = c.substate # only repos that are committed
5608 for s in sorted(subs):
5608 for s in sorted(subs):
5609 result = c.sub(s).push(opts)
5609 result = c.sub(s).push(opts)
5610 if result == 0:
5610 if result == 0:
5611 return not result
5611 return not result
5612 finally:
5612 finally:
5613 del repo._subtoppath
5613 del repo._subtoppath
5614
5614
5615 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5615 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5616 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5616 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5617
5617
5618 pushop = exchange.push(
5618 pushop = exchange.push(
5619 repo,
5619 repo,
5620 other,
5620 other,
5621 opts.get(b'force'),
5621 opts.get(b'force'),
5622 revs=revs,
5622 revs=revs,
5623 newbranch=opts.get(b'new_branch'),
5623 newbranch=opts.get(b'new_branch'),
5624 bookmarks=opts.get(b'bookmark', ()),
5624 bookmarks=opts.get(b'bookmark', ()),
5625 publish=opts.get(b'publish'),
5625 publish=opts.get(b'publish'),
5626 opargs=opargs,
5626 opargs=opargs,
5627 )
5627 )
5628
5628
5629 result = not pushop.cgresult
5629 result = not pushop.cgresult
5630
5630
5631 if pushop.bkresult is not None:
5631 if pushop.bkresult is not None:
5632 if pushop.bkresult == 2:
5632 if pushop.bkresult == 2:
5633 result = 2
5633 result = 2
5634 elif not result and pushop.bkresult:
5634 elif not result and pushop.bkresult:
5635 result = 2
5635 result = 2
5636
5636
5637 return result
5637 return result
5638
5638
5639
5639
5640 @command(
5640 @command(
5641 b'recover',
5641 b'recover',
5642 [
5642 [
5643 (b'', b'verify', False, b"run `hg verify` after successful recover"),
5643 (b'', b'verify', False, b"run `hg verify` after successful recover"),
5644 ],
5644 ],
5645 helpcategory=command.CATEGORY_MAINTENANCE,
5645 helpcategory=command.CATEGORY_MAINTENANCE,
5646 )
5646 )
5647 def recover(ui, repo, **opts):
5647 def recover(ui, repo, **opts):
5648 """roll back an interrupted transaction
5648 """roll back an interrupted transaction
5649
5649
5650 Recover from an interrupted commit or pull.
5650 Recover from an interrupted commit or pull.
5651
5651
5652 This command tries to fix the repository status after an
5652 This command tries to fix the repository status after an
5653 interrupted operation. It should only be necessary when Mercurial
5653 interrupted operation. It should only be necessary when Mercurial
5654 suggests it.
5654 suggests it.
5655
5655
5656 Returns 0 if successful, 1 if nothing to recover or verify fails.
5656 Returns 0 if successful, 1 if nothing to recover or verify fails.
5657 """
5657 """
5658 ret = repo.recover()
5658 ret = repo.recover()
5659 if ret:
5659 if ret:
5660 if opts['verify']:
5660 if opts['verify']:
5661 return hg.verify(repo)
5661 return hg.verify(repo)
5662 else:
5662 else:
5663 msg = _(
5663 msg = _(
5664 b"(verify step skipped, run `hg verify` to check your "
5664 b"(verify step skipped, run `hg verify` to check your "
5665 b"repository content)\n"
5665 b"repository content)\n"
5666 )
5666 )
5667 ui.warn(msg)
5667 ui.warn(msg)
5668 return 0
5668 return 0
5669 return 1
5669 return 1
5670
5670
5671
5671
5672 @command(
5672 @command(
5673 b'remove|rm',
5673 b'remove|rm',
5674 [
5674 [
5675 (b'A', b'after', None, _(b'record delete for missing files')),
5675 (b'A', b'after', None, _(b'record delete for missing files')),
5676 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5676 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5677 ]
5677 ]
5678 + subrepoopts
5678 + subrepoopts
5679 + walkopts
5679 + walkopts
5680 + dryrunopts,
5680 + dryrunopts,
5681 _(b'[OPTION]... FILE...'),
5681 _(b'[OPTION]... FILE...'),
5682 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5682 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5683 helpbasic=True,
5683 helpbasic=True,
5684 inferrepo=True,
5684 inferrepo=True,
5685 )
5685 )
5686 def remove(ui, repo, *pats, **opts):
5686 def remove(ui, repo, *pats, **opts):
5687 """remove the specified files on the next commit
5687 """remove the specified files on the next commit
5688
5688
5689 Schedule the indicated files for removal from the current branch.
5689 Schedule the indicated files for removal from the current branch.
5690
5690
5691 This command schedules the files to be removed at the next commit.
5691 This command schedules the files to be removed at the next commit.
5692 To undo a remove before that, see :hg:`revert`. To undo added
5692 To undo a remove before that, see :hg:`revert`. To undo added
5693 files, see :hg:`forget`.
5693 files, see :hg:`forget`.
5694
5694
5695 .. container:: verbose
5695 .. container:: verbose
5696
5696
5697 -A/--after can be used to remove only files that have already
5697 -A/--after can be used to remove only files that have already
5698 been deleted, -f/--force can be used to force deletion, and -Af
5698 been deleted, -f/--force can be used to force deletion, and -Af
5699 can be used to remove files from the next revision without
5699 can be used to remove files from the next revision without
5700 deleting them from the working directory.
5700 deleting them from the working directory.
5701
5701
5702 The following table details the behavior of remove for different
5702 The following table details the behavior of remove for different
5703 file states (columns) and option combinations (rows). The file
5703 file states (columns) and option combinations (rows). The file
5704 states are Added [A], Clean [C], Modified [M] and Missing [!]
5704 states are Added [A], Clean [C], Modified [M] and Missing [!]
5705 (as reported by :hg:`status`). The actions are Warn, Remove
5705 (as reported by :hg:`status`). The actions are Warn, Remove
5706 (from branch) and Delete (from disk):
5706 (from branch) and Delete (from disk):
5707
5707
5708 ========= == == == ==
5708 ========= == == == ==
5709 opt/state A C M !
5709 opt/state A C M !
5710 ========= == == == ==
5710 ========= == == == ==
5711 none W RD W R
5711 none W RD W R
5712 -f R RD RD R
5712 -f R RD RD R
5713 -A W W W R
5713 -A W W W R
5714 -Af R R R R
5714 -Af R R R R
5715 ========= == == == ==
5715 ========= == == == ==
5716
5716
5717 .. note::
5717 .. note::
5718
5718
5719 :hg:`remove` never deletes files in Added [A] state from the
5719 :hg:`remove` never deletes files in Added [A] state from the
5720 working directory, not even if ``--force`` is specified.
5720 working directory, not even if ``--force`` is specified.
5721
5721
5722 Returns 0 on success, 1 if any warnings encountered.
5722 Returns 0 on success, 1 if any warnings encountered.
5723 """
5723 """
5724
5724
5725 opts = pycompat.byteskwargs(opts)
5725 opts = pycompat.byteskwargs(opts)
5726 after, force = opts.get(b'after'), opts.get(b'force')
5726 after, force = opts.get(b'after'), opts.get(b'force')
5727 dryrun = opts.get(b'dry_run')
5727 dryrun = opts.get(b'dry_run')
5728 if not pats and not after:
5728 if not pats and not after:
5729 raise error.InputError(_(b'no files specified'))
5729 raise error.InputError(_(b'no files specified'))
5730
5730
5731 m = scmutil.match(repo[None], pats, opts)
5731 m = scmutil.match(repo[None], pats, opts)
5732 subrepos = opts.get(b'subrepos')
5732 subrepos = opts.get(b'subrepos')
5733 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5733 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5734 return cmdutil.remove(
5734 return cmdutil.remove(
5735 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5735 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5736 )
5736 )
5737
5737
5738
5738
5739 @command(
5739 @command(
5740 b'rename|move|mv',
5740 b'rename|move|mv',
5741 [
5741 [
5742 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5742 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5743 (
5743 (
5744 b'',
5744 b'',
5745 b'at-rev',
5745 b'at-rev',
5746 b'',
5746 b'',
5747 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5747 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5748 _(b'REV'),
5748 _(b'REV'),
5749 ),
5749 ),
5750 (
5750 (
5751 b'f',
5751 b'f',
5752 b'force',
5752 b'force',
5753 None,
5753 None,
5754 _(b'forcibly move over an existing managed file'),
5754 _(b'forcibly move over an existing managed file'),
5755 ),
5755 ),
5756 ]
5756 ]
5757 + walkopts
5757 + walkopts
5758 + dryrunopts,
5758 + dryrunopts,
5759 _(b'[OPTION]... SOURCE... DEST'),
5759 _(b'[OPTION]... SOURCE... DEST'),
5760 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5760 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5761 )
5761 )
5762 def rename(ui, repo, *pats, **opts):
5762 def rename(ui, repo, *pats, **opts):
5763 """rename files; equivalent of copy + remove
5763 """rename files; equivalent of copy + remove
5764
5764
5765 Mark dest as copies of sources; mark sources for deletion. If dest
5765 Mark dest as copies of sources; mark sources for deletion. If dest
5766 is a directory, copies are put in that directory. If dest is a
5766 is a directory, copies are put in that directory. If dest is a
5767 file, there can only be one source.
5767 file, there can only be one source.
5768
5768
5769 By default, this command copies the contents of files as they
5769 By default, this command copies the contents of files as they
5770 exist in the working directory. If invoked with -A/--after, the
5770 exist in the working directory. If invoked with -A/--after, the
5771 operation is recorded, but no copying is performed.
5771 operation is recorded, but no copying is performed.
5772
5772
5773 This command takes effect at the next commit. To undo a rename
5773 This command takes effect at the next commit. To undo a rename
5774 before that, see :hg:`revert`.
5774 before that, see :hg:`revert`.
5775
5775
5776 Returns 0 on success, 1 if errors are encountered.
5776 Returns 0 on success, 1 if errors are encountered.
5777 """
5777 """
5778 opts = pycompat.byteskwargs(opts)
5778 opts = pycompat.byteskwargs(opts)
5779 with repo.wlock():
5779 with repo.wlock():
5780 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5780 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5781
5781
5782
5782
5783 @command(
5783 @command(
5784 b'resolve',
5784 b'resolve',
5785 [
5785 [
5786 (b'a', b'all', None, _(b'select all unresolved files')),
5786 (b'a', b'all', None, _(b'select all unresolved files')),
5787 (b'l', b'list', None, _(b'list state of files needing merge')),
5787 (b'l', b'list', None, _(b'list state of files needing merge')),
5788 (b'm', b'mark', None, _(b'mark files as resolved')),
5788 (b'm', b'mark', None, _(b'mark files as resolved')),
5789 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5789 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5790 (b'n', b'no-status', None, _(b'hide status prefix')),
5790 (b'n', b'no-status', None, _(b'hide status prefix')),
5791 (b'', b're-merge', None, _(b're-merge files')),
5791 (b'', b're-merge', None, _(b're-merge files')),
5792 ]
5792 ]
5793 + mergetoolopts
5793 + mergetoolopts
5794 + walkopts
5794 + walkopts
5795 + formatteropts,
5795 + formatteropts,
5796 _(b'[OPTION]... [FILE]...'),
5796 _(b'[OPTION]... [FILE]...'),
5797 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5797 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5798 inferrepo=True,
5798 inferrepo=True,
5799 )
5799 )
5800 def resolve(ui, repo, *pats, **opts):
5800 def resolve(ui, repo, *pats, **opts):
5801 """redo merges or set/view the merge status of files
5801 """redo merges or set/view the merge status of files
5802
5802
5803 Merges with unresolved conflicts are often the result of
5803 Merges with unresolved conflicts are often the result of
5804 non-interactive merging using the ``internal:merge`` configuration
5804 non-interactive merging using the ``internal:merge`` configuration
5805 setting, or a command-line merge tool like ``diff3``. The resolve
5805 setting, or a command-line merge tool like ``diff3``. The resolve
5806 command is used to manage the files involved in a merge, after
5806 command is used to manage the files involved in a merge, after
5807 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5807 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5808 working directory must have two parents). See :hg:`help
5808 working directory must have two parents). See :hg:`help
5809 merge-tools` for information on configuring merge tools.
5809 merge-tools` for information on configuring merge tools.
5810
5810
5811 The resolve command can be used in the following ways:
5811 The resolve command can be used in the following ways:
5812
5812
5813 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5813 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5814 the specified files, discarding any previous merge attempts. Re-merging
5814 the specified files, discarding any previous merge attempts. Re-merging
5815 is not performed for files already marked as resolved. Use ``--all/-a``
5815 is not performed for files already marked as resolved. Use ``--all/-a``
5816 to select all unresolved files. ``--tool`` can be used to specify
5816 to select all unresolved files. ``--tool`` can be used to specify
5817 the merge tool used for the given files. It overrides the HGMERGE
5817 the merge tool used for the given files. It overrides the HGMERGE
5818 environment variable and your configuration files. Previous file
5818 environment variable and your configuration files. Previous file
5819 contents are saved with a ``.orig`` suffix.
5819 contents are saved with a ``.orig`` suffix.
5820
5820
5821 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5821 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5822 (e.g. after having manually fixed-up the files). The default is
5822 (e.g. after having manually fixed-up the files). The default is
5823 to mark all unresolved files.
5823 to mark all unresolved files.
5824
5824
5825 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5825 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5826 default is to mark all resolved files.
5826 default is to mark all resolved files.
5827
5827
5828 - :hg:`resolve -l`: list files which had or still have conflicts.
5828 - :hg:`resolve -l`: list files which had or still have conflicts.
5829 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5829 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5830 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5830 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5831 the list. See :hg:`help filesets` for details.
5831 the list. See :hg:`help filesets` for details.
5832
5832
5833 .. note::
5833 .. note::
5834
5834
5835 Mercurial will not let you commit files with unresolved merge
5835 Mercurial will not let you commit files with unresolved merge
5836 conflicts. You must use :hg:`resolve -m ...` before you can
5836 conflicts. You must use :hg:`resolve -m ...` before you can
5837 commit after a conflicting merge.
5837 commit after a conflicting merge.
5838
5838
5839 .. container:: verbose
5839 .. container:: verbose
5840
5840
5841 Template:
5841 Template:
5842
5842
5843 The following keywords are supported in addition to the common template
5843 The following keywords are supported in addition to the common template
5844 keywords and functions. See also :hg:`help templates`.
5844 keywords and functions. See also :hg:`help templates`.
5845
5845
5846 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5846 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5847 :path: String. Repository-absolute path of the file.
5847 :path: String. Repository-absolute path of the file.
5848
5848
5849 Returns 0 on success, 1 if any files fail a resolve attempt.
5849 Returns 0 on success, 1 if any files fail a resolve attempt.
5850 """
5850 """
5851
5851
5852 opts = pycompat.byteskwargs(opts)
5852 opts = pycompat.byteskwargs(opts)
5853 confirm = ui.configbool(b'commands', b'resolve.confirm')
5853 confirm = ui.configbool(b'commands', b'resolve.confirm')
5854 flaglist = b'all mark unmark list no_status re_merge'.split()
5854 flaglist = b'all mark unmark list no_status re_merge'.split()
5855 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5855 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5856
5856
5857 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5857 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5858 if actioncount > 1:
5858 if actioncount > 1:
5859 raise error.InputError(_(b"too many actions specified"))
5859 raise error.InputError(_(b"too many actions specified"))
5860 elif actioncount == 0 and ui.configbool(
5860 elif actioncount == 0 and ui.configbool(
5861 b'commands', b'resolve.explicit-re-merge'
5861 b'commands', b'resolve.explicit-re-merge'
5862 ):
5862 ):
5863 hint = _(b'use --mark, --unmark, --list or --re-merge')
5863 hint = _(b'use --mark, --unmark, --list or --re-merge')
5864 raise error.InputError(_(b'no action specified'), hint=hint)
5864 raise error.InputError(_(b'no action specified'), hint=hint)
5865 if pats and all:
5865 if pats and all:
5866 raise error.InputError(_(b"can't specify --all and patterns"))
5866 raise error.InputError(_(b"can't specify --all and patterns"))
5867 if not (all or pats or show or mark or unmark):
5867 if not (all or pats or show or mark or unmark):
5868 raise error.InputError(
5868 raise error.InputError(
5869 _(b'no files or directories specified'),
5869 _(b'no files or directories specified'),
5870 hint=b'use --all to re-merge all unresolved files',
5870 hint=b'use --all to re-merge all unresolved files',
5871 )
5871 )
5872
5872
5873 if confirm:
5873 if confirm:
5874 if all:
5874 if all:
5875 if ui.promptchoice(
5875 if ui.promptchoice(
5876 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5876 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5877 ):
5877 ):
5878 raise error.CanceledError(_(b'user quit'))
5878 raise error.CanceledError(_(b'user quit'))
5879 if mark and not pats:
5879 if mark and not pats:
5880 if ui.promptchoice(
5880 if ui.promptchoice(
5881 _(
5881 _(
5882 b'mark all unresolved files as resolved (yn)?'
5882 b'mark all unresolved files as resolved (yn)?'
5883 b'$$ &Yes $$ &No'
5883 b'$$ &Yes $$ &No'
5884 )
5884 )
5885 ):
5885 ):
5886 raise error.CanceledError(_(b'user quit'))
5886 raise error.CanceledError(_(b'user quit'))
5887 if unmark and not pats:
5887 if unmark and not pats:
5888 if ui.promptchoice(
5888 if ui.promptchoice(
5889 _(
5889 _(
5890 b'mark all resolved files as unresolved (yn)?'
5890 b'mark all resolved files as unresolved (yn)?'
5891 b'$$ &Yes $$ &No'
5891 b'$$ &Yes $$ &No'
5892 )
5892 )
5893 ):
5893 ):
5894 raise error.CanceledError(_(b'user quit'))
5894 raise error.CanceledError(_(b'user quit'))
5895
5895
5896 uipathfn = scmutil.getuipathfn(repo)
5896 uipathfn = scmutil.getuipathfn(repo)
5897
5897
5898 if show:
5898 if show:
5899 ui.pager(b'resolve')
5899 ui.pager(b'resolve')
5900 fm = ui.formatter(b'resolve', opts)
5900 fm = ui.formatter(b'resolve', opts)
5901 ms = mergestatemod.mergestate.read(repo)
5901 ms = mergestatemod.mergestate.read(repo)
5902 wctx = repo[None]
5902 wctx = repo[None]
5903 m = scmutil.match(wctx, pats, opts)
5903 m = scmutil.match(wctx, pats, opts)
5904
5904
5905 # Labels and keys based on merge state. Unresolved path conflicts show
5905 # Labels and keys based on merge state. Unresolved path conflicts show
5906 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5906 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5907 # resolved conflicts.
5907 # resolved conflicts.
5908 mergestateinfo = {
5908 mergestateinfo = {
5909 mergestatemod.MERGE_RECORD_UNRESOLVED: (
5909 mergestatemod.MERGE_RECORD_UNRESOLVED: (
5910 b'resolve.unresolved',
5910 b'resolve.unresolved',
5911 b'U',
5911 b'U',
5912 ),
5912 ),
5913 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5913 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5914 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
5914 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
5915 b'resolve.unresolved',
5915 b'resolve.unresolved',
5916 b'P',
5916 b'P',
5917 ),
5917 ),
5918 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
5918 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
5919 b'resolve.resolved',
5919 b'resolve.resolved',
5920 b'R',
5920 b'R',
5921 ),
5921 ),
5922 }
5922 }
5923
5923
5924 for f in ms:
5924 for f in ms:
5925 if not m(f):
5925 if not m(f):
5926 continue
5926 continue
5927
5927
5928 label, key = mergestateinfo[ms[f]]
5928 label, key = mergestateinfo[ms[f]]
5929 fm.startitem()
5929 fm.startitem()
5930 fm.context(ctx=wctx)
5930 fm.context(ctx=wctx)
5931 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5931 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5932 fm.data(path=f)
5932 fm.data(path=f)
5933 fm.plain(b'%s\n' % uipathfn(f), label=label)
5933 fm.plain(b'%s\n' % uipathfn(f), label=label)
5934 fm.end()
5934 fm.end()
5935 return 0
5935 return 0
5936
5936
5937 with repo.wlock():
5937 with repo.wlock():
5938 ms = mergestatemod.mergestate.read(repo)
5938 ms = mergestatemod.mergestate.read(repo)
5939
5939
5940 if not (ms.active() or repo.dirstate.p2() != nullid):
5940 if not (ms.active() or repo.dirstate.p2() != nullid):
5941 raise error.StateError(
5941 raise error.StateError(
5942 _(b'resolve command not applicable when not merging')
5942 _(b'resolve command not applicable when not merging')
5943 )
5943 )
5944
5944
5945 wctx = repo[None]
5945 wctx = repo[None]
5946 m = scmutil.match(wctx, pats, opts)
5946 m = scmutil.match(wctx, pats, opts)
5947 ret = 0
5947 ret = 0
5948 didwork = False
5948 didwork = False
5949
5949
5950 tocomplete = []
5950 tocomplete = []
5951 hasconflictmarkers = []
5951 hasconflictmarkers = []
5952 if mark:
5952 if mark:
5953 markcheck = ui.config(b'commands', b'resolve.mark-check')
5953 markcheck = ui.config(b'commands', b'resolve.mark-check')
5954 if markcheck not in [b'warn', b'abort']:
5954 if markcheck not in [b'warn', b'abort']:
5955 # Treat all invalid / unrecognized values as 'none'.
5955 # Treat all invalid / unrecognized values as 'none'.
5956 markcheck = False
5956 markcheck = False
5957 for f in ms:
5957 for f in ms:
5958 if not m(f):
5958 if not m(f):
5959 continue
5959 continue
5960
5960
5961 didwork = True
5961 didwork = True
5962
5962
5963 # path conflicts must be resolved manually
5963 # path conflicts must be resolved manually
5964 if ms[f] in (
5964 if ms[f] in (
5965 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
5965 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
5966 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
5966 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
5967 ):
5967 ):
5968 if mark:
5968 if mark:
5969 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
5969 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
5970 elif unmark:
5970 elif unmark:
5971 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
5971 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
5972 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
5972 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
5973 ui.warn(
5973 ui.warn(
5974 _(b'%s: path conflict must be resolved manually\n')
5974 _(b'%s: path conflict must be resolved manually\n')
5975 % uipathfn(f)
5975 % uipathfn(f)
5976 )
5976 )
5977 continue
5977 continue
5978
5978
5979 if mark:
5979 if mark:
5980 if markcheck:
5980 if markcheck:
5981 fdata = repo.wvfs.tryread(f)
5981 fdata = repo.wvfs.tryread(f)
5982 if (
5982 if (
5983 filemerge.hasconflictmarkers(fdata)
5983 filemerge.hasconflictmarkers(fdata)
5984 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
5984 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
5985 ):
5985 ):
5986 hasconflictmarkers.append(f)
5986 hasconflictmarkers.append(f)
5987 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
5987 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
5988 elif unmark:
5988 elif unmark:
5989 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
5989 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
5990 else:
5990 else:
5991 # backup pre-resolve (merge uses .orig for its own purposes)
5991 # backup pre-resolve (merge uses .orig for its own purposes)
5992 a = repo.wjoin(f)
5992 a = repo.wjoin(f)
5993 try:
5993 try:
5994 util.copyfile(a, a + b".resolve")
5994 util.copyfile(a, a + b".resolve")
5995 except (IOError, OSError) as inst:
5995 except (IOError, OSError) as inst:
5996 if inst.errno != errno.ENOENT:
5996 if inst.errno != errno.ENOENT:
5997 raise
5997 raise
5998
5998
5999 try:
5999 try:
6000 # preresolve file
6000 # preresolve file
6001 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6001 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6002 with ui.configoverride(overrides, b'resolve'):
6002 with ui.configoverride(overrides, b'resolve'):
6003 complete, r = ms.preresolve(f, wctx)
6003 complete, r = ms.preresolve(f, wctx)
6004 if not complete:
6004 if not complete:
6005 tocomplete.append(f)
6005 tocomplete.append(f)
6006 elif r:
6006 elif r:
6007 ret = 1
6007 ret = 1
6008 finally:
6008 finally:
6009 ms.commit()
6009 ms.commit()
6010
6010
6011 # replace filemerge's .orig file with our resolve file, but only
6011 # replace filemerge's .orig file with our resolve file, but only
6012 # for merges that are complete
6012 # for merges that are complete
6013 if complete:
6013 if complete:
6014 try:
6014 try:
6015 util.rename(
6015 util.rename(
6016 a + b".resolve", scmutil.backuppath(ui, repo, f)
6016 a + b".resolve", scmutil.backuppath(ui, repo, f)
6017 )
6017 )
6018 except OSError as inst:
6018 except OSError as inst:
6019 if inst.errno != errno.ENOENT:
6019 if inst.errno != errno.ENOENT:
6020 raise
6020 raise
6021
6021
6022 if hasconflictmarkers:
6022 if hasconflictmarkers:
6023 ui.warn(
6023 ui.warn(
6024 _(
6024 _(
6025 b'warning: the following files still have conflict '
6025 b'warning: the following files still have conflict '
6026 b'markers:\n'
6026 b'markers:\n'
6027 )
6027 )
6028 + b''.join(
6028 + b''.join(
6029 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6029 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6030 )
6030 )
6031 )
6031 )
6032 if markcheck == b'abort' and not all and not pats:
6032 if markcheck == b'abort' and not all and not pats:
6033 raise error.StateError(
6033 raise error.StateError(
6034 _(b'conflict markers detected'),
6034 _(b'conflict markers detected'),
6035 hint=_(b'use --all to mark anyway'),
6035 hint=_(b'use --all to mark anyway'),
6036 )
6036 )
6037
6037
6038 for f in tocomplete:
6038 for f in tocomplete:
6039 try:
6039 try:
6040 # resolve file
6040 # resolve file
6041 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6041 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6042 with ui.configoverride(overrides, b'resolve'):
6042 with ui.configoverride(overrides, b'resolve'):
6043 r = ms.resolve(f, wctx)
6043 r = ms.resolve(f, wctx)
6044 if r:
6044 if r:
6045 ret = 1
6045 ret = 1
6046 finally:
6046 finally:
6047 ms.commit()
6047 ms.commit()
6048
6048
6049 # replace filemerge's .orig file with our resolve file
6049 # replace filemerge's .orig file with our resolve file
6050 a = repo.wjoin(f)
6050 a = repo.wjoin(f)
6051 try:
6051 try:
6052 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6052 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6053 except OSError as inst:
6053 except OSError as inst:
6054 if inst.errno != errno.ENOENT:
6054 if inst.errno != errno.ENOENT:
6055 raise
6055 raise
6056
6056
6057 ms.commit()
6057 ms.commit()
6058 branchmerge = repo.dirstate.p2() != nullid
6058 branchmerge = repo.dirstate.p2() != nullid
6059 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6059 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6060
6060
6061 if not didwork and pats:
6061 if not didwork and pats:
6062 hint = None
6062 hint = None
6063 if not any([p for p in pats if p.find(b':') >= 0]):
6063 if not any([p for p in pats if p.find(b':') >= 0]):
6064 pats = [b'path:%s' % p for p in pats]
6064 pats = [b'path:%s' % p for p in pats]
6065 m = scmutil.match(wctx, pats, opts)
6065 m = scmutil.match(wctx, pats, opts)
6066 for f in ms:
6066 for f in ms:
6067 if not m(f):
6067 if not m(f):
6068 continue
6068 continue
6069
6069
6070 def flag(o):
6070 def flag(o):
6071 if o == b're_merge':
6071 if o == b're_merge':
6072 return b'--re-merge '
6072 return b'--re-merge '
6073 return b'-%s ' % o[0:1]
6073 return b'-%s ' % o[0:1]
6074
6074
6075 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6075 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6076 hint = _(b"(try: hg resolve %s%s)\n") % (
6076 hint = _(b"(try: hg resolve %s%s)\n") % (
6077 flags,
6077 flags,
6078 b' '.join(pats),
6078 b' '.join(pats),
6079 )
6079 )
6080 break
6080 break
6081 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6081 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6082 if hint:
6082 if hint:
6083 ui.warn(hint)
6083 ui.warn(hint)
6084
6084
6085 unresolvedf = list(ms.unresolved())
6085 unresolvedf = list(ms.unresolved())
6086 if not unresolvedf:
6086 if not unresolvedf:
6087 ui.status(_(b'(no more unresolved files)\n'))
6087 ui.status(_(b'(no more unresolved files)\n'))
6088 cmdutil.checkafterresolved(repo)
6088 cmdutil.checkafterresolved(repo)
6089
6089
6090 return ret
6090 return ret
6091
6091
6092
6092
6093 @command(
6093 @command(
6094 b'revert',
6094 b'revert',
6095 [
6095 [
6096 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6096 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6097 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6097 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6098 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6098 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6099 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6099 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6100 (b'i', b'interactive', None, _(b'interactively select the changes')),
6100 (b'i', b'interactive', None, _(b'interactively select the changes')),
6101 ]
6101 ]
6102 + walkopts
6102 + walkopts
6103 + dryrunopts,
6103 + dryrunopts,
6104 _(b'[OPTION]... [-r REV] [NAME]...'),
6104 _(b'[OPTION]... [-r REV] [NAME]...'),
6105 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6105 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6106 )
6106 )
6107 def revert(ui, repo, *pats, **opts):
6107 def revert(ui, repo, *pats, **opts):
6108 """restore files to their checkout state
6108 """restore files to their checkout state
6109
6109
6110 .. note::
6110 .. note::
6111
6111
6112 To check out earlier revisions, you should use :hg:`update REV`.
6112 To check out earlier revisions, you should use :hg:`update REV`.
6113 To cancel an uncommitted merge (and lose your changes),
6113 To cancel an uncommitted merge (and lose your changes),
6114 use :hg:`merge --abort`.
6114 use :hg:`merge --abort`.
6115
6115
6116 With no revision specified, revert the specified files or directories
6116 With no revision specified, revert the specified files or directories
6117 to the contents they had in the parent of the working directory.
6117 to the contents they had in the parent of the working directory.
6118 This restores the contents of files to an unmodified
6118 This restores the contents of files to an unmodified
6119 state and unschedules adds, removes, copies, and renames. If the
6119 state and unschedules adds, removes, copies, and renames. If the
6120 working directory has two parents, you must explicitly specify a
6120 working directory has two parents, you must explicitly specify a
6121 revision.
6121 revision.
6122
6122
6123 Using the -r/--rev or -d/--date options, revert the given files or
6123 Using the -r/--rev or -d/--date options, revert the given files or
6124 directories to their states as of a specific revision. Because
6124 directories to their states as of a specific revision. Because
6125 revert does not change the working directory parents, this will
6125 revert does not change the working directory parents, this will
6126 cause these files to appear modified. This can be helpful to "back
6126 cause these files to appear modified. This can be helpful to "back
6127 out" some or all of an earlier change. See :hg:`backout` for a
6127 out" some or all of an earlier change. See :hg:`backout` for a
6128 related method.
6128 related method.
6129
6129
6130 Modified files are saved with a .orig suffix before reverting.
6130 Modified files are saved with a .orig suffix before reverting.
6131 To disable these backups, use --no-backup. It is possible to store
6131 To disable these backups, use --no-backup. It is possible to store
6132 the backup files in a custom directory relative to the root of the
6132 the backup files in a custom directory relative to the root of the
6133 repository by setting the ``ui.origbackuppath`` configuration
6133 repository by setting the ``ui.origbackuppath`` configuration
6134 option.
6134 option.
6135
6135
6136 See :hg:`help dates` for a list of formats valid for -d/--date.
6136 See :hg:`help dates` for a list of formats valid for -d/--date.
6137
6137
6138 See :hg:`help backout` for a way to reverse the effect of an
6138 See :hg:`help backout` for a way to reverse the effect of an
6139 earlier changeset.
6139 earlier changeset.
6140
6140
6141 Returns 0 on success.
6141 Returns 0 on success.
6142 """
6142 """
6143
6143
6144 opts = pycompat.byteskwargs(opts)
6144 opts = pycompat.byteskwargs(opts)
6145 if opts.get(b"date"):
6145 if opts.get(b"date"):
6146 cmdutil.check_incompatible_arguments(opts, b'date', [b'rev'])
6146 cmdutil.check_incompatible_arguments(opts, b'date', [b'rev'])
6147 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6147 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6148
6148
6149 parent, p2 = repo.dirstate.parents()
6149 parent, p2 = repo.dirstate.parents()
6150 if not opts.get(b'rev') and p2 != nullid:
6150 if not opts.get(b'rev') and p2 != nullid:
6151 # revert after merge is a trap for new users (issue2915)
6151 # revert after merge is a trap for new users (issue2915)
6152 raise error.InputError(
6152 raise error.InputError(
6153 _(b'uncommitted merge with no revision specified'),
6153 _(b'uncommitted merge with no revision specified'),
6154 hint=_(b"use 'hg update' or see 'hg help revert'"),
6154 hint=_(b"use 'hg update' or see 'hg help revert'"),
6155 )
6155 )
6156
6156
6157 rev = opts.get(b'rev')
6157 rev = opts.get(b'rev')
6158 if rev:
6158 if rev:
6159 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6159 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6160 ctx = scmutil.revsingle(repo, rev)
6160 ctx = scmutil.revsingle(repo, rev)
6161
6161
6162 if not (
6162 if not (
6163 pats
6163 pats
6164 or opts.get(b'include')
6164 or opts.get(b'include')
6165 or opts.get(b'exclude')
6165 or opts.get(b'exclude')
6166 or opts.get(b'all')
6166 or opts.get(b'all')
6167 or opts.get(b'interactive')
6167 or opts.get(b'interactive')
6168 ):
6168 ):
6169 msg = _(b"no files or directories specified")
6169 msg = _(b"no files or directories specified")
6170 if p2 != nullid:
6170 if p2 != nullid:
6171 hint = _(
6171 hint = _(
6172 b"uncommitted merge, use --all to discard all changes,"
6172 b"uncommitted merge, use --all to discard all changes,"
6173 b" or 'hg update -C .' to abort the merge"
6173 b" or 'hg update -C .' to abort the merge"
6174 )
6174 )
6175 raise error.InputError(msg, hint=hint)
6175 raise error.InputError(msg, hint=hint)
6176 dirty = any(repo.status())
6176 dirty = any(repo.status())
6177 node = ctx.node()
6177 node = ctx.node()
6178 if node != parent:
6178 if node != parent:
6179 if dirty:
6179 if dirty:
6180 hint = (
6180 hint = (
6181 _(
6181 _(
6182 b"uncommitted changes, use --all to discard all"
6182 b"uncommitted changes, use --all to discard all"
6183 b" changes, or 'hg update %d' to update"
6183 b" changes, or 'hg update %d' to update"
6184 )
6184 )
6185 % ctx.rev()
6185 % ctx.rev()
6186 )
6186 )
6187 else:
6187 else:
6188 hint = (
6188 hint = (
6189 _(
6189 _(
6190 b"use --all to revert all files,"
6190 b"use --all to revert all files,"
6191 b" or 'hg update %d' to update"
6191 b" or 'hg update %d' to update"
6192 )
6192 )
6193 % ctx.rev()
6193 % ctx.rev()
6194 )
6194 )
6195 elif dirty:
6195 elif dirty:
6196 hint = _(b"uncommitted changes, use --all to discard all changes")
6196 hint = _(b"uncommitted changes, use --all to discard all changes")
6197 else:
6197 else:
6198 hint = _(b"use --all to revert all files")
6198 hint = _(b"use --all to revert all files")
6199 raise error.InputError(msg, hint=hint)
6199 raise error.InputError(msg, hint=hint)
6200
6200
6201 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6201 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6202
6202
6203
6203
6204 @command(
6204 @command(
6205 b'rollback',
6205 b'rollback',
6206 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6206 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6207 helpcategory=command.CATEGORY_MAINTENANCE,
6207 helpcategory=command.CATEGORY_MAINTENANCE,
6208 )
6208 )
6209 def rollback(ui, repo, **opts):
6209 def rollback(ui, repo, **opts):
6210 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6210 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6211
6211
6212 Please use :hg:`commit --amend` instead of rollback to correct
6212 Please use :hg:`commit --amend` instead of rollback to correct
6213 mistakes in the last commit.
6213 mistakes in the last commit.
6214
6214
6215 This command should be used with care. There is only one level of
6215 This command should be used with care. There is only one level of
6216 rollback, and there is no way to undo a rollback. It will also
6216 rollback, and there is no way to undo a rollback. It will also
6217 restore the dirstate at the time of the last transaction, losing
6217 restore the dirstate at the time of the last transaction, losing
6218 any dirstate changes since that time. This command does not alter
6218 any dirstate changes since that time. This command does not alter
6219 the working directory.
6219 the working directory.
6220
6220
6221 Transactions are used to encapsulate the effects of all commands
6221 Transactions are used to encapsulate the effects of all commands
6222 that create new changesets or propagate existing changesets into a
6222 that create new changesets or propagate existing changesets into a
6223 repository.
6223 repository.
6224
6224
6225 .. container:: verbose
6225 .. container:: verbose
6226
6226
6227 For example, the following commands are transactional, and their
6227 For example, the following commands are transactional, and their
6228 effects can be rolled back:
6228 effects can be rolled back:
6229
6229
6230 - commit
6230 - commit
6231 - import
6231 - import
6232 - pull
6232 - pull
6233 - push (with this repository as the destination)
6233 - push (with this repository as the destination)
6234 - unbundle
6234 - unbundle
6235
6235
6236 To avoid permanent data loss, rollback will refuse to rollback a
6236 To avoid permanent data loss, rollback will refuse to rollback a
6237 commit transaction if it isn't checked out. Use --force to
6237 commit transaction if it isn't checked out. Use --force to
6238 override this protection.
6238 override this protection.
6239
6239
6240 The rollback command can be entirely disabled by setting the
6240 The rollback command can be entirely disabled by setting the
6241 ``ui.rollback`` configuration setting to false. If you're here
6241 ``ui.rollback`` configuration setting to false. If you're here
6242 because you want to use rollback and it's disabled, you can
6242 because you want to use rollback and it's disabled, you can
6243 re-enable the command by setting ``ui.rollback`` to true.
6243 re-enable the command by setting ``ui.rollback`` to true.
6244
6244
6245 This command is not intended for use on public repositories. Once
6245 This command is not intended for use on public repositories. Once
6246 changes are visible for pull by other users, rolling a transaction
6246 changes are visible for pull by other users, rolling a transaction
6247 back locally is ineffective (someone else may already have pulled
6247 back locally is ineffective (someone else may already have pulled
6248 the changes). Furthermore, a race is possible with readers of the
6248 the changes). Furthermore, a race is possible with readers of the
6249 repository; for example an in-progress pull from the repository
6249 repository; for example an in-progress pull from the repository
6250 may fail if a rollback is performed.
6250 may fail if a rollback is performed.
6251
6251
6252 Returns 0 on success, 1 if no rollback data is available.
6252 Returns 0 on success, 1 if no rollback data is available.
6253 """
6253 """
6254 if not ui.configbool(b'ui', b'rollback'):
6254 if not ui.configbool(b'ui', b'rollback'):
6255 raise error.Abort(
6255 raise error.Abort(
6256 _(b'rollback is disabled because it is unsafe'),
6256 _(b'rollback is disabled because it is unsafe'),
6257 hint=b'see `hg help -v rollback` for information',
6257 hint=b'see `hg help -v rollback` for information',
6258 )
6258 )
6259 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6259 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6260
6260
6261
6261
6262 @command(
6262 @command(
6263 b'root',
6263 b'root',
6264 [] + formatteropts,
6264 [] + formatteropts,
6265 intents={INTENT_READONLY},
6265 intents={INTENT_READONLY},
6266 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6266 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6267 )
6267 )
6268 def root(ui, repo, **opts):
6268 def root(ui, repo, **opts):
6269 """print the root (top) of the current working directory
6269 """print the root (top) of the current working directory
6270
6270
6271 Print the root directory of the current repository.
6271 Print the root directory of the current repository.
6272
6272
6273 .. container:: verbose
6273 .. container:: verbose
6274
6274
6275 Template:
6275 Template:
6276
6276
6277 The following keywords are supported in addition to the common template
6277 The following keywords are supported in addition to the common template
6278 keywords and functions. See also :hg:`help templates`.
6278 keywords and functions. See also :hg:`help templates`.
6279
6279
6280 :hgpath: String. Path to the .hg directory.
6280 :hgpath: String. Path to the .hg directory.
6281 :storepath: String. Path to the directory holding versioned data.
6281 :storepath: String. Path to the directory holding versioned data.
6282
6282
6283 Returns 0 on success.
6283 Returns 0 on success.
6284 """
6284 """
6285 opts = pycompat.byteskwargs(opts)
6285 opts = pycompat.byteskwargs(opts)
6286 with ui.formatter(b'root', opts) as fm:
6286 with ui.formatter(b'root', opts) as fm:
6287 fm.startitem()
6287 fm.startitem()
6288 fm.write(b'reporoot', b'%s\n', repo.root)
6288 fm.write(b'reporoot', b'%s\n', repo.root)
6289 fm.data(hgpath=repo.path, storepath=repo.spath)
6289 fm.data(hgpath=repo.path, storepath=repo.spath)
6290
6290
6291
6291
6292 @command(
6292 @command(
6293 b'serve',
6293 b'serve',
6294 [
6294 [
6295 (
6295 (
6296 b'A',
6296 b'A',
6297 b'accesslog',
6297 b'accesslog',
6298 b'',
6298 b'',
6299 _(b'name of access log file to write to'),
6299 _(b'name of access log file to write to'),
6300 _(b'FILE'),
6300 _(b'FILE'),
6301 ),
6301 ),
6302 (b'd', b'daemon', None, _(b'run server in background')),
6302 (b'd', b'daemon', None, _(b'run server in background')),
6303 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6303 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6304 (
6304 (
6305 b'E',
6305 b'E',
6306 b'errorlog',
6306 b'errorlog',
6307 b'',
6307 b'',
6308 _(b'name of error log file to write to'),
6308 _(b'name of error log file to write to'),
6309 _(b'FILE'),
6309 _(b'FILE'),
6310 ),
6310 ),
6311 # use string type, then we can check if something was passed
6311 # use string type, then we can check if something was passed
6312 (
6312 (
6313 b'p',
6313 b'p',
6314 b'port',
6314 b'port',
6315 b'',
6315 b'',
6316 _(b'port to listen on (default: 8000)'),
6316 _(b'port to listen on (default: 8000)'),
6317 _(b'PORT'),
6317 _(b'PORT'),
6318 ),
6318 ),
6319 (
6319 (
6320 b'a',
6320 b'a',
6321 b'address',
6321 b'address',
6322 b'',
6322 b'',
6323 _(b'address to listen on (default: all interfaces)'),
6323 _(b'address to listen on (default: all interfaces)'),
6324 _(b'ADDR'),
6324 _(b'ADDR'),
6325 ),
6325 ),
6326 (
6326 (
6327 b'',
6327 b'',
6328 b'prefix',
6328 b'prefix',
6329 b'',
6329 b'',
6330 _(b'prefix path to serve from (default: server root)'),
6330 _(b'prefix path to serve from (default: server root)'),
6331 _(b'PREFIX'),
6331 _(b'PREFIX'),
6332 ),
6332 ),
6333 (
6333 (
6334 b'n',
6334 b'n',
6335 b'name',
6335 b'name',
6336 b'',
6336 b'',
6337 _(b'name to show in web pages (default: working directory)'),
6337 _(b'name to show in web pages (default: working directory)'),
6338 _(b'NAME'),
6338 _(b'NAME'),
6339 ),
6339 ),
6340 (
6340 (
6341 b'',
6341 b'',
6342 b'web-conf',
6342 b'web-conf',
6343 b'',
6343 b'',
6344 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6344 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6345 _(b'FILE'),
6345 _(b'FILE'),
6346 ),
6346 ),
6347 (
6347 (
6348 b'',
6348 b'',
6349 b'webdir-conf',
6349 b'webdir-conf',
6350 b'',
6350 b'',
6351 _(b'name of the hgweb config file (DEPRECATED)'),
6351 _(b'name of the hgweb config file (DEPRECATED)'),
6352 _(b'FILE'),
6352 _(b'FILE'),
6353 ),
6353 ),
6354 (
6354 (
6355 b'',
6355 b'',
6356 b'pid-file',
6356 b'pid-file',
6357 b'',
6357 b'',
6358 _(b'name of file to write process ID to'),
6358 _(b'name of file to write process ID to'),
6359 _(b'FILE'),
6359 _(b'FILE'),
6360 ),
6360 ),
6361 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6361 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6362 (
6362 (
6363 b'',
6363 b'',
6364 b'cmdserver',
6364 b'cmdserver',
6365 b'',
6365 b'',
6366 _(b'for remote clients (ADVANCED)'),
6366 _(b'for remote clients (ADVANCED)'),
6367 _(b'MODE'),
6367 _(b'MODE'),
6368 ),
6368 ),
6369 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6369 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6370 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6370 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6371 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6371 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6372 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6372 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6373 (b'', b'print-url', None, _(b'start and print only the URL')),
6373 (b'', b'print-url', None, _(b'start and print only the URL')),
6374 ]
6374 ]
6375 + subrepoopts,
6375 + subrepoopts,
6376 _(b'[OPTION]...'),
6376 _(b'[OPTION]...'),
6377 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6377 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6378 helpbasic=True,
6378 helpbasic=True,
6379 optionalrepo=True,
6379 optionalrepo=True,
6380 )
6380 )
6381 def serve(ui, repo, **opts):
6381 def serve(ui, repo, **opts):
6382 """start stand-alone webserver
6382 """start stand-alone webserver
6383
6383
6384 Start a local HTTP repository browser and pull server. You can use
6384 Start a local HTTP repository browser and pull server. You can use
6385 this for ad-hoc sharing and browsing of repositories. It is
6385 this for ad-hoc sharing and browsing of repositories. It is
6386 recommended to use a real web server to serve a repository for
6386 recommended to use a real web server to serve a repository for
6387 longer periods of time.
6387 longer periods of time.
6388
6388
6389 Please note that the server does not implement access control.
6389 Please note that the server does not implement access control.
6390 This means that, by default, anybody can read from the server and
6390 This means that, by default, anybody can read from the server and
6391 nobody can write to it by default. Set the ``web.allow-push``
6391 nobody can write to it by default. Set the ``web.allow-push``
6392 option to ``*`` to allow everybody to push to the server. You
6392 option to ``*`` to allow everybody to push to the server. You
6393 should use a real web server if you need to authenticate users.
6393 should use a real web server if you need to authenticate users.
6394
6394
6395 By default, the server logs accesses to stdout and errors to
6395 By default, the server logs accesses to stdout and errors to
6396 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6396 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6397 files.
6397 files.
6398
6398
6399 To have the server choose a free port number to listen on, specify
6399 To have the server choose a free port number to listen on, specify
6400 a port number of 0; in this case, the server will print the port
6400 a port number of 0; in this case, the server will print the port
6401 number it uses.
6401 number it uses.
6402
6402
6403 Returns 0 on success.
6403 Returns 0 on success.
6404 """
6404 """
6405
6405
6406 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6406 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6407 opts = pycompat.byteskwargs(opts)
6407 opts = pycompat.byteskwargs(opts)
6408 if opts[b"print_url"] and ui.verbose:
6408 if opts[b"print_url"] and ui.verbose:
6409 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6409 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6410
6410
6411 if opts[b"stdio"]:
6411 if opts[b"stdio"]:
6412 if repo is None:
6412 if repo is None:
6413 raise error.RepoError(
6413 raise error.RepoError(
6414 _(b"there is no Mercurial repository here (.hg not found)")
6414 _(b"there is no Mercurial repository here (.hg not found)")
6415 )
6415 )
6416 s = wireprotoserver.sshserver(ui, repo)
6416 s = wireprotoserver.sshserver(ui, repo)
6417 s.serve_forever()
6417 s.serve_forever()
6418 return
6418 return
6419
6419
6420 service = server.createservice(ui, repo, opts)
6420 service = server.createservice(ui, repo, opts)
6421 return server.runservice(opts, initfn=service.init, runfn=service.run)
6421 return server.runservice(opts, initfn=service.init, runfn=service.run)
6422
6422
6423
6423
6424 @command(
6424 @command(
6425 b'shelve',
6425 b'shelve',
6426 [
6426 [
6427 (
6427 (
6428 b'A',
6428 b'A',
6429 b'addremove',
6429 b'addremove',
6430 None,
6430 None,
6431 _(b'mark new/missing files as added/removed before shelving'),
6431 _(b'mark new/missing files as added/removed before shelving'),
6432 ),
6432 ),
6433 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6433 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6434 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6434 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6435 (
6435 (
6436 b'',
6436 b'',
6437 b'date',
6437 b'date',
6438 b'',
6438 b'',
6439 _(b'shelve with the specified commit date'),
6439 _(b'shelve with the specified commit date'),
6440 _(b'DATE'),
6440 _(b'DATE'),
6441 ),
6441 ),
6442 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6442 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6443 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6443 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6444 (
6444 (
6445 b'k',
6445 b'k',
6446 b'keep',
6446 b'keep',
6447 False,
6447 False,
6448 _(b'shelve, but keep changes in the working directory'),
6448 _(b'shelve, but keep changes in the working directory'),
6449 ),
6449 ),
6450 (b'l', b'list', None, _(b'list current shelves')),
6450 (b'l', b'list', None, _(b'list current shelves')),
6451 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6451 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6452 (
6452 (
6453 b'n',
6453 b'n',
6454 b'name',
6454 b'name',
6455 b'',
6455 b'',
6456 _(b'use the given name for the shelved commit'),
6456 _(b'use the given name for the shelved commit'),
6457 _(b'NAME'),
6457 _(b'NAME'),
6458 ),
6458 ),
6459 (
6459 (
6460 b'p',
6460 b'p',
6461 b'patch',
6461 b'patch',
6462 None,
6462 None,
6463 _(
6463 _(
6464 b'output patches for changes (provide the names of the shelved '
6464 b'output patches for changes (provide the names of the shelved '
6465 b'changes as positional arguments)'
6465 b'changes as positional arguments)'
6466 ),
6466 ),
6467 ),
6467 ),
6468 (b'i', b'interactive', None, _(b'interactive mode')),
6468 (b'i', b'interactive', None, _(b'interactive mode')),
6469 (
6469 (
6470 b'',
6470 b'',
6471 b'stat',
6471 b'stat',
6472 None,
6472 None,
6473 _(
6473 _(
6474 b'output diffstat-style summary of changes (provide the names of '
6474 b'output diffstat-style summary of changes (provide the names of '
6475 b'the shelved changes as positional arguments)'
6475 b'the shelved changes as positional arguments)'
6476 ),
6476 ),
6477 ),
6477 ),
6478 ]
6478 ]
6479 + cmdutil.walkopts,
6479 + cmdutil.walkopts,
6480 _(b'hg shelve [OPTION]... [FILE]...'),
6480 _(b'hg shelve [OPTION]... [FILE]...'),
6481 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6481 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6482 )
6482 )
6483 def shelve(ui, repo, *pats, **opts):
6483 def shelve(ui, repo, *pats, **opts):
6484 """save and set aside changes from the working directory
6484 """save and set aside changes from the working directory
6485
6485
6486 Shelving takes files that "hg status" reports as not clean, saves
6486 Shelving takes files that "hg status" reports as not clean, saves
6487 the modifications to a bundle (a shelved change), and reverts the
6487 the modifications to a bundle (a shelved change), and reverts the
6488 files so that their state in the working directory becomes clean.
6488 files so that their state in the working directory becomes clean.
6489
6489
6490 To restore these changes to the working directory, using "hg
6490 To restore these changes to the working directory, using "hg
6491 unshelve"; this will work even if you switch to a different
6491 unshelve"; this will work even if you switch to a different
6492 commit.
6492 commit.
6493
6493
6494 When no files are specified, "hg shelve" saves all not-clean
6494 When no files are specified, "hg shelve" saves all not-clean
6495 files. If specific files or directories are named, only changes to
6495 files. If specific files or directories are named, only changes to
6496 those files are shelved.
6496 those files are shelved.
6497
6497
6498 In bare shelve (when no files are specified, without interactive,
6498 In bare shelve (when no files are specified, without interactive,
6499 include and exclude option), shelving remembers information if the
6499 include and exclude option), shelving remembers information if the
6500 working directory was on newly created branch, in other words working
6500 working directory was on newly created branch, in other words working
6501 directory was on different branch than its first parent. In this
6501 directory was on different branch than its first parent. In this
6502 situation unshelving restores branch information to the working directory.
6502 situation unshelving restores branch information to the working directory.
6503
6503
6504 Each shelved change has a name that makes it easier to find later.
6504 Each shelved change has a name that makes it easier to find later.
6505 The name of a shelved change defaults to being based on the active
6505 The name of a shelved change defaults to being based on the active
6506 bookmark, or if there is no active bookmark, the current named
6506 bookmark, or if there is no active bookmark, the current named
6507 branch. To specify a different name, use ``--name``.
6507 branch. To specify a different name, use ``--name``.
6508
6508
6509 To see a list of existing shelved changes, use the ``--list``
6509 To see a list of existing shelved changes, use the ``--list``
6510 option. For each shelved change, this will print its name, age,
6510 option. For each shelved change, this will print its name, age,
6511 and description; use ``--patch`` or ``--stat`` for more details.
6511 and description; use ``--patch`` or ``--stat`` for more details.
6512
6512
6513 To delete specific shelved changes, use ``--delete``. To delete
6513 To delete specific shelved changes, use ``--delete``. To delete
6514 all shelved changes, use ``--cleanup``.
6514 all shelved changes, use ``--cleanup``.
6515 """
6515 """
6516 opts = pycompat.byteskwargs(opts)
6516 opts = pycompat.byteskwargs(opts)
6517 allowables = [
6517 allowables = [
6518 (b'addremove', {b'create'}), # 'create' is pseudo action
6518 (b'addremove', {b'create'}), # 'create' is pseudo action
6519 (b'unknown', {b'create'}),
6519 (b'unknown', {b'create'}),
6520 (b'cleanup', {b'cleanup'}),
6520 (b'cleanup', {b'cleanup'}),
6521 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6521 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6522 (b'delete', {b'delete'}),
6522 (b'delete', {b'delete'}),
6523 (b'edit', {b'create'}),
6523 (b'edit', {b'create'}),
6524 (b'keep', {b'create'}),
6524 (b'keep', {b'create'}),
6525 (b'list', {b'list'}),
6525 (b'list', {b'list'}),
6526 (b'message', {b'create'}),
6526 (b'message', {b'create'}),
6527 (b'name', {b'create'}),
6527 (b'name', {b'create'}),
6528 (b'patch', {b'patch', b'list'}),
6528 (b'patch', {b'patch', b'list'}),
6529 (b'stat', {b'stat', b'list'}),
6529 (b'stat', {b'stat', b'list'}),
6530 ]
6530 ]
6531
6531
6532 def checkopt(opt):
6532 def checkopt(opt):
6533 if opts.get(opt):
6533 if opts.get(opt):
6534 for i, allowable in allowables:
6534 for i, allowable in allowables:
6535 if opts[i] and opt not in allowable:
6535 if opts[i] and opt not in allowable:
6536 raise error.InputError(
6536 raise error.InputError(
6537 _(
6537 _(
6538 b"options '--%s' and '--%s' may not be "
6538 b"options '--%s' and '--%s' may not be "
6539 b"used together"
6539 b"used together"
6540 )
6540 )
6541 % (opt, i)
6541 % (opt, i)
6542 )
6542 )
6543 return True
6543 return True
6544
6544
6545 if checkopt(b'cleanup'):
6545 if checkopt(b'cleanup'):
6546 if pats:
6546 if pats:
6547 raise error.InputError(
6547 raise error.InputError(
6548 _(b"cannot specify names when using '--cleanup'")
6548 _(b"cannot specify names when using '--cleanup'")
6549 )
6549 )
6550 return shelvemod.cleanupcmd(ui, repo)
6550 return shelvemod.cleanupcmd(ui, repo)
6551 elif checkopt(b'delete'):
6551 elif checkopt(b'delete'):
6552 return shelvemod.deletecmd(ui, repo, pats)
6552 return shelvemod.deletecmd(ui, repo, pats)
6553 elif checkopt(b'list'):
6553 elif checkopt(b'list'):
6554 return shelvemod.listcmd(ui, repo, pats, opts)
6554 return shelvemod.listcmd(ui, repo, pats, opts)
6555 elif checkopt(b'patch') or checkopt(b'stat'):
6555 elif checkopt(b'patch') or checkopt(b'stat'):
6556 return shelvemod.patchcmds(ui, repo, pats, opts)
6556 return shelvemod.patchcmds(ui, repo, pats, opts)
6557 else:
6557 else:
6558 return shelvemod.createcmd(ui, repo, pats, opts)
6558 return shelvemod.createcmd(ui, repo, pats, opts)
6559
6559
6560
6560
6561 _NOTTERSE = b'nothing'
6561 _NOTTERSE = b'nothing'
6562
6562
6563
6563
6564 @command(
6564 @command(
6565 b'status|st',
6565 b'status|st',
6566 [
6566 [
6567 (b'A', b'all', None, _(b'show status of all files')),
6567 (b'A', b'all', None, _(b'show status of all files')),
6568 (b'm', b'modified', None, _(b'show only modified files')),
6568 (b'm', b'modified', None, _(b'show only modified files')),
6569 (b'a', b'added', None, _(b'show only added files')),
6569 (b'a', b'added', None, _(b'show only added files')),
6570 (b'r', b'removed', None, _(b'show only removed files')),
6570 (b'r', b'removed', None, _(b'show only removed files')),
6571 (b'd', b'deleted', None, _(b'show only missing files')),
6571 (b'd', b'deleted', None, _(b'show only missing files')),
6572 (b'c', b'clean', None, _(b'show only files without changes')),
6572 (b'c', b'clean', None, _(b'show only files without changes')),
6573 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6573 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6574 (b'i', b'ignored', None, _(b'show only ignored files')),
6574 (b'i', b'ignored', None, _(b'show only ignored files')),
6575 (b'n', b'no-status', None, _(b'hide status prefix')),
6575 (b'n', b'no-status', None, _(b'hide status prefix')),
6576 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6576 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6577 (
6577 (
6578 b'C',
6578 b'C',
6579 b'copies',
6579 b'copies',
6580 None,
6580 None,
6581 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6581 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6582 ),
6582 ),
6583 (
6583 (
6584 b'0',
6584 b'0',
6585 b'print0',
6585 b'print0',
6586 None,
6586 None,
6587 _(b'end filenames with NUL, for use with xargs'),
6587 _(b'end filenames with NUL, for use with xargs'),
6588 ),
6588 ),
6589 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6589 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6590 (
6590 (
6591 b'',
6591 b'',
6592 b'change',
6592 b'change',
6593 b'',
6593 b'',
6594 _(b'list the changed files of a revision'),
6594 _(b'list the changed files of a revision'),
6595 _(b'REV'),
6595 _(b'REV'),
6596 ),
6596 ),
6597 ]
6597 ]
6598 + walkopts
6598 + walkopts
6599 + subrepoopts
6599 + subrepoopts
6600 + formatteropts,
6600 + formatteropts,
6601 _(b'[OPTION]... [FILE]...'),
6601 _(b'[OPTION]... [FILE]...'),
6602 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6602 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6603 helpbasic=True,
6603 helpbasic=True,
6604 inferrepo=True,
6604 inferrepo=True,
6605 intents={INTENT_READONLY},
6605 intents={INTENT_READONLY},
6606 )
6606 )
6607 def status(ui, repo, *pats, **opts):
6607 def status(ui, repo, *pats, **opts):
6608 """show changed files in the working directory
6608 """show changed files in the working directory
6609
6609
6610 Show status of files in the repository. If names are given, only
6610 Show status of files in the repository. If names are given, only
6611 files that match are shown. Files that are clean or ignored or
6611 files that match are shown. Files that are clean or ignored or
6612 the source of a copy/move operation, are not listed unless
6612 the source of a copy/move operation, are not listed unless
6613 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6613 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6614 Unless options described with "show only ..." are given, the
6614 Unless options described with "show only ..." are given, the
6615 options -mardu are used.
6615 options -mardu are used.
6616
6616
6617 Option -q/--quiet hides untracked (unknown and ignored) files
6617 Option -q/--quiet hides untracked (unknown and ignored) files
6618 unless explicitly requested with -u/--unknown or -i/--ignored.
6618 unless explicitly requested with -u/--unknown or -i/--ignored.
6619
6619
6620 .. note::
6620 .. note::
6621
6621
6622 :hg:`status` may appear to disagree with diff if permissions have
6622 :hg:`status` may appear to disagree with diff if permissions have
6623 changed or a merge has occurred. The standard diff format does
6623 changed or a merge has occurred. The standard diff format does
6624 not report permission changes and diff only reports changes
6624 not report permission changes and diff only reports changes
6625 relative to one merge parent.
6625 relative to one merge parent.
6626
6626
6627 If one revision is given, it is used as the base revision.
6627 If one revision is given, it is used as the base revision.
6628 If two revisions are given, the differences between them are
6628 If two revisions are given, the differences between them are
6629 shown. The --change option can also be used as a shortcut to list
6629 shown. The --change option can also be used as a shortcut to list
6630 the changed files of a revision from its first parent.
6630 the changed files of a revision from its first parent.
6631
6631
6632 The codes used to show the status of files are::
6632 The codes used to show the status of files are::
6633
6633
6634 M = modified
6634 M = modified
6635 A = added
6635 A = added
6636 R = removed
6636 R = removed
6637 C = clean
6637 C = clean
6638 ! = missing (deleted by non-hg command, but still tracked)
6638 ! = missing (deleted by non-hg command, but still tracked)
6639 ? = not tracked
6639 ? = not tracked
6640 I = ignored
6640 I = ignored
6641 = origin of the previous file (with --copies)
6641 = origin of the previous file (with --copies)
6642
6642
6643 .. container:: verbose
6643 .. container:: verbose
6644
6644
6645 The -t/--terse option abbreviates the output by showing only the directory
6645 The -t/--terse option abbreviates the output by showing only the directory
6646 name if all the files in it share the same status. The option takes an
6646 name if all the files in it share the same status. The option takes an
6647 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6647 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6648 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6648 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6649 for 'ignored' and 'c' for clean.
6649 for 'ignored' and 'c' for clean.
6650
6650
6651 It abbreviates only those statuses which are passed. Note that clean and
6651 It abbreviates only those statuses which are passed. Note that clean and
6652 ignored files are not displayed with '--terse ic' unless the -c/--clean
6652 ignored files are not displayed with '--terse ic' unless the -c/--clean
6653 and -i/--ignored options are also used.
6653 and -i/--ignored options are also used.
6654
6654
6655 The -v/--verbose option shows information when the repository is in an
6655 The -v/--verbose option shows information when the repository is in an
6656 unfinished merge, shelve, rebase state etc. You can have this behavior
6656 unfinished merge, shelve, rebase state etc. You can have this behavior
6657 turned on by default by enabling the ``commands.status.verbose`` option.
6657 turned on by default by enabling the ``commands.status.verbose`` option.
6658
6658
6659 You can skip displaying some of these states by setting
6659 You can skip displaying some of these states by setting
6660 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6660 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6661 'histedit', 'merge', 'rebase', or 'unshelve'.
6661 'histedit', 'merge', 'rebase', or 'unshelve'.
6662
6662
6663 Template:
6663 Template:
6664
6664
6665 The following keywords are supported in addition to the common template
6665 The following keywords are supported in addition to the common template
6666 keywords and functions. See also :hg:`help templates`.
6666 keywords and functions. See also :hg:`help templates`.
6667
6667
6668 :path: String. Repository-absolute path of the file.
6668 :path: String. Repository-absolute path of the file.
6669 :source: String. Repository-absolute path of the file originated from.
6669 :source: String. Repository-absolute path of the file originated from.
6670 Available if ``--copies`` is specified.
6670 Available if ``--copies`` is specified.
6671 :status: String. Character denoting file's status.
6671 :status: String. Character denoting file's status.
6672
6672
6673 Examples:
6673 Examples:
6674
6674
6675 - show changes in the working directory relative to a
6675 - show changes in the working directory relative to a
6676 changeset::
6676 changeset::
6677
6677
6678 hg status --rev 9353
6678 hg status --rev 9353
6679
6679
6680 - show changes in the working directory relative to the
6680 - show changes in the working directory relative to the
6681 current directory (see :hg:`help patterns` for more information)::
6681 current directory (see :hg:`help patterns` for more information)::
6682
6682
6683 hg status re:
6683 hg status re:
6684
6684
6685 - show all changes including copies in an existing changeset::
6685 - show all changes including copies in an existing changeset::
6686
6686
6687 hg status --copies --change 9353
6687 hg status --copies --change 9353
6688
6688
6689 - get a NUL separated list of added files, suitable for xargs::
6689 - get a NUL separated list of added files, suitable for xargs::
6690
6690
6691 hg status -an0
6691 hg status -an0
6692
6692
6693 - show more information about the repository status, abbreviating
6693 - show more information about the repository status, abbreviating
6694 added, removed, modified, deleted, and untracked paths::
6694 added, removed, modified, deleted, and untracked paths::
6695
6695
6696 hg status -v -t mardu
6696 hg status -v -t mardu
6697
6697
6698 Returns 0 on success.
6698 Returns 0 on success.
6699
6699
6700 """
6700 """
6701
6701
6702 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6702 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6703 opts = pycompat.byteskwargs(opts)
6703 opts = pycompat.byteskwargs(opts)
6704 revs = opts.get(b'rev')
6704 revs = opts.get(b'rev')
6705 change = opts.get(b'change')
6705 change = opts.get(b'change')
6706 terse = opts.get(b'terse')
6706 terse = opts.get(b'terse')
6707 if terse is _NOTTERSE:
6707 if terse is _NOTTERSE:
6708 if revs:
6708 if revs:
6709 terse = b''
6709 terse = b''
6710 else:
6710 else:
6711 terse = ui.config(b'commands', b'status.terse')
6711 terse = ui.config(b'commands', b'status.terse')
6712
6712
6713 if revs and terse:
6713 if revs and terse:
6714 msg = _(b'cannot use --terse with --rev')
6714 msg = _(b'cannot use --terse with --rev')
6715 raise error.InputError(msg)
6715 raise error.InputError(msg)
6716 elif change:
6716 elif change:
6717 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6717 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6718 ctx2 = scmutil.revsingle(repo, change, None)
6718 ctx2 = scmutil.revsingle(repo, change, None)
6719 ctx1 = ctx2.p1()
6719 ctx1 = ctx2.p1()
6720 else:
6720 else:
6721 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6721 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6722 ctx1, ctx2 = scmutil.revpair(repo, revs)
6722 ctx1, ctx2 = scmutil.revpair(repo, revs)
6723
6723
6724 forcerelativevalue = None
6724 forcerelativevalue = None
6725 if ui.hasconfig(b'commands', b'status.relative'):
6725 if ui.hasconfig(b'commands', b'status.relative'):
6726 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6726 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6727 uipathfn = scmutil.getuipathfn(
6727 uipathfn = scmutil.getuipathfn(
6728 repo,
6728 repo,
6729 legacyrelativevalue=bool(pats),
6729 legacyrelativevalue=bool(pats),
6730 forcerelativevalue=forcerelativevalue,
6730 forcerelativevalue=forcerelativevalue,
6731 )
6731 )
6732
6732
6733 if opts.get(b'print0'):
6733 if opts.get(b'print0'):
6734 end = b'\0'
6734 end = b'\0'
6735 else:
6735 else:
6736 end = b'\n'
6736 end = b'\n'
6737 states = b'modified added removed deleted unknown ignored clean'.split()
6737 states = b'modified added removed deleted unknown ignored clean'.split()
6738 show = [k for k in states if opts.get(k)]
6738 show = [k for k in states if opts.get(k)]
6739 if opts.get(b'all'):
6739 if opts.get(b'all'):
6740 show += ui.quiet and (states[:4] + [b'clean']) or states
6740 show += ui.quiet and (states[:4] + [b'clean']) or states
6741
6741
6742 if not show:
6742 if not show:
6743 if ui.quiet:
6743 if ui.quiet:
6744 show = states[:4]
6744 show = states[:4]
6745 else:
6745 else:
6746 show = states[:5]
6746 show = states[:5]
6747
6747
6748 m = scmutil.match(ctx2, pats, opts)
6748 m = scmutil.match(ctx2, pats, opts)
6749 if terse:
6749 if terse:
6750 # we need to compute clean and unknown to terse
6750 # we need to compute clean and unknown to terse
6751 stat = repo.status(
6751 stat = repo.status(
6752 ctx1.node(),
6752 ctx1.node(),
6753 ctx2.node(),
6753 ctx2.node(),
6754 m,
6754 m,
6755 b'ignored' in show or b'i' in terse,
6755 b'ignored' in show or b'i' in terse,
6756 clean=True,
6756 clean=True,
6757 unknown=True,
6757 unknown=True,
6758 listsubrepos=opts.get(b'subrepos'),
6758 listsubrepos=opts.get(b'subrepos'),
6759 )
6759 )
6760
6760
6761 stat = cmdutil.tersedir(stat, terse)
6761 stat = cmdutil.tersedir(stat, terse)
6762 else:
6762 else:
6763 stat = repo.status(
6763 stat = repo.status(
6764 ctx1.node(),
6764 ctx1.node(),
6765 ctx2.node(),
6765 ctx2.node(),
6766 m,
6766 m,
6767 b'ignored' in show,
6767 b'ignored' in show,
6768 b'clean' in show,
6768 b'clean' in show,
6769 b'unknown' in show,
6769 b'unknown' in show,
6770 opts.get(b'subrepos'),
6770 opts.get(b'subrepos'),
6771 )
6771 )
6772
6772
6773 changestates = zip(
6773 changestates = zip(
6774 states,
6774 states,
6775 pycompat.iterbytestr(b'MAR!?IC'),
6775 pycompat.iterbytestr(b'MAR!?IC'),
6776 [getattr(stat, s.decode('utf8')) for s in states],
6776 [getattr(stat, s.decode('utf8')) for s in states],
6777 )
6777 )
6778
6778
6779 copy = {}
6779 copy = {}
6780 if (
6780 if (
6781 opts.get(b'all')
6781 opts.get(b'all')
6782 or opts.get(b'copies')
6782 or opts.get(b'copies')
6783 or ui.configbool(b'ui', b'statuscopies')
6783 or ui.configbool(b'ui', b'statuscopies')
6784 ) and not opts.get(b'no_status'):
6784 ) and not opts.get(b'no_status'):
6785 copy = copies.pathcopies(ctx1, ctx2, m)
6785 copy = copies.pathcopies(ctx1, ctx2, m)
6786
6786
6787 morestatus = None
6787 morestatus = None
6788 if (
6788 if (
6789 (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
6789 (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
6790 and not ui.plain()
6790 and not ui.plain()
6791 and not opts.get(b'print0')
6791 and not opts.get(b'print0')
6792 ):
6792 ):
6793 morestatus = cmdutil.readmorestatus(repo)
6793 morestatus = cmdutil.readmorestatus(repo)
6794
6794
6795 ui.pager(b'status')
6795 ui.pager(b'status')
6796 fm = ui.formatter(b'status', opts)
6796 fm = ui.formatter(b'status', opts)
6797 fmt = b'%s' + end
6797 fmt = b'%s' + end
6798 showchar = not opts.get(b'no_status')
6798 showchar = not opts.get(b'no_status')
6799
6799
6800 for state, char, files in changestates:
6800 for state, char, files in changestates:
6801 if state in show:
6801 if state in show:
6802 label = b'status.' + state
6802 label = b'status.' + state
6803 for f in files:
6803 for f in files:
6804 fm.startitem()
6804 fm.startitem()
6805 fm.context(ctx=ctx2)
6805 fm.context(ctx=ctx2)
6806 fm.data(itemtype=b'file', path=f)
6806 fm.data(itemtype=b'file', path=f)
6807 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6807 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6808 fm.plain(fmt % uipathfn(f), label=label)
6808 fm.plain(fmt % uipathfn(f), label=label)
6809 if f in copy:
6809 if f in copy:
6810 fm.data(source=copy[f])
6810 fm.data(source=copy[f])
6811 fm.plain(
6811 fm.plain(
6812 (b' %s' + end) % uipathfn(copy[f]),
6812 (b' %s' + end) % uipathfn(copy[f]),
6813 label=b'status.copied',
6813 label=b'status.copied',
6814 )
6814 )
6815 if morestatus:
6815 if morestatus:
6816 morestatus.formatfile(f, fm)
6816 morestatus.formatfile(f, fm)
6817
6817
6818 if morestatus:
6818 if morestatus:
6819 morestatus.formatfooter(fm)
6819 morestatus.formatfooter(fm)
6820 fm.end()
6820 fm.end()
6821
6821
6822
6822
6823 @command(
6823 @command(
6824 b'summary|sum',
6824 b'summary|sum',
6825 [(b'', b'remote', None, _(b'check for push and pull'))],
6825 [(b'', b'remote', None, _(b'check for push and pull'))],
6826 b'[--remote]',
6826 b'[--remote]',
6827 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6827 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6828 helpbasic=True,
6828 helpbasic=True,
6829 intents={INTENT_READONLY},
6829 intents={INTENT_READONLY},
6830 )
6830 )
6831 def summary(ui, repo, **opts):
6831 def summary(ui, repo, **opts):
6832 """summarize working directory state
6832 """summarize working directory state
6833
6833
6834 This generates a brief summary of the working directory state,
6834 This generates a brief summary of the working directory state,
6835 including parents, branch, commit status, phase and available updates.
6835 including parents, branch, commit status, phase and available updates.
6836
6836
6837 With the --remote option, this will check the default paths for
6837 With the --remote option, this will check the default paths for
6838 incoming and outgoing changes. This can be time-consuming.
6838 incoming and outgoing changes. This can be time-consuming.
6839
6839
6840 Returns 0 on success.
6840 Returns 0 on success.
6841 """
6841 """
6842
6842
6843 opts = pycompat.byteskwargs(opts)
6843 opts = pycompat.byteskwargs(opts)
6844 ui.pager(b'summary')
6844 ui.pager(b'summary')
6845 ctx = repo[None]
6845 ctx = repo[None]
6846 parents = ctx.parents()
6846 parents = ctx.parents()
6847 pnode = parents[0].node()
6847 pnode = parents[0].node()
6848 marks = []
6848 marks = []
6849
6849
6850 try:
6850 try:
6851 ms = mergestatemod.mergestate.read(repo)
6851 ms = mergestatemod.mergestate.read(repo)
6852 except error.UnsupportedMergeRecords as e:
6852 except error.UnsupportedMergeRecords as e:
6853 s = b' '.join(e.recordtypes)
6853 s = b' '.join(e.recordtypes)
6854 ui.warn(
6854 ui.warn(
6855 _(b'warning: merge state has unsupported record types: %s\n') % s
6855 _(b'warning: merge state has unsupported record types: %s\n') % s
6856 )
6856 )
6857 unresolved = []
6857 unresolved = []
6858 else:
6858 else:
6859 unresolved = list(ms.unresolved())
6859 unresolved = list(ms.unresolved())
6860
6860
6861 for p in parents:
6861 for p in parents:
6862 # label with log.changeset (instead of log.parent) since this
6862 # label with log.changeset (instead of log.parent) since this
6863 # shows a working directory parent *changeset*:
6863 # shows a working directory parent *changeset*:
6864 # i18n: column positioning for "hg summary"
6864 # i18n: column positioning for "hg summary"
6865 ui.write(
6865 ui.write(
6866 _(b'parent: %d:%s ') % (p.rev(), p),
6866 _(b'parent: %d:%s ') % (p.rev(), p),
6867 label=logcmdutil.changesetlabels(p),
6867 label=logcmdutil.changesetlabels(p),
6868 )
6868 )
6869 ui.write(b' '.join(p.tags()), label=b'log.tag')
6869 ui.write(b' '.join(p.tags()), label=b'log.tag')
6870 if p.bookmarks():
6870 if p.bookmarks():
6871 marks.extend(p.bookmarks())
6871 marks.extend(p.bookmarks())
6872 if p.rev() == -1:
6872 if p.rev() == -1:
6873 if not len(repo):
6873 if not len(repo):
6874 ui.write(_(b' (empty repository)'))
6874 ui.write(_(b' (empty repository)'))
6875 else:
6875 else:
6876 ui.write(_(b' (no revision checked out)'))
6876 ui.write(_(b' (no revision checked out)'))
6877 if p.obsolete():
6877 if p.obsolete():
6878 ui.write(_(b' (obsolete)'))
6878 ui.write(_(b' (obsolete)'))
6879 if p.isunstable():
6879 if p.isunstable():
6880 instabilities = (
6880 instabilities = (
6881 ui.label(instability, b'trouble.%s' % instability)
6881 ui.label(instability, b'trouble.%s' % instability)
6882 for instability in p.instabilities()
6882 for instability in p.instabilities()
6883 )
6883 )
6884 ui.write(b' (' + b', '.join(instabilities) + b')')
6884 ui.write(b' (' + b', '.join(instabilities) + b')')
6885 ui.write(b'\n')
6885 ui.write(b'\n')
6886 if p.description():
6886 if p.description():
6887 ui.status(
6887 ui.status(
6888 b' ' + p.description().splitlines()[0].strip() + b'\n',
6888 b' ' + p.description().splitlines()[0].strip() + b'\n',
6889 label=b'log.summary',
6889 label=b'log.summary',
6890 )
6890 )
6891
6891
6892 branch = ctx.branch()
6892 branch = ctx.branch()
6893 bheads = repo.branchheads(branch)
6893 bheads = repo.branchheads(branch)
6894 # i18n: column positioning for "hg summary"
6894 # i18n: column positioning for "hg summary"
6895 m = _(b'branch: %s\n') % branch
6895 m = _(b'branch: %s\n') % branch
6896 if branch != b'default':
6896 if branch != b'default':
6897 ui.write(m, label=b'log.branch')
6897 ui.write(m, label=b'log.branch')
6898 else:
6898 else:
6899 ui.status(m, label=b'log.branch')
6899 ui.status(m, label=b'log.branch')
6900
6900
6901 if marks:
6901 if marks:
6902 active = repo._activebookmark
6902 active = repo._activebookmark
6903 # i18n: column positioning for "hg summary"
6903 # i18n: column positioning for "hg summary"
6904 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6904 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6905 if active is not None:
6905 if active is not None:
6906 if active in marks:
6906 if active in marks:
6907 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6907 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6908 marks.remove(active)
6908 marks.remove(active)
6909 else:
6909 else:
6910 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6910 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6911 for m in marks:
6911 for m in marks:
6912 ui.write(b' ' + m, label=b'log.bookmark')
6912 ui.write(b' ' + m, label=b'log.bookmark')
6913 ui.write(b'\n', label=b'log.bookmark')
6913 ui.write(b'\n', label=b'log.bookmark')
6914
6914
6915 status = repo.status(unknown=True)
6915 status = repo.status(unknown=True)
6916
6916
6917 c = repo.dirstate.copies()
6917 c = repo.dirstate.copies()
6918 copied, renamed = [], []
6918 copied, renamed = [], []
6919 for d, s in pycompat.iteritems(c):
6919 for d, s in pycompat.iteritems(c):
6920 if s in status.removed:
6920 if s in status.removed:
6921 status.removed.remove(s)
6921 status.removed.remove(s)
6922 renamed.append(d)
6922 renamed.append(d)
6923 else:
6923 else:
6924 copied.append(d)
6924 copied.append(d)
6925 if d in status.added:
6925 if d in status.added:
6926 status.added.remove(d)
6926 status.added.remove(d)
6927
6927
6928 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6928 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6929
6929
6930 labels = [
6930 labels = [
6931 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
6931 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
6932 (ui.label(_(b'%d added'), b'status.added'), status.added),
6932 (ui.label(_(b'%d added'), b'status.added'), status.added),
6933 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
6933 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
6934 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
6934 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
6935 (ui.label(_(b'%d copied'), b'status.copied'), copied),
6935 (ui.label(_(b'%d copied'), b'status.copied'), copied),
6936 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
6936 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
6937 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
6937 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
6938 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
6938 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
6939 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
6939 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
6940 ]
6940 ]
6941 t = []
6941 t = []
6942 for l, s in labels:
6942 for l, s in labels:
6943 if s:
6943 if s:
6944 t.append(l % len(s))
6944 t.append(l % len(s))
6945
6945
6946 t = b', '.join(t)
6946 t = b', '.join(t)
6947 cleanworkdir = False
6947 cleanworkdir = False
6948
6948
6949 if repo.vfs.exists(b'graftstate'):
6949 if repo.vfs.exists(b'graftstate'):
6950 t += _(b' (graft in progress)')
6950 t += _(b' (graft in progress)')
6951 if repo.vfs.exists(b'updatestate'):
6951 if repo.vfs.exists(b'updatestate'):
6952 t += _(b' (interrupted update)')
6952 t += _(b' (interrupted update)')
6953 elif len(parents) > 1:
6953 elif len(parents) > 1:
6954 t += _(b' (merge)')
6954 t += _(b' (merge)')
6955 elif branch != parents[0].branch():
6955 elif branch != parents[0].branch():
6956 t += _(b' (new branch)')
6956 t += _(b' (new branch)')
6957 elif parents[0].closesbranch() and pnode in repo.branchheads(
6957 elif parents[0].closesbranch() and pnode in repo.branchheads(
6958 branch, closed=True
6958 branch, closed=True
6959 ):
6959 ):
6960 t += _(b' (head closed)')
6960 t += _(b' (head closed)')
6961 elif not (
6961 elif not (
6962 status.modified
6962 status.modified
6963 or status.added
6963 or status.added
6964 or status.removed
6964 or status.removed
6965 or renamed
6965 or renamed
6966 or copied
6966 or copied
6967 or subs
6967 or subs
6968 ):
6968 ):
6969 t += _(b' (clean)')
6969 t += _(b' (clean)')
6970 cleanworkdir = True
6970 cleanworkdir = True
6971 elif pnode not in bheads:
6971 elif pnode not in bheads:
6972 t += _(b' (new branch head)')
6972 t += _(b' (new branch head)')
6973
6973
6974 if parents:
6974 if parents:
6975 pendingphase = max(p.phase() for p in parents)
6975 pendingphase = max(p.phase() for p in parents)
6976 else:
6976 else:
6977 pendingphase = phases.public
6977 pendingphase = phases.public
6978
6978
6979 if pendingphase > phases.newcommitphase(ui):
6979 if pendingphase > phases.newcommitphase(ui):
6980 t += b' (%s)' % phases.phasenames[pendingphase]
6980 t += b' (%s)' % phases.phasenames[pendingphase]
6981
6981
6982 if cleanworkdir:
6982 if cleanworkdir:
6983 # i18n: column positioning for "hg summary"
6983 # i18n: column positioning for "hg summary"
6984 ui.status(_(b'commit: %s\n') % t.strip())
6984 ui.status(_(b'commit: %s\n') % t.strip())
6985 else:
6985 else:
6986 # i18n: column positioning for "hg summary"
6986 # i18n: column positioning for "hg summary"
6987 ui.write(_(b'commit: %s\n') % t.strip())
6987 ui.write(_(b'commit: %s\n') % t.strip())
6988
6988
6989 # all ancestors of branch heads - all ancestors of parent = new csets
6989 # all ancestors of branch heads - all ancestors of parent = new csets
6990 new = len(
6990 new = len(
6991 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
6991 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
6992 )
6992 )
6993
6993
6994 if new == 0:
6994 if new == 0:
6995 # i18n: column positioning for "hg summary"
6995 # i18n: column positioning for "hg summary"
6996 ui.status(_(b'update: (current)\n'))
6996 ui.status(_(b'update: (current)\n'))
6997 elif pnode not in bheads:
6997 elif pnode not in bheads:
6998 # i18n: column positioning for "hg summary"
6998 # i18n: column positioning for "hg summary"
6999 ui.write(_(b'update: %d new changesets (update)\n') % new)
6999 ui.write(_(b'update: %d new changesets (update)\n') % new)
7000 else:
7000 else:
7001 # i18n: column positioning for "hg summary"
7001 # i18n: column positioning for "hg summary"
7002 ui.write(
7002 ui.write(
7003 _(b'update: %d new changesets, %d branch heads (merge)\n')
7003 _(b'update: %d new changesets, %d branch heads (merge)\n')
7004 % (new, len(bheads))
7004 % (new, len(bheads))
7005 )
7005 )
7006
7006
7007 t = []
7007 t = []
7008 draft = len(repo.revs(b'draft()'))
7008 draft = len(repo.revs(b'draft()'))
7009 if draft:
7009 if draft:
7010 t.append(_(b'%d draft') % draft)
7010 t.append(_(b'%d draft') % draft)
7011 secret = len(repo.revs(b'secret()'))
7011 secret = len(repo.revs(b'secret()'))
7012 if secret:
7012 if secret:
7013 t.append(_(b'%d secret') % secret)
7013 t.append(_(b'%d secret') % secret)
7014
7014
7015 if draft or secret:
7015 if draft or secret:
7016 ui.status(_(b'phases: %s\n') % b', '.join(t))
7016 ui.status(_(b'phases: %s\n') % b', '.join(t))
7017
7017
7018 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7018 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7019 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7019 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7020 numtrouble = len(repo.revs(trouble + b"()"))
7020 numtrouble = len(repo.revs(trouble + b"()"))
7021 # We write all the possibilities to ease translation
7021 # We write all the possibilities to ease translation
7022 troublemsg = {
7022 troublemsg = {
7023 b"orphan": _(b"orphan: %d changesets"),
7023 b"orphan": _(b"orphan: %d changesets"),
7024 b"contentdivergent": _(b"content-divergent: %d changesets"),
7024 b"contentdivergent": _(b"content-divergent: %d changesets"),
7025 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7025 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7026 }
7026 }
7027 if numtrouble > 0:
7027 if numtrouble > 0:
7028 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7028 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7029
7029
7030 cmdutil.summaryhooks(ui, repo)
7030 cmdutil.summaryhooks(ui, repo)
7031
7031
7032 if opts.get(b'remote'):
7032 if opts.get(b'remote'):
7033 needsincoming, needsoutgoing = True, True
7033 needsincoming, needsoutgoing = True, True
7034 else:
7034 else:
7035 needsincoming, needsoutgoing = False, False
7035 needsincoming, needsoutgoing = False, False
7036 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7036 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7037 if i:
7037 if i:
7038 needsincoming = True
7038 needsincoming = True
7039 if o:
7039 if o:
7040 needsoutgoing = True
7040 needsoutgoing = True
7041 if not needsincoming and not needsoutgoing:
7041 if not needsincoming and not needsoutgoing:
7042 return
7042 return
7043
7043
7044 def getincoming():
7044 def getincoming():
7045 source, branches = hg.parseurl(ui.expandpath(b'default'))
7045 source, branches = hg.parseurl(ui.expandpath(b'default'))
7046 sbranch = branches[0]
7046 sbranch = branches[0]
7047 try:
7047 try:
7048 other = hg.peer(repo, {}, source)
7048 other = hg.peer(repo, {}, source)
7049 except error.RepoError:
7049 except error.RepoError:
7050 if opts.get(b'remote'):
7050 if opts.get(b'remote'):
7051 raise
7051 raise
7052 return source, sbranch, None, None, None
7052 return source, sbranch, None, None, None
7053 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7053 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7054 if revs:
7054 if revs:
7055 revs = [other.lookup(rev) for rev in revs]
7055 revs = [other.lookup(rev) for rev in revs]
7056 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7056 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7057 repo.ui.pushbuffer()
7057 repo.ui.pushbuffer()
7058 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7058 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7059 repo.ui.popbuffer()
7059 repo.ui.popbuffer()
7060 return source, sbranch, other, commoninc, commoninc[1]
7060 return source, sbranch, other, commoninc, commoninc[1]
7061
7061
7062 if needsincoming:
7062 if needsincoming:
7063 source, sbranch, sother, commoninc, incoming = getincoming()
7063 source, sbranch, sother, commoninc, incoming = getincoming()
7064 else:
7064 else:
7065 source = sbranch = sother = commoninc = incoming = None
7065 source = sbranch = sother = commoninc = incoming = None
7066
7066
7067 def getoutgoing():
7067 def getoutgoing():
7068 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7068 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7069 dbranch = branches[0]
7069 dbranch = branches[0]
7070 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7070 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7071 if source != dest:
7071 if source != dest:
7072 try:
7072 try:
7073 dother = hg.peer(repo, {}, dest)
7073 dother = hg.peer(repo, {}, dest)
7074 except error.RepoError:
7074 except error.RepoError:
7075 if opts.get(b'remote'):
7075 if opts.get(b'remote'):
7076 raise
7076 raise
7077 return dest, dbranch, None, None
7077 return dest, dbranch, None, None
7078 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7078 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7079 elif sother is None:
7079 elif sother is None:
7080 # there is no explicit destination peer, but source one is invalid
7080 # there is no explicit destination peer, but source one is invalid
7081 return dest, dbranch, None, None
7081 return dest, dbranch, None, None
7082 else:
7082 else:
7083 dother = sother
7083 dother = sother
7084 if source != dest or (sbranch is not None and sbranch != dbranch):
7084 if source != dest or (sbranch is not None and sbranch != dbranch):
7085 common = None
7085 common = None
7086 else:
7086 else:
7087 common = commoninc
7087 common = commoninc
7088 if revs:
7088 if revs:
7089 revs = [repo.lookup(rev) for rev in revs]
7089 revs = [repo.lookup(rev) for rev in revs]
7090 repo.ui.pushbuffer()
7090 repo.ui.pushbuffer()
7091 outgoing = discovery.findcommonoutgoing(
7091 outgoing = discovery.findcommonoutgoing(
7092 repo, dother, onlyheads=revs, commoninc=common
7092 repo, dother, onlyheads=revs, commoninc=common
7093 )
7093 )
7094 repo.ui.popbuffer()
7094 repo.ui.popbuffer()
7095 return dest, dbranch, dother, outgoing
7095 return dest, dbranch, dother, outgoing
7096
7096
7097 if needsoutgoing:
7097 if needsoutgoing:
7098 dest, dbranch, dother, outgoing = getoutgoing()
7098 dest, dbranch, dother, outgoing = getoutgoing()
7099 else:
7099 else:
7100 dest = dbranch = dother = outgoing = None
7100 dest = dbranch = dother = outgoing = None
7101
7101
7102 if opts.get(b'remote'):
7102 if opts.get(b'remote'):
7103 t = []
7103 t = []
7104 if incoming:
7104 if incoming:
7105 t.append(_(b'1 or more incoming'))
7105 t.append(_(b'1 or more incoming'))
7106 o = outgoing.missing
7106 o = outgoing.missing
7107 if o:
7107 if o:
7108 t.append(_(b'%d outgoing') % len(o))
7108 t.append(_(b'%d outgoing') % len(o))
7109 other = dother or sother
7109 other = dother or sother
7110 if b'bookmarks' in other.listkeys(b'namespaces'):
7110 if b'bookmarks' in other.listkeys(b'namespaces'):
7111 counts = bookmarks.summary(repo, other)
7111 counts = bookmarks.summary(repo, other)
7112 if counts[0] > 0:
7112 if counts[0] > 0:
7113 t.append(_(b'%d incoming bookmarks') % counts[0])
7113 t.append(_(b'%d incoming bookmarks') % counts[0])
7114 if counts[1] > 0:
7114 if counts[1] > 0:
7115 t.append(_(b'%d outgoing bookmarks') % counts[1])
7115 t.append(_(b'%d outgoing bookmarks') % counts[1])
7116
7116
7117 if t:
7117 if t:
7118 # i18n: column positioning for "hg summary"
7118 # i18n: column positioning for "hg summary"
7119 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7119 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7120 else:
7120 else:
7121 # i18n: column positioning for "hg summary"
7121 # i18n: column positioning for "hg summary"
7122 ui.status(_(b'remote: (synced)\n'))
7122 ui.status(_(b'remote: (synced)\n'))
7123
7123
7124 cmdutil.summaryremotehooks(
7124 cmdutil.summaryremotehooks(
7125 ui,
7125 ui,
7126 repo,
7126 repo,
7127 opts,
7127 opts,
7128 (
7128 (
7129 (source, sbranch, sother, commoninc),
7129 (source, sbranch, sother, commoninc),
7130 (dest, dbranch, dother, outgoing),
7130 (dest, dbranch, dother, outgoing),
7131 ),
7131 ),
7132 )
7132 )
7133
7133
7134
7134
7135 @command(
7135 @command(
7136 b'tag',
7136 b'tag',
7137 [
7137 [
7138 (b'f', b'force', None, _(b'force tag')),
7138 (b'f', b'force', None, _(b'force tag')),
7139 (b'l', b'local', None, _(b'make the tag local')),
7139 (b'l', b'local', None, _(b'make the tag local')),
7140 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7140 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7141 (b'', b'remove', None, _(b'remove a tag')),
7141 (b'', b'remove', None, _(b'remove a tag')),
7142 # -l/--local is already there, commitopts cannot be used
7142 # -l/--local is already there, commitopts cannot be used
7143 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7143 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7144 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7144 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7145 ]
7145 ]
7146 + commitopts2,
7146 + commitopts2,
7147 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7147 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7148 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7148 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7149 )
7149 )
7150 def tag(ui, repo, name1, *names, **opts):
7150 def tag(ui, repo, name1, *names, **opts):
7151 """add one or more tags for the current or given revision
7151 """add one or more tags for the current or given revision
7152
7152
7153 Name a particular revision using <name>.
7153 Name a particular revision using <name>.
7154
7154
7155 Tags are used to name particular revisions of the repository and are
7155 Tags are used to name particular revisions of the repository and are
7156 very useful to compare different revisions, to go back to significant
7156 very useful to compare different revisions, to go back to significant
7157 earlier versions or to mark branch points as releases, etc. Changing
7157 earlier versions or to mark branch points as releases, etc. Changing
7158 an existing tag is normally disallowed; use -f/--force to override.
7158 an existing tag is normally disallowed; use -f/--force to override.
7159
7159
7160 If no revision is given, the parent of the working directory is
7160 If no revision is given, the parent of the working directory is
7161 used.
7161 used.
7162
7162
7163 To facilitate version control, distribution, and merging of tags,
7163 To facilitate version control, distribution, and merging of tags,
7164 they are stored as a file named ".hgtags" which is managed similarly
7164 they are stored as a file named ".hgtags" which is managed similarly
7165 to other project files and can be hand-edited if necessary. This
7165 to other project files and can be hand-edited if necessary. This
7166 also means that tagging creates a new commit. The file
7166 also means that tagging creates a new commit. The file
7167 ".hg/localtags" is used for local tags (not shared among
7167 ".hg/localtags" is used for local tags (not shared among
7168 repositories).
7168 repositories).
7169
7169
7170 Tag commits are usually made at the head of a branch. If the parent
7170 Tag commits are usually made at the head of a branch. If the parent
7171 of the working directory is not a branch head, :hg:`tag` aborts; use
7171 of the working directory is not a branch head, :hg:`tag` aborts; use
7172 -f/--force to force the tag commit to be based on a non-head
7172 -f/--force to force the tag commit to be based on a non-head
7173 changeset.
7173 changeset.
7174
7174
7175 See :hg:`help dates` for a list of formats valid for -d/--date.
7175 See :hg:`help dates` for a list of formats valid for -d/--date.
7176
7176
7177 Since tag names have priority over branch names during revision
7177 Since tag names have priority over branch names during revision
7178 lookup, using an existing branch name as a tag name is discouraged.
7178 lookup, using an existing branch name as a tag name is discouraged.
7179
7179
7180 Returns 0 on success.
7180 Returns 0 on success.
7181 """
7181 """
7182 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7182 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7183 opts = pycompat.byteskwargs(opts)
7183 opts = pycompat.byteskwargs(opts)
7184 with repo.wlock(), repo.lock():
7184 with repo.wlock(), repo.lock():
7185 rev_ = b"."
7185 rev_ = b"."
7186 names = [t.strip() for t in (name1,) + names]
7186 names = [t.strip() for t in (name1,) + names]
7187 if len(names) != len(set(names)):
7187 if len(names) != len(set(names)):
7188 raise error.InputError(_(b'tag names must be unique'))
7188 raise error.InputError(_(b'tag names must be unique'))
7189 for n in names:
7189 for n in names:
7190 scmutil.checknewlabel(repo, n, b'tag')
7190 scmutil.checknewlabel(repo, n, b'tag')
7191 if not n:
7191 if not n:
7192 raise error.InputError(
7192 raise error.InputError(
7193 _(b'tag names cannot consist entirely of whitespace')
7193 _(b'tag names cannot consist entirely of whitespace')
7194 )
7194 )
7195 if opts.get(b'rev'):
7195 if opts.get(b'rev'):
7196 rev_ = opts[b'rev']
7196 rev_ = opts[b'rev']
7197 message = opts.get(b'message')
7197 message = opts.get(b'message')
7198 if opts.get(b'remove'):
7198 if opts.get(b'remove'):
7199 if opts.get(b'local'):
7199 if opts.get(b'local'):
7200 expectedtype = b'local'
7200 expectedtype = b'local'
7201 else:
7201 else:
7202 expectedtype = b'global'
7202 expectedtype = b'global'
7203
7203
7204 for n in names:
7204 for n in names:
7205 if repo.tagtype(n) == b'global':
7205 if repo.tagtype(n) == b'global':
7206 alltags = tagsmod.findglobaltags(ui, repo)
7206 alltags = tagsmod.findglobaltags(ui, repo)
7207 if alltags[n][0] == nullid:
7207 if alltags[n][0] == nullid:
7208 raise error.InputError(
7208 raise error.InputError(
7209 _(b"tag '%s' is already removed") % n
7209 _(b"tag '%s' is already removed") % n
7210 )
7210 )
7211 if not repo.tagtype(n):
7211 if not repo.tagtype(n):
7212 raise error.InputError(_(b"tag '%s' does not exist") % n)
7212 raise error.InputError(_(b"tag '%s' does not exist") % n)
7213 if repo.tagtype(n) != expectedtype:
7213 if repo.tagtype(n) != expectedtype:
7214 if expectedtype == b'global':
7214 if expectedtype == b'global':
7215 raise error.InputError(
7215 raise error.InputError(
7216 _(b"tag '%s' is not a global tag") % n
7216 _(b"tag '%s' is not a global tag") % n
7217 )
7217 )
7218 else:
7218 else:
7219 raise error.InputError(
7219 raise error.InputError(
7220 _(b"tag '%s' is not a local tag") % n
7220 _(b"tag '%s' is not a local tag") % n
7221 )
7221 )
7222 rev_ = b'null'
7222 rev_ = b'null'
7223 if not message:
7223 if not message:
7224 # we don't translate commit messages
7224 # we don't translate commit messages
7225 message = b'Removed tag %s' % b', '.join(names)
7225 message = b'Removed tag %s' % b', '.join(names)
7226 elif not opts.get(b'force'):
7226 elif not opts.get(b'force'):
7227 for n in names:
7227 for n in names:
7228 if n in repo.tags():
7228 if n in repo.tags():
7229 raise error.InputError(
7229 raise error.InputError(
7230 _(b"tag '%s' already exists (use -f to force)") % n
7230 _(b"tag '%s' already exists (use -f to force)") % n
7231 )
7231 )
7232 if not opts.get(b'local'):
7232 if not opts.get(b'local'):
7233 p1, p2 = repo.dirstate.parents()
7233 p1, p2 = repo.dirstate.parents()
7234 if p2 != nullid:
7234 if p2 != nullid:
7235 raise error.StateError(_(b'uncommitted merge'))
7235 raise error.StateError(_(b'uncommitted merge'))
7236 bheads = repo.branchheads()
7236 bheads = repo.branchheads()
7237 if not opts.get(b'force') and bheads and p1 not in bheads:
7237 if not opts.get(b'force') and bheads and p1 not in bheads:
7238 raise error.InputError(
7238 raise error.InputError(
7239 _(
7239 _(
7240 b'working directory is not at a branch head '
7240 b'working directory is not at a branch head '
7241 b'(use -f to force)'
7241 b'(use -f to force)'
7242 )
7242 )
7243 )
7243 )
7244 node = scmutil.revsingle(repo, rev_).node()
7244 node = scmutil.revsingle(repo, rev_).node()
7245
7245
7246 if not message:
7246 if not message:
7247 # we don't translate commit messages
7247 # we don't translate commit messages
7248 message = b'Added tag %s for changeset %s' % (
7248 message = b'Added tag %s for changeset %s' % (
7249 b', '.join(names),
7249 b', '.join(names),
7250 short(node),
7250 short(node),
7251 )
7251 )
7252
7252
7253 date = opts.get(b'date')
7253 date = opts.get(b'date')
7254 if date:
7254 if date:
7255 date = dateutil.parsedate(date)
7255 date = dateutil.parsedate(date)
7256
7256
7257 if opts.get(b'remove'):
7257 if opts.get(b'remove'):
7258 editform = b'tag.remove'
7258 editform = b'tag.remove'
7259 else:
7259 else:
7260 editform = b'tag.add'
7260 editform = b'tag.add'
7261 editor = cmdutil.getcommiteditor(
7261 editor = cmdutil.getcommiteditor(
7262 editform=editform, **pycompat.strkwargs(opts)
7262 editform=editform, **pycompat.strkwargs(opts)
7263 )
7263 )
7264
7264
7265 # don't allow tagging the null rev
7265 # don't allow tagging the null rev
7266 if (
7266 if (
7267 not opts.get(b'remove')
7267 not opts.get(b'remove')
7268 and scmutil.revsingle(repo, rev_).rev() == nullrev
7268 and scmutil.revsingle(repo, rev_).rev() == nullrev
7269 ):
7269 ):
7270 raise error.InputError(_(b"cannot tag null revision"))
7270 raise error.InputError(_(b"cannot tag null revision"))
7271
7271
7272 tagsmod.tag(
7272 tagsmod.tag(
7273 repo,
7273 repo,
7274 names,
7274 names,
7275 node,
7275 node,
7276 message,
7276 message,
7277 opts.get(b'local'),
7277 opts.get(b'local'),
7278 opts.get(b'user'),
7278 opts.get(b'user'),
7279 date,
7279 date,
7280 editor=editor,
7280 editor=editor,
7281 )
7281 )
7282
7282
7283
7283
7284 @command(
7284 @command(
7285 b'tags',
7285 b'tags',
7286 formatteropts,
7286 formatteropts,
7287 b'',
7287 b'',
7288 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7288 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7289 intents={INTENT_READONLY},
7289 intents={INTENT_READONLY},
7290 )
7290 )
7291 def tags(ui, repo, **opts):
7291 def tags(ui, repo, **opts):
7292 """list repository tags
7292 """list repository tags
7293
7293
7294 This lists both regular and local tags. When the -v/--verbose
7294 This lists both regular and local tags. When the -v/--verbose
7295 switch is used, a third column "local" is printed for local tags.
7295 switch is used, a third column "local" is printed for local tags.
7296 When the -q/--quiet switch is used, only the tag name is printed.
7296 When the -q/--quiet switch is used, only the tag name is printed.
7297
7297
7298 .. container:: verbose
7298 .. container:: verbose
7299
7299
7300 Template:
7300 Template:
7301
7301
7302 The following keywords are supported in addition to the common template
7302 The following keywords are supported in addition to the common template
7303 keywords and functions such as ``{tag}``. See also
7303 keywords and functions such as ``{tag}``. See also
7304 :hg:`help templates`.
7304 :hg:`help templates`.
7305
7305
7306 :type: String. ``local`` for local tags.
7306 :type: String. ``local`` for local tags.
7307
7307
7308 Returns 0 on success.
7308 Returns 0 on success.
7309 """
7309 """
7310
7310
7311 opts = pycompat.byteskwargs(opts)
7311 opts = pycompat.byteskwargs(opts)
7312 ui.pager(b'tags')
7312 ui.pager(b'tags')
7313 fm = ui.formatter(b'tags', opts)
7313 fm = ui.formatter(b'tags', opts)
7314 hexfunc = fm.hexfunc
7314 hexfunc = fm.hexfunc
7315
7315
7316 for t, n in reversed(repo.tagslist()):
7316 for t, n in reversed(repo.tagslist()):
7317 hn = hexfunc(n)
7317 hn = hexfunc(n)
7318 label = b'tags.normal'
7318 label = b'tags.normal'
7319 tagtype = repo.tagtype(t)
7319 tagtype = repo.tagtype(t)
7320 if not tagtype or tagtype == b'global':
7320 if not tagtype or tagtype == b'global':
7321 tagtype = b''
7321 tagtype = b''
7322 else:
7322 else:
7323 label = b'tags.' + tagtype
7323 label = b'tags.' + tagtype
7324
7324
7325 fm.startitem()
7325 fm.startitem()
7326 fm.context(repo=repo)
7326 fm.context(repo=repo)
7327 fm.write(b'tag', b'%s', t, label=label)
7327 fm.write(b'tag', b'%s', t, label=label)
7328 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7328 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7329 fm.condwrite(
7329 fm.condwrite(
7330 not ui.quiet,
7330 not ui.quiet,
7331 b'rev node',
7331 b'rev node',
7332 fmt,
7332 fmt,
7333 repo.changelog.rev(n),
7333 repo.changelog.rev(n),
7334 hn,
7334 hn,
7335 label=label,
7335 label=label,
7336 )
7336 )
7337 fm.condwrite(
7337 fm.condwrite(
7338 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7338 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7339 )
7339 )
7340 fm.plain(b'\n')
7340 fm.plain(b'\n')
7341 fm.end()
7341 fm.end()
7342
7342
7343
7343
7344 @command(
7344 @command(
7345 b'tip',
7345 b'tip',
7346 [
7346 [
7347 (b'p', b'patch', None, _(b'show patch')),
7347 (b'p', b'patch', None, _(b'show patch')),
7348 (b'g', b'git', None, _(b'use git extended diff format')),
7348 (b'g', b'git', None, _(b'use git extended diff format')),
7349 ]
7349 ]
7350 + templateopts,
7350 + templateopts,
7351 _(b'[-p] [-g]'),
7351 _(b'[-p] [-g]'),
7352 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7352 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7353 )
7353 )
7354 def tip(ui, repo, **opts):
7354 def tip(ui, repo, **opts):
7355 """show the tip revision (DEPRECATED)
7355 """show the tip revision (DEPRECATED)
7356
7356
7357 The tip revision (usually just called the tip) is the changeset
7357 The tip revision (usually just called the tip) is the changeset
7358 most recently added to the repository (and therefore the most
7358 most recently added to the repository (and therefore the most
7359 recently changed head).
7359 recently changed head).
7360
7360
7361 If you have just made a commit, that commit will be the tip. If
7361 If you have just made a commit, that commit will be the tip. If
7362 you have just pulled changes from another repository, the tip of
7362 you have just pulled changes from another repository, the tip of
7363 that repository becomes the current tip. The "tip" tag is special
7363 that repository becomes the current tip. The "tip" tag is special
7364 and cannot be renamed or assigned to a different changeset.
7364 and cannot be renamed or assigned to a different changeset.
7365
7365
7366 This command is deprecated, please use :hg:`heads` instead.
7366 This command is deprecated, please use :hg:`heads` instead.
7367
7367
7368 Returns 0 on success.
7368 Returns 0 on success.
7369 """
7369 """
7370 opts = pycompat.byteskwargs(opts)
7370 opts = pycompat.byteskwargs(opts)
7371 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7371 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7372 displayer.show(repo[b'tip'])
7372 displayer.show(repo[b'tip'])
7373 displayer.close()
7373 displayer.close()
7374
7374
7375
7375
7376 @command(
7376 @command(
7377 b'unbundle',
7377 b'unbundle',
7378 [
7378 [
7379 (
7379 (
7380 b'u',
7380 b'u',
7381 b'update',
7381 b'update',
7382 None,
7382 None,
7383 _(b'update to new branch head if changesets were unbundled'),
7383 _(b'update to new branch head if changesets were unbundled'),
7384 )
7384 )
7385 ],
7385 ],
7386 _(b'[-u] FILE...'),
7386 _(b'[-u] FILE...'),
7387 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7387 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7388 )
7388 )
7389 def unbundle(ui, repo, fname1, *fnames, **opts):
7389 def unbundle(ui, repo, fname1, *fnames, **opts):
7390 """apply one or more bundle files
7390 """apply one or more bundle files
7391
7391
7392 Apply one or more bundle files generated by :hg:`bundle`.
7392 Apply one or more bundle files generated by :hg:`bundle`.
7393
7393
7394 Returns 0 on success, 1 if an update has unresolved files.
7394 Returns 0 on success, 1 if an update has unresolved files.
7395 """
7395 """
7396 fnames = (fname1,) + fnames
7396 fnames = (fname1,) + fnames
7397
7397
7398 with repo.lock():
7398 with repo.lock():
7399 for fname in fnames:
7399 for fname in fnames:
7400 f = hg.openpath(ui, fname)
7400 f = hg.openpath(ui, fname)
7401 gen = exchange.readbundle(ui, f, fname)
7401 gen = exchange.readbundle(ui, f, fname)
7402 if isinstance(gen, streamclone.streamcloneapplier):
7402 if isinstance(gen, streamclone.streamcloneapplier):
7403 raise error.InputError(
7403 raise error.InputError(
7404 _(
7404 _(
7405 b'packed bundles cannot be applied with '
7405 b'packed bundles cannot be applied with '
7406 b'"hg unbundle"'
7406 b'"hg unbundle"'
7407 ),
7407 ),
7408 hint=_(b'use "hg debugapplystreamclonebundle"'),
7408 hint=_(b'use "hg debugapplystreamclonebundle"'),
7409 )
7409 )
7410 url = b'bundle:' + fname
7410 url = b'bundle:' + fname
7411 try:
7411 try:
7412 txnname = b'unbundle'
7412 txnname = b'unbundle'
7413 if not isinstance(gen, bundle2.unbundle20):
7413 if not isinstance(gen, bundle2.unbundle20):
7414 txnname = b'unbundle\n%s' % util.hidepassword(url)
7414 txnname = b'unbundle\n%s' % util.hidepassword(url)
7415 with repo.transaction(txnname) as tr:
7415 with repo.transaction(txnname) as tr:
7416 op = bundle2.applybundle(
7416 op = bundle2.applybundle(
7417 repo, gen, tr, source=b'unbundle', url=url
7417 repo, gen, tr, source=b'unbundle', url=url
7418 )
7418 )
7419 except error.BundleUnknownFeatureError as exc:
7419 except error.BundleUnknownFeatureError as exc:
7420 raise error.Abort(
7420 raise error.Abort(
7421 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7421 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7422 hint=_(
7422 hint=_(
7423 b"see https://mercurial-scm.org/"
7423 b"see https://mercurial-scm.org/"
7424 b"wiki/BundleFeature for more "
7424 b"wiki/BundleFeature for more "
7425 b"information"
7425 b"information"
7426 ),
7426 ),
7427 )
7427 )
7428 modheads = bundle2.combinechangegroupresults(op)
7428 modheads = bundle2.combinechangegroupresults(op)
7429
7429
7430 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7430 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7431
7431
7432
7432
7433 @command(
7433 @command(
7434 b'unshelve',
7434 b'unshelve',
7435 [
7435 [
7436 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7436 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7437 (
7437 (
7438 b'c',
7438 b'c',
7439 b'continue',
7439 b'continue',
7440 None,
7440 None,
7441 _(b'continue an incomplete unshelve operation'),
7441 _(b'continue an incomplete unshelve operation'),
7442 ),
7442 ),
7443 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7443 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7444 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7444 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7445 (
7445 (
7446 b'n',
7446 b'n',
7447 b'name',
7447 b'name',
7448 b'',
7448 b'',
7449 _(b'restore shelved change with given name'),
7449 _(b'restore shelved change with given name'),
7450 _(b'NAME'),
7450 _(b'NAME'),
7451 ),
7451 ),
7452 (b't', b'tool', b'', _(b'specify merge tool')),
7452 (b't', b'tool', b'', _(b'specify merge tool')),
7453 (
7453 (
7454 b'',
7454 b'',
7455 b'date',
7455 b'date',
7456 b'',
7456 b'',
7457 _(b'set date for temporary commits (DEPRECATED)'),
7457 _(b'set date for temporary commits (DEPRECATED)'),
7458 _(b'DATE'),
7458 _(b'DATE'),
7459 ),
7459 ),
7460 ],
7460 ],
7461 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7461 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7462 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7462 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7463 )
7463 )
7464 def unshelve(ui, repo, *shelved, **opts):
7464 def unshelve(ui, repo, *shelved, **opts):
7465 """restore a shelved change to the working directory
7465 """restore a shelved change to the working directory
7466
7466
7467 This command accepts an optional name of a shelved change to
7467 This command accepts an optional name of a shelved change to
7468 restore. If none is given, the most recent shelved change is used.
7468 restore. If none is given, the most recent shelved change is used.
7469
7469
7470 If a shelved change is applied successfully, the bundle that
7470 If a shelved change is applied successfully, the bundle that
7471 contains the shelved changes is moved to a backup location
7471 contains the shelved changes is moved to a backup location
7472 (.hg/shelve-backup).
7472 (.hg/shelve-backup).
7473
7473
7474 Since you can restore a shelved change on top of an arbitrary
7474 Since you can restore a shelved change on top of an arbitrary
7475 commit, it is possible that unshelving will result in a conflict
7475 commit, it is possible that unshelving will result in a conflict
7476 between your changes and the commits you are unshelving onto. If
7476 between your changes and the commits you are unshelving onto. If
7477 this occurs, you must resolve the conflict, then use
7477 this occurs, you must resolve the conflict, then use
7478 ``--continue`` to complete the unshelve operation. (The bundle
7478 ``--continue`` to complete the unshelve operation. (The bundle
7479 will not be moved until you successfully complete the unshelve.)
7479 will not be moved until you successfully complete the unshelve.)
7480
7480
7481 (Alternatively, you can use ``--abort`` to abandon an unshelve
7481 (Alternatively, you can use ``--abort`` to abandon an unshelve
7482 that causes a conflict. This reverts the unshelved changes, and
7482 that causes a conflict. This reverts the unshelved changes, and
7483 leaves the bundle in place.)
7483 leaves the bundle in place.)
7484
7484
7485 If bare shelved change (without interactive, include and exclude
7485 If bare shelved change (without interactive, include and exclude
7486 option) was done on newly created branch it would restore branch
7486 option) was done on newly created branch it would restore branch
7487 information to the working directory.
7487 information to the working directory.
7488
7488
7489 After a successful unshelve, the shelved changes are stored in a
7489 After a successful unshelve, the shelved changes are stored in a
7490 backup directory. Only the N most recent backups are kept. N
7490 backup directory. Only the N most recent backups are kept. N
7491 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7491 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7492 configuration option.
7492 configuration option.
7493
7493
7494 .. container:: verbose
7494 .. container:: verbose
7495
7495
7496 Timestamp in seconds is used to decide order of backups. More
7496 Timestamp in seconds is used to decide order of backups. More
7497 than ``maxbackups`` backups are kept, if same timestamp
7497 than ``maxbackups`` backups are kept, if same timestamp
7498 prevents from deciding exact order of them, for safety.
7498 prevents from deciding exact order of them, for safety.
7499
7499
7500 Selected changes can be unshelved with ``--interactive`` flag.
7500 Selected changes can be unshelved with ``--interactive`` flag.
7501 The working directory is updated with the selected changes, and
7501 The working directory is updated with the selected changes, and
7502 only the unselected changes remain shelved.
7502 only the unselected changes remain shelved.
7503 Note: The whole shelve is applied to working directory first before
7503 Note: The whole shelve is applied to working directory first before
7504 running interactively. So, this will bring up all the conflicts between
7504 running interactively. So, this will bring up all the conflicts between
7505 working directory and the shelve, irrespective of which changes will be
7505 working directory and the shelve, irrespective of which changes will be
7506 unshelved.
7506 unshelved.
7507 """
7507 """
7508 with repo.wlock():
7508 with repo.wlock():
7509 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7509 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7510
7510
7511
7511
7512 statemod.addunfinished(
7512 statemod.addunfinished(
7513 b'unshelve',
7513 b'unshelve',
7514 fname=b'shelvedstate',
7514 fname=b'shelvedstate',
7515 continueflag=True,
7515 continueflag=True,
7516 abortfunc=shelvemod.hgabortunshelve,
7516 abortfunc=shelvemod.hgabortunshelve,
7517 continuefunc=shelvemod.hgcontinueunshelve,
7517 continuefunc=shelvemod.hgcontinueunshelve,
7518 cmdmsg=_(b'unshelve already in progress'),
7518 cmdmsg=_(b'unshelve already in progress'),
7519 )
7519 )
7520
7520
7521
7521
7522 @command(
7522 @command(
7523 b'update|up|checkout|co',
7523 b'update|up|checkout|co',
7524 [
7524 [
7525 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7525 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7526 (b'c', b'check', None, _(b'require clean working directory')),
7526 (b'c', b'check', None, _(b'require clean working directory')),
7527 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7527 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7528 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7528 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7529 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7529 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7530 ]
7530 ]
7531 + mergetoolopts,
7531 + mergetoolopts,
7532 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7532 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7533 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7533 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7534 helpbasic=True,
7534 helpbasic=True,
7535 )
7535 )
7536 def update(ui, repo, node=None, **opts):
7536 def update(ui, repo, node=None, **opts):
7537 """update working directory (or switch revisions)
7537 """update working directory (or switch revisions)
7538
7538
7539 Update the repository's working directory to the specified
7539 Update the repository's working directory to the specified
7540 changeset. If no changeset is specified, update to the tip of the
7540 changeset. If no changeset is specified, update to the tip of the
7541 current named branch and move the active bookmark (see :hg:`help
7541 current named branch and move the active bookmark (see :hg:`help
7542 bookmarks`).
7542 bookmarks`).
7543
7543
7544 Update sets the working directory's parent revision to the specified
7544 Update sets the working directory's parent revision to the specified
7545 changeset (see :hg:`help parents`).
7545 changeset (see :hg:`help parents`).
7546
7546
7547 If the changeset is not a descendant or ancestor of the working
7547 If the changeset is not a descendant or ancestor of the working
7548 directory's parent and there are uncommitted changes, the update is
7548 directory's parent and there are uncommitted changes, the update is
7549 aborted. With the -c/--check option, the working directory is checked
7549 aborted. With the -c/--check option, the working directory is checked
7550 for uncommitted changes; if none are found, the working directory is
7550 for uncommitted changes; if none are found, the working directory is
7551 updated to the specified changeset.
7551 updated to the specified changeset.
7552
7552
7553 .. container:: verbose
7553 .. container:: verbose
7554
7554
7555 The -C/--clean, -c/--check, and -m/--merge options control what
7555 The -C/--clean, -c/--check, and -m/--merge options control what
7556 happens if the working directory contains uncommitted changes.
7556 happens if the working directory contains uncommitted changes.
7557 At most of one of them can be specified.
7557 At most of one of them can be specified.
7558
7558
7559 1. If no option is specified, and if
7559 1. If no option is specified, and if
7560 the requested changeset is an ancestor or descendant of
7560 the requested changeset is an ancestor or descendant of
7561 the working directory's parent, the uncommitted changes
7561 the working directory's parent, the uncommitted changes
7562 are merged into the requested changeset and the merged
7562 are merged into the requested changeset and the merged
7563 result is left uncommitted. If the requested changeset is
7563 result is left uncommitted. If the requested changeset is
7564 not an ancestor or descendant (that is, it is on another
7564 not an ancestor or descendant (that is, it is on another
7565 branch), the update is aborted and the uncommitted changes
7565 branch), the update is aborted and the uncommitted changes
7566 are preserved.
7566 are preserved.
7567
7567
7568 2. With the -m/--merge option, the update is allowed even if the
7568 2. With the -m/--merge option, the update is allowed even if the
7569 requested changeset is not an ancestor or descendant of
7569 requested changeset is not an ancestor or descendant of
7570 the working directory's parent.
7570 the working directory's parent.
7571
7571
7572 3. With the -c/--check option, the update is aborted and the
7572 3. With the -c/--check option, the update is aborted and the
7573 uncommitted changes are preserved.
7573 uncommitted changes are preserved.
7574
7574
7575 4. With the -C/--clean option, uncommitted changes are discarded and
7575 4. With the -C/--clean option, uncommitted changes are discarded and
7576 the working directory is updated to the requested changeset.
7576 the working directory is updated to the requested changeset.
7577
7577
7578 To cancel an uncommitted merge (and lose your changes), use
7578 To cancel an uncommitted merge (and lose your changes), use
7579 :hg:`merge --abort`.
7579 :hg:`merge --abort`.
7580
7580
7581 Use null as the changeset to remove the working directory (like
7581 Use null as the changeset to remove the working directory (like
7582 :hg:`clone -U`).
7582 :hg:`clone -U`).
7583
7583
7584 If you want to revert just one file to an older revision, use
7584 If you want to revert just one file to an older revision, use
7585 :hg:`revert [-r REV] NAME`.
7585 :hg:`revert [-r REV] NAME`.
7586
7586
7587 See :hg:`help dates` for a list of formats valid for -d/--date.
7587 See :hg:`help dates` for a list of formats valid for -d/--date.
7588
7588
7589 Returns 0 on success, 1 if there are unresolved files.
7589 Returns 0 on success, 1 if there are unresolved files.
7590 """
7590 """
7591 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7591 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7592 rev = opts.get('rev')
7592 rev = opts.get('rev')
7593 date = opts.get('date')
7593 date = opts.get('date')
7594 clean = opts.get('clean')
7594 clean = opts.get('clean')
7595 check = opts.get('check')
7595 check = opts.get('check')
7596 merge = opts.get('merge')
7596 merge = opts.get('merge')
7597 if rev and node:
7597 if rev and node:
7598 raise error.InputError(_(b"please specify just one revision"))
7598 raise error.InputError(_(b"please specify just one revision"))
7599
7599
7600 if ui.configbool(b'commands', b'update.requiredest'):
7600 if ui.configbool(b'commands', b'update.requiredest'):
7601 if not node and not rev and not date:
7601 if not node and not rev and not date:
7602 raise error.InputError(
7602 raise error.InputError(
7603 _(b'you must specify a destination'),
7603 _(b'you must specify a destination'),
7604 hint=_(b'for example: hg update ".::"'),
7604 hint=_(b'for example: hg update ".::"'),
7605 )
7605 )
7606
7606
7607 if rev is None or rev == b'':
7607 if rev is None or rev == b'':
7608 rev = node
7608 rev = node
7609
7609
7610 if date and rev is not None:
7610 if date and rev is not None:
7611 raise error.InputError(_(b"you can't specify a revision and a date"))
7611 raise error.InputError(_(b"you can't specify a revision and a date"))
7612
7612
7613 updatecheck = None
7613 updatecheck = None
7614 if check:
7614 if check:
7615 updatecheck = b'abort'
7615 updatecheck = b'abort'
7616 elif merge:
7616 elif merge:
7617 updatecheck = b'none'
7617 updatecheck = b'none'
7618
7618
7619 with repo.wlock():
7619 with repo.wlock():
7620 cmdutil.clearunfinished(repo)
7620 cmdutil.clearunfinished(repo)
7621 if date:
7621 if date:
7622 rev = cmdutil.finddate(ui, repo, date)
7622 rev = cmdutil.finddate(ui, repo, date)
7623
7623
7624 # if we defined a bookmark, we have to remember the original name
7624 # if we defined a bookmark, we have to remember the original name
7625 brev = rev
7625 brev = rev
7626 if rev:
7626 if rev:
7627 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7627 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7628 ctx = scmutil.revsingle(repo, rev, default=None)
7628 ctx = scmutil.revsingle(repo, rev, default=None)
7629 rev = ctx.rev()
7629 rev = ctx.rev()
7630 hidden = ctx.hidden()
7630 hidden = ctx.hidden()
7631 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7631 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7632 with ui.configoverride(overrides, b'update'):
7632 with ui.configoverride(overrides, b'update'):
7633 ret = hg.updatetotally(
7633 ret = hg.updatetotally(
7634 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7634 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7635 )
7635 )
7636 if hidden:
7636 if hidden:
7637 ctxstr = ctx.hex()[:12]
7637 ctxstr = ctx.hex()[:12]
7638 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7638 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7639
7639
7640 if ctx.obsolete():
7640 if ctx.obsolete():
7641 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7641 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7642 ui.warn(b"(%s)\n" % obsfatemsg)
7642 ui.warn(b"(%s)\n" % obsfatemsg)
7643 return ret
7643 return ret
7644
7644
7645
7645
7646 @command(
7646 @command(
7647 b'verify',
7647 b'verify',
7648 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7648 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7649 helpcategory=command.CATEGORY_MAINTENANCE,
7649 helpcategory=command.CATEGORY_MAINTENANCE,
7650 )
7650 )
7651 def verify(ui, repo, **opts):
7651 def verify(ui, repo, **opts):
7652 """verify the integrity of the repository
7652 """verify the integrity of the repository
7653
7653
7654 Verify the integrity of the current repository.
7654 Verify the integrity of the current repository.
7655
7655
7656 This will perform an extensive check of the repository's
7656 This will perform an extensive check of the repository's
7657 integrity, validating the hashes and checksums of each entry in
7657 integrity, validating the hashes and checksums of each entry in
7658 the changelog, manifest, and tracked files, as well as the
7658 the changelog, manifest, and tracked files, as well as the
7659 integrity of their crosslinks and indices.
7659 integrity of their crosslinks and indices.
7660
7660
7661 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7661 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7662 for more information about recovery from corruption of the
7662 for more information about recovery from corruption of the
7663 repository.
7663 repository.
7664
7664
7665 Returns 0 on success, 1 if errors are encountered.
7665 Returns 0 on success, 1 if errors are encountered.
7666 """
7666 """
7667 opts = pycompat.byteskwargs(opts)
7667 opts = pycompat.byteskwargs(opts)
7668
7668
7669 level = None
7669 level = None
7670 if opts[b'full']:
7670 if opts[b'full']:
7671 level = verifymod.VERIFY_FULL
7671 level = verifymod.VERIFY_FULL
7672 return hg.verify(repo, level)
7672 return hg.verify(repo, level)
7673
7673
7674
7674
7675 @command(
7675 @command(
7676 b'version',
7676 b'version',
7677 [] + formatteropts,
7677 [] + formatteropts,
7678 helpcategory=command.CATEGORY_HELP,
7678 helpcategory=command.CATEGORY_HELP,
7679 norepo=True,
7679 norepo=True,
7680 intents={INTENT_READONLY},
7680 intents={INTENT_READONLY},
7681 )
7681 )
7682 def version_(ui, **opts):
7682 def version_(ui, **opts):
7683 """output version and copyright information
7683 """output version and copyright information
7684
7684
7685 .. container:: verbose
7685 .. container:: verbose
7686
7686
7687 Template:
7687 Template:
7688
7688
7689 The following keywords are supported. See also :hg:`help templates`.
7689 The following keywords are supported. See also :hg:`help templates`.
7690
7690
7691 :extensions: List of extensions.
7691 :extensions: List of extensions.
7692 :ver: String. Version number.
7692 :ver: String. Version number.
7693
7693
7694 And each entry of ``{extensions}`` provides the following sub-keywords
7694 And each entry of ``{extensions}`` provides the following sub-keywords
7695 in addition to ``{ver}``.
7695 in addition to ``{ver}``.
7696
7696
7697 :bundled: Boolean. True if included in the release.
7697 :bundled: Boolean. True if included in the release.
7698 :name: String. Extension name.
7698 :name: String. Extension name.
7699 """
7699 """
7700 opts = pycompat.byteskwargs(opts)
7700 opts = pycompat.byteskwargs(opts)
7701 if ui.verbose:
7701 if ui.verbose:
7702 ui.pager(b'version')
7702 ui.pager(b'version')
7703 fm = ui.formatter(b"version", opts)
7703 fm = ui.formatter(b"version", opts)
7704 fm.startitem()
7704 fm.startitem()
7705 fm.write(
7705 fm.write(
7706 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7706 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7707 )
7707 )
7708 license = _(
7708 license = _(
7709 b"(see https://mercurial-scm.org for more information)\n"
7709 b"(see https://mercurial-scm.org for more information)\n"
7710 b"\nCopyright (C) 2005-2020 Matt Mackall and others\n"
7710 b"\nCopyright (C) 2005-2021 Matt Mackall and others\n"
7711 b"This is free software; see the source for copying conditions. "
7711 b"This is free software; see the source for copying conditions. "
7712 b"There is NO\nwarranty; "
7712 b"There is NO\nwarranty; "
7713 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7713 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7714 )
7714 )
7715 if not ui.quiet:
7715 if not ui.quiet:
7716 fm.plain(license)
7716 fm.plain(license)
7717
7717
7718 if ui.verbose:
7718 if ui.verbose:
7719 fm.plain(_(b"\nEnabled extensions:\n\n"))
7719 fm.plain(_(b"\nEnabled extensions:\n\n"))
7720 # format names and versions into columns
7720 # format names and versions into columns
7721 names = []
7721 names = []
7722 vers = []
7722 vers = []
7723 isinternals = []
7723 isinternals = []
7724 for name, module in sorted(extensions.extensions()):
7724 for name, module in sorted(extensions.extensions()):
7725 names.append(name)
7725 names.append(name)
7726 vers.append(extensions.moduleversion(module) or None)
7726 vers.append(extensions.moduleversion(module) or None)
7727 isinternals.append(extensions.ismoduleinternal(module))
7727 isinternals.append(extensions.ismoduleinternal(module))
7728 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7728 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7729 if names:
7729 if names:
7730 namefmt = b" %%-%ds " % max(len(n) for n in names)
7730 namefmt = b" %%-%ds " % max(len(n) for n in names)
7731 places = [_(b"external"), _(b"internal")]
7731 places = [_(b"external"), _(b"internal")]
7732 for n, v, p in zip(names, vers, isinternals):
7732 for n, v, p in zip(names, vers, isinternals):
7733 fn.startitem()
7733 fn.startitem()
7734 fn.condwrite(ui.verbose, b"name", namefmt, n)
7734 fn.condwrite(ui.verbose, b"name", namefmt, n)
7735 if ui.verbose:
7735 if ui.verbose:
7736 fn.plain(b"%s " % places[p])
7736 fn.plain(b"%s " % places[p])
7737 fn.data(bundled=p)
7737 fn.data(bundled=p)
7738 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7738 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7739 if ui.verbose:
7739 if ui.verbose:
7740 fn.plain(b"\n")
7740 fn.plain(b"\n")
7741 fn.end()
7741 fn.end()
7742 fm.end()
7742 fm.end()
7743
7743
7744
7744
7745 def loadcmdtable(ui, name, cmdtable):
7745 def loadcmdtable(ui, name, cmdtable):
7746 """Load command functions from specified cmdtable"""
7746 """Load command functions from specified cmdtable"""
7747 overrides = [cmd for cmd in cmdtable if cmd in table]
7747 overrides = [cmd for cmd in cmdtable if cmd in table]
7748 if overrides:
7748 if overrides:
7749 ui.warn(
7749 ui.warn(
7750 _(b"extension '%s' overrides commands: %s\n")
7750 _(b"extension '%s' overrides commands: %s\n")
7751 % (name, b" ".join(overrides))
7751 % (name, b" ".join(overrides))
7752 )
7752 )
7753 table.update(cmdtable)
7753 table.update(cmdtable)
@@ -1,119 +1,119 b''
1 ====
1 ====
2 hg
2 hg
3 ====
3 ====
4
4
5 ---------------------------------------
5 ---------------------------------------
6 Mercurial source code management system
6 Mercurial source code management system
7 ---------------------------------------
7 ---------------------------------------
8
8
9 :Author: Matt Mackall <mpm@selenic.com>
9 :Author: Matt Mackall <mpm@selenic.com>
10 :Organization: Mercurial
10 :Organization: Mercurial
11 :Manual section: 1
11 :Manual section: 1
12 :Manual group: Mercurial Manual
12 :Manual group: Mercurial Manual
13
13
14 .. contents::
14 .. contents::
15 :backlinks: top
15 :backlinks: top
16 :class: htmlonly
16 :class: htmlonly
17 :depth: 1
17 :depth: 1
18
18
19
19
20 Synopsis
20 Synopsis
21 """"""""
21 """"""""
22 **hg** *command* [*option*]... [*argument*]...
22 **hg** *command* [*option*]... [*argument*]...
23
23
24 Description
24 Description
25 """""""""""
25 """""""""""
26 The **hg** command provides a command line interface to the Mercurial
26 The **hg** command provides a command line interface to the Mercurial
27 system.
27 system.
28
28
29 Command Elements
29 Command Elements
30 """"""""""""""""
30 """"""""""""""""
31
31
32 files...
32 files...
33 indicates one or more filename or relative path filenames; see
33 indicates one or more filename or relative path filenames; see
34 `File Name Patterns`_ for information on pattern matching
34 `File Name Patterns`_ for information on pattern matching
35
35
36 path
36 path
37 indicates a path on the local machine
37 indicates a path on the local machine
38
38
39 revision
39 revision
40 indicates a changeset which can be specified as a changeset
40 indicates a changeset which can be specified as a changeset
41 revision number, a tag, or a unique substring of the changeset
41 revision number, a tag, or a unique substring of the changeset
42 hash value
42 hash value
43
43
44 repository path
44 repository path
45 either the pathname of a local repository or the URI of a remote
45 either the pathname of a local repository or the URI of a remote
46 repository.
46 repository.
47
47
48 .. include:: hg.1.gendoc.txt
48 .. include:: hg.1.gendoc.txt
49
49
50 Files
50 Files
51 """""
51 """""
52
52
53 ``/etc/mercurial/hgrc``, ``$HOME/.hgrc``, ``.hg/hgrc``
53 ``/etc/mercurial/hgrc``, ``$HOME/.hgrc``, ``.hg/hgrc``
54 This file contains defaults and configuration. Values in
54 This file contains defaults and configuration. Values in
55 ``.hg/hgrc`` override those in ``$HOME/.hgrc``, and these override
55 ``.hg/hgrc`` override those in ``$HOME/.hgrc``, and these override
56 settings made in the global ``/etc/mercurial/hgrc`` configuration.
56 settings made in the global ``/etc/mercurial/hgrc`` configuration.
57 See |hgrc(5)|_ for details of the contents and format of these
57 See |hgrc(5)|_ for details of the contents and format of these
58 files.
58 files.
59
59
60 ``.hgignore``
60 ``.hgignore``
61 This file contains regular expressions (one per line) that
61 This file contains regular expressions (one per line) that
62 describe file names that should be ignored by **hg**. For details,
62 describe file names that should be ignored by **hg**. For details,
63 see |hgignore(5)|_.
63 see |hgignore(5)|_.
64
64
65 ``.hgsub``
65 ``.hgsub``
66 This file defines the locations of all subrepositories, and
66 This file defines the locations of all subrepositories, and
67 tells where the subrepository checkouts came from. For details, see
67 tells where the subrepository checkouts came from. For details, see
68 :hg:`help subrepos`.
68 :hg:`help subrepos`.
69
69
70 ``.hgsubstate``
70 ``.hgsubstate``
71 This file is where Mercurial stores all nested repository states. *NB: This
71 This file is where Mercurial stores all nested repository states. *NB: This
72 file should not be edited manually.*
72 file should not be edited manually.*
73
73
74 ``.hgtags``
74 ``.hgtags``
75 This file contains changeset hash values and text tag names (one
75 This file contains changeset hash values and text tag names (one
76 of each separated by spaces) that correspond to tagged versions of
76 of each separated by spaces) that correspond to tagged versions of
77 the repository contents. The file content is encoded using UTF-8.
77 the repository contents. The file content is encoded using UTF-8.
78
78
79 ``.hg/last-message.txt``
79 ``.hg/last-message.txt``
80 This file is used by :hg:`commit` to store a backup of the commit message
80 This file is used by :hg:`commit` to store a backup of the commit message
81 in case the commit fails.
81 in case the commit fails.
82
82
83 ``.hg/localtags``
83 ``.hg/localtags``
84 This file can be used to define local tags which are not shared among
84 This file can be used to define local tags which are not shared among
85 repositories. The file format is the same as for ``.hgtags``, but it is
85 repositories. The file format is the same as for ``.hgtags``, but it is
86 encoded using the local system encoding.
86 encoded using the local system encoding.
87
87
88 Some commands (e.g. revert) produce backup files ending in ``.orig``,
88 Some commands (e.g. revert) produce backup files ending in ``.orig``,
89 if the ``.orig`` file already exists and is not tracked by Mercurial,
89 if the ``.orig`` file already exists and is not tracked by Mercurial,
90 it will be overwritten.
90 it will be overwritten.
91
91
92 Bugs
92 Bugs
93 """"
93 """"
94 Probably lots, please post them to the mailing list (see Resources_
94 Probably lots, please post them to the mailing list (see Resources_
95 below) when you find them.
95 below) when you find them.
96
96
97 See Also
97 See Also
98 """"""""
98 """"""""
99 |hgignore(5)|_, |hgrc(5)|_
99 |hgignore(5)|_, |hgrc(5)|_
100
100
101 Author
101 Author
102 """"""
102 """"""
103 Written by Matt Mackall <mpm@selenic.com>
103 Written by Matt Mackall <mpm@selenic.com>
104
104
105 Resources
105 Resources
106 """""""""
106 """""""""
107 Main Web Site: https://mercurial-scm.org/
107 Main Web Site: https://mercurial-scm.org/
108
108
109 Source code repository: https://www.mercurial-scm.org/repo/hg
109 Source code repository: https://www.mercurial-scm.org/repo/hg
110
110
111 Mailing list: https://www.mercurial-scm.org/mailman/listinfo/mercurial/
111 Mailing list: https://www.mercurial-scm.org/mailman/listinfo/mercurial/
112
112
113 Copying
113 Copying
114 """""""
114 """""""
115 Copyright (C) 2005-2020 Matt Mackall.
115 Copyright (C) 2005-2021 Matt Mackall.
116 Free use of this software is granted under the terms of the GNU General
116 Free use of this software is granted under the terms of the GNU General
117 Public License version 2 or any later version.
117 Public License version 2 or any later version.
118
118
119 .. include:: common.txt
119 .. include:: common.txt
@@ -1,34 +1,34 b''
1 ==========
1 ==========
2 hgignore
2 hgignore
3 ==========
3 ==========
4
4
5 ---------------------------------
5 ---------------------------------
6 syntax for Mercurial ignore files
6 syntax for Mercurial ignore files
7 ---------------------------------
7 ---------------------------------
8
8
9 :Author: Vadim Gelfer <vadim.gelfer@gmail.com>
9 :Author: Vadim Gelfer <vadim.gelfer@gmail.com>
10 :Organization: Mercurial
10 :Organization: Mercurial
11 :Manual section: 5
11 :Manual section: 5
12 :Manual group: Mercurial Manual
12 :Manual group: Mercurial Manual
13
13
14 .. include:: hgignore.5.gendoc.txt
14 .. include:: hgignore.5.gendoc.txt
15
15
16 Author
16 Author
17 ======
17 ======
18 Vadim Gelfer <vadim.gelfer@gmail.com>
18 Vadim Gelfer <vadim.gelfer@gmail.com>
19
19
20 Mercurial was written by Matt Mackall <mpm@selenic.com>.
20 Mercurial was written by Matt Mackall <mpm@selenic.com>.
21
21
22 See Also
22 See Also
23 ========
23 ========
24 |hg(1)|_, |hgrc(5)|_
24 |hg(1)|_, |hgrc(5)|_
25
25
26 Copying
26 Copying
27 =======
27 =======
28 This manual page is copyright 2006 Vadim Gelfer.
28 This manual page is copyright 2006 Vadim Gelfer.
29 Mercurial is copyright 2005-2020 Matt Mackall.
29 Mercurial is copyright 2005-2021 Matt Mackall.
30 Free use of this software is granted under the terms of the GNU General
30 Free use of this software is granted under the terms of the GNU General
31 Public License version 2 or any later version.
31 Public License version 2 or any later version.
32
32
33 .. include:: common.txt
33 .. include:: common.txt
34
34
@@ -1,41 +1,41 b''
1 ======
1 ======
2 hgrc
2 hgrc
3 ======
3 ======
4
4
5 ---------------------------------
5 ---------------------------------
6 configuration files for Mercurial
6 configuration files for Mercurial
7 ---------------------------------
7 ---------------------------------
8
8
9 :Author: Bryan O'Sullivan <bos@serpentine.com>
9 :Author: Bryan O'Sullivan <bos@serpentine.com>
10 :Organization: Mercurial
10 :Organization: Mercurial
11 :Manual section: 5
11 :Manual section: 5
12 :Manual group: Mercurial Manual
12 :Manual group: Mercurial Manual
13
13
14 .. contents::
14 .. contents::
15 :backlinks: top
15 :backlinks: top
16 :class: htmlonly
16 :class: htmlonly
17
17
18
18
19 Description
19 Description
20 ===========
20 ===========
21
21
22 .. include:: hgrc.5.gendoc.txt
22 .. include:: hgrc.5.gendoc.txt
23
23
24 Author
24 Author
25 ======
25 ======
26 Bryan O'Sullivan <bos@serpentine.com>.
26 Bryan O'Sullivan <bos@serpentine.com>.
27
27
28 Mercurial was written by Matt Mackall <mpm@selenic.com>.
28 Mercurial was written by Matt Mackall <mpm@selenic.com>.
29
29
30 See Also
30 See Also
31 ========
31 ========
32 |hg(1)|_, |hgignore(5)|_
32 |hg(1)|_, |hgignore(5)|_
33
33
34 Copying
34 Copying
35 =======
35 =======
36 This manual page is copyright 2005 Bryan O'Sullivan.
36 This manual page is copyright 2005 Bryan O'Sullivan.
37 Mercurial is copyright 2005-2020 Matt Mackall.
37 Mercurial is copyright 2005-2021 Matt Mackall.
38 Free use of this software is granted under the terms of the GNU General
38 Free use of this software is granted under the terms of the GNU General
39 Public License version 2 or any later version.
39 Public License version 2 or any later version.
40
40
41 .. include:: common.txt
41 .. include:: common.txt
@@ -1,1842 +1,1842 b''
1 #
1 #
2 # This is the mercurial setup script.
2 # This is the mercurial setup script.
3 #
3 #
4 # 'python setup.py install', or
4 # 'python setup.py install', or
5 # 'python setup.py --help' for more options
5 # 'python setup.py --help' for more options
6 import os
6 import os
7
7
8 # Mercurial will never work on Python 3 before 3.5 due to a lack
8 # Mercurial will never work on Python 3 before 3.5 due to a lack
9 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
9 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
10 # due to a bug in % formatting in bytestrings.
10 # due to a bug in % formatting in bytestrings.
11 # We cannot support Python 3.5.0, 3.5.1, 3.5.2 because of bug in
11 # We cannot support Python 3.5.0, 3.5.1, 3.5.2 because of bug in
12 # codecs.escape_encode() where it raises SystemError on empty bytestring
12 # codecs.escape_encode() where it raises SystemError on empty bytestring
13 # bug link: https://bugs.python.org/issue25270
13 # bug link: https://bugs.python.org/issue25270
14 supportedpy = ','.join(
14 supportedpy = ','.join(
15 [
15 [
16 '>=2.7.4',
16 '>=2.7.4',
17 '!=3.0.*',
17 '!=3.0.*',
18 '!=3.1.*',
18 '!=3.1.*',
19 '!=3.2.*',
19 '!=3.2.*',
20 '!=3.3.*',
20 '!=3.3.*',
21 '!=3.4.*',
21 '!=3.4.*',
22 '!=3.5.0',
22 '!=3.5.0',
23 '!=3.5.1',
23 '!=3.5.1',
24 '!=3.5.2',
24 '!=3.5.2',
25 '!=3.6.0',
25 '!=3.6.0',
26 '!=3.6.1',
26 '!=3.6.1',
27 ]
27 ]
28 )
28 )
29
29
30 import sys, platform
30 import sys, platform
31 import sysconfig
31 import sysconfig
32
32
33 if sys.version_info[0] >= 3:
33 if sys.version_info[0] >= 3:
34 printf = eval('print')
34 printf = eval('print')
35 libdir_escape = 'unicode_escape'
35 libdir_escape = 'unicode_escape'
36
36
37 def sysstr(s):
37 def sysstr(s):
38 return s.decode('latin-1')
38 return s.decode('latin-1')
39
39
40
40
41 else:
41 else:
42 libdir_escape = 'string_escape'
42 libdir_escape = 'string_escape'
43
43
44 def printf(*args, **kwargs):
44 def printf(*args, **kwargs):
45 f = kwargs.get('file', sys.stdout)
45 f = kwargs.get('file', sys.stdout)
46 end = kwargs.get('end', '\n')
46 end = kwargs.get('end', '\n')
47 f.write(b' '.join(args) + end)
47 f.write(b' '.join(args) + end)
48
48
49 def sysstr(s):
49 def sysstr(s):
50 return s
50 return s
51
51
52
52
53 # Attempt to guide users to a modern pip - this means that 2.6 users
53 # Attempt to guide users to a modern pip - this means that 2.6 users
54 # should have a chance of getting a 4.2 release, and when we ratchet
54 # should have a chance of getting a 4.2 release, and when we ratchet
55 # the version requirement forward again hopefully everyone will get
55 # the version requirement forward again hopefully everyone will get
56 # something that works for them.
56 # something that works for them.
57 if sys.version_info < (2, 7, 4, 'final'):
57 if sys.version_info < (2, 7, 4, 'final'):
58 pip_message = (
58 pip_message = (
59 'This may be due to an out of date pip. '
59 'This may be due to an out of date pip. '
60 'Make sure you have pip >= 9.0.1.'
60 'Make sure you have pip >= 9.0.1.'
61 )
61 )
62 try:
62 try:
63 import pip
63 import pip
64
64
65 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
65 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
66 if pip_version < (9, 0, 1):
66 if pip_version < (9, 0, 1):
67 pip_message = (
67 pip_message = (
68 'Your pip version is out of date, please install '
68 'Your pip version is out of date, please install '
69 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__)
69 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__)
70 )
70 )
71 else:
71 else:
72 # pip is new enough - it must be something else
72 # pip is new enough - it must be something else
73 pip_message = ''
73 pip_message = ''
74 except Exception:
74 except Exception:
75 pass
75 pass
76 error = """
76 error = """
77 Mercurial does not support Python older than 2.7.4.
77 Mercurial does not support Python older than 2.7.4.
78 Python {py} detected.
78 Python {py} detected.
79 {pip}
79 {pip}
80 """.format(
80 """.format(
81 py=sys.version_info, pip=pip_message
81 py=sys.version_info, pip=pip_message
82 )
82 )
83 printf(error, file=sys.stderr)
83 printf(error, file=sys.stderr)
84 sys.exit(1)
84 sys.exit(1)
85
85
86 import ssl
86 import ssl
87
87
88 try:
88 try:
89 ssl.SSLContext
89 ssl.SSLContext
90 except AttributeError:
90 except AttributeError:
91 error = """
91 error = """
92 The `ssl` module does not have the `SSLContext` class. This indicates an old
92 The `ssl` module does not have the `SSLContext` class. This indicates an old
93 Python version which does not support modern security features (which were
93 Python version which does not support modern security features (which were
94 added to Python 2.7 as part of "PEP 466"). Please make sure you have installed
94 added to Python 2.7 as part of "PEP 466"). Please make sure you have installed
95 at least Python 2.7.9 or a Python version with backports of these security
95 at least Python 2.7.9 or a Python version with backports of these security
96 features.
96 features.
97 """
97 """
98 printf(error, file=sys.stderr)
98 printf(error, file=sys.stderr)
99 sys.exit(1)
99 sys.exit(1)
100
100
101 # ssl.HAS_TLSv1* are preferred to check support but they were added in Python
101 # ssl.HAS_TLSv1* are preferred to check support but they were added in Python
102 # 3.7. Prior to CPython commit 6e8cda91d92da72800d891b2fc2073ecbc134d98
102 # 3.7. Prior to CPython commit 6e8cda91d92da72800d891b2fc2073ecbc134d98
103 # (backported to the 3.7 branch), ssl.PROTOCOL_TLSv1_1 / ssl.PROTOCOL_TLSv1_2
103 # (backported to the 3.7 branch), ssl.PROTOCOL_TLSv1_1 / ssl.PROTOCOL_TLSv1_2
104 # were defined only if compiled against a OpenSSL version with TLS 1.1 / 1.2
104 # were defined only if compiled against a OpenSSL version with TLS 1.1 / 1.2
105 # support. At the mentioned commit, they were unconditionally defined.
105 # support. At the mentioned commit, they were unconditionally defined.
106 _notset = object()
106 _notset = object()
107 has_tlsv1_1 = getattr(ssl, 'HAS_TLSv1_1', _notset)
107 has_tlsv1_1 = getattr(ssl, 'HAS_TLSv1_1', _notset)
108 if has_tlsv1_1 is _notset:
108 if has_tlsv1_1 is _notset:
109 has_tlsv1_1 = getattr(ssl, 'PROTOCOL_TLSv1_1', _notset) is not _notset
109 has_tlsv1_1 = getattr(ssl, 'PROTOCOL_TLSv1_1', _notset) is not _notset
110 has_tlsv1_2 = getattr(ssl, 'HAS_TLSv1_2', _notset)
110 has_tlsv1_2 = getattr(ssl, 'HAS_TLSv1_2', _notset)
111 if has_tlsv1_2 is _notset:
111 if has_tlsv1_2 is _notset:
112 has_tlsv1_2 = getattr(ssl, 'PROTOCOL_TLSv1_2', _notset) is not _notset
112 has_tlsv1_2 = getattr(ssl, 'PROTOCOL_TLSv1_2', _notset) is not _notset
113 if not (has_tlsv1_1 or has_tlsv1_2):
113 if not (has_tlsv1_1 or has_tlsv1_2):
114 error = """
114 error = """
115 The `ssl` module does not advertise support for TLS 1.1 or TLS 1.2.
115 The `ssl` module does not advertise support for TLS 1.1 or TLS 1.2.
116 Please make sure that your Python installation was compiled against an OpenSSL
116 Please make sure that your Python installation was compiled against an OpenSSL
117 version enabling these features (likely this requires the OpenSSL version to
117 version enabling these features (likely this requires the OpenSSL version to
118 be at least 1.0.1).
118 be at least 1.0.1).
119 """
119 """
120 printf(error, file=sys.stderr)
120 printf(error, file=sys.stderr)
121 sys.exit(1)
121 sys.exit(1)
122
122
123 if sys.version_info[0] >= 3:
123 if sys.version_info[0] >= 3:
124 DYLIB_SUFFIX = sysconfig.get_config_vars()['EXT_SUFFIX']
124 DYLIB_SUFFIX = sysconfig.get_config_vars()['EXT_SUFFIX']
125 else:
125 else:
126 # deprecated in Python 3
126 # deprecated in Python 3
127 DYLIB_SUFFIX = sysconfig.get_config_vars()['SO']
127 DYLIB_SUFFIX = sysconfig.get_config_vars()['SO']
128
128
129 # Solaris Python packaging brain damage
129 # Solaris Python packaging brain damage
130 try:
130 try:
131 import hashlib
131 import hashlib
132
132
133 sha = hashlib.sha1()
133 sha = hashlib.sha1()
134 except ImportError:
134 except ImportError:
135 try:
135 try:
136 import sha
136 import sha
137
137
138 sha.sha # silence unused import warning
138 sha.sha # silence unused import warning
139 except ImportError:
139 except ImportError:
140 raise SystemExit(
140 raise SystemExit(
141 "Couldn't import standard hashlib (incomplete Python install)."
141 "Couldn't import standard hashlib (incomplete Python install)."
142 )
142 )
143
143
144 try:
144 try:
145 import zlib
145 import zlib
146
146
147 zlib.compressobj # silence unused import warning
147 zlib.compressobj # silence unused import warning
148 except ImportError:
148 except ImportError:
149 raise SystemExit(
149 raise SystemExit(
150 "Couldn't import standard zlib (incomplete Python install)."
150 "Couldn't import standard zlib (incomplete Python install)."
151 )
151 )
152
152
153 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
153 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
154 isironpython = False
154 isironpython = False
155 try:
155 try:
156 isironpython = (
156 isironpython = (
157 platform.python_implementation().lower().find("ironpython") != -1
157 platform.python_implementation().lower().find("ironpython") != -1
158 )
158 )
159 except AttributeError:
159 except AttributeError:
160 pass
160 pass
161
161
162 if isironpython:
162 if isironpython:
163 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
163 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
164 else:
164 else:
165 try:
165 try:
166 import bz2
166 import bz2
167
167
168 bz2.BZ2Compressor # silence unused import warning
168 bz2.BZ2Compressor # silence unused import warning
169 except ImportError:
169 except ImportError:
170 raise SystemExit(
170 raise SystemExit(
171 "Couldn't import standard bz2 (incomplete Python install)."
171 "Couldn't import standard bz2 (incomplete Python install)."
172 )
172 )
173
173
174 ispypy = "PyPy" in sys.version
174 ispypy = "PyPy" in sys.version
175
175
176 import ctypes
176 import ctypes
177 import errno
177 import errno
178 import stat, subprocess, time
178 import stat, subprocess, time
179 import re
179 import re
180 import shutil
180 import shutil
181 import tempfile
181 import tempfile
182
182
183 # We have issues with setuptools on some platforms and builders. Until
183 # We have issues with setuptools on some platforms and builders. Until
184 # those are resolved, setuptools is opt-in except for platforms where
184 # those are resolved, setuptools is opt-in except for platforms where
185 # we don't have issues.
185 # we don't have issues.
186 issetuptools = os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ
186 issetuptools = os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ
187 if issetuptools:
187 if issetuptools:
188 from setuptools import setup
188 from setuptools import setup
189 else:
189 else:
190 from distutils.core import setup
190 from distutils.core import setup
191 from distutils.ccompiler import new_compiler
191 from distutils.ccompiler import new_compiler
192 from distutils.core import Command, Extension
192 from distutils.core import Command, Extension
193 from distutils.dist import Distribution
193 from distutils.dist import Distribution
194 from distutils.command.build import build
194 from distutils.command.build import build
195 from distutils.command.build_ext import build_ext
195 from distutils.command.build_ext import build_ext
196 from distutils.command.build_py import build_py
196 from distutils.command.build_py import build_py
197 from distutils.command.build_scripts import build_scripts
197 from distutils.command.build_scripts import build_scripts
198 from distutils.command.install import install
198 from distutils.command.install import install
199 from distutils.command.install_lib import install_lib
199 from distutils.command.install_lib import install_lib
200 from distutils.command.install_scripts import install_scripts
200 from distutils.command.install_scripts import install_scripts
201 from distutils import log
201 from distutils import log
202 from distutils.spawn import spawn, find_executable
202 from distutils.spawn import spawn, find_executable
203 from distutils import file_util
203 from distutils import file_util
204 from distutils.errors import (
204 from distutils.errors import (
205 CCompilerError,
205 CCompilerError,
206 DistutilsError,
206 DistutilsError,
207 DistutilsExecError,
207 DistutilsExecError,
208 )
208 )
209 from distutils.sysconfig import get_python_inc, get_config_var
209 from distutils.sysconfig import get_python_inc, get_config_var
210 from distutils.version import StrictVersion
210 from distutils.version import StrictVersion
211
211
212 # Explain to distutils.StrictVersion how our release candidates are versionned
212 # Explain to distutils.StrictVersion how our release candidates are versionned
213 StrictVersion.version_re = re.compile(r'^(\d+)\.(\d+)(\.(\d+))?-?(rc(\d+))?$')
213 StrictVersion.version_re = re.compile(r'^(\d+)\.(\d+)(\.(\d+))?-?(rc(\d+))?$')
214
214
215
215
216 def write_if_changed(path, content):
216 def write_if_changed(path, content):
217 """Write content to a file iff the content hasn't changed."""
217 """Write content to a file iff the content hasn't changed."""
218 if os.path.exists(path):
218 if os.path.exists(path):
219 with open(path, 'rb') as fh:
219 with open(path, 'rb') as fh:
220 current = fh.read()
220 current = fh.read()
221 else:
221 else:
222 current = b''
222 current = b''
223
223
224 if current != content:
224 if current != content:
225 with open(path, 'wb') as fh:
225 with open(path, 'wb') as fh:
226 fh.write(content)
226 fh.write(content)
227
227
228
228
229 scripts = ['hg']
229 scripts = ['hg']
230 if os.name == 'nt':
230 if os.name == 'nt':
231 # We remove hg.bat if we are able to build hg.exe.
231 # We remove hg.bat if we are able to build hg.exe.
232 scripts.append('contrib/win32/hg.bat')
232 scripts.append('contrib/win32/hg.bat')
233
233
234
234
235 def cancompile(cc, code):
235 def cancompile(cc, code):
236 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
236 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
237 devnull = oldstderr = None
237 devnull = oldstderr = None
238 try:
238 try:
239 fname = os.path.join(tmpdir, 'testcomp.c')
239 fname = os.path.join(tmpdir, 'testcomp.c')
240 f = open(fname, 'w')
240 f = open(fname, 'w')
241 f.write(code)
241 f.write(code)
242 f.close()
242 f.close()
243 # Redirect stderr to /dev/null to hide any error messages
243 # Redirect stderr to /dev/null to hide any error messages
244 # from the compiler.
244 # from the compiler.
245 # This will have to be changed if we ever have to check
245 # This will have to be changed if we ever have to check
246 # for a function on Windows.
246 # for a function on Windows.
247 devnull = open('/dev/null', 'w')
247 devnull = open('/dev/null', 'w')
248 oldstderr = os.dup(sys.stderr.fileno())
248 oldstderr = os.dup(sys.stderr.fileno())
249 os.dup2(devnull.fileno(), sys.stderr.fileno())
249 os.dup2(devnull.fileno(), sys.stderr.fileno())
250 objects = cc.compile([fname], output_dir=tmpdir)
250 objects = cc.compile([fname], output_dir=tmpdir)
251 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
251 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
252 return True
252 return True
253 except Exception:
253 except Exception:
254 return False
254 return False
255 finally:
255 finally:
256 if oldstderr is not None:
256 if oldstderr is not None:
257 os.dup2(oldstderr, sys.stderr.fileno())
257 os.dup2(oldstderr, sys.stderr.fileno())
258 if devnull is not None:
258 if devnull is not None:
259 devnull.close()
259 devnull.close()
260 shutil.rmtree(tmpdir)
260 shutil.rmtree(tmpdir)
261
261
262
262
263 # simplified version of distutils.ccompiler.CCompiler.has_function
263 # simplified version of distutils.ccompiler.CCompiler.has_function
264 # that actually removes its temporary files.
264 # that actually removes its temporary files.
265 def hasfunction(cc, funcname):
265 def hasfunction(cc, funcname):
266 code = 'int main(void) { %s(); }\n' % funcname
266 code = 'int main(void) { %s(); }\n' % funcname
267 return cancompile(cc, code)
267 return cancompile(cc, code)
268
268
269
269
270 def hasheader(cc, headername):
270 def hasheader(cc, headername):
271 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
271 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
272 return cancompile(cc, code)
272 return cancompile(cc, code)
273
273
274
274
275 # py2exe needs to be installed to work
275 # py2exe needs to be installed to work
276 try:
276 try:
277 import py2exe
277 import py2exe
278
278
279 py2exe.Distribution # silence unused import warning
279 py2exe.Distribution # silence unused import warning
280 py2exeloaded = True
280 py2exeloaded = True
281 # import py2exe's patched Distribution class
281 # import py2exe's patched Distribution class
282 from distutils.core import Distribution
282 from distutils.core import Distribution
283 except ImportError:
283 except ImportError:
284 py2exeloaded = False
284 py2exeloaded = False
285
285
286
286
287 def runcmd(cmd, env, cwd=None):
287 def runcmd(cmd, env, cwd=None):
288 p = subprocess.Popen(
288 p = subprocess.Popen(
289 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, cwd=cwd
289 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, cwd=cwd
290 )
290 )
291 out, err = p.communicate()
291 out, err = p.communicate()
292 return p.returncode, out, err
292 return p.returncode, out, err
293
293
294
294
295 class hgcommand(object):
295 class hgcommand(object):
296 def __init__(self, cmd, env):
296 def __init__(self, cmd, env):
297 self.cmd = cmd
297 self.cmd = cmd
298 self.env = env
298 self.env = env
299
299
300 def run(self, args):
300 def run(self, args):
301 cmd = self.cmd + args
301 cmd = self.cmd + args
302 returncode, out, err = runcmd(cmd, self.env)
302 returncode, out, err = runcmd(cmd, self.env)
303 err = filterhgerr(err)
303 err = filterhgerr(err)
304 if err or returncode != 0:
304 if err or returncode != 0:
305 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
305 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
306 printf(err, file=sys.stderr)
306 printf(err, file=sys.stderr)
307 return b''
307 return b''
308 return out
308 return out
309
309
310
310
311 def filterhgerr(err):
311 def filterhgerr(err):
312 # If root is executing setup.py, but the repository is owned by
312 # If root is executing setup.py, but the repository is owned by
313 # another user (as in "sudo python setup.py install") we will get
313 # another user (as in "sudo python setup.py install") we will get
314 # trust warnings since the .hg/hgrc file is untrusted. That is
314 # trust warnings since the .hg/hgrc file is untrusted. That is
315 # fine, we don't want to load it anyway. Python may warn about
315 # fine, we don't want to load it anyway. Python may warn about
316 # a missing __init__.py in mercurial/locale, we also ignore that.
316 # a missing __init__.py in mercurial/locale, we also ignore that.
317 err = [
317 err = [
318 e
318 e
319 for e in err.splitlines()
319 for e in err.splitlines()
320 if (
320 if (
321 not e.startswith(b'not trusting file')
321 not e.startswith(b'not trusting file')
322 and not e.startswith(b'warning: Not importing')
322 and not e.startswith(b'warning: Not importing')
323 and not e.startswith(b'obsolete feature not enabled')
323 and not e.startswith(b'obsolete feature not enabled')
324 and not e.startswith(b'*** failed to import extension')
324 and not e.startswith(b'*** failed to import extension')
325 and not e.startswith(b'devel-warn:')
325 and not e.startswith(b'devel-warn:')
326 and not (
326 and not (
327 e.startswith(b'(third party extension')
327 e.startswith(b'(third party extension')
328 and e.endswith(b'or newer of Mercurial; disabling)')
328 and e.endswith(b'or newer of Mercurial; disabling)')
329 )
329 )
330 )
330 )
331 ]
331 ]
332 return b'\n'.join(b' ' + e for e in err)
332 return b'\n'.join(b' ' + e for e in err)
333
333
334
334
335 def findhg():
335 def findhg():
336 """Try to figure out how we should invoke hg for examining the local
336 """Try to figure out how we should invoke hg for examining the local
337 repository contents.
337 repository contents.
338
338
339 Returns an hgcommand object."""
339 Returns an hgcommand object."""
340 # By default, prefer the "hg" command in the user's path. This was
340 # By default, prefer the "hg" command in the user's path. This was
341 # presumably the hg command that the user used to create this repository.
341 # presumably the hg command that the user used to create this repository.
342 #
342 #
343 # This repository may require extensions or other settings that would not
343 # This repository may require extensions or other settings that would not
344 # be enabled by running the hg script directly from this local repository.
344 # be enabled by running the hg script directly from this local repository.
345 hgenv = os.environ.copy()
345 hgenv = os.environ.copy()
346 # Use HGPLAIN to disable hgrc settings that would change output formatting,
346 # Use HGPLAIN to disable hgrc settings that would change output formatting,
347 # and disable localization for the same reasons.
347 # and disable localization for the same reasons.
348 hgenv['HGPLAIN'] = '1'
348 hgenv['HGPLAIN'] = '1'
349 hgenv['LANGUAGE'] = 'C'
349 hgenv['LANGUAGE'] = 'C'
350 hgcmd = ['hg']
350 hgcmd = ['hg']
351 # Run a simple "hg log" command just to see if using hg from the user's
351 # Run a simple "hg log" command just to see if using hg from the user's
352 # path works and can successfully interact with this repository. Windows
352 # path works and can successfully interact with this repository. Windows
353 # gives precedence to hg.exe in the current directory, so fall back to the
353 # gives precedence to hg.exe in the current directory, so fall back to the
354 # python invocation of local hg, where pythonXY.dll can always be found.
354 # python invocation of local hg, where pythonXY.dll can always be found.
355 check_cmd = ['log', '-r.', '-Ttest']
355 check_cmd = ['log', '-r.', '-Ttest']
356 if os.name != 'nt' or not os.path.exists("hg.exe"):
356 if os.name != 'nt' or not os.path.exists("hg.exe"):
357 try:
357 try:
358 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
358 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
359 except EnvironmentError:
359 except EnvironmentError:
360 retcode = -1
360 retcode = -1
361 if retcode == 0 and not filterhgerr(err):
361 if retcode == 0 and not filterhgerr(err):
362 return hgcommand(hgcmd, hgenv)
362 return hgcommand(hgcmd, hgenv)
363
363
364 # Fall back to trying the local hg installation.
364 # Fall back to trying the local hg installation.
365 hgenv = localhgenv()
365 hgenv = localhgenv()
366 hgcmd = [sys.executable, 'hg']
366 hgcmd = [sys.executable, 'hg']
367 try:
367 try:
368 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
368 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
369 except EnvironmentError:
369 except EnvironmentError:
370 retcode = -1
370 retcode = -1
371 if retcode == 0 and not filterhgerr(err):
371 if retcode == 0 and not filterhgerr(err):
372 return hgcommand(hgcmd, hgenv)
372 return hgcommand(hgcmd, hgenv)
373
373
374 raise SystemExit(
374 raise SystemExit(
375 'Unable to find a working hg binary to extract the '
375 'Unable to find a working hg binary to extract the '
376 'version from the repository tags'
376 'version from the repository tags'
377 )
377 )
378
378
379
379
380 def localhgenv():
380 def localhgenv():
381 """Get an environment dictionary to use for invoking or importing
381 """Get an environment dictionary to use for invoking or importing
382 mercurial from the local repository."""
382 mercurial from the local repository."""
383 # Execute hg out of this directory with a custom environment which takes
383 # Execute hg out of this directory with a custom environment which takes
384 # care to not use any hgrc files and do no localization.
384 # care to not use any hgrc files and do no localization.
385 env = {
385 env = {
386 'HGMODULEPOLICY': 'py',
386 'HGMODULEPOLICY': 'py',
387 'HGRCPATH': '',
387 'HGRCPATH': '',
388 'LANGUAGE': 'C',
388 'LANGUAGE': 'C',
389 'PATH': '',
389 'PATH': '',
390 } # make pypi modules that use os.environ['PATH'] happy
390 } # make pypi modules that use os.environ['PATH'] happy
391 if 'LD_LIBRARY_PATH' in os.environ:
391 if 'LD_LIBRARY_PATH' in os.environ:
392 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
392 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
393 if 'SystemRoot' in os.environ:
393 if 'SystemRoot' in os.environ:
394 # SystemRoot is required by Windows to load various DLLs. See:
394 # SystemRoot is required by Windows to load various DLLs. See:
395 # https://bugs.python.org/issue13524#msg148850
395 # https://bugs.python.org/issue13524#msg148850
396 env['SystemRoot'] = os.environ['SystemRoot']
396 env['SystemRoot'] = os.environ['SystemRoot']
397 return env
397 return env
398
398
399
399
400 version = ''
400 version = ''
401
401
402 if os.path.isdir('.hg'):
402 if os.path.isdir('.hg'):
403 hg = findhg()
403 hg = findhg()
404 cmd = ['log', '-r', '.', '--template', '{tags}\n']
404 cmd = ['log', '-r', '.', '--template', '{tags}\n']
405 numerictags = [t for t in sysstr(hg.run(cmd)).split() if t[0:1].isdigit()]
405 numerictags = [t for t in sysstr(hg.run(cmd)).split() if t[0:1].isdigit()]
406 hgid = sysstr(hg.run(['id', '-i'])).strip()
406 hgid = sysstr(hg.run(['id', '-i'])).strip()
407 if not hgid:
407 if not hgid:
408 # Bail out if hg is having problems interacting with this repository,
408 # Bail out if hg is having problems interacting with this repository,
409 # rather than falling through and producing a bogus version number.
409 # rather than falling through and producing a bogus version number.
410 # Continuing with an invalid version number will break extensions
410 # Continuing with an invalid version number will break extensions
411 # that define minimumhgversion.
411 # that define minimumhgversion.
412 raise SystemExit('Unable to determine hg version from local repository')
412 raise SystemExit('Unable to determine hg version from local repository')
413 if numerictags: # tag(s) found
413 if numerictags: # tag(s) found
414 version = numerictags[-1]
414 version = numerictags[-1]
415 if hgid.endswith('+'): # propagate the dirty status to the tag
415 if hgid.endswith('+'): # propagate the dirty status to the tag
416 version += '+'
416 version += '+'
417 else: # no tag found
417 else: # no tag found
418 ltagcmd = ['parents', '--template', '{latesttag}']
418 ltagcmd = ['parents', '--template', '{latesttag}']
419 ltag = sysstr(hg.run(ltagcmd))
419 ltag = sysstr(hg.run(ltagcmd))
420 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
420 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
421 changessince = len(hg.run(changessincecmd).splitlines())
421 changessince = len(hg.run(changessincecmd).splitlines())
422 version = '%s+%s-%s' % (ltag, changessince, hgid)
422 version = '%s+%s-%s' % (ltag, changessince, hgid)
423 if version.endswith('+'):
423 if version.endswith('+'):
424 version += time.strftime('%Y%m%d')
424 version += time.strftime('%Y%m%d')
425 elif os.path.exists('.hg_archival.txt'):
425 elif os.path.exists('.hg_archival.txt'):
426 kw = dict(
426 kw = dict(
427 [[t.strip() for t in l.split(':', 1)] for l in open('.hg_archival.txt')]
427 [[t.strip() for t in l.split(':', 1)] for l in open('.hg_archival.txt')]
428 )
428 )
429 if 'tag' in kw:
429 if 'tag' in kw:
430 version = kw['tag']
430 version = kw['tag']
431 elif 'latesttag' in kw:
431 elif 'latesttag' in kw:
432 if 'changessincelatesttag' in kw:
432 if 'changessincelatesttag' in kw:
433 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
433 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
434 else:
434 else:
435 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
435 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
436 else:
436 else:
437 version = kw.get('node', '')[:12]
437 version = kw.get('node', '')[:12]
438
438
439 if version:
439 if version:
440 versionb = version
440 versionb = version
441 if not isinstance(versionb, bytes):
441 if not isinstance(versionb, bytes):
442 versionb = versionb.encode('ascii')
442 versionb = versionb.encode('ascii')
443
443
444 write_if_changed(
444 write_if_changed(
445 'mercurial/__version__.py',
445 'mercurial/__version__.py',
446 b''.join(
446 b''.join(
447 [
447 [
448 b'# this file is autogenerated by setup.py\n'
448 b'# this file is autogenerated by setup.py\n'
449 b'version = b"%s"\n' % versionb,
449 b'version = b"%s"\n' % versionb,
450 ]
450 ]
451 ),
451 ),
452 )
452 )
453
453
454 try:
454 try:
455 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
455 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
456 os.environ['HGMODULEPOLICY'] = 'py'
456 os.environ['HGMODULEPOLICY'] = 'py'
457 from mercurial import __version__
457 from mercurial import __version__
458
458
459 version = __version__.version
459 version = __version__.version
460 except ImportError:
460 except ImportError:
461 version = b'unknown'
461 version = b'unknown'
462 finally:
462 finally:
463 if oldpolicy is None:
463 if oldpolicy is None:
464 del os.environ['HGMODULEPOLICY']
464 del os.environ['HGMODULEPOLICY']
465 else:
465 else:
466 os.environ['HGMODULEPOLICY'] = oldpolicy
466 os.environ['HGMODULEPOLICY'] = oldpolicy
467
467
468
468
469 class hgbuild(build):
469 class hgbuild(build):
470 # Insert hgbuildmo first so that files in mercurial/locale/ are found
470 # Insert hgbuildmo first so that files in mercurial/locale/ are found
471 # when build_py is run next.
471 # when build_py is run next.
472 sub_commands = [('build_mo', None)] + build.sub_commands
472 sub_commands = [('build_mo', None)] + build.sub_commands
473
473
474
474
475 class hgbuildmo(build):
475 class hgbuildmo(build):
476
476
477 description = "build translations (.mo files)"
477 description = "build translations (.mo files)"
478
478
479 def run(self):
479 def run(self):
480 if not find_executable('msgfmt'):
480 if not find_executable('msgfmt'):
481 self.warn(
481 self.warn(
482 "could not find msgfmt executable, no translations "
482 "could not find msgfmt executable, no translations "
483 "will be built"
483 "will be built"
484 )
484 )
485 return
485 return
486
486
487 podir = 'i18n'
487 podir = 'i18n'
488 if not os.path.isdir(podir):
488 if not os.path.isdir(podir):
489 self.warn("could not find %s/ directory" % podir)
489 self.warn("could not find %s/ directory" % podir)
490 return
490 return
491
491
492 join = os.path.join
492 join = os.path.join
493 for po in os.listdir(podir):
493 for po in os.listdir(podir):
494 if not po.endswith('.po'):
494 if not po.endswith('.po'):
495 continue
495 continue
496 pofile = join(podir, po)
496 pofile = join(podir, po)
497 modir = join('locale', po[:-3], 'LC_MESSAGES')
497 modir = join('locale', po[:-3], 'LC_MESSAGES')
498 mofile = join(modir, 'hg.mo')
498 mofile = join(modir, 'hg.mo')
499 mobuildfile = join('mercurial', mofile)
499 mobuildfile = join('mercurial', mofile)
500 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
500 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
501 if sys.platform != 'sunos5':
501 if sys.platform != 'sunos5':
502 # msgfmt on Solaris does not know about -c
502 # msgfmt on Solaris does not know about -c
503 cmd.append('-c')
503 cmd.append('-c')
504 self.mkpath(join('mercurial', modir))
504 self.mkpath(join('mercurial', modir))
505 self.make_file([pofile], mobuildfile, spawn, (cmd,))
505 self.make_file([pofile], mobuildfile, spawn, (cmd,))
506
506
507
507
508 class hgdist(Distribution):
508 class hgdist(Distribution):
509 pure = False
509 pure = False
510 rust = False
510 rust = False
511 no_rust = False
511 no_rust = False
512 cffi = ispypy
512 cffi = ispypy
513
513
514 global_options = Distribution.global_options + [
514 global_options = Distribution.global_options + [
515 ('pure', None, "use pure (slow) Python code instead of C extensions"),
515 ('pure', None, "use pure (slow) Python code instead of C extensions"),
516 ('rust', None, "use Rust extensions additionally to C extensions"),
516 ('rust', None, "use Rust extensions additionally to C extensions"),
517 (
517 (
518 'no-rust',
518 'no-rust',
519 None,
519 None,
520 "do not use Rust extensions additionally to C extensions",
520 "do not use Rust extensions additionally to C extensions",
521 ),
521 ),
522 ]
522 ]
523
523
524 negative_opt = Distribution.negative_opt.copy()
524 negative_opt = Distribution.negative_opt.copy()
525 boolean_options = ['pure', 'rust', 'no-rust']
525 boolean_options = ['pure', 'rust', 'no-rust']
526 negative_opt['no-rust'] = 'rust'
526 negative_opt['no-rust'] = 'rust'
527
527
528 def _set_command_options(self, command_obj, option_dict=None):
528 def _set_command_options(self, command_obj, option_dict=None):
529 # Not all distutils versions in the wild have boolean_options.
529 # Not all distutils versions in the wild have boolean_options.
530 # This should be cleaned up when we're Python 3 only.
530 # This should be cleaned up when we're Python 3 only.
531 command_obj.boolean_options = (
531 command_obj.boolean_options = (
532 getattr(command_obj, 'boolean_options', []) + self.boolean_options
532 getattr(command_obj, 'boolean_options', []) + self.boolean_options
533 )
533 )
534 return Distribution._set_command_options(
534 return Distribution._set_command_options(
535 self, command_obj, option_dict=option_dict
535 self, command_obj, option_dict=option_dict
536 )
536 )
537
537
538 def parse_command_line(self):
538 def parse_command_line(self):
539 ret = Distribution.parse_command_line(self)
539 ret = Distribution.parse_command_line(self)
540 if not (self.rust or self.no_rust):
540 if not (self.rust or self.no_rust):
541 hgrustext = os.environ.get('HGWITHRUSTEXT')
541 hgrustext = os.environ.get('HGWITHRUSTEXT')
542 # TODO record it for proper rebuild upon changes
542 # TODO record it for proper rebuild upon changes
543 # (see mercurial/__modulepolicy__.py)
543 # (see mercurial/__modulepolicy__.py)
544 if hgrustext != 'cpython' and hgrustext is not None:
544 if hgrustext != 'cpython' and hgrustext is not None:
545 if hgrustext:
545 if hgrustext:
546 msg = 'unkown HGWITHRUSTEXT value: %s' % hgrustext
546 msg = 'unkown HGWITHRUSTEXT value: %s' % hgrustext
547 printf(msg, file=sys.stderr)
547 printf(msg, file=sys.stderr)
548 hgrustext = None
548 hgrustext = None
549 self.rust = hgrustext is not None
549 self.rust = hgrustext is not None
550 self.no_rust = not self.rust
550 self.no_rust = not self.rust
551 return ret
551 return ret
552
552
553 def has_ext_modules(self):
553 def has_ext_modules(self):
554 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
554 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
555 # too late for some cases
555 # too late for some cases
556 return not self.pure and Distribution.has_ext_modules(self)
556 return not self.pure and Distribution.has_ext_modules(self)
557
557
558
558
559 # This is ugly as a one-liner. So use a variable.
559 # This is ugly as a one-liner. So use a variable.
560 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
560 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
561 buildextnegops['no-zstd'] = 'zstd'
561 buildextnegops['no-zstd'] = 'zstd'
562 buildextnegops['no-rust'] = 'rust'
562 buildextnegops['no-rust'] = 'rust'
563
563
564
564
565 class hgbuildext(build_ext):
565 class hgbuildext(build_ext):
566 user_options = build_ext.user_options + [
566 user_options = build_ext.user_options + [
567 ('zstd', None, 'compile zstd bindings [default]'),
567 ('zstd', None, 'compile zstd bindings [default]'),
568 ('no-zstd', None, 'do not compile zstd bindings'),
568 ('no-zstd', None, 'do not compile zstd bindings'),
569 (
569 (
570 'rust',
570 'rust',
571 None,
571 None,
572 'compile Rust extensions if they are in use '
572 'compile Rust extensions if they are in use '
573 '(requires Cargo) [default]',
573 '(requires Cargo) [default]',
574 ),
574 ),
575 ('no-rust', None, 'do not compile Rust extensions'),
575 ('no-rust', None, 'do not compile Rust extensions'),
576 ]
576 ]
577
577
578 boolean_options = build_ext.boolean_options + ['zstd', 'rust']
578 boolean_options = build_ext.boolean_options + ['zstd', 'rust']
579 negative_opt = buildextnegops
579 negative_opt = buildextnegops
580
580
581 def initialize_options(self):
581 def initialize_options(self):
582 self.zstd = True
582 self.zstd = True
583 self.rust = True
583 self.rust = True
584
584
585 return build_ext.initialize_options(self)
585 return build_ext.initialize_options(self)
586
586
587 def finalize_options(self):
587 def finalize_options(self):
588 # Unless overridden by the end user, build extensions in parallel.
588 # Unless overridden by the end user, build extensions in parallel.
589 # Only influences behavior on Python 3.5+.
589 # Only influences behavior on Python 3.5+.
590 if getattr(self, 'parallel', None) is None:
590 if getattr(self, 'parallel', None) is None:
591 self.parallel = True
591 self.parallel = True
592
592
593 return build_ext.finalize_options(self)
593 return build_ext.finalize_options(self)
594
594
595 def build_extensions(self):
595 def build_extensions(self):
596 ruststandalones = [
596 ruststandalones = [
597 e for e in self.extensions if isinstance(e, RustStandaloneExtension)
597 e for e in self.extensions if isinstance(e, RustStandaloneExtension)
598 ]
598 ]
599 self.extensions = [
599 self.extensions = [
600 e for e in self.extensions if e not in ruststandalones
600 e for e in self.extensions if e not in ruststandalones
601 ]
601 ]
602 # Filter out zstd if disabled via argument.
602 # Filter out zstd if disabled via argument.
603 if not self.zstd:
603 if not self.zstd:
604 self.extensions = [
604 self.extensions = [
605 e for e in self.extensions if e.name != 'mercurial.zstd'
605 e for e in self.extensions if e.name != 'mercurial.zstd'
606 ]
606 ]
607
607
608 # Build Rust standalon extensions if it'll be used
608 # Build Rust standalon extensions if it'll be used
609 # and its build is not explictely disabled (for external build
609 # and its build is not explictely disabled (for external build
610 # as Linux distributions would do)
610 # as Linux distributions would do)
611 if self.distribution.rust and self.rust:
611 if self.distribution.rust and self.rust:
612 for rustext in ruststandalones:
612 for rustext in ruststandalones:
613 rustext.build('' if self.inplace else self.build_lib)
613 rustext.build('' if self.inplace else self.build_lib)
614
614
615 return build_ext.build_extensions(self)
615 return build_ext.build_extensions(self)
616
616
617 def build_extension(self, ext):
617 def build_extension(self, ext):
618 if (
618 if (
619 self.distribution.rust
619 self.distribution.rust
620 and self.rust
620 and self.rust
621 and isinstance(ext, RustExtension)
621 and isinstance(ext, RustExtension)
622 ):
622 ):
623 ext.rustbuild()
623 ext.rustbuild()
624 try:
624 try:
625 build_ext.build_extension(self, ext)
625 build_ext.build_extension(self, ext)
626 except CCompilerError:
626 except CCompilerError:
627 if not getattr(ext, 'optional', False):
627 if not getattr(ext, 'optional', False):
628 raise
628 raise
629 log.warn(
629 log.warn(
630 "Failed to build optional extension '%s' (skipping)", ext.name
630 "Failed to build optional extension '%s' (skipping)", ext.name
631 )
631 )
632
632
633
633
634 class hgbuildscripts(build_scripts):
634 class hgbuildscripts(build_scripts):
635 def run(self):
635 def run(self):
636 if os.name != 'nt' or self.distribution.pure:
636 if os.name != 'nt' or self.distribution.pure:
637 return build_scripts.run(self)
637 return build_scripts.run(self)
638
638
639 exebuilt = False
639 exebuilt = False
640 try:
640 try:
641 self.run_command('build_hgexe')
641 self.run_command('build_hgexe')
642 exebuilt = True
642 exebuilt = True
643 except (DistutilsError, CCompilerError):
643 except (DistutilsError, CCompilerError):
644 log.warn('failed to build optional hg.exe')
644 log.warn('failed to build optional hg.exe')
645
645
646 if exebuilt:
646 if exebuilt:
647 # Copying hg.exe to the scripts build directory ensures it is
647 # Copying hg.exe to the scripts build directory ensures it is
648 # installed by the install_scripts command.
648 # installed by the install_scripts command.
649 hgexecommand = self.get_finalized_command('build_hgexe')
649 hgexecommand = self.get_finalized_command('build_hgexe')
650 dest = os.path.join(self.build_dir, 'hg.exe')
650 dest = os.path.join(self.build_dir, 'hg.exe')
651 self.mkpath(self.build_dir)
651 self.mkpath(self.build_dir)
652 self.copy_file(hgexecommand.hgexepath, dest)
652 self.copy_file(hgexecommand.hgexepath, dest)
653
653
654 # Remove hg.bat because it is redundant with hg.exe.
654 # Remove hg.bat because it is redundant with hg.exe.
655 self.scripts.remove('contrib/win32/hg.bat')
655 self.scripts.remove('contrib/win32/hg.bat')
656
656
657 return build_scripts.run(self)
657 return build_scripts.run(self)
658
658
659
659
660 class hgbuildpy(build_py):
660 class hgbuildpy(build_py):
661 def finalize_options(self):
661 def finalize_options(self):
662 build_py.finalize_options(self)
662 build_py.finalize_options(self)
663
663
664 if self.distribution.pure:
664 if self.distribution.pure:
665 self.distribution.ext_modules = []
665 self.distribution.ext_modules = []
666 elif self.distribution.cffi:
666 elif self.distribution.cffi:
667 from mercurial.cffi import (
667 from mercurial.cffi import (
668 bdiffbuild,
668 bdiffbuild,
669 mpatchbuild,
669 mpatchbuild,
670 )
670 )
671
671
672 exts = [
672 exts = [
673 mpatchbuild.ffi.distutils_extension(),
673 mpatchbuild.ffi.distutils_extension(),
674 bdiffbuild.ffi.distutils_extension(),
674 bdiffbuild.ffi.distutils_extension(),
675 ]
675 ]
676 # cffi modules go here
676 # cffi modules go here
677 if sys.platform == 'darwin':
677 if sys.platform == 'darwin':
678 from mercurial.cffi import osutilbuild
678 from mercurial.cffi import osutilbuild
679
679
680 exts.append(osutilbuild.ffi.distutils_extension())
680 exts.append(osutilbuild.ffi.distutils_extension())
681 self.distribution.ext_modules = exts
681 self.distribution.ext_modules = exts
682 else:
682 else:
683 h = os.path.join(get_python_inc(), 'Python.h')
683 h = os.path.join(get_python_inc(), 'Python.h')
684 if not os.path.exists(h):
684 if not os.path.exists(h):
685 raise SystemExit(
685 raise SystemExit(
686 'Python headers are required to build '
686 'Python headers are required to build '
687 'Mercurial but weren\'t found in %s' % h
687 'Mercurial but weren\'t found in %s' % h
688 )
688 )
689
689
690 def run(self):
690 def run(self):
691 basepath = os.path.join(self.build_lib, 'mercurial')
691 basepath = os.path.join(self.build_lib, 'mercurial')
692 self.mkpath(basepath)
692 self.mkpath(basepath)
693
693
694 rust = self.distribution.rust
694 rust = self.distribution.rust
695 if self.distribution.pure:
695 if self.distribution.pure:
696 modulepolicy = 'py'
696 modulepolicy = 'py'
697 elif self.build_lib == '.':
697 elif self.build_lib == '.':
698 # in-place build should run without rebuilding and Rust extensions
698 # in-place build should run without rebuilding and Rust extensions
699 modulepolicy = 'rust+c-allow' if rust else 'allow'
699 modulepolicy = 'rust+c-allow' if rust else 'allow'
700 else:
700 else:
701 modulepolicy = 'rust+c' if rust else 'c'
701 modulepolicy = 'rust+c' if rust else 'c'
702
702
703 content = b''.join(
703 content = b''.join(
704 [
704 [
705 b'# this file is autogenerated by setup.py\n',
705 b'# this file is autogenerated by setup.py\n',
706 b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
706 b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
707 ]
707 ]
708 )
708 )
709 write_if_changed(os.path.join(basepath, '__modulepolicy__.py'), content)
709 write_if_changed(os.path.join(basepath, '__modulepolicy__.py'), content)
710
710
711 build_py.run(self)
711 build_py.run(self)
712
712
713
713
714 class buildhgextindex(Command):
714 class buildhgextindex(Command):
715 description = 'generate prebuilt index of hgext (for frozen package)'
715 description = 'generate prebuilt index of hgext (for frozen package)'
716 user_options = []
716 user_options = []
717 _indexfilename = 'hgext/__index__.py'
717 _indexfilename = 'hgext/__index__.py'
718
718
719 def initialize_options(self):
719 def initialize_options(self):
720 pass
720 pass
721
721
722 def finalize_options(self):
722 def finalize_options(self):
723 pass
723 pass
724
724
725 def run(self):
725 def run(self):
726 if os.path.exists(self._indexfilename):
726 if os.path.exists(self._indexfilename):
727 with open(self._indexfilename, 'w') as f:
727 with open(self._indexfilename, 'w') as f:
728 f.write('# empty\n')
728 f.write('# empty\n')
729
729
730 # here no extension enabled, disabled() lists up everything
730 # here no extension enabled, disabled() lists up everything
731 code = (
731 code = (
732 'import pprint; from mercurial import extensions; '
732 'import pprint; from mercurial import extensions; '
733 'ext = extensions.disabled();'
733 'ext = extensions.disabled();'
734 'ext.pop("__index__", None);'
734 'ext.pop("__index__", None);'
735 'pprint.pprint(ext)'
735 'pprint.pprint(ext)'
736 )
736 )
737 returncode, out, err = runcmd(
737 returncode, out, err = runcmd(
738 [sys.executable, '-c', code], localhgenv()
738 [sys.executable, '-c', code], localhgenv()
739 )
739 )
740 if err or returncode != 0:
740 if err or returncode != 0:
741 raise DistutilsExecError(err)
741 raise DistutilsExecError(err)
742
742
743 with open(self._indexfilename, 'wb') as f:
743 with open(self._indexfilename, 'wb') as f:
744 f.write(b'# this file is autogenerated by setup.py\n')
744 f.write(b'# this file is autogenerated by setup.py\n')
745 f.write(b'docs = ')
745 f.write(b'docs = ')
746 f.write(out)
746 f.write(out)
747
747
748
748
749 class buildhgexe(build_ext):
749 class buildhgexe(build_ext):
750 description = 'compile hg.exe from mercurial/exewrapper.c'
750 description = 'compile hg.exe from mercurial/exewrapper.c'
751 user_options = build_ext.user_options + [
751 user_options = build_ext.user_options + [
752 (
752 (
753 'long-paths-support',
753 'long-paths-support',
754 None,
754 None,
755 'enable support for long paths on '
755 'enable support for long paths on '
756 'Windows (off by default and '
756 'Windows (off by default and '
757 'experimental)',
757 'experimental)',
758 ),
758 ),
759 ]
759 ]
760
760
761 LONG_PATHS_MANIFEST = """
761 LONG_PATHS_MANIFEST = """
762 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
762 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
763 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
763 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
764 <application>
764 <application>
765 <windowsSettings
765 <windowsSettings
766 xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
766 xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
767 <ws2:longPathAware>true</ws2:longPathAware>
767 <ws2:longPathAware>true</ws2:longPathAware>
768 </windowsSettings>
768 </windowsSettings>
769 </application>
769 </application>
770 </assembly>"""
770 </assembly>"""
771
771
772 def initialize_options(self):
772 def initialize_options(self):
773 build_ext.initialize_options(self)
773 build_ext.initialize_options(self)
774 self.long_paths_support = False
774 self.long_paths_support = False
775
775
776 def build_extensions(self):
776 def build_extensions(self):
777 if os.name != 'nt':
777 if os.name != 'nt':
778 return
778 return
779 if isinstance(self.compiler, HackedMingw32CCompiler):
779 if isinstance(self.compiler, HackedMingw32CCompiler):
780 self.compiler.compiler_so = self.compiler.compiler # no -mdll
780 self.compiler.compiler_so = self.compiler.compiler # no -mdll
781 self.compiler.dll_libraries = [] # no -lmsrvc90
781 self.compiler.dll_libraries = [] # no -lmsrvc90
782
782
783 pythonlib = None
783 pythonlib = None
784
784
785 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
785 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
786 self.hgtarget = os.path.join(dir, 'hg')
786 self.hgtarget = os.path.join(dir, 'hg')
787
787
788 if getattr(sys, 'dllhandle', None):
788 if getattr(sys, 'dllhandle', None):
789 # Different Python installs can have different Python library
789 # Different Python installs can have different Python library
790 # names. e.g. the official CPython distribution uses pythonXY.dll
790 # names. e.g. the official CPython distribution uses pythonXY.dll
791 # and MinGW uses libpythonX.Y.dll.
791 # and MinGW uses libpythonX.Y.dll.
792 _kernel32 = ctypes.windll.kernel32
792 _kernel32 = ctypes.windll.kernel32
793 _kernel32.GetModuleFileNameA.argtypes = [
793 _kernel32.GetModuleFileNameA.argtypes = [
794 ctypes.c_void_p,
794 ctypes.c_void_p,
795 ctypes.c_void_p,
795 ctypes.c_void_p,
796 ctypes.c_ulong,
796 ctypes.c_ulong,
797 ]
797 ]
798 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
798 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
799 size = 1000
799 size = 1000
800 buf = ctypes.create_string_buffer(size + 1)
800 buf = ctypes.create_string_buffer(size + 1)
801 filelen = _kernel32.GetModuleFileNameA(
801 filelen = _kernel32.GetModuleFileNameA(
802 sys.dllhandle, ctypes.byref(buf), size
802 sys.dllhandle, ctypes.byref(buf), size
803 )
803 )
804
804
805 if filelen > 0 and filelen != size:
805 if filelen > 0 and filelen != size:
806 dllbasename = os.path.basename(buf.value)
806 dllbasename = os.path.basename(buf.value)
807 if not dllbasename.lower().endswith(b'.dll'):
807 if not dllbasename.lower().endswith(b'.dll'):
808 raise SystemExit(
808 raise SystemExit(
809 'Python DLL does not end with .dll: %s' % dllbasename
809 'Python DLL does not end with .dll: %s' % dllbasename
810 )
810 )
811 pythonlib = dllbasename[:-4]
811 pythonlib = dllbasename[:-4]
812
812
813 # Copy the pythonXY.dll next to the binary so that it runs
813 # Copy the pythonXY.dll next to the binary so that it runs
814 # without tampering with PATH.
814 # without tampering with PATH.
815 fsdecode = lambda x: x
815 fsdecode = lambda x: x
816 if sys.version_info[0] >= 3:
816 if sys.version_info[0] >= 3:
817 fsdecode = os.fsdecode
817 fsdecode = os.fsdecode
818 dest = os.path.join(
818 dest = os.path.join(
819 os.path.dirname(self.hgtarget),
819 os.path.dirname(self.hgtarget),
820 fsdecode(dllbasename),
820 fsdecode(dllbasename),
821 )
821 )
822
822
823 if not os.path.exists(dest):
823 if not os.path.exists(dest):
824 shutil.copy(buf.value, dest)
824 shutil.copy(buf.value, dest)
825
825
826 if not pythonlib:
826 if not pythonlib:
827 log.warn(
827 log.warn(
828 'could not determine Python DLL filename; assuming pythonXY'
828 'could not determine Python DLL filename; assuming pythonXY'
829 )
829 )
830
830
831 hv = sys.hexversion
831 hv = sys.hexversion
832 pythonlib = b'python%d%d' % (hv >> 24, (hv >> 16) & 0xFF)
832 pythonlib = b'python%d%d' % (hv >> 24, (hv >> 16) & 0xFF)
833
833
834 log.info('using %s as Python library name' % pythonlib)
834 log.info('using %s as Python library name' % pythonlib)
835 with open('mercurial/hgpythonlib.h', 'wb') as f:
835 with open('mercurial/hgpythonlib.h', 'wb') as f:
836 f.write(b'/* this file is autogenerated by setup.py */\n')
836 f.write(b'/* this file is autogenerated by setup.py */\n')
837 f.write(b'#define HGPYTHONLIB "%s"\n' % pythonlib)
837 f.write(b'#define HGPYTHONLIB "%s"\n' % pythonlib)
838
838
839 macros = None
839 macros = None
840 if sys.version_info[0] >= 3:
840 if sys.version_info[0] >= 3:
841 macros = [('_UNICODE', None), ('UNICODE', None)]
841 macros = [('_UNICODE', None), ('UNICODE', None)]
842
842
843 objects = self.compiler.compile(
843 objects = self.compiler.compile(
844 ['mercurial/exewrapper.c'],
844 ['mercurial/exewrapper.c'],
845 output_dir=self.build_temp,
845 output_dir=self.build_temp,
846 macros=macros,
846 macros=macros,
847 )
847 )
848 self.compiler.link_executable(
848 self.compiler.link_executable(
849 objects, self.hgtarget, libraries=[], output_dir=self.build_temp
849 objects, self.hgtarget, libraries=[], output_dir=self.build_temp
850 )
850 )
851 if self.long_paths_support:
851 if self.long_paths_support:
852 self.addlongpathsmanifest()
852 self.addlongpathsmanifest()
853
853
854 def addlongpathsmanifest(self):
854 def addlongpathsmanifest(self):
855 r"""Add manifest pieces so that hg.exe understands long paths
855 r"""Add manifest pieces so that hg.exe understands long paths
856
856
857 This is an EXPERIMENTAL feature, use with care.
857 This is an EXPERIMENTAL feature, use with care.
858 To enable long paths support, one needs to do two things:
858 To enable long paths support, one needs to do two things:
859 - build Mercurial with --long-paths-support option
859 - build Mercurial with --long-paths-support option
860 - change HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\
860 - change HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\
861 LongPathsEnabled to have value 1.
861 LongPathsEnabled to have value 1.
862
862
863 Please ignore 'warning 81010002: Unrecognized Element "longPathAware"';
863 Please ignore 'warning 81010002: Unrecognized Element "longPathAware"';
864 it happens because Mercurial uses mt.exe circa 2008, which is not
864 it happens because Mercurial uses mt.exe circa 2008, which is not
865 yet aware of long paths support in the manifest (I think so at least).
865 yet aware of long paths support in the manifest (I think so at least).
866 This does not stop mt.exe from embedding/merging the XML properly.
866 This does not stop mt.exe from embedding/merging the XML properly.
867
867
868 Why resource #1 should be used for .exe manifests? I don't know and
868 Why resource #1 should be used for .exe manifests? I don't know and
869 wasn't able to find an explanation for mortals. But it seems to work.
869 wasn't able to find an explanation for mortals. But it seems to work.
870 """
870 """
871 exefname = self.compiler.executable_filename(self.hgtarget)
871 exefname = self.compiler.executable_filename(self.hgtarget)
872 fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
872 fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
873 os.close(fdauto)
873 os.close(fdauto)
874 with open(manfname, 'w') as f:
874 with open(manfname, 'w') as f:
875 f.write(self.LONG_PATHS_MANIFEST)
875 f.write(self.LONG_PATHS_MANIFEST)
876 log.info("long paths manifest is written to '%s'" % manfname)
876 log.info("long paths manifest is written to '%s'" % manfname)
877 inputresource = '-inputresource:%s;#1' % exefname
877 inputresource = '-inputresource:%s;#1' % exefname
878 outputresource = '-outputresource:%s;#1' % exefname
878 outputresource = '-outputresource:%s;#1' % exefname
879 log.info("running mt.exe to update hg.exe's manifest in-place")
879 log.info("running mt.exe to update hg.exe's manifest in-place")
880 # supplying both -manifest and -inputresource to mt.exe makes
880 # supplying both -manifest and -inputresource to mt.exe makes
881 # it merge the embedded and supplied manifests in the -outputresource
881 # it merge the embedded and supplied manifests in the -outputresource
882 self.spawn(
882 self.spawn(
883 [
883 [
884 'mt.exe',
884 'mt.exe',
885 '-nologo',
885 '-nologo',
886 '-manifest',
886 '-manifest',
887 manfname,
887 manfname,
888 inputresource,
888 inputresource,
889 outputresource,
889 outputresource,
890 ]
890 ]
891 )
891 )
892 log.info("done updating hg.exe's manifest")
892 log.info("done updating hg.exe's manifest")
893 os.remove(manfname)
893 os.remove(manfname)
894
894
895 @property
895 @property
896 def hgexepath(self):
896 def hgexepath(self):
897 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
897 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
898 return os.path.join(self.build_temp, dir, 'hg.exe')
898 return os.path.join(self.build_temp, dir, 'hg.exe')
899
899
900
900
901 class hgbuilddoc(Command):
901 class hgbuilddoc(Command):
902 description = 'build documentation'
902 description = 'build documentation'
903 user_options = [
903 user_options = [
904 ('man', None, 'generate man pages'),
904 ('man', None, 'generate man pages'),
905 ('html', None, 'generate html pages'),
905 ('html', None, 'generate html pages'),
906 ]
906 ]
907
907
908 def initialize_options(self):
908 def initialize_options(self):
909 self.man = None
909 self.man = None
910 self.html = None
910 self.html = None
911
911
912 def finalize_options(self):
912 def finalize_options(self):
913 # If --man or --html are set, only generate what we're told to.
913 # If --man or --html are set, only generate what we're told to.
914 # Otherwise generate everything.
914 # Otherwise generate everything.
915 have_subset = self.man is not None or self.html is not None
915 have_subset = self.man is not None or self.html is not None
916
916
917 if have_subset:
917 if have_subset:
918 self.man = True if self.man else False
918 self.man = True if self.man else False
919 self.html = True if self.html else False
919 self.html = True if self.html else False
920 else:
920 else:
921 self.man = True
921 self.man = True
922 self.html = True
922 self.html = True
923
923
924 def run(self):
924 def run(self):
925 def normalizecrlf(p):
925 def normalizecrlf(p):
926 with open(p, 'rb') as fh:
926 with open(p, 'rb') as fh:
927 orig = fh.read()
927 orig = fh.read()
928
928
929 if b'\r\n' not in orig:
929 if b'\r\n' not in orig:
930 return
930 return
931
931
932 log.info('normalizing %s to LF line endings' % p)
932 log.info('normalizing %s to LF line endings' % p)
933 with open(p, 'wb') as fh:
933 with open(p, 'wb') as fh:
934 fh.write(orig.replace(b'\r\n', b'\n'))
934 fh.write(orig.replace(b'\r\n', b'\n'))
935
935
936 def gentxt(root):
936 def gentxt(root):
937 txt = 'doc/%s.txt' % root
937 txt = 'doc/%s.txt' % root
938 log.info('generating %s' % txt)
938 log.info('generating %s' % txt)
939 res, out, err = runcmd(
939 res, out, err = runcmd(
940 [sys.executable, 'gendoc.py', root], os.environ, cwd='doc'
940 [sys.executable, 'gendoc.py', root], os.environ, cwd='doc'
941 )
941 )
942 if res:
942 if res:
943 raise SystemExit(
943 raise SystemExit(
944 'error running gendoc.py: %s'
944 'error running gendoc.py: %s'
945 % '\n'.join([sysstr(out), sysstr(err)])
945 % '\n'.join([sysstr(out), sysstr(err)])
946 )
946 )
947
947
948 with open(txt, 'wb') as fh:
948 with open(txt, 'wb') as fh:
949 fh.write(out)
949 fh.write(out)
950
950
951 def gengendoc(root):
951 def gengendoc(root):
952 gendoc = 'doc/%s.gendoc.txt' % root
952 gendoc = 'doc/%s.gendoc.txt' % root
953
953
954 log.info('generating %s' % gendoc)
954 log.info('generating %s' % gendoc)
955 res, out, err = runcmd(
955 res, out, err = runcmd(
956 [sys.executable, 'gendoc.py', '%s.gendoc' % root],
956 [sys.executable, 'gendoc.py', '%s.gendoc' % root],
957 os.environ,
957 os.environ,
958 cwd='doc',
958 cwd='doc',
959 )
959 )
960 if res:
960 if res:
961 raise SystemExit(
961 raise SystemExit(
962 'error running gendoc: %s'
962 'error running gendoc: %s'
963 % '\n'.join([sysstr(out), sysstr(err)])
963 % '\n'.join([sysstr(out), sysstr(err)])
964 )
964 )
965
965
966 with open(gendoc, 'wb') as fh:
966 with open(gendoc, 'wb') as fh:
967 fh.write(out)
967 fh.write(out)
968
968
969 def genman(root):
969 def genman(root):
970 log.info('generating doc/%s' % root)
970 log.info('generating doc/%s' % root)
971 res, out, err = runcmd(
971 res, out, err = runcmd(
972 [
972 [
973 sys.executable,
973 sys.executable,
974 'runrst',
974 'runrst',
975 'hgmanpage',
975 'hgmanpage',
976 '--halt',
976 '--halt',
977 'warning',
977 'warning',
978 '--strip-elements-with-class',
978 '--strip-elements-with-class',
979 'htmlonly',
979 'htmlonly',
980 '%s.txt' % root,
980 '%s.txt' % root,
981 root,
981 root,
982 ],
982 ],
983 os.environ,
983 os.environ,
984 cwd='doc',
984 cwd='doc',
985 )
985 )
986 if res:
986 if res:
987 raise SystemExit(
987 raise SystemExit(
988 'error running runrst: %s'
988 'error running runrst: %s'
989 % '\n'.join([sysstr(out), sysstr(err)])
989 % '\n'.join([sysstr(out), sysstr(err)])
990 )
990 )
991
991
992 normalizecrlf('doc/%s' % root)
992 normalizecrlf('doc/%s' % root)
993
993
994 def genhtml(root):
994 def genhtml(root):
995 log.info('generating doc/%s.html' % root)
995 log.info('generating doc/%s.html' % root)
996 res, out, err = runcmd(
996 res, out, err = runcmd(
997 [
997 [
998 sys.executable,
998 sys.executable,
999 'runrst',
999 'runrst',
1000 'html',
1000 'html',
1001 '--halt',
1001 '--halt',
1002 'warning',
1002 'warning',
1003 '--link-stylesheet',
1003 '--link-stylesheet',
1004 '--stylesheet-path',
1004 '--stylesheet-path',
1005 'style.css',
1005 'style.css',
1006 '%s.txt' % root,
1006 '%s.txt' % root,
1007 '%s.html' % root,
1007 '%s.html' % root,
1008 ],
1008 ],
1009 os.environ,
1009 os.environ,
1010 cwd='doc',
1010 cwd='doc',
1011 )
1011 )
1012 if res:
1012 if res:
1013 raise SystemExit(
1013 raise SystemExit(
1014 'error running runrst: %s'
1014 'error running runrst: %s'
1015 % '\n'.join([sysstr(out), sysstr(err)])
1015 % '\n'.join([sysstr(out), sysstr(err)])
1016 )
1016 )
1017
1017
1018 normalizecrlf('doc/%s.html' % root)
1018 normalizecrlf('doc/%s.html' % root)
1019
1019
1020 # This logic is duplicated in doc/Makefile.
1020 # This logic is duplicated in doc/Makefile.
1021 sources = {
1021 sources = {
1022 f
1022 f
1023 for f in os.listdir('mercurial/helptext')
1023 for f in os.listdir('mercurial/helptext')
1024 if re.search(r'[0-9]\.txt$', f)
1024 if re.search(r'[0-9]\.txt$', f)
1025 }
1025 }
1026
1026
1027 # common.txt is a one-off.
1027 # common.txt is a one-off.
1028 gentxt('common')
1028 gentxt('common')
1029
1029
1030 for source in sorted(sources):
1030 for source in sorted(sources):
1031 assert source[-4:] == '.txt'
1031 assert source[-4:] == '.txt'
1032 root = source[:-4]
1032 root = source[:-4]
1033
1033
1034 gentxt(root)
1034 gentxt(root)
1035 gengendoc(root)
1035 gengendoc(root)
1036
1036
1037 if self.man:
1037 if self.man:
1038 genman(root)
1038 genman(root)
1039 if self.html:
1039 if self.html:
1040 genhtml(root)
1040 genhtml(root)
1041
1041
1042
1042
1043 class hginstall(install):
1043 class hginstall(install):
1044
1044
1045 user_options = install.user_options + [
1045 user_options = install.user_options + [
1046 (
1046 (
1047 'old-and-unmanageable',
1047 'old-and-unmanageable',
1048 None,
1048 None,
1049 'noop, present for eggless setuptools compat',
1049 'noop, present for eggless setuptools compat',
1050 ),
1050 ),
1051 (
1051 (
1052 'single-version-externally-managed',
1052 'single-version-externally-managed',
1053 None,
1053 None,
1054 'noop, present for eggless setuptools compat',
1054 'noop, present for eggless setuptools compat',
1055 ),
1055 ),
1056 ]
1056 ]
1057
1057
1058 # Also helps setuptools not be sad while we refuse to create eggs.
1058 # Also helps setuptools not be sad while we refuse to create eggs.
1059 single_version_externally_managed = True
1059 single_version_externally_managed = True
1060
1060
1061 def get_sub_commands(self):
1061 def get_sub_commands(self):
1062 # Screen out egg related commands to prevent egg generation. But allow
1062 # Screen out egg related commands to prevent egg generation. But allow
1063 # mercurial.egg-info generation, since that is part of modern
1063 # mercurial.egg-info generation, since that is part of modern
1064 # packaging.
1064 # packaging.
1065 excl = {'bdist_egg'}
1065 excl = {'bdist_egg'}
1066 return filter(lambda x: x not in excl, install.get_sub_commands(self))
1066 return filter(lambda x: x not in excl, install.get_sub_commands(self))
1067
1067
1068
1068
1069 class hginstalllib(install_lib):
1069 class hginstalllib(install_lib):
1070 """
1070 """
1071 This is a specialization of install_lib that replaces the copy_file used
1071 This is a specialization of install_lib that replaces the copy_file used
1072 there so that it supports setting the mode of files after copying them,
1072 there so that it supports setting the mode of files after copying them,
1073 instead of just preserving the mode that the files originally had. If your
1073 instead of just preserving the mode that the files originally had. If your
1074 system has a umask of something like 027, preserving the permissions when
1074 system has a umask of something like 027, preserving the permissions when
1075 copying will lead to a broken install.
1075 copying will lead to a broken install.
1076
1076
1077 Note that just passing keep_permissions=False to copy_file would be
1077 Note that just passing keep_permissions=False to copy_file would be
1078 insufficient, as it might still be applying a umask.
1078 insufficient, as it might still be applying a umask.
1079 """
1079 """
1080
1080
1081 def run(self):
1081 def run(self):
1082 realcopyfile = file_util.copy_file
1082 realcopyfile = file_util.copy_file
1083
1083
1084 def copyfileandsetmode(*args, **kwargs):
1084 def copyfileandsetmode(*args, **kwargs):
1085 src, dst = args[0], args[1]
1085 src, dst = args[0], args[1]
1086 dst, copied = realcopyfile(*args, **kwargs)
1086 dst, copied = realcopyfile(*args, **kwargs)
1087 if copied:
1087 if copied:
1088 st = os.stat(src)
1088 st = os.stat(src)
1089 # Persist executable bit (apply it to group and other if user
1089 # Persist executable bit (apply it to group and other if user
1090 # has it)
1090 # has it)
1091 if st[stat.ST_MODE] & stat.S_IXUSR:
1091 if st[stat.ST_MODE] & stat.S_IXUSR:
1092 setmode = int('0755', 8)
1092 setmode = int('0755', 8)
1093 else:
1093 else:
1094 setmode = int('0644', 8)
1094 setmode = int('0644', 8)
1095 m = stat.S_IMODE(st[stat.ST_MODE])
1095 m = stat.S_IMODE(st[stat.ST_MODE])
1096 m = (m & ~int('0777', 8)) | setmode
1096 m = (m & ~int('0777', 8)) | setmode
1097 os.chmod(dst, m)
1097 os.chmod(dst, m)
1098
1098
1099 file_util.copy_file = copyfileandsetmode
1099 file_util.copy_file = copyfileandsetmode
1100 try:
1100 try:
1101 install_lib.run(self)
1101 install_lib.run(self)
1102 finally:
1102 finally:
1103 file_util.copy_file = realcopyfile
1103 file_util.copy_file = realcopyfile
1104
1104
1105
1105
1106 class hginstallscripts(install_scripts):
1106 class hginstallscripts(install_scripts):
1107 """
1107 """
1108 This is a specialization of install_scripts that replaces the @LIBDIR@ with
1108 This is a specialization of install_scripts that replaces the @LIBDIR@ with
1109 the configured directory for modules. If possible, the path is made relative
1109 the configured directory for modules. If possible, the path is made relative
1110 to the directory for scripts.
1110 to the directory for scripts.
1111 """
1111 """
1112
1112
1113 def initialize_options(self):
1113 def initialize_options(self):
1114 install_scripts.initialize_options(self)
1114 install_scripts.initialize_options(self)
1115
1115
1116 self.install_lib = None
1116 self.install_lib = None
1117
1117
1118 def finalize_options(self):
1118 def finalize_options(self):
1119 install_scripts.finalize_options(self)
1119 install_scripts.finalize_options(self)
1120 self.set_undefined_options('install', ('install_lib', 'install_lib'))
1120 self.set_undefined_options('install', ('install_lib', 'install_lib'))
1121
1121
1122 def run(self):
1122 def run(self):
1123 install_scripts.run(self)
1123 install_scripts.run(self)
1124
1124
1125 # It only makes sense to replace @LIBDIR@ with the install path if
1125 # It only makes sense to replace @LIBDIR@ with the install path if
1126 # the install path is known. For wheels, the logic below calculates
1126 # the install path is known. For wheels, the logic below calculates
1127 # the libdir to be "../..". This is because the internal layout of a
1127 # the libdir to be "../..". This is because the internal layout of a
1128 # wheel archive looks like:
1128 # wheel archive looks like:
1129 #
1129 #
1130 # mercurial-3.6.1.data/scripts/hg
1130 # mercurial-3.6.1.data/scripts/hg
1131 # mercurial/__init__.py
1131 # mercurial/__init__.py
1132 #
1132 #
1133 # When installing wheels, the subdirectories of the "<pkg>.data"
1133 # When installing wheels, the subdirectories of the "<pkg>.data"
1134 # directory are translated to system local paths and files therein
1134 # directory are translated to system local paths and files therein
1135 # are copied in place. The mercurial/* files are installed into the
1135 # are copied in place. The mercurial/* files are installed into the
1136 # site-packages directory. However, the site-packages directory
1136 # site-packages directory. However, the site-packages directory
1137 # isn't known until wheel install time. This means we have no clue
1137 # isn't known until wheel install time. This means we have no clue
1138 # at wheel generation time what the installed site-packages directory
1138 # at wheel generation time what the installed site-packages directory
1139 # will be. And, wheels don't appear to provide the ability to register
1139 # will be. And, wheels don't appear to provide the ability to register
1140 # custom code to run during wheel installation. This all means that
1140 # custom code to run during wheel installation. This all means that
1141 # we can't reliably set the libdir in wheels: the default behavior
1141 # we can't reliably set the libdir in wheels: the default behavior
1142 # of looking in sys.path must do.
1142 # of looking in sys.path must do.
1143
1143
1144 if (
1144 if (
1145 os.path.splitdrive(self.install_dir)[0]
1145 os.path.splitdrive(self.install_dir)[0]
1146 != os.path.splitdrive(self.install_lib)[0]
1146 != os.path.splitdrive(self.install_lib)[0]
1147 ):
1147 ):
1148 # can't make relative paths from one drive to another, so use an
1148 # can't make relative paths from one drive to another, so use an
1149 # absolute path instead
1149 # absolute path instead
1150 libdir = self.install_lib
1150 libdir = self.install_lib
1151 else:
1151 else:
1152 libdir = os.path.relpath(self.install_lib, self.install_dir)
1152 libdir = os.path.relpath(self.install_lib, self.install_dir)
1153
1153
1154 for outfile in self.outfiles:
1154 for outfile in self.outfiles:
1155 with open(outfile, 'rb') as fp:
1155 with open(outfile, 'rb') as fp:
1156 data = fp.read()
1156 data = fp.read()
1157
1157
1158 # skip binary files
1158 # skip binary files
1159 if b'\0' in data:
1159 if b'\0' in data:
1160 continue
1160 continue
1161
1161
1162 # During local installs, the shebang will be rewritten to the final
1162 # During local installs, the shebang will be rewritten to the final
1163 # install path. During wheel packaging, the shebang has a special
1163 # install path. During wheel packaging, the shebang has a special
1164 # value.
1164 # value.
1165 if data.startswith(b'#!python'):
1165 if data.startswith(b'#!python'):
1166 log.info(
1166 log.info(
1167 'not rewriting @LIBDIR@ in %s because install path '
1167 'not rewriting @LIBDIR@ in %s because install path '
1168 'not known' % outfile
1168 'not known' % outfile
1169 )
1169 )
1170 continue
1170 continue
1171
1171
1172 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
1172 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
1173 with open(outfile, 'wb') as fp:
1173 with open(outfile, 'wb') as fp:
1174 fp.write(data)
1174 fp.write(data)
1175
1175
1176
1176
1177 # virtualenv installs custom distutils/__init__.py and
1177 # virtualenv installs custom distutils/__init__.py and
1178 # distutils/distutils.cfg files which essentially proxy back to the
1178 # distutils/distutils.cfg files which essentially proxy back to the
1179 # "real" distutils in the main Python install. The presence of this
1179 # "real" distutils in the main Python install. The presence of this
1180 # directory causes py2exe to pick up the "hacked" distutils package
1180 # directory causes py2exe to pick up the "hacked" distutils package
1181 # from the virtualenv and "import distutils" will fail from the py2exe
1181 # from the virtualenv and "import distutils" will fail from the py2exe
1182 # build because the "real" distutils files can't be located.
1182 # build because the "real" distutils files can't be located.
1183 #
1183 #
1184 # We work around this by monkeypatching the py2exe code finding Python
1184 # We work around this by monkeypatching the py2exe code finding Python
1185 # modules to replace the found virtualenv distutils modules with the
1185 # modules to replace the found virtualenv distutils modules with the
1186 # original versions via filesystem scanning. This is a bit hacky. But
1186 # original versions via filesystem scanning. This is a bit hacky. But
1187 # it allows us to use virtualenvs for py2exe packaging, which is more
1187 # it allows us to use virtualenvs for py2exe packaging, which is more
1188 # deterministic and reproducible.
1188 # deterministic and reproducible.
1189 #
1189 #
1190 # It's worth noting that the common StackOverflow suggestions for this
1190 # It's worth noting that the common StackOverflow suggestions for this
1191 # problem involve copying the original distutils files into the
1191 # problem involve copying the original distutils files into the
1192 # virtualenv or into the staging directory after setup() is invoked.
1192 # virtualenv or into the staging directory after setup() is invoked.
1193 # The former is very brittle and can easily break setup(). Our hacking
1193 # The former is very brittle and can easily break setup(). Our hacking
1194 # of the found modules routine has a similar result as copying the files
1194 # of the found modules routine has a similar result as copying the files
1195 # manually. But it makes fewer assumptions about how py2exe works and
1195 # manually. But it makes fewer assumptions about how py2exe works and
1196 # is less brittle.
1196 # is less brittle.
1197
1197
1198 # This only catches virtualenvs made with virtualenv (as opposed to
1198 # This only catches virtualenvs made with virtualenv (as opposed to
1199 # venv, which is likely what Python 3 uses).
1199 # venv, which is likely what Python 3 uses).
1200 py2exehacked = py2exeloaded and getattr(sys, 'real_prefix', None) is not None
1200 py2exehacked = py2exeloaded and getattr(sys, 'real_prefix', None) is not None
1201
1201
1202 if py2exehacked:
1202 if py2exehacked:
1203 from distutils.command.py2exe import py2exe as buildpy2exe
1203 from distutils.command.py2exe import py2exe as buildpy2exe
1204 from py2exe.mf import Module as py2exemodule
1204 from py2exe.mf import Module as py2exemodule
1205
1205
1206 class hgbuildpy2exe(buildpy2exe):
1206 class hgbuildpy2exe(buildpy2exe):
1207 def find_needed_modules(self, mf, files, modules):
1207 def find_needed_modules(self, mf, files, modules):
1208 res = buildpy2exe.find_needed_modules(self, mf, files, modules)
1208 res = buildpy2exe.find_needed_modules(self, mf, files, modules)
1209
1209
1210 # Replace virtualenv's distutils modules with the real ones.
1210 # Replace virtualenv's distutils modules with the real ones.
1211 modules = {}
1211 modules = {}
1212 for k, v in res.modules.items():
1212 for k, v in res.modules.items():
1213 if k != 'distutils' and not k.startswith('distutils.'):
1213 if k != 'distutils' and not k.startswith('distutils.'):
1214 modules[k] = v
1214 modules[k] = v
1215
1215
1216 res.modules = modules
1216 res.modules = modules
1217
1217
1218 import opcode
1218 import opcode
1219
1219
1220 distutilsreal = os.path.join(
1220 distutilsreal = os.path.join(
1221 os.path.dirname(opcode.__file__), 'distutils'
1221 os.path.dirname(opcode.__file__), 'distutils'
1222 )
1222 )
1223
1223
1224 for root, dirs, files in os.walk(distutilsreal):
1224 for root, dirs, files in os.walk(distutilsreal):
1225 for f in sorted(files):
1225 for f in sorted(files):
1226 if not f.endswith('.py'):
1226 if not f.endswith('.py'):
1227 continue
1227 continue
1228
1228
1229 full = os.path.join(root, f)
1229 full = os.path.join(root, f)
1230
1230
1231 parents = ['distutils']
1231 parents = ['distutils']
1232
1232
1233 if root != distutilsreal:
1233 if root != distutilsreal:
1234 rel = os.path.relpath(root, distutilsreal)
1234 rel = os.path.relpath(root, distutilsreal)
1235 parents.extend(p for p in rel.split(os.sep))
1235 parents.extend(p for p in rel.split(os.sep))
1236
1236
1237 modname = '%s.%s' % ('.'.join(parents), f[:-3])
1237 modname = '%s.%s' % ('.'.join(parents), f[:-3])
1238
1238
1239 if modname.startswith('distutils.tests.'):
1239 if modname.startswith('distutils.tests.'):
1240 continue
1240 continue
1241
1241
1242 if modname.endswith('.__init__'):
1242 if modname.endswith('.__init__'):
1243 modname = modname[: -len('.__init__')]
1243 modname = modname[: -len('.__init__')]
1244 path = os.path.dirname(full)
1244 path = os.path.dirname(full)
1245 else:
1245 else:
1246 path = None
1246 path = None
1247
1247
1248 res.modules[modname] = py2exemodule(
1248 res.modules[modname] = py2exemodule(
1249 modname, full, path=path
1249 modname, full, path=path
1250 )
1250 )
1251
1251
1252 if 'distutils' not in res.modules:
1252 if 'distutils' not in res.modules:
1253 raise SystemExit('could not find distutils modules')
1253 raise SystemExit('could not find distutils modules')
1254
1254
1255 return res
1255 return res
1256
1256
1257
1257
1258 cmdclass = {
1258 cmdclass = {
1259 'build': hgbuild,
1259 'build': hgbuild,
1260 'build_doc': hgbuilddoc,
1260 'build_doc': hgbuilddoc,
1261 'build_mo': hgbuildmo,
1261 'build_mo': hgbuildmo,
1262 'build_ext': hgbuildext,
1262 'build_ext': hgbuildext,
1263 'build_py': hgbuildpy,
1263 'build_py': hgbuildpy,
1264 'build_scripts': hgbuildscripts,
1264 'build_scripts': hgbuildscripts,
1265 'build_hgextindex': buildhgextindex,
1265 'build_hgextindex': buildhgextindex,
1266 'install': hginstall,
1266 'install': hginstall,
1267 'install_lib': hginstalllib,
1267 'install_lib': hginstalllib,
1268 'install_scripts': hginstallscripts,
1268 'install_scripts': hginstallscripts,
1269 'build_hgexe': buildhgexe,
1269 'build_hgexe': buildhgexe,
1270 }
1270 }
1271
1271
1272 if py2exehacked:
1272 if py2exehacked:
1273 cmdclass['py2exe'] = hgbuildpy2exe
1273 cmdclass['py2exe'] = hgbuildpy2exe
1274
1274
1275 packages = [
1275 packages = [
1276 'mercurial',
1276 'mercurial',
1277 'mercurial.cext',
1277 'mercurial.cext',
1278 'mercurial.cffi',
1278 'mercurial.cffi',
1279 'mercurial.defaultrc',
1279 'mercurial.defaultrc',
1280 'mercurial.helptext',
1280 'mercurial.helptext',
1281 'mercurial.helptext.internals',
1281 'mercurial.helptext.internals',
1282 'mercurial.hgweb',
1282 'mercurial.hgweb',
1283 'mercurial.interfaces',
1283 'mercurial.interfaces',
1284 'mercurial.pure',
1284 'mercurial.pure',
1285 'mercurial.templates',
1285 'mercurial.templates',
1286 'mercurial.thirdparty',
1286 'mercurial.thirdparty',
1287 'mercurial.thirdparty.attr',
1287 'mercurial.thirdparty.attr',
1288 'mercurial.thirdparty.zope',
1288 'mercurial.thirdparty.zope',
1289 'mercurial.thirdparty.zope.interface',
1289 'mercurial.thirdparty.zope.interface',
1290 'mercurial.upgrade_utils',
1290 'mercurial.upgrade_utils',
1291 'mercurial.utils',
1291 'mercurial.utils',
1292 'mercurial.revlogutils',
1292 'mercurial.revlogutils',
1293 'mercurial.testing',
1293 'mercurial.testing',
1294 'hgext',
1294 'hgext',
1295 'hgext.convert',
1295 'hgext.convert',
1296 'hgext.fsmonitor',
1296 'hgext.fsmonitor',
1297 'hgext.fastannotate',
1297 'hgext.fastannotate',
1298 'hgext.fsmonitor.pywatchman',
1298 'hgext.fsmonitor.pywatchman',
1299 'hgext.git',
1299 'hgext.git',
1300 'hgext.highlight',
1300 'hgext.highlight',
1301 'hgext.hooklib',
1301 'hgext.hooklib',
1302 'hgext.infinitepush',
1302 'hgext.infinitepush',
1303 'hgext.largefiles',
1303 'hgext.largefiles',
1304 'hgext.lfs',
1304 'hgext.lfs',
1305 'hgext.narrow',
1305 'hgext.narrow',
1306 'hgext.remotefilelog',
1306 'hgext.remotefilelog',
1307 'hgext.zeroconf',
1307 'hgext.zeroconf',
1308 'hgext3rd',
1308 'hgext3rd',
1309 'hgdemandimport',
1309 'hgdemandimport',
1310 ]
1310 ]
1311
1311
1312 # The pygit2 dependency dropped py2 support with the 1.0 release in Dec 2019.
1312 # The pygit2 dependency dropped py2 support with the 1.0 release in Dec 2019.
1313 # Prior releases do not build at all on Windows, because Visual Studio 2008
1313 # Prior releases do not build at all on Windows, because Visual Studio 2008
1314 # doesn't understand C 11. Older Linux releases are buggy.
1314 # doesn't understand C 11. Older Linux releases are buggy.
1315 if sys.version_info[0] == 2:
1315 if sys.version_info[0] == 2:
1316 packages.remove('hgext.git')
1316 packages.remove('hgext.git')
1317
1317
1318
1318
1319 for name in os.listdir(os.path.join('mercurial', 'templates')):
1319 for name in os.listdir(os.path.join('mercurial', 'templates')):
1320 if name != '__pycache__' and os.path.isdir(
1320 if name != '__pycache__' and os.path.isdir(
1321 os.path.join('mercurial', 'templates', name)
1321 os.path.join('mercurial', 'templates', name)
1322 ):
1322 ):
1323 packages.append('mercurial.templates.%s' % name)
1323 packages.append('mercurial.templates.%s' % name)
1324
1324
1325 if sys.version_info[0] == 2:
1325 if sys.version_info[0] == 2:
1326 packages.extend(
1326 packages.extend(
1327 [
1327 [
1328 'mercurial.thirdparty.concurrent',
1328 'mercurial.thirdparty.concurrent',
1329 'mercurial.thirdparty.concurrent.futures',
1329 'mercurial.thirdparty.concurrent.futures',
1330 ]
1330 ]
1331 )
1331 )
1332
1332
1333 if 'HG_PY2EXE_EXTRA_INSTALL_PACKAGES' in os.environ:
1333 if 'HG_PY2EXE_EXTRA_INSTALL_PACKAGES' in os.environ:
1334 # py2exe can't cope with namespace packages very well, so we have to
1334 # py2exe can't cope with namespace packages very well, so we have to
1335 # install any hgext3rd.* extensions that we want in the final py2exe
1335 # install any hgext3rd.* extensions that we want in the final py2exe
1336 # image here. This is gross, but you gotta do what you gotta do.
1336 # image here. This is gross, but you gotta do what you gotta do.
1337 packages.extend(os.environ['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'].split(' '))
1337 packages.extend(os.environ['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'].split(' '))
1338
1338
1339 common_depends = [
1339 common_depends = [
1340 'mercurial/bitmanipulation.h',
1340 'mercurial/bitmanipulation.h',
1341 'mercurial/compat.h',
1341 'mercurial/compat.h',
1342 'mercurial/cext/util.h',
1342 'mercurial/cext/util.h',
1343 ]
1343 ]
1344 common_include_dirs = ['mercurial']
1344 common_include_dirs = ['mercurial']
1345
1345
1346 common_cflags = []
1346 common_cflags = []
1347
1347
1348 # MSVC 2008 still needs declarations at the top of the scope, but Python 3.9
1348 # MSVC 2008 still needs declarations at the top of the scope, but Python 3.9
1349 # makes declarations not at the top of a scope in the headers.
1349 # makes declarations not at the top of a scope in the headers.
1350 if os.name != 'nt' and sys.version_info[1] < 9:
1350 if os.name != 'nt' and sys.version_info[1] < 9:
1351 common_cflags = ['-Werror=declaration-after-statement']
1351 common_cflags = ['-Werror=declaration-after-statement']
1352
1352
1353 osutil_cflags = []
1353 osutil_cflags = []
1354 osutil_ldflags = []
1354 osutil_ldflags = []
1355
1355
1356 # platform specific macros
1356 # platform specific macros
1357 for plat, func in [('bsd', 'setproctitle')]:
1357 for plat, func in [('bsd', 'setproctitle')]:
1358 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
1358 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
1359 osutil_cflags.append('-DHAVE_%s' % func.upper())
1359 osutil_cflags.append('-DHAVE_%s' % func.upper())
1360
1360
1361 for plat, macro, code in [
1361 for plat, macro, code in [
1362 (
1362 (
1363 'bsd|darwin',
1363 'bsd|darwin',
1364 'BSD_STATFS',
1364 'BSD_STATFS',
1365 '''
1365 '''
1366 #include <sys/param.h>
1366 #include <sys/param.h>
1367 #include <sys/mount.h>
1367 #include <sys/mount.h>
1368 int main() { struct statfs s; return sizeof(s.f_fstypename); }
1368 int main() { struct statfs s; return sizeof(s.f_fstypename); }
1369 ''',
1369 ''',
1370 ),
1370 ),
1371 (
1371 (
1372 'linux',
1372 'linux',
1373 'LINUX_STATFS',
1373 'LINUX_STATFS',
1374 '''
1374 '''
1375 #include <linux/magic.h>
1375 #include <linux/magic.h>
1376 #include <sys/vfs.h>
1376 #include <sys/vfs.h>
1377 int main() { struct statfs s; return sizeof(s.f_type); }
1377 int main() { struct statfs s; return sizeof(s.f_type); }
1378 ''',
1378 ''',
1379 ),
1379 ),
1380 ]:
1380 ]:
1381 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
1381 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
1382 osutil_cflags.append('-DHAVE_%s' % macro)
1382 osutil_cflags.append('-DHAVE_%s' % macro)
1383
1383
1384 if sys.platform == 'darwin':
1384 if sys.platform == 'darwin':
1385 osutil_ldflags += ['-framework', 'ApplicationServices']
1385 osutil_ldflags += ['-framework', 'ApplicationServices']
1386
1386
1387 if sys.platform == 'sunos5':
1387 if sys.platform == 'sunos5':
1388 osutil_ldflags += ['-lsocket']
1388 osutil_ldflags += ['-lsocket']
1389
1389
1390 xdiff_srcs = [
1390 xdiff_srcs = [
1391 'mercurial/thirdparty/xdiff/xdiffi.c',
1391 'mercurial/thirdparty/xdiff/xdiffi.c',
1392 'mercurial/thirdparty/xdiff/xprepare.c',
1392 'mercurial/thirdparty/xdiff/xprepare.c',
1393 'mercurial/thirdparty/xdiff/xutils.c',
1393 'mercurial/thirdparty/xdiff/xutils.c',
1394 ]
1394 ]
1395
1395
1396 xdiff_headers = [
1396 xdiff_headers = [
1397 'mercurial/thirdparty/xdiff/xdiff.h',
1397 'mercurial/thirdparty/xdiff/xdiff.h',
1398 'mercurial/thirdparty/xdiff/xdiffi.h',
1398 'mercurial/thirdparty/xdiff/xdiffi.h',
1399 'mercurial/thirdparty/xdiff/xinclude.h',
1399 'mercurial/thirdparty/xdiff/xinclude.h',
1400 'mercurial/thirdparty/xdiff/xmacros.h',
1400 'mercurial/thirdparty/xdiff/xmacros.h',
1401 'mercurial/thirdparty/xdiff/xprepare.h',
1401 'mercurial/thirdparty/xdiff/xprepare.h',
1402 'mercurial/thirdparty/xdiff/xtypes.h',
1402 'mercurial/thirdparty/xdiff/xtypes.h',
1403 'mercurial/thirdparty/xdiff/xutils.h',
1403 'mercurial/thirdparty/xdiff/xutils.h',
1404 ]
1404 ]
1405
1405
1406
1406
1407 class RustCompilationError(CCompilerError):
1407 class RustCompilationError(CCompilerError):
1408 """Exception class for Rust compilation errors."""
1408 """Exception class for Rust compilation errors."""
1409
1409
1410
1410
1411 class RustExtension(Extension):
1411 class RustExtension(Extension):
1412 """Base classes for concrete Rust Extension classes."""
1412 """Base classes for concrete Rust Extension classes."""
1413
1413
1414 rusttargetdir = os.path.join('rust', 'target', 'release')
1414 rusttargetdir = os.path.join('rust', 'target', 'release')
1415
1415
1416 def __init__(
1416 def __init__(
1417 self, mpath, sources, rustlibname, subcrate, py3_features=None, **kw
1417 self, mpath, sources, rustlibname, subcrate, py3_features=None, **kw
1418 ):
1418 ):
1419 Extension.__init__(self, mpath, sources, **kw)
1419 Extension.__init__(self, mpath, sources, **kw)
1420 srcdir = self.rustsrcdir = os.path.join('rust', subcrate)
1420 srcdir = self.rustsrcdir = os.path.join('rust', subcrate)
1421 self.py3_features = py3_features
1421 self.py3_features = py3_features
1422
1422
1423 # adding Rust source and control files to depends so that the extension
1423 # adding Rust source and control files to depends so that the extension
1424 # gets rebuilt if they've changed
1424 # gets rebuilt if they've changed
1425 self.depends.append(os.path.join(srcdir, 'Cargo.toml'))
1425 self.depends.append(os.path.join(srcdir, 'Cargo.toml'))
1426 cargo_lock = os.path.join(srcdir, 'Cargo.lock')
1426 cargo_lock = os.path.join(srcdir, 'Cargo.lock')
1427 if os.path.exists(cargo_lock):
1427 if os.path.exists(cargo_lock):
1428 self.depends.append(cargo_lock)
1428 self.depends.append(cargo_lock)
1429 for dirpath, subdir, fnames in os.walk(os.path.join(srcdir, 'src')):
1429 for dirpath, subdir, fnames in os.walk(os.path.join(srcdir, 'src')):
1430 self.depends.extend(
1430 self.depends.extend(
1431 os.path.join(dirpath, fname)
1431 os.path.join(dirpath, fname)
1432 for fname in fnames
1432 for fname in fnames
1433 if os.path.splitext(fname)[1] == '.rs'
1433 if os.path.splitext(fname)[1] == '.rs'
1434 )
1434 )
1435
1435
1436 @staticmethod
1436 @staticmethod
1437 def rustdylibsuffix():
1437 def rustdylibsuffix():
1438 """Return the suffix for shared libraries produced by rustc.
1438 """Return the suffix for shared libraries produced by rustc.
1439
1439
1440 See also: https://doc.rust-lang.org/reference/linkage.html
1440 See also: https://doc.rust-lang.org/reference/linkage.html
1441 """
1441 """
1442 if sys.platform == 'darwin':
1442 if sys.platform == 'darwin':
1443 return '.dylib'
1443 return '.dylib'
1444 elif os.name == 'nt':
1444 elif os.name == 'nt':
1445 return '.dll'
1445 return '.dll'
1446 else:
1446 else:
1447 return '.so'
1447 return '.so'
1448
1448
1449 def rustbuild(self):
1449 def rustbuild(self):
1450 env = os.environ.copy()
1450 env = os.environ.copy()
1451 if 'HGTEST_RESTOREENV' in env:
1451 if 'HGTEST_RESTOREENV' in env:
1452 # Mercurial tests change HOME to a temporary directory,
1452 # Mercurial tests change HOME to a temporary directory,
1453 # but, if installed with rustup, the Rust toolchain needs
1453 # but, if installed with rustup, the Rust toolchain needs
1454 # HOME to be correct (otherwise the 'no default toolchain'
1454 # HOME to be correct (otherwise the 'no default toolchain'
1455 # error message is issued and the build fails).
1455 # error message is issued and the build fails).
1456 # This happens currently with test-hghave.t, which does
1456 # This happens currently with test-hghave.t, which does
1457 # invoke this build.
1457 # invoke this build.
1458
1458
1459 # Unix only fix (os.path.expanduser not really reliable if
1459 # Unix only fix (os.path.expanduser not really reliable if
1460 # HOME is shadowed like this)
1460 # HOME is shadowed like this)
1461 import pwd
1461 import pwd
1462
1462
1463 env['HOME'] = pwd.getpwuid(os.getuid()).pw_dir
1463 env['HOME'] = pwd.getpwuid(os.getuid()).pw_dir
1464
1464
1465 cargocmd = ['cargo', 'rustc', '--release']
1465 cargocmd = ['cargo', 'rustc', '--release']
1466
1466
1467 feature_flags = []
1467 feature_flags = []
1468
1468
1469 if sys.version_info[0] == 3 and self.py3_features is not None:
1469 if sys.version_info[0] == 3 and self.py3_features is not None:
1470 feature_flags.append(self.py3_features)
1470 feature_flags.append(self.py3_features)
1471 cargocmd.append('--no-default-features')
1471 cargocmd.append('--no-default-features')
1472
1472
1473 rust_features = env.get("HG_RUST_FEATURES")
1473 rust_features = env.get("HG_RUST_FEATURES")
1474 if rust_features:
1474 if rust_features:
1475 feature_flags.append(rust_features)
1475 feature_flags.append(rust_features)
1476
1476
1477 cargocmd.extend(('--features', " ".join(feature_flags)))
1477 cargocmd.extend(('--features', " ".join(feature_flags)))
1478
1478
1479 cargocmd.append('--')
1479 cargocmd.append('--')
1480 if sys.platform == 'darwin':
1480 if sys.platform == 'darwin':
1481 cargocmd.extend(
1481 cargocmd.extend(
1482 ("-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup")
1482 ("-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup")
1483 )
1483 )
1484 try:
1484 try:
1485 subprocess.check_call(cargocmd, env=env, cwd=self.rustsrcdir)
1485 subprocess.check_call(cargocmd, env=env, cwd=self.rustsrcdir)
1486 except OSError as exc:
1486 except OSError as exc:
1487 if exc.errno == errno.ENOENT:
1487 if exc.errno == errno.ENOENT:
1488 raise RustCompilationError("Cargo not found")
1488 raise RustCompilationError("Cargo not found")
1489 elif exc.errno == errno.EACCES:
1489 elif exc.errno == errno.EACCES:
1490 raise RustCompilationError(
1490 raise RustCompilationError(
1491 "Cargo found, but permisssion to execute it is denied"
1491 "Cargo found, but permisssion to execute it is denied"
1492 )
1492 )
1493 else:
1493 else:
1494 raise
1494 raise
1495 except subprocess.CalledProcessError:
1495 except subprocess.CalledProcessError:
1496 raise RustCompilationError(
1496 raise RustCompilationError(
1497 "Cargo failed. Working directory: %r, "
1497 "Cargo failed. Working directory: %r, "
1498 "command: %r, environment: %r"
1498 "command: %r, environment: %r"
1499 % (self.rustsrcdir, cargocmd, env)
1499 % (self.rustsrcdir, cargocmd, env)
1500 )
1500 )
1501
1501
1502
1502
1503 class RustStandaloneExtension(RustExtension):
1503 class RustStandaloneExtension(RustExtension):
1504 def __init__(self, pydottedname, rustcrate, dylibname, **kw):
1504 def __init__(self, pydottedname, rustcrate, dylibname, **kw):
1505 RustExtension.__init__(
1505 RustExtension.__init__(
1506 self, pydottedname, [], dylibname, rustcrate, **kw
1506 self, pydottedname, [], dylibname, rustcrate, **kw
1507 )
1507 )
1508 self.dylibname = dylibname
1508 self.dylibname = dylibname
1509
1509
1510 def build(self, target_dir):
1510 def build(self, target_dir):
1511 self.rustbuild()
1511 self.rustbuild()
1512 target = [target_dir]
1512 target = [target_dir]
1513 target.extend(self.name.split('.'))
1513 target.extend(self.name.split('.'))
1514 target[-1] += DYLIB_SUFFIX
1514 target[-1] += DYLIB_SUFFIX
1515 shutil.copy2(
1515 shutil.copy2(
1516 os.path.join(
1516 os.path.join(
1517 self.rusttargetdir, self.dylibname + self.rustdylibsuffix()
1517 self.rusttargetdir, self.dylibname + self.rustdylibsuffix()
1518 ),
1518 ),
1519 os.path.join(*target),
1519 os.path.join(*target),
1520 )
1520 )
1521
1521
1522
1522
1523 extmodules = [
1523 extmodules = [
1524 Extension(
1524 Extension(
1525 'mercurial.cext.base85',
1525 'mercurial.cext.base85',
1526 ['mercurial/cext/base85.c'],
1526 ['mercurial/cext/base85.c'],
1527 include_dirs=common_include_dirs,
1527 include_dirs=common_include_dirs,
1528 extra_compile_args=common_cflags,
1528 extra_compile_args=common_cflags,
1529 depends=common_depends,
1529 depends=common_depends,
1530 ),
1530 ),
1531 Extension(
1531 Extension(
1532 'mercurial.cext.bdiff',
1532 'mercurial.cext.bdiff',
1533 ['mercurial/bdiff.c', 'mercurial/cext/bdiff.c'] + xdiff_srcs,
1533 ['mercurial/bdiff.c', 'mercurial/cext/bdiff.c'] + xdiff_srcs,
1534 include_dirs=common_include_dirs,
1534 include_dirs=common_include_dirs,
1535 extra_compile_args=common_cflags,
1535 extra_compile_args=common_cflags,
1536 depends=common_depends + ['mercurial/bdiff.h'] + xdiff_headers,
1536 depends=common_depends + ['mercurial/bdiff.h'] + xdiff_headers,
1537 ),
1537 ),
1538 Extension(
1538 Extension(
1539 'mercurial.cext.mpatch',
1539 'mercurial.cext.mpatch',
1540 ['mercurial/mpatch.c', 'mercurial/cext/mpatch.c'],
1540 ['mercurial/mpatch.c', 'mercurial/cext/mpatch.c'],
1541 include_dirs=common_include_dirs,
1541 include_dirs=common_include_dirs,
1542 extra_compile_args=common_cflags,
1542 extra_compile_args=common_cflags,
1543 depends=common_depends,
1543 depends=common_depends,
1544 ),
1544 ),
1545 Extension(
1545 Extension(
1546 'mercurial.cext.parsers',
1546 'mercurial.cext.parsers',
1547 [
1547 [
1548 'mercurial/cext/charencode.c',
1548 'mercurial/cext/charencode.c',
1549 'mercurial/cext/dirs.c',
1549 'mercurial/cext/dirs.c',
1550 'mercurial/cext/manifest.c',
1550 'mercurial/cext/manifest.c',
1551 'mercurial/cext/parsers.c',
1551 'mercurial/cext/parsers.c',
1552 'mercurial/cext/pathencode.c',
1552 'mercurial/cext/pathencode.c',
1553 'mercurial/cext/revlog.c',
1553 'mercurial/cext/revlog.c',
1554 ],
1554 ],
1555 include_dirs=common_include_dirs,
1555 include_dirs=common_include_dirs,
1556 extra_compile_args=common_cflags,
1556 extra_compile_args=common_cflags,
1557 depends=common_depends
1557 depends=common_depends
1558 + [
1558 + [
1559 'mercurial/cext/charencode.h',
1559 'mercurial/cext/charencode.h',
1560 'mercurial/cext/revlog.h',
1560 'mercurial/cext/revlog.h',
1561 ],
1561 ],
1562 ),
1562 ),
1563 Extension(
1563 Extension(
1564 'mercurial.cext.osutil',
1564 'mercurial.cext.osutil',
1565 ['mercurial/cext/osutil.c'],
1565 ['mercurial/cext/osutil.c'],
1566 include_dirs=common_include_dirs,
1566 include_dirs=common_include_dirs,
1567 extra_compile_args=common_cflags + osutil_cflags,
1567 extra_compile_args=common_cflags + osutil_cflags,
1568 extra_link_args=osutil_ldflags,
1568 extra_link_args=osutil_ldflags,
1569 depends=common_depends,
1569 depends=common_depends,
1570 ),
1570 ),
1571 Extension(
1571 Extension(
1572 'mercurial.thirdparty.zope.interface._zope_interface_coptimizations',
1572 'mercurial.thirdparty.zope.interface._zope_interface_coptimizations',
1573 [
1573 [
1574 'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c',
1574 'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c',
1575 ],
1575 ],
1576 extra_compile_args=common_cflags,
1576 extra_compile_args=common_cflags,
1577 ),
1577 ),
1578 Extension(
1578 Extension(
1579 'mercurial.thirdparty.sha1dc',
1579 'mercurial.thirdparty.sha1dc',
1580 [
1580 [
1581 'mercurial/thirdparty/sha1dc/cext.c',
1581 'mercurial/thirdparty/sha1dc/cext.c',
1582 'mercurial/thirdparty/sha1dc/lib/sha1.c',
1582 'mercurial/thirdparty/sha1dc/lib/sha1.c',
1583 'mercurial/thirdparty/sha1dc/lib/ubc_check.c',
1583 'mercurial/thirdparty/sha1dc/lib/ubc_check.c',
1584 ],
1584 ],
1585 extra_compile_args=common_cflags,
1585 extra_compile_args=common_cflags,
1586 ),
1586 ),
1587 Extension(
1587 Extension(
1588 'hgext.fsmonitor.pywatchman.bser',
1588 'hgext.fsmonitor.pywatchman.bser',
1589 ['hgext/fsmonitor/pywatchman/bser.c'],
1589 ['hgext/fsmonitor/pywatchman/bser.c'],
1590 extra_compile_args=common_cflags,
1590 extra_compile_args=common_cflags,
1591 ),
1591 ),
1592 RustStandaloneExtension(
1592 RustStandaloneExtension(
1593 'mercurial.rustext', 'hg-cpython', 'librusthg', py3_features='python3'
1593 'mercurial.rustext', 'hg-cpython', 'librusthg', py3_features='python3'
1594 ),
1594 ),
1595 ]
1595 ]
1596
1596
1597
1597
1598 sys.path.insert(0, 'contrib/python-zstandard')
1598 sys.path.insert(0, 'contrib/python-zstandard')
1599 import setup_zstd
1599 import setup_zstd
1600
1600
1601 zstd = setup_zstd.get_c_extension(
1601 zstd = setup_zstd.get_c_extension(
1602 name='mercurial.zstd', root=os.path.abspath(os.path.dirname(__file__))
1602 name='mercurial.zstd', root=os.path.abspath(os.path.dirname(__file__))
1603 )
1603 )
1604 zstd.extra_compile_args += common_cflags
1604 zstd.extra_compile_args += common_cflags
1605 extmodules.append(zstd)
1605 extmodules.append(zstd)
1606
1606
1607 try:
1607 try:
1608 from distutils import cygwinccompiler
1608 from distutils import cygwinccompiler
1609
1609
1610 # the -mno-cygwin option has been deprecated for years
1610 # the -mno-cygwin option has been deprecated for years
1611 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
1611 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
1612
1612
1613 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
1613 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
1614 def __init__(self, *args, **kwargs):
1614 def __init__(self, *args, **kwargs):
1615 mingw32compilerclass.__init__(self, *args, **kwargs)
1615 mingw32compilerclass.__init__(self, *args, **kwargs)
1616 for i in 'compiler compiler_so linker_exe linker_so'.split():
1616 for i in 'compiler compiler_so linker_exe linker_so'.split():
1617 try:
1617 try:
1618 getattr(self, i).remove('-mno-cygwin')
1618 getattr(self, i).remove('-mno-cygwin')
1619 except ValueError:
1619 except ValueError:
1620 pass
1620 pass
1621
1621
1622 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
1622 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
1623 except ImportError:
1623 except ImportError:
1624 # the cygwinccompiler package is not available on some Python
1624 # the cygwinccompiler package is not available on some Python
1625 # distributions like the ones from the optware project for Synology
1625 # distributions like the ones from the optware project for Synology
1626 # DiskStation boxes
1626 # DiskStation boxes
1627 class HackedMingw32CCompiler(object):
1627 class HackedMingw32CCompiler(object):
1628 pass
1628 pass
1629
1629
1630
1630
1631 if os.name == 'nt':
1631 if os.name == 'nt':
1632 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
1632 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
1633 # extra_link_args to distutils.extensions.Extension() doesn't have any
1633 # extra_link_args to distutils.extensions.Extension() doesn't have any
1634 # effect.
1634 # effect.
1635 from distutils import msvccompiler
1635 from distutils import msvccompiler
1636
1636
1637 msvccompilerclass = msvccompiler.MSVCCompiler
1637 msvccompilerclass = msvccompiler.MSVCCompiler
1638
1638
1639 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
1639 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
1640 def initialize(self):
1640 def initialize(self):
1641 msvccompilerclass.initialize(self)
1641 msvccompilerclass.initialize(self)
1642 # "warning LNK4197: export 'func' specified multiple times"
1642 # "warning LNK4197: export 'func' specified multiple times"
1643 self.ldflags_shared.append('/ignore:4197')
1643 self.ldflags_shared.append('/ignore:4197')
1644 self.ldflags_shared_debug.append('/ignore:4197')
1644 self.ldflags_shared_debug.append('/ignore:4197')
1645
1645
1646 msvccompiler.MSVCCompiler = HackedMSVCCompiler
1646 msvccompiler.MSVCCompiler = HackedMSVCCompiler
1647
1647
1648 packagedata = {
1648 packagedata = {
1649 'mercurial': [
1649 'mercurial': [
1650 'locale/*/LC_MESSAGES/hg.mo',
1650 'locale/*/LC_MESSAGES/hg.mo',
1651 'dummycert.pem',
1651 'dummycert.pem',
1652 ],
1652 ],
1653 'mercurial.defaultrc': [
1653 'mercurial.defaultrc': [
1654 '*.rc',
1654 '*.rc',
1655 ],
1655 ],
1656 'mercurial.helptext': [
1656 'mercurial.helptext': [
1657 '*.txt',
1657 '*.txt',
1658 ],
1658 ],
1659 'mercurial.helptext.internals': [
1659 'mercurial.helptext.internals': [
1660 '*.txt',
1660 '*.txt',
1661 ],
1661 ],
1662 }
1662 }
1663
1663
1664
1664
1665 def ordinarypath(p):
1665 def ordinarypath(p):
1666 return p and p[0] != '.' and p[-1] != '~'
1666 return p and p[0] != '.' and p[-1] != '~'
1667
1667
1668
1668
1669 for root in ('templates',):
1669 for root in ('templates',):
1670 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
1670 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
1671 packagename = curdir.replace(os.sep, '.')
1671 packagename = curdir.replace(os.sep, '.')
1672 packagedata[packagename] = list(filter(ordinarypath, files))
1672 packagedata[packagename] = list(filter(ordinarypath, files))
1673
1673
1674 datafiles = []
1674 datafiles = []
1675
1675
1676 # distutils expects version to be str/unicode. Converting it to
1676 # distutils expects version to be str/unicode. Converting it to
1677 # unicode on Python 2 still works because it won't contain any
1677 # unicode on Python 2 still works because it won't contain any
1678 # non-ascii bytes and will be implicitly converted back to bytes
1678 # non-ascii bytes and will be implicitly converted back to bytes
1679 # when operated on.
1679 # when operated on.
1680 assert isinstance(version, bytes)
1680 assert isinstance(version, bytes)
1681 setupversion = version.decode('ascii')
1681 setupversion = version.decode('ascii')
1682
1682
1683 extra = {}
1683 extra = {}
1684
1684
1685 py2exepackages = [
1685 py2exepackages = [
1686 'hgdemandimport',
1686 'hgdemandimport',
1687 'hgext3rd',
1687 'hgext3rd',
1688 'hgext',
1688 'hgext',
1689 'email',
1689 'email',
1690 # implicitly imported per module policy
1690 # implicitly imported per module policy
1691 # (cffi wouldn't be used as a frozen exe)
1691 # (cffi wouldn't be used as a frozen exe)
1692 'mercurial.cext',
1692 'mercurial.cext',
1693 #'mercurial.cffi',
1693 #'mercurial.cffi',
1694 'mercurial.pure',
1694 'mercurial.pure',
1695 ]
1695 ]
1696
1696
1697 py2exe_includes = []
1697 py2exe_includes = []
1698
1698
1699 py2exeexcludes = []
1699 py2exeexcludes = []
1700 py2exedllexcludes = ['crypt32.dll']
1700 py2exedllexcludes = ['crypt32.dll']
1701
1701
1702 if issetuptools:
1702 if issetuptools:
1703 extra['python_requires'] = supportedpy
1703 extra['python_requires'] = supportedpy
1704
1704
1705 if py2exeloaded:
1705 if py2exeloaded:
1706 extra['console'] = [
1706 extra['console'] = [
1707 {
1707 {
1708 'script': 'hg',
1708 'script': 'hg',
1709 'copyright': 'Copyright (C) 2005-2020 Matt Mackall and others',
1709 'copyright': 'Copyright (C) 2005-2021 Matt Mackall and others',
1710 'product_version': version,
1710 'product_version': version,
1711 }
1711 }
1712 ]
1712 ]
1713 # Sub command of 'build' because 'py2exe' does not handle sub_commands.
1713 # Sub command of 'build' because 'py2exe' does not handle sub_commands.
1714 # Need to override hgbuild because it has a private copy of
1714 # Need to override hgbuild because it has a private copy of
1715 # build.sub_commands.
1715 # build.sub_commands.
1716 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1716 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1717 # put dlls in sub directory so that they won't pollute PATH
1717 # put dlls in sub directory so that they won't pollute PATH
1718 extra['zipfile'] = 'lib/library.zip'
1718 extra['zipfile'] = 'lib/library.zip'
1719
1719
1720 # We allow some configuration to be supplemented via environment
1720 # We allow some configuration to be supplemented via environment
1721 # variables. This is better than setup.cfg files because it allows
1721 # variables. This is better than setup.cfg files because it allows
1722 # supplementing configs instead of replacing them.
1722 # supplementing configs instead of replacing them.
1723 extrapackages = os.environ.get('HG_PY2EXE_EXTRA_PACKAGES')
1723 extrapackages = os.environ.get('HG_PY2EXE_EXTRA_PACKAGES')
1724 if extrapackages:
1724 if extrapackages:
1725 py2exepackages.extend(extrapackages.split(' '))
1725 py2exepackages.extend(extrapackages.split(' '))
1726
1726
1727 extra_includes = os.environ.get('HG_PY2EXE_EXTRA_INCLUDES')
1727 extra_includes = os.environ.get('HG_PY2EXE_EXTRA_INCLUDES')
1728 if extra_includes:
1728 if extra_includes:
1729 py2exe_includes.extend(extra_includes.split(' '))
1729 py2exe_includes.extend(extra_includes.split(' '))
1730
1730
1731 excludes = os.environ.get('HG_PY2EXE_EXTRA_EXCLUDES')
1731 excludes = os.environ.get('HG_PY2EXE_EXTRA_EXCLUDES')
1732 if excludes:
1732 if excludes:
1733 py2exeexcludes.extend(excludes.split(' '))
1733 py2exeexcludes.extend(excludes.split(' '))
1734
1734
1735 dllexcludes = os.environ.get('HG_PY2EXE_EXTRA_DLL_EXCLUDES')
1735 dllexcludes = os.environ.get('HG_PY2EXE_EXTRA_DLL_EXCLUDES')
1736 if dllexcludes:
1736 if dllexcludes:
1737 py2exedllexcludes.extend(dllexcludes.split(' '))
1737 py2exedllexcludes.extend(dllexcludes.split(' '))
1738
1738
1739 if os.environ.get('PYOXIDIZER'):
1739 if os.environ.get('PYOXIDIZER'):
1740 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1740 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1741
1741
1742 if os.name == 'nt':
1742 if os.name == 'nt':
1743 # Windows binary file versions for exe/dll files must have the
1743 # Windows binary file versions for exe/dll files must have the
1744 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
1744 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
1745 setupversion = setupversion.split(r'+', 1)[0]
1745 setupversion = setupversion.split(r'+', 1)[0]
1746
1746
1747 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
1747 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
1748 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
1748 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
1749 if version:
1749 if version:
1750 version = version[0]
1750 version = version[0]
1751 if sys.version_info[0] == 3:
1751 if sys.version_info[0] == 3:
1752 version = version.decode('utf-8')
1752 version = version.decode('utf-8')
1753 xcode4 = version.startswith('Xcode') and StrictVersion(
1753 xcode4 = version.startswith('Xcode') and StrictVersion(
1754 version.split()[1]
1754 version.split()[1]
1755 ) >= StrictVersion('4.0')
1755 ) >= StrictVersion('4.0')
1756 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
1756 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
1757 else:
1757 else:
1758 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
1758 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
1759 # installed, but instead with only command-line tools. Assume
1759 # installed, but instead with only command-line tools. Assume
1760 # that only happens on >= Lion, thus no PPC support.
1760 # that only happens on >= Lion, thus no PPC support.
1761 xcode4 = True
1761 xcode4 = True
1762 xcode51 = False
1762 xcode51 = False
1763
1763
1764 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
1764 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
1765 # distutils.sysconfig
1765 # distutils.sysconfig
1766 if xcode4:
1766 if xcode4:
1767 os.environ['ARCHFLAGS'] = ''
1767 os.environ['ARCHFLAGS'] = ''
1768
1768
1769 # XCode 5.1 changes clang such that it now fails to compile if the
1769 # XCode 5.1 changes clang such that it now fails to compile if the
1770 # -mno-fused-madd flag is passed, but the version of Python shipped with
1770 # -mno-fused-madd flag is passed, but the version of Python shipped with
1771 # OS X 10.9 Mavericks includes this flag. This causes problems in all
1771 # OS X 10.9 Mavericks includes this flag. This causes problems in all
1772 # C extension modules, and a bug has been filed upstream at
1772 # C extension modules, and a bug has been filed upstream at
1773 # http://bugs.python.org/issue21244. We also need to patch this here
1773 # http://bugs.python.org/issue21244. We also need to patch this here
1774 # so Mercurial can continue to compile in the meantime.
1774 # so Mercurial can continue to compile in the meantime.
1775 if xcode51:
1775 if xcode51:
1776 cflags = get_config_var('CFLAGS')
1776 cflags = get_config_var('CFLAGS')
1777 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
1777 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
1778 os.environ['CFLAGS'] = (
1778 os.environ['CFLAGS'] = (
1779 os.environ.get('CFLAGS', '') + ' -Qunused-arguments'
1779 os.environ.get('CFLAGS', '') + ' -Qunused-arguments'
1780 )
1780 )
1781
1781
1782 setup(
1782 setup(
1783 name='mercurial',
1783 name='mercurial',
1784 version=setupversion,
1784 version=setupversion,
1785 author='Matt Mackall and many others',
1785 author='Matt Mackall and many others',
1786 author_email='mercurial@mercurial-scm.org',
1786 author_email='mercurial@mercurial-scm.org',
1787 url='https://mercurial-scm.org/',
1787 url='https://mercurial-scm.org/',
1788 download_url='https://mercurial-scm.org/release/',
1788 download_url='https://mercurial-scm.org/release/',
1789 description=(
1789 description=(
1790 'Fast scalable distributed SCM (revision control, version '
1790 'Fast scalable distributed SCM (revision control, version '
1791 'control) system'
1791 'control) system'
1792 ),
1792 ),
1793 long_description=(
1793 long_description=(
1794 'Mercurial is a distributed SCM tool written in Python.'
1794 'Mercurial is a distributed SCM tool written in Python.'
1795 ' It is used by a number of large projects that require'
1795 ' It is used by a number of large projects that require'
1796 ' fast, reliable distributed revision control, such as '
1796 ' fast, reliable distributed revision control, such as '
1797 'Mozilla.'
1797 'Mozilla.'
1798 ),
1798 ),
1799 license='GNU GPLv2 or any later version',
1799 license='GNU GPLv2 or any later version',
1800 classifiers=[
1800 classifiers=[
1801 'Development Status :: 6 - Mature',
1801 'Development Status :: 6 - Mature',
1802 'Environment :: Console',
1802 'Environment :: Console',
1803 'Intended Audience :: Developers',
1803 'Intended Audience :: Developers',
1804 'Intended Audience :: System Administrators',
1804 'Intended Audience :: System Administrators',
1805 'License :: OSI Approved :: GNU General Public License (GPL)',
1805 'License :: OSI Approved :: GNU General Public License (GPL)',
1806 'Natural Language :: Danish',
1806 'Natural Language :: Danish',
1807 'Natural Language :: English',
1807 'Natural Language :: English',
1808 'Natural Language :: German',
1808 'Natural Language :: German',
1809 'Natural Language :: Italian',
1809 'Natural Language :: Italian',
1810 'Natural Language :: Japanese',
1810 'Natural Language :: Japanese',
1811 'Natural Language :: Portuguese (Brazilian)',
1811 'Natural Language :: Portuguese (Brazilian)',
1812 'Operating System :: Microsoft :: Windows',
1812 'Operating System :: Microsoft :: Windows',
1813 'Operating System :: OS Independent',
1813 'Operating System :: OS Independent',
1814 'Operating System :: POSIX',
1814 'Operating System :: POSIX',
1815 'Programming Language :: C',
1815 'Programming Language :: C',
1816 'Programming Language :: Python',
1816 'Programming Language :: Python',
1817 'Topic :: Software Development :: Version Control',
1817 'Topic :: Software Development :: Version Control',
1818 ],
1818 ],
1819 scripts=scripts,
1819 scripts=scripts,
1820 packages=packages,
1820 packages=packages,
1821 ext_modules=extmodules,
1821 ext_modules=extmodules,
1822 data_files=datafiles,
1822 data_files=datafiles,
1823 package_data=packagedata,
1823 package_data=packagedata,
1824 cmdclass=cmdclass,
1824 cmdclass=cmdclass,
1825 distclass=hgdist,
1825 distclass=hgdist,
1826 options={
1826 options={
1827 'py2exe': {
1827 'py2exe': {
1828 'bundle_files': 3,
1828 'bundle_files': 3,
1829 'dll_excludes': py2exedllexcludes,
1829 'dll_excludes': py2exedllexcludes,
1830 'includes': py2exe_includes,
1830 'includes': py2exe_includes,
1831 'excludes': py2exeexcludes,
1831 'excludes': py2exeexcludes,
1832 'packages': py2exepackages,
1832 'packages': py2exepackages,
1833 },
1833 },
1834 'bdist_mpkg': {
1834 'bdist_mpkg': {
1835 'zipdist': False,
1835 'zipdist': False,
1836 'license': 'COPYING',
1836 'license': 'COPYING',
1837 'readme': 'contrib/packaging/macosx/Readme.html',
1837 'readme': 'contrib/packaging/macosx/Readme.html',
1838 'welcome': 'contrib/packaging/macosx/Welcome.html',
1838 'welcome': 'contrib/packaging/macosx/Welcome.html',
1839 },
1839 },
1840 },
1840 },
1841 **extra
1841 **extra
1842 )
1842 )
General Comments 0
You need to be logged in to leave comments. Login now