mirror of
https://github.com/facebook/sapling.git
synced 2024-10-06 06:47:41 +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):
|
||||
# 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 ""
|
||||
create = opts.get("create") or False
|
||||
|
||||
|
@ -28,6 +28,7 @@ import re
|
||||
import shutil
|
||||
import typing
|
||||
|
||||
from edenscm import tracing
|
||||
from edenscm.mercurial import (
|
||||
bookmarks,
|
||||
commands,
|
||||
@ -36,6 +37,7 @@ from edenscm.mercurial import (
|
||||
error,
|
||||
exchange,
|
||||
extensions,
|
||||
git,
|
||||
hg,
|
||||
localrepo,
|
||||
mutation,
|
||||
@ -784,7 +786,53 @@ def _getrebasedest(repo, opts):
|
||||
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):
|
||||
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
|
||||
# will continue working if remotenames.forcecompat is enabled
|
||||
forcecompat = ui.configbool("remotenames", "forcecompat")
|
||||
|
@ -17,6 +17,7 @@ from edenscm import tracing
|
||||
|
||||
from . import error, util
|
||||
from .i18n import _
|
||||
from .node import hex
|
||||
|
||||
GIT_DIR_FILE = "gitdir"
|
||||
GIT_REQUIREMENT = "git"
|
||||
@ -195,6 +196,24 @@ def postpullupdate(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):
|
||||
"""Run git command in the backing git repo, return its output"""
|
||||
gitdir = readgitdir(repo)
|
||||
|
@ -1,4 +1,5 @@
|
||||
#chg-compatible
|
||||
#require git no-windows
|
||||
|
||||
$ export GIT_AUTHOR_NAME='test'
|
||||
$ export GIT_AUTHOR_EMAIL='test@example.org'
|
||||
@ -6,7 +7,7 @@
|
||||
$ export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
|
||||
$ export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
|
||||
$ export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
|
||||
$ setconfig diff.git=true
|
||||
$ setconfig diff.git=true ui.allowemptycommit=true
|
||||
$ unset GIT_DIR
|
||||
|
||||
Prepare a git repo:
|
||||
@ -206,3 +207,85 @@ Test clone with flags (--noupdate, --updaterev):
|
||||
5c9a5ee451a8 origin/foo alpha3
|
||||
$ 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