mirror of
https://github.com/facebook/sapling.git
synced 2024-10-06 14:58:03 +03:00
git: support simple push
Summary: Support pushing to a git repo using `git push` command. This is implemented in remotenames' `push`, since that's what used in production. The vanilla `push` is unchanged. The `%include builtin:git.rc` in hgrc should have remotenames enabled. Reviewed By: DurhamG Differential Revision: D33351380 fbshipit-source-id: f1e2dfd64168b83d1cf608c0490e56634688da15
This commit is contained in:
parent
2209a1c032
commit
471b5a49a4
@ -109,6 +109,10 @@ def extsetup(ui):
|
|||||||
|
|
||||||
|
|
||||||
def _push(orig, ui, repo, dest=None, *args, **opts):
|
def _push(orig, ui, repo, dest=None, *args, **opts):
|
||||||
|
# use the original push logic to handle "no default path" case.
|
||||||
|
if "default" not in repo.ui.paths:
|
||||||
|
return orig(ui, repo, dest, *args, **opts)
|
||||||
|
|
||||||
bookmark = opts.get("to") or ""
|
bookmark = opts.get("to") or ""
|
||||||
create = opts.get("create") or False
|
create = opts.get("create") or False
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ import re
|
|||||||
import shutil
|
import shutil
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
|
from edenscm import tracing
|
||||||
from edenscm.mercurial import (
|
from edenscm.mercurial import (
|
||||||
bookmarks,
|
bookmarks,
|
||||||
commands,
|
commands,
|
||||||
@ -36,6 +37,7 @@ from edenscm.mercurial import (
|
|||||||
error,
|
error,
|
||||||
exchange,
|
exchange,
|
||||||
extensions,
|
extensions,
|
||||||
|
git,
|
||||||
hg,
|
hg,
|
||||||
localrepo,
|
localrepo,
|
||||||
mutation,
|
mutation,
|
||||||
@ -784,7 +786,53 @@ def _getrebasedest(repo, opts):
|
|||||||
return tracking.get(active)
|
return tracking.get(active)
|
||||||
|
|
||||||
|
|
||||||
|
def _guesspushtobookmark(repo, pushnode, remotename):
|
||||||
|
"""try to guess the "push --to" bookmark name
|
||||||
|
|
||||||
|
Find the remote name that starts with "{remotename}/" that does not have
|
||||||
|
another remotename descandant, and can fast-forward to pushnode (aka. is
|
||||||
|
an ancestor of node) with the least distance.
|
||||||
|
|
||||||
|
Return the name that can be used as "push --to", or None if there are no
|
||||||
|
unique choice.
|
||||||
|
"""
|
||||||
|
prefix = "%s/" % remotename
|
||||||
|
remotenamenodes = [
|
||||||
|
nodes[0] for nodes in repo._remotenames["bookmarks"].values() if len(nodes) == 1
|
||||||
|
]
|
||||||
|
candidatenodes = set(
|
||||||
|
repo.dageval(lambda: parents(only([pushnode], remotenamenodes)))
|
||||||
|
)
|
||||||
|
candidates = [
|
||||||
|
(name[len(prefix) :], nodes[0])
|
||||||
|
for name, nodes in repo._remotenames["bookmarks"].items()
|
||||||
|
if name.startswith(prefix) and len(nodes) == 1 and nodes[0] in candidatenodes
|
||||||
|
]
|
||||||
|
names = [item[0] for item in candidates]
|
||||||
|
if len(names) == 1:
|
||||||
|
return names[0]
|
||||||
|
tracing.debug("candidates of --to: %r" % (names,))
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def expushcmd(orig, ui, repo, dest=None, **opts):
|
def expushcmd(orig, ui, repo, dest=None, **opts):
|
||||||
|
if git.isgit(repo):
|
||||||
|
force = opts.get("force")
|
||||||
|
delete = opts.get("delete")
|
||||||
|
if dest == "default":
|
||||||
|
dest = None
|
||||||
|
dest = dest or "origin"
|
||||||
|
if delete:
|
||||||
|
pushnode = None
|
||||||
|
to = delete
|
||||||
|
else:
|
||||||
|
revspec = (["."] + opts.get("bookmark", []) + opts.get("rev", []))[-1]
|
||||||
|
pushnode = scmutil.revsingle(repo, revspec).node()
|
||||||
|
to = opts.get("to") or _guesspushtobookmark(repo, pushnode, dest)
|
||||||
|
if not to:
|
||||||
|
raise error.Abort(_("use '--to' to specify destination bookmark"))
|
||||||
|
return git.push(repo, dest, pushnode, to, force=force)
|
||||||
|
|
||||||
# during the upgrade from old to new remotenames, tooling that uses --force
|
# during the upgrade from old to new remotenames, tooling that uses --force
|
||||||
# will continue working if remotenames.forcecompat is enabled
|
# will continue working if remotenames.forcecompat is enabled
|
||||||
forcecompat = ui.configbool("remotenames", "forcecompat")
|
forcecompat = ui.configbool("remotenames", "forcecompat")
|
||||||
|
@ -17,6 +17,7 @@ from edenscm import tracing
|
|||||||
|
|
||||||
from . import error, util
|
from . import error, util
|
||||||
from .i18n import _
|
from .i18n import _
|
||||||
|
from .node import hex
|
||||||
|
|
||||||
GIT_DIR_FILE = "gitdir"
|
GIT_DIR_FILE = "gitdir"
|
||||||
GIT_REQUIREMENT = "git"
|
GIT_REQUIREMENT = "git"
|
||||||
@ -195,6 +196,24 @@ def postpullupdate(repo, node=None):
|
|||||||
return hg.updatetotally(repo.ui, repo, node, None)
|
return hg.updatetotally(repo.ui, repo, node, None)
|
||||||
|
|
||||||
|
|
||||||
|
def push(repo, dest, pushnode, to, force=False):
|
||||||
|
"""Push "pushnode" to remote "dest" bookmark "to"
|
||||||
|
|
||||||
|
If force is True, enable non-fast-forward moves.
|
||||||
|
If pushnode is None, delete the remote bookmark.
|
||||||
|
"""
|
||||||
|
if pushnode is None:
|
||||||
|
fromspec = ""
|
||||||
|
elif force:
|
||||||
|
fromspec = "+%s" % hex(pushnode)
|
||||||
|
else:
|
||||||
|
fromspec = "%s" % hex(pushnode)
|
||||||
|
refspec = "%s:refs/heads/%s" % (fromspec, to)
|
||||||
|
ret = rungit(repo, ["push", "-u", dest, refspec])
|
||||||
|
repo.invalidatechangelog()
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def callgit(repo, args):
|
def callgit(repo, args):
|
||||||
"""Run git command in the backing git repo, return its output"""
|
"""Run git command in the backing git repo, return its output"""
|
||||||
gitdir = readgitdir(repo)
|
gitdir = readgitdir(repo)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#chg-compatible
|
#chg-compatible
|
||||||
|
#require git no-windows
|
||||||
|
|
||||||
$ export GIT_AUTHOR_NAME='test'
|
$ export GIT_AUTHOR_NAME='test'
|
||||||
$ export GIT_AUTHOR_EMAIL='test@example.org'
|
$ export GIT_AUTHOR_EMAIL='test@example.org'
|
||||||
@ -6,7 +7,7 @@
|
|||||||
$ export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
|
$ export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
|
||||||
$ export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
|
$ export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
|
||||||
$ export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
|
$ export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
|
||||||
$ setconfig diff.git=true
|
$ setconfig diff.git=true ui.allowemptycommit=true
|
||||||
$ unset GIT_DIR
|
$ unset GIT_DIR
|
||||||
|
|
||||||
Prepare a git repo:
|
Prepare a git repo:
|
||||||
@ -206,3 +207,85 @@ Test clone with flags (--noupdate, --updaterev):
|
|||||||
5c9a5ee451a8 origin/foo alpha3
|
5c9a5ee451a8 origin/foo alpha3
|
||||||
$ cd ..
|
$ cd ..
|
||||||
|
|
||||||
|
Test push:
|
||||||
|
|
||||||
|
$ cd "$TESTTMP/clonetest/cloned1"
|
||||||
|
$ echo 3 > beta
|
||||||
|
$ hg commit -m 'beta.change'
|
||||||
|
|
||||||
|
- --to without -r
|
||||||
|
$ hg push -q --to book_change_beta
|
||||||
|
|
||||||
|
- --to with -r
|
||||||
|
$ hg push -r '.^' --to parent_change_beta
|
||||||
|
To file:/*/$TESTTMP/gitrepo (glob)
|
||||||
|
* [new branch] 5c9a5ee451a8051f0d16433dee8a2c2259d5fed8 -> parent_change_beta
|
||||||
|
|
||||||
|
$ hg log -r '.^+.' -T '{desc} {remotenames}\n'
|
||||||
|
alpha3 origin/foo origin/parent_change_beta
|
||||||
|
beta.change origin/book_change_beta
|
||||||
|
|
||||||
|
- delete bookmark
|
||||||
|
$ hg push --delete book_change_beta
|
||||||
|
To file:/*/$TESTTMP/gitrepo (glob)
|
||||||
|
- [deleted] book_change_beta
|
||||||
|
|
||||||
|
$ hg log -r '.^+.' -T '{desc} {remotenames}\n'
|
||||||
|
alpha3 origin/foo origin/parent_change_beta
|
||||||
|
beta.change
|
||||||
|
|
||||||
|
- infinitepush compatibility
|
||||||
|
$ hg push -q -r '.^' --to push_with_infinitepush --config extensions.infinitepush=
|
||||||
|
|
||||||
|
- push with --force
|
||||||
|
|
||||||
|
$ cd "$TESTTMP"
|
||||||
|
$ git init -qb main --bare "pushforce.git"
|
||||||
|
$ hg clone "git+file://$TESTTMP/pushforce.git"
|
||||||
|
$ cd pushforce
|
||||||
|
$ git --git-dir=.hg/store/git config advice.pushUpdateRejected false
|
||||||
|
|
||||||
|
$ drawdag << 'EOS'
|
||||||
|
> B C
|
||||||
|
> |/
|
||||||
|
> A
|
||||||
|
> EOS
|
||||||
|
|
||||||
|
$ hg push -qr $B --to foo
|
||||||
|
$ hg push -qr $C --to foo
|
||||||
|
To file:/*/$TESTTMP/pushforce.git (glob)
|
||||||
|
! [rejected] 5d38a953d58b0c80a4416ba62e62d3f2985a3726 -> foo (non-fast-forward)
|
||||||
|
error: failed to push some refs to 'file:/*/$TESTTMP/pushforce.git' (glob)
|
||||||
|
[1]
|
||||||
|
$ hg push -qr $C --to foo --force
|
||||||
|
|
||||||
|
- push without --to
|
||||||
|
|
||||||
|
$ cd "$TESTTMP"
|
||||||
|
$ git init -qb main --bare "pushto.git"
|
||||||
|
$ hg clone "git+file://$TESTTMP/pushto.git"
|
||||||
|
$ cd pushto
|
||||||
|
|
||||||
|
$ drawdag << 'EOS'
|
||||||
|
> B
|
||||||
|
> |
|
||||||
|
> A
|
||||||
|
> EOS
|
||||||
|
|
||||||
|
$ hg push -qr $A --to stable
|
||||||
|
$ hg push -qr $B --to main
|
||||||
|
$ hg up -q $B
|
||||||
|
$ hg commit -m C
|
||||||
|
|
||||||
|
(pick "main" automatically)
|
||||||
|
$ hg push
|
||||||
|
To file:/*/$TESTTMP/pushto.git (glob)
|
||||||
|
0de3093..a9d5bd6 a9d5bd6ac8bcf89de9cd99fd215cca243e8aeed9 -> main
|
||||||
|
$ hg push -q --to stable
|
||||||
|
|
||||||
|
(cannot pick with multiple candidates)
|
||||||
|
$ hg commit -m D
|
||||||
|
$ hg push
|
||||||
|
abort: use '--to' to specify destination bookmark
|
||||||
|
[255]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user