在Git中是否可以为分支设置别名?

82

我正在考虑在大规模应用Git。我希望通过将主分支更名为trunk来增加采用率并使事情变得更加简单。

这可以并且将会给SVN用户带来一些安慰感。我知道我可以创建一个名为trunk的分支,但那似乎偏离了Git的规范,可能会让一些用户感到困惑。

我知道我也可以随心所欲地创建和删除标签,但是当我检出这些标签时,它告诉我这是一个非本地分支,对我来说很好,但可能不是我想做的。

我是一个完全的Git新手,但是熟练掌握发布和构建系统。

我想要做的是能够将主分支叫做trunk。 我看到了命令别名的功能 - 这也适用于版本化对象的名称吗?

我知道git-svn和其他工具存在,但是层次化存储库系统的开销让我感到害怕。

4个回答

123

你可以像Greg建议的那样将主分支重命名为trunk,或者创建一个符号引用到主分支的trunk,这样git和svn用户都可以使用他们习惯的'main'分支。

git symbolic-ref refs/heads/trunk refs/heads/master

请注意,trunk不是一等公民。如果您检出trunk并执行git status,实际上您会在master上,但是您可以在使用分支名称(日志、合并等)的所有地方使用trunk命令。


2
我认为如果使用git://或ssh协议,symrefs不会传输到克隆中-最好检查一下。 - Jakub Narębski
1
我认为他们也不会这样做,它们只是变成了普通的远程引用,但它们仍然跟踪symref-ed中央分支,所以在某种程度上(也许)并不重要。我能想到的一个区别是,在推送后必须进行获取才能注意到其他远程分支也移动了。 - CB Bailey
12
非常小心参数的顺序。它和 ln -s 不一样,如果你把它们放错了位置,git 会欣然(且悄无声息地)用一个指向不存在东西的符号引用覆盖真实分支的 HEAD 引用(此时你最好希望能轻松恢复该分支的正确提交哈希值)。 - phils
13
请勿尝试使用git branch -d来删除符号引用。即使进行这种操作,它也会被取消引用,所以你实际上会删除源分支,但留下引用(如果你当前正在使用该分支,则甚至允许你这样做)。你必须使用 git symbolic-ref --delete(如果你使用的是旧版本,则必须手动删除文件)。 - phils
3
git symbolic-ref refs/heads/master refs/heads/main — 如果您在现代存储库中使用老派的习惯感到不舒服 :) - kolypto
显示剩余4条评论

11

在Git中,"master"这个名称并没有什么特殊之处,它只是按照惯例(和默认设置)被称为那样。如果你愿意,你完全可以把它叫做"trunk":

git branch -m master trunk

这非常类似于Subversion,其中“trunk”这个名称也只是惯例而已。在Subversion中,您也可以将主分支称为“master”。


不知道你的新约定,新的拉取者会不知道从哪里开始吗? - ojblass
公共代码库的HEAD指向“trunk”,因此拉取者不需要知道。 - Greg Hewgill

10
这是对Charles Bailey回答中所展示技巧的一种安全包装。
$ git branch-alias <alias> <long-and-unwieldy-branch-name> # create alias
$ git branch-alias <alias> # create alias for current branch
$ git branch # view branches and branch aliases
$ git log <alias>
$ git checkout <alias>
$ git push origin <alias> # pushes the branch, not the alias/reference
$ git branch-alias -d <alias> # delete an alias safely
$ git branch-alias -h # help / usage details

请注意,git版本2.7.0至2.8.2(含)中存在一个错误,导致“git branch”显示分支别名为“alias -> alias”,而不是“alias -> branch”。如果您受到该错误的影响,我建议升级到2.8.3或更高版本。
#!/bin/sh
# git branch-alias
# Author: Phil S.
# Version 1.13.1
version=1.13.1

# Creates branch aliases, so that you can refer to a long branch name
# by a convenient short alias.  This is particularly useful for branch
# names beginning with bug-tracker ID numbers (or similar), where the
# benefits of tab-completion are greatly reduced.

# This is mostly a "do what I mean" wrapper around "git symbolic-ref",
# with numerous safety measures included in order to eliminate the
# (otherwise considerable) risk of trashing a branch if you get your
# arguments wrong.

# Installation:
# Place this script somewhere in your PATH and name it "git-branch-alias"
# and you will be able to invoke it with "git branch-alias" as per the
# following examples.  If you have obtained the script from the git
# mailing list, please see the "Mailing list archives" note below.

# Examples:
# git branch-alias <alias> <long-and-unwieldy-branch-name> # create alias
# git branch-alias <alias> # create alias for current branch
# git branch # view branches and branch aliases
# git log <alias>
# git checkout <alias>
# git push origin <alias> # pushes the branch, not the alias/reference
# git branch-alias -d <alias> # delete an alias safely
# git branch-alias -h # help / usage details

# Caveats:
# Although everything else I've tried works seamlessly, I note that
# git merge <alias> will cause the alias name to be mentioned in the
# commit message, rather than the name of the real branch.  It would
# be nicer if the branch name appeared.

# Compatibility:
# Originally developed with git version 1.7.12.4
# Also tested with git versions 1.9.0, 2.5.4, 2.6.6, 2.8.3
#
# Related git changes between versions 1.7.12.4 and 2.8.3:
# git v1.8.0.1
#  * A symbolic ref refs/heads/SYM was not correctly removed with "git
#    branch -d SYM"; the command removed the ref pointed by SYM
#    instead.
#
# git v1.8.1
#  * "git symbolic-ref" learned the "-d $symref" option to delete the
#    named symbolic ref, which is more intuitive way to spell it than
#    "update-ref -d --no-deref $symref".
#
# git v2.6.5
#  * "git symbolic-ref" forgot to report a failure with its exit status.
#
#  I believe this is commit 3e4068ed90fd3c6f24303560113aae6dbb758699:
#  > symbolic-ref: propagate error code from create_symref()
#  > If create_symref() fails, git-symbolic-ref will still exit with
#  > code 0, and our caller has no idea that the command did nothing.
#  > This appears to have been broken since the beginning of time
#
#  As this affects symref creation only, the sole adverse effect here
#  would be an unintended message to the user if symref creation had
#  actually failed (but not even a misleading one, on account of our
#  reading the reference after its creation, and thus displaying an
#  error if it turned out to be invalid).
#
# git v2.8.3
#  * A change back in version 2.7 to "git branch" broke display of a
#    symbolic ref in a non-standard place in the refs/ hierarchy (we
#    expect symbolic refs to appear in refs/remotes/*/HEAD to point at
#    the primary branch the remote has, and as .git/HEAD to point at the
#    branch we locally checked out).
#
#  This caused "git branch" to display "ref -> ref" instead of "ref -> branch"
#  for branch aliases.  The functionality still works otherwise, but is not
#  nearly so convenient to work with when you cannot trivially see what each
#  alias points to.  This bug affected git versions 2.7.0 - 2.8.2 (inclusive).

# Change log:
# v1.13.1
# Change incorrect uses of git show-ref, introduced by v1.10 (including
# effective regression of v1.08), to use git symbolic-ref instead.
#
# v1.12:
# Fix the option handling for '--', and added it to the help text.
#
# v1.11:
# Minor tidy-ups.  Re-posted to git mailing list:
# https://www.mail-archive.com/git%40vger.kernel.org/msg161274.html
#
# v1.10:
# No longer dependent on refs existing as individual files, as they
# may be packed in .git/packed-refs.
#
# v1.09:
# POSIX-compatible option handling and output.
# Documented an issue with "git branch" in git versions 2.7.0 - 2.8.2.
#
# v1.08:
# Remove test git show-ref --verify --heads --quiet "refs/heads/${symref}"
# for asserting that the specified reference was valid before deleting a
# reference, as we need to permit the deletion of references to branches
# which have /already/ been deleted, and this test prevented that.
# n.b. We already had another validation test to fall back on, using
# git symbolic-ref "refs/heads/${symref}"
#
# v1.07:
# Minor tweaks.  Posted as feature-request to git mailing list:
# https://www.mail-archive.com/git%40vger.kernel.org/msg49171.html

# Mailing list archives:
# If you are reading this via the git mailing list archives at gmane.org
# then this code will probably be broken by an email obfuscation filter
# which automatically converts the symbol '@' to the string ' <at> '.
# Specifically the shell positional parameter expansion "$@" is changed
# to "$ <at> "), so don't try to use the version from gmane.  The copy
# of this message at http://www.mail-archive.com/git%40vger.kernel.org/
# should have the correct code.

command=$(basename $0)
if [ "${command##git-}" != "${command}" ]; then
    command="git ${command##git-}"
fi

# Print argument (and newline) to stdout or stderr.
stdout () {
    printf %s\\n "$1"
}
stderr () {
    printf %s\\n "$1" >&2
}

# Returns the supplied parameters suitably quoted for later evaluation.
quote () {
    for param; do
        printf %s "${param}Z" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/Z\$/' /"
    done
}

# Process option parameters.
parameters=
while [ $# -gt 0 ]; do
    case "$1" in
        ( -- ) {
            shift
            parameters="${parameters}$(quote "$@")"
            break
        };;
        ( -v | --version ) version_help=1; shift;;
        ( -h | --help    ) help=1; shift;;
        ( -d | --delete  ) delete=1; shift;;
        ( -* ) {
            stdout "Invalid option: $1"
            stdout
            shorthelp=1
            shift
        };;
        ( * ) { # non-option parameter
            parameters="${parameters}$(quote "$1")"
            shift
        };;
    esac
done

# Process non-option parameters.
eval "set -- ${parameters}"
symref=$1
branch=$2

# If too few or too many parameters were supplied, display shorthelp.
if [ -z "${symref}" ] || [ -n "$3" ]; then
    shorthelp=1
fi

# If displaying the version, exit immediately.
if [ -n "${version_help}" ]; then
    stdout "${command} version ${version}"
    exit 0
fi

# Don't let short help override long help.
if [ -n "${help}" ]; then
    shorthelp=
fi

# Include the usage summary in both short and long help.
if [ -n "${help}" ] || [ -n "${shorthelp}" ]; then
    cat <<EOF
Usage:
${command} [--] <alias> [<branch>]
${command} (-d | --delete) [--] <alias>
${command} (-v | --version)

EOF
fi

# n.b. Calling "git branch-alias --help" causes git to look for
# a man page for "git-branch-alias", so we shouldn't advertise
# the long option (although we support it if the script is called
# by its real name, rather than via git).
if [ -n "${shorthelp}" ]; then
    cat <<EOF
For help, use: ${command} -h

EOF
    exit 0
fi

# Detailed help.
if [ -n "${help}" ]; then
    cat <<EOF
Creates a symbolic reference <alias> referring to <branch>.
<branch> defaults to the current checked-out branch.

This symbolic reference acts as an alias for <branch>, and can be
used in its place.  More specifically, it WILL be dereferenced to
its target in nearly all situations, so for any given command you
should treat every usage of <alias> as if it were actually <branch>.

If either <alias> or <branch> begins with a hyphen, you can use the
'--' option to prevent subsequent arguments being treated as options.

To safely delete a branch alias, always use:
${command} -d <alias>

WARNING: These symbolic references appear in your branch list as:
 <alias> -> <branch>
and so you might be tempted to try to delete them like a branch:
 git branch -d <alias>

However this can cause problems.  In git versions prior to 1.8.0.1
<alias> will be dereferenced and you will instead delete the
branch it refers to (git will allow this even if you currently
have that branch checked out), and the symbolic reference will
still remain (referencing a branch which is no longer available).

In later versions of git the <alias> will be deleted rather than
the branch; however git will still not check to see whether you
currently have <alias> checked out, and will not prevent you
from deleting it in that situation.  This will leave your HEAD ref
in an invalid state.  Using ${command} -d <alias> resolves
this situation by first switching HEAD to <alias>'s target branch
if HEAD was currently set to <alias>.

EOF
    exit 0
fi

# Confirm the CWD is within a git repository.
#cwd=$(git rev-parse --show-toplevel)
git=$(git rev-parse --git-dir)
if [ $? -ne 0 ]; then
    exit 1
fi

# Use the current branch by default.
if [ -z "${branch}" ]; then
    branch=$(git symbolic-ref -q HEAD)
    if [ $? -ne 0 ]; then
        stderr "Could not establish current HEAD."
        exit 1
    fi
fi

# We expect plain branch names, but also accept the fully-qualified
# (refs/heads/NAME) paths needed by git symbolic-ref; so strip that
# refs/heads/ prefix if it is specified.
branch=${branch##refs/heads/}
symref=${symref##refs/heads/}

# Deleting a symref.
if [ -n "${delete}" ]; then
    # Verify that it IS a symbolic reference.
    if ! git symbolic-ref "refs/heads/${symref}" >/dev/null; then
        stderr "Error validating refs/heads/${symref} as symbolic reference."
        exit 1
    fi

    # If we currently have <symref> checked out, deleting it is bad
    # (as HEAD would no longer be a valid reference).  I believe we do
    # need to inspect the file here, as attempting to read the HEAD
    # reference via git dereferences it to its target branch, and thus
    # we are unable to distinguish between the branch and the symref.
    if grep "^ref: refs/heads/${symref}\$" "${git}/HEAD" >/dev/null 2>&1; then
        stdout "Cannot delete the currently checked out symbolic reference."
        branch=$(git symbolic-ref -q HEAD)
        if [ $? -ne 0 ]; then
            stderr "Could not establish current HEAD."
            exit 1
        fi
        stdout "Switching HEAD to target branch ${branch}"
        # By using git symbolic-ref HEAD to find the target ref
        # and setting HEAD to that target, nothing really changes,
        # but we can now delete the reference safely.
        if ! git symbolic-ref HEAD "${branch}"; then
            stderr "Error updating HEAD from ${symref} to ${branch}"
            stderr "Aborting."
            exit 1
        fi
    fi

    # Delete the reference.
    # git 1.8.1+ provides: git symbolic-ref --delete <symref>
    # but older versions do not include that option, so we use
    # the backwards-compatible command.
    stdout "Deleting symbolic reference refs/heads/${symref}"
    git update-ref -d --no-deref "refs/heads/${symref}"
    exit $?
fi

# Creating a new symbolic reference.

# Error checking.  git symbolic-ref doesn't really do any, and will
# happily mess up your branches; particularly if you get the arguments
# the wrong way around (treating it like ln -s is a really bad idea).
if ! git show-ref --verify --heads --quiet "refs/heads/${branch}"; then
    stderr "Target branch refs/heads/${branch} does not exist."
    exit 1
fi
if target=$(git symbolic-ref -q "refs/heads/${symref}"); then
    stderr "Symbolic reference refs/heads/${symref} already exists:"
    stderr "  ${symref} -> ${target##refs/heads/}"
    stderr "To delete it, use: ${command} -d ${symref}"
    exit 1
elif git show-ref --verify --heads --quiet "refs/heads/${symref}"; then
    stderr "Reference refs/heads/${symref} already exists"
    stderr "(and is not a symbolic reference!)"
    exit 1
fi

# The parameters are good.
# Generate the reference and display the confirmed result.
if git symbolic-ref "refs/heads/${symref}" "refs/heads/${branch}"; then
    target=$(git symbolic-ref "refs/heads/${symref}")
    stdout "  ${symref} -> ${target##refs/heads/}"
else
    stderr "Failed to create branch alias."
    exit 1
fi
# EOF

上游功能请求: https://www.mail-archive.com/git@vger.kernel.org/msg161274.html

该请求涉及IT技术,具体内容请参考链接。

无论我使用什么参数,这在我的OS X上的Git 2.7.0和zsh中都失败了:“目标refs/heads/-l不存在。” - iconoclast
感谢回复!针对你的问题,是的,这正是出现的错误信息:目标 refs/heads/-l 不存在。我在 bash 下进行了测试,仍然出现了相同的错误。我尝试了反转参数,但仍然出现了相同的错误。我在 zsh 中执行了 unalias git,以确保没有任何干扰,但仍然出现了相同的错误。我使用了完整的 git 路径,但仍然出现了相同的错误。我使用了 git-branch-alias 的完整路径,但仍然出现了相同的错误。 - iconoclast
1
@iconoclast:2.8.3版本现已发布。发行说明中的第6项涵盖了先前评论中讨论的错误:https://raw.githubusercontent.com/git/git/master/Documentation/RelNotes/2.8.3.txt - phils
4
这个上游功能请求有进展吗?现在许多 Git 存储库都从 master 切换到 main,这样的工具将非常有用,可以保持本地克隆一致,这样您就不必记住哪些存储库已经切换了。 - asmeurer
2
@asmeurer,我已经实现了这个功能 - 如果你在main上,git branch --alias master就可以工作,如果不是,则使用git branch --alias master main。我在2019年向邮件列表提交了一个补丁,但没有任何进展。http://public-inbox.org/git/47CA9077-E8C1-4093-8B36-2D5D3DE2D886@gmail.com/。如果有人想玩一下,它都在github上https://github.com/git/git/compare/v2.26.0...iCodeSometime:master。 - iCodeSometime
显示剩余7条评论

0

使用 git symbolic-ref <alias-branch> <targetbranch> 命令:

git symbolic-ref refs/heads/trunk refs/heads/master
git symbolic-ref refs/heads/master refs/heads/main

但 "当前分支" (git branch --show-current) 会是 "错误的" (仍然显示目标分支而不是您的别名分支):

在 Git 2.39(2022年第4季度)之前,检出一个指向另一个分支的符号引用的“分支”后,“git symbolic-ref HEAD手册报告底层分支,而不是用户作为参数传递的符号引用。

该命令学习了"--no-recurse"选项,以在解除引用symbolic-ref一次后停止。

请参见commit b77e3bd(2022年10月7日),作者为Junio C Hamano(gitster
(由Junio C Hamano -- gitster --commit 4a48c7d合并,2022年10月21日)

symbolic-ref:教授“--[no-]recurse”选项

Suppose you are managing many maintenance tracks in your project, and some of the more recent ones are maint-2.36 and maint-2.37.
Further imagine that your project recently tagged the official 2.38 release, which means you would need to start maint-2.38 track soon, by doing:

$ git checkout -b maint-2.38 v2.38.0^0
$ git branch --list 'maint-2.3[6-9]'
* maint-2.38 
  maint-2.36 
  maint-2.37  

So far, so good.
But it also is reasonable to want not to have to worry about which maintenance track is the latest, by pointing a more generic-sounding 'maint' branch at it, by doing:

$ git symbolic-ref refs/heads/maint refs/heads/maint-2.38

which would allow you to say "whichever it is, check out the latest maintenance track", by doing:

$ git checkout maint
$ git branch --show-current
maint-2.38

It is arguably better to say that we are on 'maint-2.38' rather than on 'maint', and git merge/pull would record into maint-2.38 and not into maint, so I think what we have is a good behaviour.

One thing that is slightly irritating, however, is that I do not think there is a good way (other than "cat .git/HEAD") to learn that you checked out 'maint' to get into that state.
Just like the output of "git branch --show-current"(man) shows above, "git symbolic-ref"(man) HEAD would report 'refs/heads/maint-2.38', bypassing the intermediate symbolic ref at 'refs/heads/maint' that is pointed at by HEAD.

The internal resolve_ref() API already has the necessary support for stopping after resolving a single level of a symbolic-ref, and we can expose it by adding a "--[no-]recurse" option to the command.

git symbolic-ref现在在其手册页面中包含:

 'git symbolic-ref' [-q] [--short] [--no-recurse] <name>

--recurse

--no-recurse

When showing the value of as a symbolic ref, if refers to another symbolic ref, follow such a chain of symbolic refs until the result no longer points at a symbolic ref (--recurse, which is the default). --no-recurse stops after dereferencing only a single level of symbolic ref.


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接