##// END OF EJS Templates
git: fix index handling of removed files during commit (issue6398)...
Augie Fackler -
r45992:d4cf8034 default
parent child Browse files
Show More
@@ -1,339 +1,341 b''
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 import contextlib
3 import contextlib
4 import errno
4 import errno
5 import os
5 import os
6
6
7 from mercurial import (
7 from mercurial import (
8 error,
8 error,
9 extensions,
9 extensions,
10 match as matchmod,
10 match as matchmod,
11 node as nodemod,
11 node as nodemod,
12 pycompat,
12 pycompat,
13 scmutil,
13 scmutil,
14 util,
14 util,
15 )
15 )
16 from mercurial.interfaces import (
16 from mercurial.interfaces import (
17 dirstate as intdirstate,
17 dirstate as intdirstate,
18 util as interfaceutil,
18 util as interfaceutil,
19 )
19 )
20
20
21 from . import gitutil
21 from . import gitutil
22
22
23 pygit2 = gitutil.get_pygit2()
23 pygit2 = gitutil.get_pygit2()
24
24
25
25
26 def readpatternfile(orig, filepath, warn, sourceinfo=False):
26 def readpatternfile(orig, filepath, warn, sourceinfo=False):
27 if not (b'info/exclude' in filepath or filepath.endswith(b'.gitignore')):
27 if not (b'info/exclude' in filepath or filepath.endswith(b'.gitignore')):
28 return orig(filepath, warn, sourceinfo=False)
28 return orig(filepath, warn, sourceinfo=False)
29 result = []
29 result = []
30 warnings = []
30 warnings = []
31 with open(filepath, b'rb') as fp:
31 with open(filepath, b'rb') as fp:
32 for l in fp:
32 for l in fp:
33 l = l.strip()
33 l = l.strip()
34 if not l or l.startswith(b'#'):
34 if not l or l.startswith(b'#'):
35 continue
35 continue
36 if l.startswith(b'!'):
36 if l.startswith(b'!'):
37 warnings.append(b'unsupported ignore pattern %s' % l)
37 warnings.append(b'unsupported ignore pattern %s' % l)
38 continue
38 continue
39 if l.startswith(b'/'):
39 if l.startswith(b'/'):
40 result.append(b'rootglob:' + l[1:])
40 result.append(b'rootglob:' + l[1:])
41 else:
41 else:
42 result.append(b'relglob:' + l)
42 result.append(b'relglob:' + l)
43 return result, warnings
43 return result, warnings
44
44
45
45
46 extensions.wrapfunction(matchmod, b'readpatternfile', readpatternfile)
46 extensions.wrapfunction(matchmod, b'readpatternfile', readpatternfile)
47
47
48
48
49 _STATUS_MAP = {}
49 _STATUS_MAP = {}
50 if pygit2:
50 if pygit2:
51 _STATUS_MAP = {
51 _STATUS_MAP = {
52 pygit2.GIT_STATUS_CONFLICTED: b'm',
52 pygit2.GIT_STATUS_CONFLICTED: b'm',
53 pygit2.GIT_STATUS_CURRENT: b'n',
53 pygit2.GIT_STATUS_CURRENT: b'n',
54 pygit2.GIT_STATUS_IGNORED: b'?',
54 pygit2.GIT_STATUS_IGNORED: b'?',
55 pygit2.GIT_STATUS_INDEX_DELETED: b'r',
55 pygit2.GIT_STATUS_INDEX_DELETED: b'r',
56 pygit2.GIT_STATUS_INDEX_MODIFIED: b'n',
56 pygit2.GIT_STATUS_INDEX_MODIFIED: b'n',
57 pygit2.GIT_STATUS_INDEX_NEW: b'a',
57 pygit2.GIT_STATUS_INDEX_NEW: b'a',
58 pygit2.GIT_STATUS_INDEX_RENAMED: b'a',
58 pygit2.GIT_STATUS_INDEX_RENAMED: b'a',
59 pygit2.GIT_STATUS_INDEX_TYPECHANGE: b'n',
59 pygit2.GIT_STATUS_INDEX_TYPECHANGE: b'n',
60 pygit2.GIT_STATUS_WT_DELETED: b'r',
60 pygit2.GIT_STATUS_WT_DELETED: b'r',
61 pygit2.GIT_STATUS_WT_MODIFIED: b'n',
61 pygit2.GIT_STATUS_WT_MODIFIED: b'n',
62 pygit2.GIT_STATUS_WT_NEW: b'?',
62 pygit2.GIT_STATUS_WT_NEW: b'?',
63 pygit2.GIT_STATUS_WT_RENAMED: b'a',
63 pygit2.GIT_STATUS_WT_RENAMED: b'a',
64 pygit2.GIT_STATUS_WT_TYPECHANGE: b'n',
64 pygit2.GIT_STATUS_WT_TYPECHANGE: b'n',
65 pygit2.GIT_STATUS_WT_UNREADABLE: b'?',
65 pygit2.GIT_STATUS_WT_UNREADABLE: b'?',
66 pygit2.GIT_STATUS_INDEX_MODIFIED | pygit2.GIT_STATUS_WT_MODIFIED: 'm',
66 pygit2.GIT_STATUS_INDEX_MODIFIED | pygit2.GIT_STATUS_WT_MODIFIED: 'm',
67 }
67 }
68
68
69
69
70 @interfaceutil.implementer(intdirstate.idirstate)
70 @interfaceutil.implementer(intdirstate.idirstate)
71 class gitdirstate(object):
71 class gitdirstate(object):
72 def __init__(self, ui, root, gitrepo):
72 def __init__(self, ui, root, gitrepo):
73 self._ui = ui
73 self._ui = ui
74 self._root = os.path.dirname(root)
74 self._root = os.path.dirname(root)
75 self.git = gitrepo
75 self.git = gitrepo
76 self._plchangecallbacks = {}
76 self._plchangecallbacks = {}
77
77
78 def p1(self):
78 def p1(self):
79 try:
79 try:
80 return self.git.head.peel().id.raw
80 return self.git.head.peel().id.raw
81 except pygit2.GitError:
81 except pygit2.GitError:
82 # Typically happens when peeling HEAD fails, as in an
82 # Typically happens when peeling HEAD fails, as in an
83 # empty repository.
83 # empty repository.
84 return nodemod.nullid
84 return nodemod.nullid
85
85
86 def p2(self):
86 def p2(self):
87 # TODO: MERGE_HEAD? something like that, right?
87 # TODO: MERGE_HEAD? something like that, right?
88 return nodemod.nullid
88 return nodemod.nullid
89
89
90 def setparents(self, p1, p2=nodemod.nullid):
90 def setparents(self, p1, p2=nodemod.nullid):
91 assert p2 == nodemod.nullid, b'TODO merging support'
91 assert p2 == nodemod.nullid, b'TODO merging support'
92 self.git.head.set_target(gitutil.togitnode(p1))
92 self.git.head.set_target(gitutil.togitnode(p1))
93
93
94 @util.propertycache
94 @util.propertycache
95 def identity(self):
95 def identity(self):
96 return util.filestat.frompath(
96 return util.filestat.frompath(
97 os.path.join(self._root, b'.git', b'index')
97 os.path.join(self._root, b'.git', b'index')
98 )
98 )
99
99
100 def branch(self):
100 def branch(self):
101 return b'default'
101 return b'default'
102
102
103 def parents(self):
103 def parents(self):
104 # TODO how on earth do we find p2 if a merge is in flight?
104 # TODO how on earth do we find p2 if a merge is in flight?
105 return self.p1(), nodemod.nullid
105 return self.p1(), nodemod.nullid
106
106
107 def __iter__(self):
107 def __iter__(self):
108 return (pycompat.fsencode(f.path) for f in self.git.index)
108 return (pycompat.fsencode(f.path) for f in self.git.index)
109
109
110 def items(self):
110 def items(self):
111 for ie in self.git.index:
111 for ie in self.git.index:
112 yield ie.path, None # value should be a dirstatetuple
112 yield ie.path, None # value should be a dirstatetuple
113
113
114 # py2,3 compat forward
114 # py2,3 compat forward
115 iteritems = items
115 iteritems = items
116
116
117 def __getitem__(self, filename):
117 def __getitem__(self, filename):
118 try:
118 try:
119 gs = self.git.status_file(filename)
119 gs = self.git.status_file(filename)
120 except KeyError:
120 except KeyError:
121 return b'?'
121 return b'?'
122 return _STATUS_MAP[gs]
122 return _STATUS_MAP[gs]
123
123
124 def __contains__(self, filename):
124 def __contains__(self, filename):
125 try:
125 try:
126 gs = self.git.status_file(filename)
126 gs = self.git.status_file(filename)
127 return _STATUS_MAP[gs] != b'?'
127 return _STATUS_MAP[gs] != b'?'
128 except KeyError:
128 except KeyError:
129 return False
129 return False
130
130
131 def status(self, match, subrepos, ignored, clean, unknown):
131 def status(self, match, subrepos, ignored, clean, unknown):
132 listignored, listclean, listunknown = ignored, clean, unknown
132 listignored, listclean, listunknown = ignored, clean, unknown
133 # TODO handling of clean files - can we get that from git.status()?
133 # TODO handling of clean files - can we get that from git.status()?
134 modified, added, removed, deleted, unknown, ignored, clean = (
134 modified, added, removed, deleted, unknown, ignored, clean = (
135 [],
135 [],
136 [],
136 [],
137 [],
137 [],
138 [],
138 [],
139 [],
139 [],
140 [],
140 [],
141 [],
141 [],
142 )
142 )
143 gstatus = self.git.status()
143 gstatus = self.git.status()
144 for path, status in gstatus.items():
144 for path, status in gstatus.items():
145 path = pycompat.fsencode(path)
145 path = pycompat.fsencode(path)
146 if not match(path):
146 if not match(path):
147 continue
147 continue
148 if status == pygit2.GIT_STATUS_IGNORED:
148 if status == pygit2.GIT_STATUS_IGNORED:
149 if path.endswith(b'/'):
149 if path.endswith(b'/'):
150 continue
150 continue
151 ignored.append(path)
151 ignored.append(path)
152 elif status in (
152 elif status in (
153 pygit2.GIT_STATUS_WT_MODIFIED,
153 pygit2.GIT_STATUS_WT_MODIFIED,
154 pygit2.GIT_STATUS_INDEX_MODIFIED,
154 pygit2.GIT_STATUS_INDEX_MODIFIED,
155 pygit2.GIT_STATUS_WT_MODIFIED
155 pygit2.GIT_STATUS_WT_MODIFIED
156 | pygit2.GIT_STATUS_INDEX_MODIFIED,
156 | pygit2.GIT_STATUS_INDEX_MODIFIED,
157 ):
157 ):
158 modified.append(path)
158 modified.append(path)
159 elif status == pygit2.GIT_STATUS_INDEX_NEW:
159 elif status == pygit2.GIT_STATUS_INDEX_NEW:
160 added.append(path)
160 added.append(path)
161 elif status == pygit2.GIT_STATUS_WT_NEW:
161 elif status == pygit2.GIT_STATUS_WT_NEW:
162 unknown.append(path)
162 unknown.append(path)
163 elif status == pygit2.GIT_STATUS_WT_DELETED:
163 elif status == pygit2.GIT_STATUS_WT_DELETED:
164 deleted.append(path)
164 deleted.append(path)
165 elif status == pygit2.GIT_STATUS_INDEX_DELETED:
165 elif status == pygit2.GIT_STATUS_INDEX_DELETED:
166 removed.append(path)
166 removed.append(path)
167 else:
167 else:
168 raise error.Abort(
168 raise error.Abort(
169 b'unhandled case: status for %r is %r' % (path, status)
169 b'unhandled case: status for %r is %r' % (path, status)
170 )
170 )
171
171
172 if listclean:
172 if listclean:
173 observed = set(
173 observed = set(
174 modified + added + removed + deleted + unknown + ignored
174 modified + added + removed + deleted + unknown + ignored
175 )
175 )
176 index = self.git.index
176 index = self.git.index
177 index.read()
177 index.read()
178 for entry in index:
178 for entry in index:
179 path = pycompat.fsencode(entry.path)
179 path = pycompat.fsencode(entry.path)
180 if not match(path):
180 if not match(path):
181 continue
181 continue
182 if path in observed:
182 if path in observed:
183 continue # already in some other set
183 continue # already in some other set
184 if path[-1] == b'/':
184 if path[-1] == b'/':
185 continue # directory
185 continue # directory
186 clean.append(path)
186 clean.append(path)
187
187
188 # TODO are we really always sure of status here?
188 # TODO are we really always sure of status here?
189 return (
189 return (
190 False,
190 False,
191 scmutil.status(
191 scmutil.status(
192 modified, added, removed, deleted, unknown, ignored, clean
192 modified, added, removed, deleted, unknown, ignored, clean
193 ),
193 ),
194 )
194 )
195
195
196 def flagfunc(self, buildfallback):
196 def flagfunc(self, buildfallback):
197 # TODO we can do better
197 # TODO we can do better
198 return buildfallback()
198 return buildfallback()
199
199
200 def getcwd(self):
200 def getcwd(self):
201 # TODO is this a good way to do this?
201 # TODO is this a good way to do this?
202 return os.path.dirname(
202 return os.path.dirname(
203 os.path.dirname(pycompat.fsencode(self.git.path))
203 os.path.dirname(pycompat.fsencode(self.git.path))
204 )
204 )
205
205
206 def normalize(self, path):
206 def normalize(self, path):
207 normed = util.normcase(path)
207 normed = util.normcase(path)
208 assert normed == path, b"TODO handling of case folding: %s != %s" % (
208 assert normed == path, b"TODO handling of case folding: %s != %s" % (
209 normed,
209 normed,
210 path,
210 path,
211 )
211 )
212 return path
212 return path
213
213
214 @property
214 @property
215 def _checklink(self):
215 def _checklink(self):
216 return util.checklink(os.path.dirname(pycompat.fsencode(self.git.path)))
216 return util.checklink(os.path.dirname(pycompat.fsencode(self.git.path)))
217
217
218 def copies(self):
218 def copies(self):
219 # TODO support copies?
219 # TODO support copies?
220 return {}
220 return {}
221
221
222 # # TODO what the heck is this
222 # # TODO what the heck is this
223 _filecache = set()
223 _filecache = set()
224
224
225 def pendingparentchange(self):
225 def pendingparentchange(self):
226 # TODO: we need to implement the context manager bits and
226 # TODO: we need to implement the context manager bits and
227 # correctly stage/revert index edits.
227 # correctly stage/revert index edits.
228 return False
228 return False
229
229
230 def write(self, tr):
230 def write(self, tr):
231 # TODO: call parent change callbacks
231 # TODO: call parent change callbacks
232
232
233 if tr:
233 if tr:
234
234
235 def writeinner(category):
235 def writeinner(category):
236 self.git.index.write()
236 self.git.index.write()
237
237
238 tr.addpending(b'gitdirstate', writeinner)
238 tr.addpending(b'gitdirstate', writeinner)
239 else:
239 else:
240 self.git.index.write()
240 self.git.index.write()
241
241
242 def pathto(self, f, cwd=None):
242 def pathto(self, f, cwd=None):
243 if cwd is None:
243 if cwd is None:
244 cwd = self.getcwd()
244 cwd = self.getcwd()
245 # TODO core dirstate does something about slashes here
245 # TODO core dirstate does something about slashes here
246 assert isinstance(f, bytes)
246 assert isinstance(f, bytes)
247 r = util.pathto(self._root, cwd, f)
247 r = util.pathto(self._root, cwd, f)
248 return r
248 return r
249
249
250 def matches(self, match):
250 def matches(self, match):
251 for x in self.git.index:
251 for x in self.git.index:
252 p = pycompat.fsencode(x.path)
252 p = pycompat.fsencode(x.path)
253 if match(p):
253 if match(p):
254 yield p
254 yield p
255
255
256 def normal(self, f, parentfiledata=None):
256 def normal(self, f, parentfiledata=None):
257 """Mark a file normal and clean."""
257 """Mark a file normal and clean."""
258 # TODO: for now we just let libgit2 re-stat the file. We can
258 # TODO: for now we just let libgit2 re-stat the file. We can
259 # clearly do better.
259 # clearly do better.
260
260
261 def normallookup(self, f):
261 def normallookup(self, f):
262 """Mark a file normal, but possibly dirty."""
262 """Mark a file normal, but possibly dirty."""
263 # TODO: for now we just let libgit2 re-stat the file. We can
263 # TODO: for now we just let libgit2 re-stat the file. We can
264 # clearly do better.
264 # clearly do better.
265
265
266 def walk(self, match, subrepos, unknown, ignored, full=True):
266 def walk(self, match, subrepos, unknown, ignored, full=True):
267 # TODO: we need to use .status() and not iterate the index,
267 # TODO: we need to use .status() and not iterate the index,
268 # because the index doesn't force a re-walk and so `hg add` of
268 # because the index doesn't force a re-walk and so `hg add` of
269 # a new file without an intervening call to status will
269 # a new file without an intervening call to status will
270 # silently do nothing.
270 # silently do nothing.
271 r = {}
271 r = {}
272 cwd = self.getcwd()
272 cwd = self.getcwd()
273 for path, status in self.git.status().items():
273 for path, status in self.git.status().items():
274 if path.startswith('.hg/'):
274 if path.startswith('.hg/'):
275 continue
275 continue
276 path = pycompat.fsencode(path)
276 path = pycompat.fsencode(path)
277 if not match(path):
277 if not match(path):
278 continue
278 continue
279 # TODO construct the stat info from the status object?
279 # TODO construct the stat info from the status object?
280 try:
280 try:
281 s = os.stat(os.path.join(cwd, path))
281 s = os.stat(os.path.join(cwd, path))
282 except OSError as e:
282 except OSError as e:
283 if e.errno != errno.ENOENT:
283 if e.errno != errno.ENOENT:
284 raise
284 raise
285 continue
285 continue
286 r[path] = s
286 r[path] = s
287 return r
287 return r
288
288
289 def savebackup(self, tr, backupname):
289 def savebackup(self, tr, backupname):
290 # TODO: figure out a strategy for saving index backups.
290 # TODO: figure out a strategy for saving index backups.
291 pass
291 pass
292
292
293 def restorebackup(self, tr, backupname):
293 def restorebackup(self, tr, backupname):
294 # TODO: figure out a strategy for saving index backups.
294 # TODO: figure out a strategy for saving index backups.
295 pass
295 pass
296
296
297 def add(self, f):
297 def add(self, f):
298 index = self.git.index
298 index = self.git.index
299 index.read()
299 index.read()
300 index.add(pycompat.fsdecode(f))
300 index.add(pycompat.fsdecode(f))
301 index.write()
301 index.write()
302
302
303 def drop(self, f):
303 def drop(self, f):
304 index = self.git.index
304 index = self.git.index
305 index.read()
305 index.read()
306 index.remove(pycompat.fsdecode(f))
306 fs = pycompat.fsdecode(f)
307 index.write()
307 if fs in index:
308 index.remove(fs)
309 index.write()
308
310
309 def remove(self, f):
311 def remove(self, f):
310 index = self.git.index
312 index = self.git.index
311 index.read()
313 index.read()
312 index.remove(pycompat.fsdecode(f))
314 index.remove(pycompat.fsdecode(f))
313 index.write()
315 index.write()
314
316
315 def copied(self, path):
317 def copied(self, path):
316 # TODO: track copies?
318 # TODO: track copies?
317 return None
319 return None
318
320
319 def prefetch_parents(self):
321 def prefetch_parents(self):
320 # TODO
322 # TODO
321 pass
323 pass
322
324
323 @contextlib.contextmanager
325 @contextlib.contextmanager
324 def parentchange(self):
326 def parentchange(self):
325 # TODO: track this maybe?
327 # TODO: track this maybe?
326 yield
328 yield
327
329
328 def addparentchangecallback(self, category, callback):
330 def addparentchangecallback(self, category, callback):
329 # TODO: should this be added to the dirstate interface?
331 # TODO: should this be added to the dirstate interface?
330 self._plchangecallbacks[category] = callback
332 self._plchangecallbacks[category] = callback
331
333
332 def clearbackup(self, tr, backupname):
334 def clearbackup(self, tr, backupname):
333 # TODO
335 # TODO
334 pass
336 pass
335
337
336 def setbranch(self, branch):
338 def setbranch(self, branch):
337 raise error.Abort(
339 raise error.Abort(
338 b'git repos do not support branches. try using bookmarks'
340 b'git repos do not support branches. try using bookmarks'
339 )
341 )
@@ -1,272 +1,277 b''
1 #require pygit2
1 #require pygit2
2
2
3 Setup:
3 Setup:
4 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
4 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
5 > GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
5 > GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
6 > GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
6 > GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
7 > GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
7 > GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
8 > GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
8 > GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
9 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
9 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
10 > count=10
10 > count=10
11 > gitcommit() {
11 > gitcommit() {
12 > GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000";
12 > GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000";
13 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
13 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
14 > git commit "$@" >/dev/null 2>/dev/null || echo "git commit error"
14 > git commit "$@" >/dev/null 2>/dev/null || echo "git commit error"
15 > count=`expr $count + 1`
15 > count=`expr $count + 1`
16 > }
16 > }
17
17
18
18
19 Test auto-loading extension works:
19 Test auto-loading extension works:
20 $ mkdir nogit
20 $ mkdir nogit
21 $ cd nogit
21 $ cd nogit
22 $ mkdir .hg
22 $ mkdir .hg
23 $ echo git >> .hg/requires
23 $ echo git >> .hg/requires
24 $ hg status
24 $ hg status
25 abort: repository specified git format in .hg/requires but has no .git directory
25 abort: repository specified git format in .hg/requires but has no .git directory
26 [255]
26 [255]
27 $ git init
27 $ git init
28 Initialized empty Git repository in $TESTTMP/nogit/.git/
28 Initialized empty Git repository in $TESTTMP/nogit/.git/
29 This status invocation shows some hg gunk because we didn't use
29 This status invocation shows some hg gunk because we didn't use
30 `hg init --git`, which fixes up .git/info/exclude for us.
30 `hg init --git`, which fixes up .git/info/exclude for us.
31 $ hg status
31 $ hg status
32 ? .hg/cache/git-commits.sqlite
32 ? .hg/cache/git-commits.sqlite
33 ? .hg/cache/git-commits.sqlite-shm
33 ? .hg/cache/git-commits.sqlite-shm
34 ? .hg/cache/git-commits.sqlite-wal
34 ? .hg/cache/git-commits.sqlite-wal
35 ? .hg/requires
35 ? .hg/requires
36 $ cd ..
36 $ cd ..
37
37
38 Now globally enable extension for the rest of the test:
38 Now globally enable extension for the rest of the test:
39 $ cat <<EOF >> $HGRCPATH
39 $ cat <<EOF >> $HGRCPATH
40 > [extensions]
40 > [extensions]
41 > git=
41 > git=
42 > [git]
42 > [git]
43 > log-index-cache-miss = yes
43 > log-index-cache-miss = yes
44 > EOF
44 > EOF
45
45
46 Make a new repo with git:
46 Make a new repo with git:
47 $ mkdir foo
47 $ mkdir foo
48 $ cd foo
48 $ cd foo
49 $ git init
49 $ git init
50 Initialized empty Git repository in $TESTTMP/foo/.git/
50 Initialized empty Git repository in $TESTTMP/foo/.git/
51 Ignore the .hg directory within git:
51 Ignore the .hg directory within git:
52 $ echo .hg >> .git/info/exclude
52 $ echo .hg >> .git/info/exclude
53 $ echo alpha > alpha
53 $ echo alpha > alpha
54 $ git add alpha
54 $ git add alpha
55 $ gitcommit -am 'Add alpha'
55 $ gitcommit -am 'Add alpha'
56 $ echo beta > beta
56 $ echo beta > beta
57 $ git add beta
57 $ git add beta
58 $ gitcommit -am 'Add beta'
58 $ gitcommit -am 'Add beta'
59 $ echo gamma > gamma
59 $ echo gamma > gamma
60 $ git status
60 $ git status
61 On branch master
61 On branch master
62 Untracked files:
62 Untracked files:
63 (use "git add <file>..." to include in what will be committed)
63 (use "git add <file>..." to include in what will be committed)
64 gamma
64 gamma
65
65
66 nothing added to commit but untracked files present (use "git add" to track)
66 nothing added to commit but untracked files present (use "git add" to track)
67
67
68 Without creating the .hg, hg status fails:
68 Without creating the .hg, hg status fails:
69 $ hg status
69 $ hg status
70 abort: no repository found in '$TESTTMP/foo' (.hg not found)!
70 abort: no repository found in '$TESTTMP/foo' (.hg not found)!
71 [255]
71 [255]
72 But if you run hg init --git, it works:
72 But if you run hg init --git, it works:
73 $ hg init --git
73 $ hg init --git
74 $ hg id --traceback
74 $ hg id --traceback
75 heads mismatch, rebuilding dagcache
75 heads mismatch, rebuilding dagcache
76 3d9be8deba43 tip master
76 3d9be8deba43 tip master
77 $ hg status
77 $ hg status
78 ? gamma
78 ? gamma
79 Log works too:
79 Log works too:
80 $ hg log
80 $ hg log
81 changeset: 1:3d9be8deba43
81 changeset: 1:3d9be8deba43
82 bookmark: master
82 bookmark: master
83 tag: tip
83 tag: tip
84 user: test <test@example.org>
84 user: test <test@example.org>
85 date: Mon Jan 01 00:00:11 2007 +0000
85 date: Mon Jan 01 00:00:11 2007 +0000
86 summary: Add beta
86 summary: Add beta
87
87
88 changeset: 0:c5864c9d16fb
88 changeset: 0:c5864c9d16fb
89 user: test <test@example.org>
89 user: test <test@example.org>
90 date: Mon Jan 01 00:00:10 2007 +0000
90 date: Mon Jan 01 00:00:10 2007 +0000
91 summary: Add alpha
91 summary: Add alpha
92
92
93
93
94
94
95 and bookmarks:
95 and bookmarks:
96 $ hg bookmarks
96 $ hg bookmarks
97 * master 1:3d9be8deba43
97 * master 1:3d9be8deba43
98
98
99 diff even works transparently in both systems:
99 diff even works transparently in both systems:
100 $ echo blah >> alpha
100 $ echo blah >> alpha
101 $ git diff
101 $ git diff
102 diff --git a/alpha b/alpha
102 diff --git a/alpha b/alpha
103 index 4a58007..faed1b7 100644
103 index 4a58007..faed1b7 100644
104 --- a/alpha
104 --- a/alpha
105 +++ b/alpha
105 +++ b/alpha
106 @@ -1* +1,2 @@ (glob)
106 @@ -1* +1,2 @@ (glob)
107 alpha
107 alpha
108 +blah
108 +blah
109 $ hg diff --git
109 $ hg diff --git
110 diff --git a/alpha b/alpha
110 diff --git a/alpha b/alpha
111 --- a/alpha
111 --- a/alpha
112 +++ b/alpha
112 +++ b/alpha
113 @@ -1,1 +1,2 @@
113 @@ -1,1 +1,2 @@
114 alpha
114 alpha
115 +blah
115 +blah
116
116
117 Remove a file, it shows as such:
117 Remove a file, it shows as such:
118 $ rm alpha
118 $ rm alpha
119 $ hg status
119 $ hg status
120 ! alpha
120 ! alpha
121 ? gamma
121 ? gamma
122
122
123 Revert works:
123 Revert works:
124 $ hg revert alpha --traceback
124 $ hg revert alpha --traceback
125 $ hg status
125 $ hg status
126 ? gamma
126 ? gamma
127 $ git status
127 $ git status
128 On branch master
128 On branch master
129 Untracked files:
129 Untracked files:
130 (use "git add <file>..." to include in what will be committed)
130 (use "git add <file>..." to include in what will be committed)
131 gamma
131 gamma
132
132
133 nothing added to commit but untracked files present (use "git add" to track)
133 nothing added to commit but untracked files present (use "git add" to track)
134
134
135 Add shows sanely in both:
135 Add shows sanely in both:
136 $ hg add gamma
136 $ hg add gamma
137 $ hg status
137 $ hg status
138 A gamma
138 A gamma
139 $ hg files
139 $ hg files
140 alpha
140 alpha
141 beta
141 beta
142 gamma
142 gamma
143 $ git ls-files
143 $ git ls-files
144 alpha
144 alpha
145 beta
145 beta
146 gamma
146 gamma
147 $ git status
147 $ git status
148 On branch master
148 On branch master
149 Changes to be committed:
149 Changes to be committed:
150 (use "git restore --staged <file>..." to unstage)
150 (use "git restore --staged <file>..." to unstage)
151 new file: gamma
151 new file: gamma
152
152
153
153
154 forget does what it should as well:
154 forget does what it should as well:
155 $ hg forget gamma
155 $ hg forget gamma
156 $ hg status
156 $ hg status
157 ? gamma
157 ? gamma
158 $ git status
158 $ git status
159 On branch master
159 On branch master
160 Untracked files:
160 Untracked files:
161 (use "git add <file>..." to include in what will be committed)
161 (use "git add <file>..." to include in what will be committed)
162 gamma
162 gamma
163
163
164 nothing added to commit but untracked files present (use "git add" to track)
164 nothing added to commit but untracked files present (use "git add" to track)
165
165
166 clean up untracked file
166 clean up untracked file
167 $ rm gamma
167 $ rm gamma
168
168
169 hg log FILE
169 hg log FILE
170
170
171 $ echo a >> alpha
171 $ echo a >> alpha
172 $ hg ci -m 'more alpha' --traceback --date '1583522787 18000'
172 $ hg ci -m 'more alpha' --traceback --date '1583522787 18000'
173 $ echo b >> beta
173 $ echo b >> beta
174 $ hg ci -m 'more beta'
174 $ hg ci -m 'more beta'
175 heads mismatch, rebuilding dagcache
175 heads mismatch, rebuilding dagcache
176 $ echo a >> alpha
176 $ echo a >> alpha
177 $ hg ci -m 'even more alpha'
177 $ hg ci -m 'even more alpha'
178 heads mismatch, rebuilding dagcache
178 heads mismatch, rebuilding dagcache
179 $ hg log -G alpha
179 $ hg log -G alpha
180 heads mismatch, rebuilding dagcache
180 heads mismatch, rebuilding dagcache
181 @ changeset: 4:6626247b7dc8
181 @ changeset: 4:6626247b7dc8
182 : bookmark: master
182 : bookmark: master
183 : tag: tip
183 : tag: tip
184 : user: test <test>
184 : user: test <test>
185 : date: Thu Jan 01 00:00:00 1970 +0000
185 : date: Thu Jan 01 00:00:00 1970 +0000
186 : summary: even more alpha
186 : summary: even more alpha
187 :
187 :
188 o changeset: 2:a1983dd7fb19
188 o changeset: 2:a1983dd7fb19
189 : user: test <test>
189 : user: test <test>
190 : date: Fri Mar 06 14:26:27 2020 -0500
190 : date: Fri Mar 06 14:26:27 2020 -0500
191 : summary: more alpha
191 : summary: more alpha
192 :
192 :
193 o changeset: 0:c5864c9d16fb
193 o changeset: 0:c5864c9d16fb
194 user: test <test@example.org>
194 user: test <test@example.org>
195 date: Mon Jan 01 00:00:10 2007 +0000
195 date: Mon Jan 01 00:00:10 2007 +0000
196 summary: Add alpha
196 summary: Add alpha
197
197
198 $ hg log -G beta
198 $ hg log -G beta
199 o changeset: 3:d8ee22687733
199 o changeset: 3:d8ee22687733
200 : user: test <test>
200 : user: test <test>
201 : date: Thu Jan 01 00:00:00 1970 +0000
201 : date: Thu Jan 01 00:00:00 1970 +0000
202 : summary: more beta
202 : summary: more beta
203 :
203 :
204 o changeset: 1:3d9be8deba43
204 o changeset: 1:3d9be8deba43
205 | user: test <test@example.org>
205 | user: test <test@example.org>
206 ~ date: Mon Jan 01 00:00:11 2007 +0000
206 ~ date: Mon Jan 01 00:00:11 2007 +0000
207 summary: Add beta
207 summary: Add beta
208
208
209
209
210 $ hg log -r "children(3d9be8deba43)" -T"{node|short} {children}\n"
210 $ hg log -r "children(3d9be8deba43)" -T"{node|short} {children}\n"
211 a1983dd7fb19 3:d8ee22687733
211 a1983dd7fb19 3:d8ee22687733
212
212
213 hg annotate
213 hg annotate
214
214
215 $ hg annotate alpha
215 $ hg annotate alpha
216 0: alpha
216 0: alpha
217 2: a
217 2: a
218 4: a
218 4: a
219 $ hg annotate beta
219 $ hg annotate beta
220 1: beta
220 1: beta
221 3: b
221 3: b
222
222
223
223
224 Files in subdirectories. TODO: case-folding support, make this `A`
224 Files in subdirectories. TODO: case-folding support, make this `A`
225 instead of `a`.
225 instead of `a`.
226
226
227 $ mkdir a
227 $ mkdir a
228 $ echo "This is file mu." > a/mu
228 $ echo "This is file mu." > a/mu
229 $ hg ci -A -m 'Introduce file a/mu'
229 $ hg ci -A -m 'Introduce file a/mu'
230 adding a/mu
230 adding a/mu
231
231
232 Both hg and git agree a/mu is part of the repo
232 Both hg and git agree a/mu is part of the repo
233
233
234 $ git ls-files
234 $ git ls-files
235 a/mu
235 a/mu
236 alpha
236 alpha
237 beta
237 beta
238 $ hg files
238 $ hg files
239 a/mu
239 a/mu
240 alpha
240 alpha
241 beta
241 beta
242
242
243 hg and git status both clean
243 hg and git status both clean
244
244
245 $ git status
245 $ git status
246 On branch master
246 On branch master
247 nothing to commit, working tree clean
247 nothing to commit, working tree clean
248 $ hg status
248 $ hg status
249 heads mismatch, rebuilding dagcache
249 heads mismatch, rebuilding dagcache
250
250
251
251
252 node|shortest works correctly
252 node|shortest works correctly
253 $ hg log -T '{node}\n' | sort
253 $ hg log -T '{node}\n' | sort
254 3d9be8deba43482be2c81a4cb4be1f10d85fa8bc
254 3d9be8deba43482be2c81a4cb4be1f10d85fa8bc
255 6626247b7dc8f231b183b8a4761c89139baca2ad
255 6626247b7dc8f231b183b8a4761c89139baca2ad
256 a1983dd7fb19cbd83ad5a1c2fc8bf3d775dea12f
256 a1983dd7fb19cbd83ad5a1c2fc8bf3d775dea12f
257 ae1ab744f95bfd5b07cf573baef98a778058537b
257 ae1ab744f95bfd5b07cf573baef98a778058537b
258 c5864c9d16fb3431fe2c175ff84dc6accdbb2c18
258 c5864c9d16fb3431fe2c175ff84dc6accdbb2c18
259 d8ee22687733a1991813560b15128cd9734f4b48
259 d8ee22687733a1991813560b15128cd9734f4b48
260 $ hg log -r ae1ab744f95bfd5b07cf573baef98a778058537b --template "{shortest(node,1)}\n"
260 $ hg log -r ae1ab744f95bfd5b07cf573baef98a778058537b --template "{shortest(node,1)}\n"
261 ae
261 ae
262
262
263 This coveres changelog.findmissing()
263 This coveres changelog.findmissing()
264 $ hg merge --preview 3d9be8deba43
264 $ hg merge --preview 3d9be8deba43
265
265
266 This covers manifest.diff()
266 This covers manifest.diff()
267 $ hg diff -c 3d9be8deba43
267 $ hg diff -c 3d9be8deba43
268 diff -r c5864c9d16fb -r 3d9be8deba43 beta
268 diff -r c5864c9d16fb -r 3d9be8deba43 beta
269 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
269 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
270 +++ b/beta Mon Jan 01 00:00:11 2007 +0000
270 +++ b/beta Mon Jan 01 00:00:11 2007 +0000
271 @@ -0,0 +1,1 @@
271 @@ -0,0 +1,1 @@
272 +beta
272 +beta
273
274
275 Deleting files should also work (this was issue6398)
276 $ hg rm beta
277 $ hg ci -m 'remove beta'
General Comments 0
You need to be logged in to leave comments. Login now