How to create a GitLab merge request via command line - gitlab

We are working on integrating GitLab (enterprise edition) in our tooling, but one thing that is still on our wishlist is to create a merge request in GitLab via a command line (or batchfile or similar, for that matter). We would like to integrate this in our tooling. Searching here and on the web lead me to believe that this is not possible with native GitLab, but that we need additional tooling for that.
Am I correct? And what kind of tooling would I want to use for this?

As of GitLab 11.10, if you're using git 2.10 or newer, you can automatically create a merge request from the command line like this:
git push -o merge_request.create
More information can be found in the docs.

It's not natively supported, but it's not hard to throw together. The gitlab API has support for opening MR: https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/merge_requests.md#create-mr

You can use following utility.
Disclosure : I developed it.
https://github.com/vishwanatharondekar/gitlab-cli
You can create merge request using this.
Some of the features it has are.
Base branch is optional. If base branch is not provided. Current branch is used as base branch.
target branch is optional. If target branch is not provided, default branch of the repo in gitlab will be used.
Created pull request page will be opened automatically after successful creation.
If title is not supported with -m option value. It will be taken from in place editor opened. First line is taken as title.
In the editor opened third line onwards takes as description.
Comma separated list of labels can be provided with its option.
Supports CI.
Repository specific configs can be given.
squash option is available.
remove source branch option is available.

If you push your branch before this command (git push -o merge_request.create) it will not work. Git will response with Everything up-to-date and merge request will not be created (gitlab 12.3).
When I tried to remove my branch from a server (do not remove your local branch!!!) then it worked for me in this form.
git push --set-upstream origin your-branch-name -o merge_request.create

In addition to answering of #AhmadSherif, You can use merge_request.target=<branch_name> for declaring the target branch.
sample usage:
git push -o merge_request.create -o merge_request.target=develop origin feature
Simple This:
According to the Gitlab documents, you can define an alias for this command to simpler usage.
git config --global alias.mwps "push -o merge_request.create -o
merge_request.target=master -o merge_request.merge_when_pipeline_succeeds"

I made a shell function which opens up the GitLab MR web page with desired parameters.
Based on the directory with the git repo you are currently in, it:
Finds the correct URL to your repo.
Sets the source branch to the branch you're currently on.
As a optional first argument you can provide the target branch. Otherwise, GitLab defaults to your default branch, which is typically master.
gmr() {
# A quick way to open a GitLab merge request URL for the current git branch
# you're on. The optional first argument is the target branch.
repo_path=$(git remote get-url origin --push | sed 's/^.*://g' | sed 's/.git$//g')
current_branch=$(git rev-parse --abbrev-ref HEAD)
if [[ -n $1 ]]; then
target_branch="&merge_request[target_branch]=$1"
else
target_branch=""
fi
xdg-open "https://gitlab.com/$repo_path/merge_requests/new?merge_request[source_branch]=$current_branch$target_branch"
}
You can set more default values in the URL, like removing the source branch after merge:
&merge_request[force_remove_source_branch]=true
Or assignee to someone:
&merge_request[assignee_ids][]=12345
Or add a reviewer:
&merge_request[reviewer_ids][]=54321
You can easily find the possible query string parameters by searching the source of the GitLab MR webpage for merge_request[.

As of now, GitLab sadly does not support this, however I recently saw it on their issue tracker. It appears one can expect a 'native tool' in the upcoming months.
GitLab tweeted out about numa08/git-gitlab some time ago, so I guess this would be worth a try.

In our build script we just pop up the browser with the correct URL and let the developer write his comments in the form hit save to create the merge request. You get this url with the correct parameters by creating a merge request manually and copying the url of the form.
#!/bin/bash
set -e
set -o pipefail
BRANCH=${2}
....
git push -f origin-gitlab $BRANCH
open "https://gitlab.com/**username**/**project-name**/merge_requests/new?merge_request%5Bsource_branch%5D=$BRANCH&merge_request%5Bsource_project_id%5D=99999&merge_request%5Btarget_branch%5D=master&merge_request%5Btarget_project_id%5D=99999"

You can write a local git alias to open a Gitlab Merge Request creation page in the default browser for the currently checked-out branch.
[alias]
lab = "!start https://gitlab.com/path/to/repo/-/merge_requests/new?merge_request%5Bsource_branch%5D=\"$(git rev-parse --abbrev-ref HEAD)\""
(this is a very simple alias for windows; I guess there are equivalent replacements for "start" on linux and fancier aliases that work with github and bitbucket too)
As well as being able to immediately see&modify the details of the MR, the advantage of this over using the merge_request.create push option is that you don't need your local branch to be behind the remote for it to work.
You might additionally want to store the alias in the repo itself.

I use https://github.com/mdsb100/cli-gitlab
I am creating the MR from inside of a gitlab CI docker container based on alpine linux, so I include the install command in before-script (that could also be included in your image). All commands in the following .gitlab-ci.yml file, are also relevant for normal command line usage (as long as you have the cli-gitlab npm installed).
variables:
TARGET_BRANCH: 'live'
GITLAB_URL: 'https://your.gitlab.net'
GITLAB_TOKEN: $PRIVATE_TOKEN #created in user profile & added in project settings
before-script:
-apk update && apk add nodejs && npm install cli-gitlab -g
script:
- gitlab url $GITLAB_URL && gitlab token $GITLAB_TOKEN
- 'echo "gitlab addMergeRequest $CI_PROJECT_ID $CI_COMMIT_REF_NAME \"$TARGET_BRANCH\" 13 `date +%Y%m%d%H%M%S`"'
- 'gitlab addMergeRequest $CI_PROJECT_ID $CI_COMMIT_REF_NAME "$TARGET_BRANCH" 13 `date +%Y%m%d%H%M%S` 2> ./mr.json'
- cat ./mr.json
This will echo true if the merge request already exists, and echo the json result of the new MR if it succeeds to create one (also saving to a mr.json file).

Since GitLab 15.7 (Dec. 2022), the GitLab CLI glab is officially integrated to GitLab.
Introducing the GitLab CLI
The command line is one of the most important tools in a software engineer’s toolkit and the majority of their process and work revolve around tools available there. They customize their CLI with styles and extend it through applications to ensure maximum efficiency while performing tasks. The CLI is the backbone of scripts and workflows developers depend on to complete their work.
To support more developers where they’re already working, we’ve adopted the open source project glab, which will form the foundation of GitLab’s native CLI experience.
The GitLab CLI brings GitLab together with Git and your code, with no application or tab switching required.
You can read about our adoption of glab, our partnership with 1Password, and how to contribute to the project in our blog post.
A special thank you to Clement Sam for creating glab and trusting us with its future.
That means you can create a MR with glab mr create:
glab mr create -a username -t "fix annoying bug"

Related

Git push send parallel copy to test server

I search the method to copy files to "test" host when I launch the "git push" command.
git push ------- TO REPO --> REPO_SERVER
\
\_________ TO DIR --> Host_TEST
Git Version: 2.20.1
It sounds like you are trying to (re) invent CI/CD.
If you are using GitHub or GitLab as a remote server you can use Pipelines (or Actions in GitHub).
In there you can define (almost) anything you want to happen after a push, I am assuming your Host_TEST is accessible online.
In case you are running your own git server
You can implement "push to deploy" using the post-receive hook. Hooks are scripts that are placed inside .git/hooks and executed at a precise phase of a git command. you can find several example implementations in .git/hook. See here for more information: Setting up Push-to-Deploy with git
In case you don't have access to your own git server
You can use the pre-push script on your local machine, BUT THIS IS A BAD IDEA
This hooks is executed after you execute git push but before git actually pushes anything. If your script fails (i.e non-zero return code) it will not push.
Also, if your script manages to copy but then git fails to push you will end up testing code that's not in your repo.
If all this sound way too complicated
You can create a bash function that does both operations and add it to your .bashrc.
Here is an example:
push_copy() {
if git push
then
# Copy for command here: scp ...
else
echo "Failed..."
fi
}

Gitlab CI/CD cannot git push from .gitlab-ci.yml

I'm writing GitLab CI/CD pipeline script in .gitlab-ci.yml
I want to check if a specific file changed in another repo and if so I would like to copy the file, commit and push to the current repo.
everything works until I get to the 'git push' part
I tried several ways to fixed it:
stages:
- build
build:
stage: build
script:
- echo "Building"
- git checkout -b try
- git remote add -f b https://gitlab-ci-token:${CI_JOB_TOKEN}#gitlab.{otherRepo}.git
- git remote update
- CHANGED=$(git diff try:mobile_map.conf b/master:mobile_map.conf)
- if [ -n "${CHANGED}" ]; then
echo 'changed';
FILE=$(git show b/master:mobile_map.conf > mobile_map.conf);
git add mobile_map.conf;
git commit -m "updating conf file";
git push;
else
echo 'not changed';
fi
- git remote rm b
for this code I get :
fatal: unable to access 'https://gitlab-ci-token:[MASKED]#gitlab.{curr_repo}.git/': The requested URL returned error: 403
also I tried to add this line in the beginning :
git remote set-url origin 'https://{MY_USER_NAME}:"\"${PASSWORD}\""#gitlab.{curr_repo}.git'
and I get this error message:
fatal: Authentication failed for 'https://{MY_USER_NAME}:"\"${PASSWORD}\""#{curr_repo}.git/'
also I added:
- git config --global user.name {MY_USER_NAME}
- git config --global user.email {MY_EMAIL}
please help me,
Thanks
Job-tokens only have read-permission to your repository.
A unique job token is generated for each job and provides the user read access all projects that would be normally accessible to the user creating that job. The unique job token does not have any write permissions, but there is a proposal to add support.
You can't use deploy-tokens because they can't have write-access to a repository (possible tokens).
You could use a project-access-token with read-write-access to your repository.
You can use project access tokens:
On GitLab SaaS if you have the Premium license tier or higher. Project
access tokens are not available with a trial license.
On self-managed instances of GitLab, with any license tier. If you
have the Free tier: [...]
Then you can use your project-access-token as an environment variable in the url.
git push "https://gitlab-ci-token:$PROJECT_ACCESS_TOKEN#$CI_SERVER_HOST/$CI_PROJECT_PATH.git"
At least that's how we use it in our pipelines.
I hope this helps you further.
Complete example of simple GitLab CI stage, that commits back to its own repo
ci_section_name:
# N.B. This stage produces an extra commit to the repo!
stage: stage_name
script:
- apt-get update && apt-get install -y git
- echo "hello" >> file_to_be_modified.txt # your real job may do smth else
after_script:
- git config user.name "Name On Your Choice"
- git config user.email "email_on_your_choice#$CI_SERVER_HOST"
- git pull "https://project_access_token_name:$PROJECT_VARIABLE_WITH_ACCESS_TOKEN_VALUE#$CI_SERVER_HOST/$CI_PROJECT_PATH.git" $CI_COMMIT_BRANCH --rebase --autostash
- git commit -a -m "Message on your choice"
- git push "https://project_access_token_name:$PROJECT_VARIABLE_WITH_ACCESS_TOKEN_VALUE#$CI_SERVER_HOST/$CI_PROJECT_PATH.git" HEAD:$CI_COMMIT_BRANCH
I want to stop on few important aspects:
file_to_be_modified.txt - any modified files in the example are supposed to already exist in the repo, if you need add smth new, you will need
include at least git add command
project_access_token_name - is the name of used Project Access Token, not the token value itself (see screenshots below); you should create it by hands for your GitLab project if necessary
PROJECT_VARIABLE_WITH_ACCESS_TOKEN_VALUE - is the name of Project Variable (see screenshots below), you should create this variable by hands for your GitLab project and populate it with value of chosen Project Access Token; btw, you can add literal value of the token into stage code instead, but it is probably bad for security reasons
--rebase --autostash - note that autostashing your changes instead of simply trying to pull-push them will help you to guarantee the push in case of conflicts (even our new GitLab stage may conflict between its launches) but files would be left with conflict trace instead of correct content; because it is hard to resolve conflicts automatically, it is supposed that you control such situation with another tools (for example, further build on conflict state will simply fail)
This simple example will lead to an infinite flow of commits, probably real stage should contain some only conditions, etc.
Screenshots
GitLab Project Access Tokens page
GitLab Project CI/CD Settings menu, where to find Variables settings
Add project Variable pop-up

Push gitlab repository code to Google source repository

I followed below article to push gitlab repository code to Google cloud source repository but I'm getting an error on this command
git push -f google master
error: src refspec master does not match any.
error: failed to push some refs to 'https://source.developers.google.com/p/project/r/test/'
Article followed:
https://medium.com/#bamnet/cloud-source-repositories-gitlab-2fdcf1a8e50c
Is there anything , I'm doing wrong 😜? Any thoughts as to how I can avoid this error message?
src refspec master does not match any
The issue is the date of the article you are following: Aug. 2018.
GitLab Runner has changed since then, more precisely in May 2019.
The problem is described in this thread from May 2019:
Since we are using refspec to clone/fetch the repository, we checkout a specific commit and not checking out a specific branch.
When the script does git push master, the branch is nowhere to be found so git doesn’t know what to push.
That was because of, on GitLab side, MR 1203:
Basically, GitLab CE/EE sends refspecs parameter to GitLab Runner gitlab-org/gitlab-foss app/presenters/ci/build_runner_presenter.rb: this parameter is to used in GitLab Runners for fetching branch/tag refs from remote repository.
This change was introduced because we wanted GitLab Rails side to leverage respecs in order for issue 7380 "Combined ref pipelines (source+target branch)" though, there should not be a big difference between git clone $URL or mkdir $REPO_DIR && git remote add origin $URL && git fetch +refs/heads/branch_name:refs/remotes/origin/branch_name.
In fact, the new behavior has already run on our development project
https://gitlab.com/gitlab-org/gitlab-ce/pipelines and has no issues so far.
Issue 4097 was opened at the time
Workaround
Use HEAD when you want to push this to another remote.
deploy:
stage: deploy
script:
- git remote add heroku https://heroku:$HEROKU_API_KEY#git.heroku.com/<project>.git
- git push -f heroku HEAD:master
So don't push master. Push HEAD.
The OP Adam uses another workaround and add:
before_script:
- git checkout master

Remote trigger for (re)build CI Gitlab

I'm trying to use a remote trigger for (re)building in ci.gitlab. For explaining this, I made up this scenario:
2 repository, "lib" and "app1"
app1 will successfully build only if lib is included (solved simply by .gitlab-ci.yml)
I need to trigger the build of app1 (only for the master branch, in best-case) on commit (or merge request) of lib
I tried to figure it out using web hooks, but I wasn't able to find a url for ci.gitlab.com. Is this possible in a gitlab environment?
You can do this with newly added triggers functionality.
In your CI's project, find the section "Triggers". Add a trigger and use its token like this:
curl -X POST \
-F token=TOKEN \
https://ci.gitlab.com/api/v1/projects/{project_id}/refs/REF_NAME/trigger
(https://about.gitlab.com/2015/08/22/gitlab-7-14-released/)
Obsolete:
we have the same problem, and the way we solved it is by pushing and subsequently deleting a tag.
The assumption is that you manage the machine with Gitlab-CI runner. First, clone the main repository, app1 for you. And in lib's .gitlab-ci.yml add the steps:
- cd /path/to/app1_repository
- git pull
- git tag ci-trigger master
- git push origin ci-trigger
- git push --delete origin ci-trigger
- git tag -d ci-trigger
Make sure that you have the option Tag push events checked in your Gitlab Services settings for Gitlab-CI.
This solution has drawbacks:
Gitlab-CI runner must have write permissions to the repository, so it won't work for shared runners
git history will be bloated with all this tagging (especially Gitlab UI)
I opened an issue for this (https://gitlab.com/gitlab-org/gitlab-ci/issues/223) so let's hope they add this functionality to the API (http://doc.gitlab.com/ci/api/README.html).

gitolite_admin hooks and mirroring

I'm wondering is there a simple way to install hooks for certain repo using gitolite_admin.
Let's imagine i want to have post-update hook for repo awesome using gitolite_admin repo cloned to my workstation...
#conf/gitolite_conf
repo awesome
RW+ = deployer
contents of post-update:
#!/bin/sh
echo "Post receive-hook => updating Redmine repository"
sudo -u deployer perl -we '`cd /home/deployer/repo/awesome.git && git fetch -q --all`'
You could also look at "repo-specific environment variables"
A special form of the option syntax can be used to set repo-specific environment variables that are visible to gitolite triggers and any git hooks you may install.
For example, let's say you installed a post-update hook that initiates a CI job. By default, of course, this hook will be active for all gitolite-managed repos. However, you only want it to run for some specific repos, say r1, r2, and r4.
To do that, first add this to the gitolite.conf:
repo r1 r2 r4
option ENV.CI = 1
This creates an environment variable called GL_OPTION_CI with the value 1, before any trigger or hook is invoked.
Note: option names must start with ENV., followed by a sequence of characters composed of alphas, numbers, and the underscore character.
Now the hook running the CI job can easily decide what to do:
# exit if $GL_OPTION_CI is not set
[ -z $GL_OPTION_CI ] && exit
... rest of CI job code as before ...
Of course you can also do the opposite; i.e. decide that the listed repos should not run the CI job but all other repos should:
repo #all
option ENV.CI = 1
repo r1 r2 r4
option ENV.CI = ""
That feature is fairly recent (started in commit 999f9cd39, but in this case, completed in commit 63865a16 June 2013 for 3.5.2).
But even you don't have that version, there are other ways to do this using option variables, as the last part of that section explains.
Before this feature was added, you could still do this, by using the gitolite git-config command inside the hook code to test for options and configs set for the repo, like:
if gitolite git-config -q reponame gitolite-options.option-name
then
...
And you can use git config variables in the same way.
Or you can use group membership -- see the comments against function "in_group" in "Easy.pm" for details.
# in_group()
# return true if $ENV{GL_USER} is set and is in the given group
# shell equivalent
# if gitolite list-memberships $GL_USER | grep -x $GROUPNAME >/dev/null; then ...
In addition to sitaram's answer, the recent (August 29th, 2013) commit 62fb31755a formerly introduce repo specific hooks:
it's basically just creating a symlink in <repo.git>/hooks pointing to some file inside $rc{LOCAL_CODE}/hooks/repo-specific (except the gitolite-admin repo)
You cannot specific a hook for gitolite-admin though.
And you hook is only one of the three following authorized hooks:
pre-receive
post-receive
post-update
That means you can:
store your repo specific hooks in your gitolite-admin/hooks/repo-specific/xx
declare those your in the gitolite-admin local options on the server.
First enable those hooks:
ENABLE => [
# allow repo-specific hooks to be added
# 'repo-specific-hooks',
Then declare the hooks on the server gitolite-admin repo:
gitolite git-config gitolite-options.hook=reponame hookname scriptname
(with a tab or \t between reponame hookname scriptname)
Original answer:
As mention in the gitolite man page on hooks
if you want to install a hook in only a few specific repositories, do it directly on the server.
(otherwise, you would be managing hooks for all git repos through gitolite-admin/common/hooks)
That being said, you could take advantage of VREF in gitolite V3.x, which are update hooks: those can be set for some repos and for some user, like any other rule.
You could then:
make your VREF script leave a 'flag' (a file) in the appropriate bare git repo being updated
make a common 'deploy' post-update hook, which would first look for that flag, and if found, deploy the repo (and remove the flag).
Again:
a post-update hook managed through gitolite-admin can only be common to all git repos (not what you want)
only VREFs can be associated to repos and users through gitolite.conf
The solution above tries to take those two facts into account to achieve what you are looking for: a deploy script running only for certain repos and managed through the gitolite.conf config file of the gitolite-admin repo.
Here are step by step instructions to complement #VonC's answer
gitolite hook for specific repository

Resources