changeset 1791:1ed9e97d9d6d

Merge with jeffpc's hg-static
author Thomas Arendsen Hein <thomas@intevation.de>
date Wed, 22 Feb 2006 08:11:52 +0100
parents d5248726d22f (diff) 88f0345d82e9 (current diff)
children a161c61ba8ed
files
diffstat 13 files changed, 388 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/doc/hgrc.5.txt
+++ b/doc/hgrc.5.txt
@@ -247,6 +247,9 @@ ui::
     remote command to use for clone/push/pull operations. Default is 'hg'.
   ssh;;
     command to use for SSH connections. Default is 'ssh'.
+  timeout;;
+    The timeout used when a lock is held (in seconds), a negative value
+    means no timeout. Default is 600.
   username;;
     The committer of a changeset created when running "commit".
     Typically a person's name and email address, e.g. "Fred Widget
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -1800,7 +1800,7 @@ def pull(ui, repo, source="default", **o
 
     return r
 
-def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
+def push(ui, repo, dest="default-push", **opts):
     """push changes to the specified destination
 
     Push changes from the local repository to the given destination.
@@ -1825,13 +1825,16 @@ def push(ui, repo, dest="default-push", 
     dest = ui.expandpath(dest, repo.root)
     ui.status('pushing to %s\n' % (dest))
 
-    if ssh:
-        ui.setconfig("ui", "ssh", ssh)
-    if remotecmd:
-        ui.setconfig("ui", "remotecmd", remotecmd)
+    if opts['ssh']:
+        ui.setconfig("ui", "ssh", opts['ssh'])
+    if opts['remotecmd']:
+        ui.setconfig("ui", "remotecmd", opts['remotecmd'])
 
     other = hg.repository(ui, dest)
-    r = repo.push(other, force)
+    revs = None
+    if opts['rev']:
+        revs = [repo.lookup(rev) for rev in opts['rev']]
+    r = repo.push(other, opts['force'], revs=revs)
     return r
 
 def rawcommit(ui, repo, *flist, **rc):
@@ -2505,14 +2508,15 @@ table = {
           ('r', 'rev', [], _('a specific revision you would like to pull')),
           ('', 'remotecmd', '',
            _('specify hg command to run on the remote side'))],
-         _('hg pull [-u] [-e FILE] [-r rev] [--remotecmd FILE] [SOURCE]')),
+         _('hg pull [-u] [-e FILE] [-r rev]... [--remotecmd FILE] [SOURCE]')),
     "^push":
         (push,
          [('f', 'force', None, _('force push')),
           ('e', 'ssh', '', _('specify ssh command to use')),
+          ('r', 'rev', [], _('a specific revision you would like to push')),
           ('', 'remotecmd', '',
            _('specify hg command to run on the remote side'))],
-         _('hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]')),
+         _('hg push [-f] [-e FILE] [-r rev]... [--remotecmd FILE] [DEST]')),
     "rawcommit":
         (rawcommit,
          [('p', 'parent', [], _('parent')),
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -235,8 +235,7 @@ class localrepository(object):
         if os.path.exists(self.join("journal")):
             self.ui.status(_("rolling back interrupted transaction\n"))
             transaction.rollback(self.opener, self.join("journal"))
-            self.manifest = manifest.manifest(self.opener)
-            self.changelog = changelog.changelog(self.opener)
+            self.reload()
             return True
         else:
             self.ui.warn(_("no interrupted transaction available\n"))
@@ -250,10 +249,20 @@ class localrepository(object):
             self.ui.status(_("rolling back last transaction\n"))
             transaction.rollback(self.opener, self.join("undo"))
             util.rename(self.join("undo.dirstate"), self.join("dirstate"))
-            self.dirstate.read()
+            self.reload()
+            self.wreload()
         else:
             self.ui.warn(_("no undo information available\n"))
 
+    def wreload(self):
+        self.dirstate.read()
+
+    def reload(self):
+        self.changelog.load()
+        self.manifest.load()
+        self.tagscache = None
+        self.nodetagscache = None
+
     def do_lock(self, lockname, wait, releasefn=None, acquirefn=None):
         try:
             l = lock.lock(self.join(lockname), 0, releasefn)
@@ -261,18 +270,25 @@ class localrepository(object):
             if not wait:
                 raise inst
             self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
-            l = lock.lock(self.join(lockname), wait, releasefn)
+            try:
+                # default to 600 seconds timeout
+                l = lock.lock(self.join(lockname),
+                              int(self.ui.config("ui", "timeout") or 600),
+                              releasefn)
+            except lock.LockHeld, inst:
+                raise util.Abort(_("timeout while waiting for "
+                                   "lock held by %s") % inst.args[0])
         if acquirefn:
             acquirefn()
         return l
 
     def lock(self, wait=1):
-        return self.do_lock("lock", wait)
+        return self.do_lock("lock", wait, acquirefn=self.reload)
 
     def wlock(self, wait=1):
         return self.do_lock("wlock", wait,
                             self.dirstate.write,
-                            self.dirstate.read)
+                            self.wreload)
 
     def checkfilemerge(self, filename, text, filelog, manifest1, manifest2):
         "determine whether a new filenode is needed"
@@ -950,8 +966,8 @@ class localrepository(object):
             cg = remote.changegroupsubset(fetch, heads, 'pull')
         return self.addchangegroup(cg)
 
-    def push(self, remote, force=False):
-        l = remote.lock()
+    def push(self, remote, force=False, revs=None):
+        lock = remote.lock()
 
         base = {}
         heads = remote.heads()
@@ -962,17 +978,25 @@ class localrepository(object):
             return 1
 
         update = self.findoutgoing(remote, base)
-        if not update:
+        if revs is not None:
+            msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
+        else:
+            bases, heads = update, self.changelog.heads()
+
+        if not bases:
             self.ui.status(_("no changes found\n"))
             return 1
         elif not force:
-            if len(heads) < len(self.changelog.heads()):
+            if len(bases) < len(heads):
                 self.ui.warn(_("abort: push creates new remote branches!\n"))
                 self.ui.status(_("(did you forget to merge?"
                                  " use push -f to force)\n"))
                 return 1
 
-        cg = self.changegroup(update, 'push')
+        if revs is None:
+            cg = self.changegroup(update, 'push')
+        else:
+            cg = self.changegroupsubset(update, revs, 'push')
         return remote.addchangegroup(cg)
 
     def changegroupsubset(self, bases, heads, source):
--- a/mercurial/lock.py
+++ b/mercurial/lock.py
@@ -16,10 +16,10 @@ class LockUnavailable(LockException):
     pass
 
 class lock(object):
-    def __init__(self, file, wait=1, releasefn=None):
+    def __init__(self, file, timeout=-1, releasefn=None):
         self.f = file
         self.held = 0
-        self.wait = wait
+        self.timeout = timeout
         self.releasefn = releasefn
         self.lock()
 
@@ -27,13 +27,16 @@ class lock(object):
         self.release()
 
     def lock(self):
+        timeout = self.timeout
         while 1:
             try:
                 self.trylock()
                 return 1
             except LockHeld, inst:
-                if self.wait:
+                if timeout != 0:
                     time.sleep(1)
+                    if timeout > 0:
+                        timeout -= 1
                     continue
                 raise inst
 
--- a/mercurial/revlog.py
+++ b/mercurial/revlog.py
@@ -13,7 +13,7 @@ of the GNU General Public License, incor
 from node import *
 from i18n import gettext as _
 from demandload import demandload
-demandload(globals(), "binascii errno heapq mdiff sha struct zlib")
+demandload(globals(), "binascii errno heapq mdiff os sha struct zlib")
 
 def hash(text, p1, p2):
     """generate a hash from the given text and its parent hashes
@@ -187,15 +187,33 @@ class revlog(object):
         self.indexfile = indexfile
         self.datafile = datafile
         self.opener = opener
+
+        self.indexstat = None
         self.cache = None
         self.chunkcache = None
+        self.load()
 
+    def load(self):
         try:
-            i = self.opener(self.indexfile).read()
+            f = self.opener(self.indexfile)
         except IOError, inst:
             if inst.errno != errno.ENOENT:
                 raise
             i = ""
+        else:
+            try:
+                st = os.fstat(f.fileno())
+            except AttributeError, inst:
+                st = None
+            else:
+                oldst = self.indexstat
+                if (oldst and st.st_dev == oldst.st_dev
+                    and st.st_ino == oldst.st_ino
+                    and st.st_mtime == oldst.st_mtime
+                    and st.st_ctime == oldst.st_ctime):
+                    return
+            self.indexstat = st
+            i = f.read()
 
         if i and i[:4] != "\0\0\0\0":
             raise RevlogError(_("incompatible revlog signature on %s") %
--- a/tests/test-archive
+++ b/tests/test-archive
@@ -18,8 +18,7 @@ echo "name = test-archive" >> .hg/hgrc
 echo "allowzip = true" >> .hg/hgrc
 echo "allowgz = true" >> .hg/hgrc
 echo "allowbz2 = true" >> .hg/hgrc
-serverpid=`mktemp`
-hg serve -p 20059 -d --pid-file=$serverpid
+hg serve -p 20059 -d --pid-file=hg.pid
 
 TIP=`hg id -v | cut -f1 -d' '`
 QTIP=`hg id -q`
@@ -35,5 +34,4 @@ http_proxy= python getarchive.py "$TIP" 
 http_proxy= python getarchive.py "$TIP" zip > archive.zip
 unzip -t archive.zip | sed "s/$QTIP/TIP/"
 
-kill `cat $serverpid`
-rm $serverpid
+kill `cat hg.pid`
new file mode 100755
--- /dev/null
+++ b/tests/test-clone-pull-corruption
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# Corrupt an hg repo with a pull started during an aborted commit
+#
+
+# Create two repos, so that one of them can pull from the other one.
+hg init source
+cd source
+touch foo
+hg add foo
+hg ci -m 'add foo'
+hg clone . ../corrupted
+echo >> foo
+hg ci -m 'change foo'
+
+# Add a hook to wait 5 seconds and then abort the commit
+cd ../corrupted
+echo '[hooks]' >> .hg/hgrc
+echo 'pretxncommit = sleep 5; exit 1' >> .hg/hgrc
+
+# start a commit...
+touch bar
+hg add bar
+hg ci -m 'add bar' &
+
+# ... and start a pull while the commit is still running
+sleep 1
+hg pull ../source 2>/dev/null
+
+# see what happened
+wait
+hg verify
new file mode 100644
--- /dev/null
+++ b/tests/test-clone-pull-corruption.out
@@ -0,0 +1,15 @@
+pulling from ../source
+abort: pretxncommit hook exited with status 1
+transaction abort!
+rollback completed
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+(run 'hg update' to get a working copy)
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+1 files, 2 changesets, 2 total revisions
--- a/tests/test-pull
+++ b/tests/test-pull
@@ -7,8 +7,7 @@ hg init
 hg addremove
 hg commit -m 1
 hg verify
-serverpid=`mktemp`
-hg serve -p 20059 -d --pid-file=$serverpid
+hg serve -p 20059 -d --pid-file=hg.pid
 cd ..
 
 hg clone http://localhost:20059/ copy
@@ -19,5 +18,4 @@ cat foo
 hg manifest
 hg pull
 
-kill `cat $serverpid`
-rm $serverpid
+kill `cat ../test/hg.pid`
new file mode 100755
--- /dev/null
+++ b/tests/test-pull-pull-corruption
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# Corrupt an hg repo with two pulls.
+#
+
+# create one repo with a long history
+hg init source1
+cd source1
+touch foo
+hg add foo
+for i in 1 2 3 4 5 6 7 8 9 10; do
+    echo $i >> foo
+    hg ci -m $i
+done
+cd ..
+
+# create one repo with a shorter history
+hg clone -r 0 source1 source2
+cd source2
+echo a >> foo
+hg ci -m a
+cd ..
+
+# create a third repo to pull both other repos into it
+hg init corrupted
+cd corrupted
+# use a hook to make the second pull start while the first one is still running
+echo '[hooks]' >> .hg/hgrc
+echo 'prechangegroup = sleep 5' >> .hg/hgrc
+
+# start a pull...
+hg pull ../source1 &
+
+# ... and start another pull before the first one has finished
+sleep 1
+hg pull ../source2 2>/dev/null
+
+# see the result
+wait
+hg verify
+
new file mode 100644
--- /dev/null
+++ b/tests/test-pull-pull-corruption.out
@@ -0,0 +1,24 @@
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+pulling from ../source2
+pulling from ../source1
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 10 changesets with 10 changes to 1 files
+(run 'hg update' to get a working copy)
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files (+1 heads)
+(run 'hg update' to get a working copy)
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+1 files, 11 changesets, 11 total revisions
new file mode 100755
--- /dev/null
+++ b/tests/test-push-r
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+hg init test
+cd test
+cat >>afile <<EOF
+0
+EOF
+hg add afile
+hg commit -m "0.0"
+cat >>afile <<EOF
+1
+EOF
+hg commit -m "0.1"
+cat >>afile <<EOF
+2
+EOF
+hg commit -m "0.2"
+cat >>afile <<EOF
+3
+EOF
+hg commit -m "0.3"
+hg update -C 0
+cat >>afile <<EOF
+1
+EOF
+hg commit -m "1.1"
+cat >>afile <<EOF
+2
+EOF
+hg commit -m "1.2"
+cat >fred <<EOF
+a line
+EOF
+cat >>afile <<EOF
+3
+EOF
+hg add fred
+hg commit -m "1.3"
+hg mv afile adifferentfile
+hg commit -m "1.3m"
+hg update -C 3
+hg mv afile anotherfile
+hg commit -m "0.3m"
+hg debugindex .hg/data/afile.i
+hg debugindex .hg/data/adifferentfile.i
+hg debugindex .hg/data/anotherfile.i
+hg debugindex .hg/data/fred.i
+hg debugindex .hg/00manifest.i
+hg verify
+cd ..
+for i in 0 1 2 3 4 5 6 7 8; do
+   mkdir test-"$i"
+   hg --cwd test-"$i" init
+   hg -R test push -r "$i" test-"$i"
+   cd test-"$i"
+   hg verify
+   cd ..
+done
+cd test-8
+hg pull ../test-7
+hg verify
new file mode 100644
--- /dev/null
+++ b/tests/test-push-r.out
@@ -0,0 +1,135 @@
+   rev    offset  length   base linkrev nodeid       p1           p2
+     0         0       3      0       0 362fef284ce2 000000000000 000000000000
+     1         3       5      1       1 125144f7e028 362fef284ce2 000000000000
+     2         8       7      2       2 4c982badb186 125144f7e028 000000000000
+     3        15       9      3       3 19b1fc555737 4c982badb186 000000000000
+   rev    offset  length   base linkrev nodeid       p1           p2
+     0         0      75      0       7 905359268f77 000000000000 000000000000
+   rev    offset  length   base linkrev nodeid       p1           p2
+     0         0      75      0       8 905359268f77 000000000000 000000000000
+   rev    offset  length   base linkrev nodeid       p1           p2
+     0         0       8      0       6 12ab3bcc5ea4 000000000000 000000000000
+   rev    offset  length   base linkrev nodeid       p1           p2
+     0         0      48      0       0 43eadb1d2d06 000000000000 000000000000
+     1        48      48      1       1 8b89697eba2c 43eadb1d2d06 000000000000
+     2        96      48      2       2 626a32663c2f 8b89697eba2c 000000000000
+     3       144      48      3       3 f54c32f13478 626a32663c2f 000000000000
+     4       192      58      3       6 de68e904d169 626a32663c2f 000000000000
+     5       250      68      3       7 3b45cc2ab868 de68e904d169 000000000000
+     6       318      54      6       8 24d86153a002 f54c32f13478 000000000000
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+4 files, 9 changesets, 7 total revisions
+pushing to test-0
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+1 files, 1 changesets, 1 total revisions
+pushing to test-1
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 2 changesets with 2 changes to 1 files
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+1 files, 2 changesets, 2 total revisions
+pushing to test-2
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 3 changesets with 3 changes to 1 files
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+1 files, 3 changesets, 3 total revisions
+pushing to test-3
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 4 changesets with 4 changes to 1 files
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+1 files, 4 changesets, 4 total revisions
+pushing to test-4
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 2 changesets with 2 changes to 1 files
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+1 files, 2 changesets, 2 total revisions
+pushing to test-5
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 3 changesets with 3 changes to 1 files
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+1 files, 3 changesets, 3 total revisions
+pushing to test-6
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 4 changesets with 5 changes to 2 files
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+2 files, 4 changesets, 5 total revisions
+pushing to test-7
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 5 changesets with 6 changes to 3 files
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+3 files, 5 changesets, 6 total revisions
+pushing to test-8
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 5 changesets with 5 changes to 2 files
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+2 files, 5 changesets, 5 total revisions
+pulling from ../test-7
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 4 changesets with 2 changes to 3 files (+1 heads)
+(run 'hg update' to get a working copy)
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+4 files, 9 changesets, 7 total revisions