Automate "git merge" commit message [duplicate] - bash

This question already has answers here:
Stop git merge from opening text editor
(3 answers)
Closed last year.
I constantly need to update my remote branch with current master. I am trying to automate this as;
#!/usr/bin/env bash
cd directory
git checkout master
git pull
git checkout <remote_branch>
git pull origin <remote_branch>
git merge master
git push
but when I merge master I am thrown into my editor where I need to :wq to be able to push. How can I replicate this :wq in my Bash script?

There are 3 ways listed below to do this and you don’t have to replicate :wqto do so.
1 - Using --no-edit
using the --no-edit option, the auto-generated message will be accepted (this is generally discouraged, see the git docs)
So after you git pull origin <remote_branch>, you can use
git merge master --no-edit
2 - Using environement variable
Older scripts may depend on the historical behaviour of not allowing the user to edit the merge log message. They will see an editor opened when they run git merge. To make it easier to adjust such scripts to the updated behaviour, the environment variable GIT_MERGE_AUTOEDIT can be set to no at the beginning of them. — git docs
Use
GIT_MERGE_AUTOEDIT=no
before the merge line
3 - Use -m to provide the message
Set the commit message to be used for the merge commit (in case one is created).
If --log is specified, a shortlog of the commits being merged will be appended to the specified message.
The git fmt-merge-msg command can be used to give a good default for automated git merge invocations. The automated message can include the branch description. — git docs
git merge master -m "Merge master"
Read more: the git merge command

Related

Why does Git prints an incorrect diff at the bottom of a commit message?

I use a pre-commit hook to run Prettier formatter against my HTML documents:
#!/bin/sh
# Retrieving a list of the staged files
stagedFiles=$(git diff --staged --name-only)
# Format staged files automatically
echo "Formatting with Prettier..."
npx prettier --write $stagedFiles
git add $stagedFiles
The formatting successfully applies right after I enter git commit command and right before my configured editor opens the commit message. According to commit.verbose setting, Git appends there a diff showing what is to be committed, but this diff does not take into account changes made by Prettier — it demonstrates the unformatted version of my code. If I then abort the commit (leaving the message empty), I can see that no unstaged changes are in the repository; it means that all formatting modifications had been properly staged during execution of the pre-commit hook. Moreover, if I write some text to the message and complete the commit without aborting, the resulting snapshot does contain all formatting that was missing in the diff.
I finally concluded that the diff lies. But does a solution exist?
Git for Windows version: 2.33.0
OS: Windows 10 21h1 x64
The short answer is : the pre-commit hook is not intended to modify files to be committed.
You can relate to the following rationale : the content you commit will be the exact content you could review and test before running git commit.
(the technical reason is : git creates the tree -- the content for the commit -- that will be used before calling the pre-commit hook)
A pre-commit hook should be written as a read-only action, which may prevent the commit from happening, e.g : if a file is not formatted correctly, reject the commit with a message "please run format.sh before committing".
Of course, you could work your way around that, but, FWIW, I advise you to follow this rule.

Git: How to update the master branch and switch to it in one step?

I'm working on a project that's hosted on GitLab and uses issue/work branches and merge requests to bring that work into the master branch when it's done. Usually I work on issue branches. When it has been merged by GitLab, I need to switch to the current master to do a build, locally.
My workflow is this:
Switch to master
Pull from remote (--ff-only)
Remove stale remote tracking branches
Also remove their local tracking branches
There's also a client-side tool that watches the code directory and updates some files (CSS, JavaScript). When it sees a change in the first step (switch to master), I first need to wait for it to finish before going on (to avoid confusion). If there's a difference between the issue branch and the old master, there's a good chance that the difference will disappear when updating master (as that issue branch is now merged).
I'm looking for a way to switch to the already-updated master branch in one step. How can I do that with a git command? I want to bundle up all these actions in a batch file to avoid repeating all those manual steps in TortoiseGit every time.
This question is different from the suggested one in that the local master branch already exists. I'm not switching to a new branch from a remote, but to a branch that already exists and it just behind the remote.
TL;DR
Unless you write your own script (or use a Git alias to run multiple commands and/or scripts), you can't get this down to a single command, but you can get closer. See the long section for many caveats: the biggest one is that it assumes you're not already on master when you do it. If you are, the second step won't work (see the long section for what will).
git fetch -p &&
git fetch . refs/remotes/origin/master:refs/heads/master &&
git checkout master
will take care of the first three bullet points—not in the same order—with a single work-tree-updating git checkout step.
(Note that I split this into three lines for posting purposes, but as a Git alias using !, it's really all one big line.)
Long
There are several approaches, including actual, literal batch files (shell scripts on Unix-like systems, or .BAT files, or whatever) and aliases (as suggested by Joe in a comment).
There's also a client-side tool that watches the code directory and updates some files ...
This is ... not necessarily a good idea, let's say. :-)
While git checkout master runs, it's changing various files. Let's say that for some reason, it changes one of several files that the watcher watches, but then it pauses for a few minutes (or seconds, or microseconds, or some unit of time anyway). While it is paused, the watcher tries to combine the multiple files that are now out of sync.
Maybe this is OK and self-correcting when Git un-pauses and finishes the checkout—but it might be better if you could make sure the update only happens when the checkout is done.
That aside, let's take a look at this particular series of commands, and be very concrete about which Git command you're using:
Switch to master
I assume this is git checkout master.
Pull from remote (--ff-only)
I assume this is git pull origin master --ff-only or perhaps just git pull --ff-only.
Remove stale remote tracking branches
I'll assume for now that this is git fetch --prune. If you are doing something different, you should include that in your question.
Also remove their local tracking branches
If I understand what you mean, this requires a script. Note that this is somewhat dangerous: suppose you have your own branch X on which you are doing development. This X is not related to anyone else's X. Then someone creates their own X—using the same name—and sends it to the machine from which you git fetch. You now have origin/X. Then they delete their X (because they're done with it) and delete origin/X. If you now have your script delete your X, because origin/X went away, that would probably be bad.
If you only delete your X when it explicitly has origin/X set as its upstream, this particular case won't occur—but if someone accidentally deletes your origin/X thinking it was their origin/X, the same problem crops up again, and this time that particular protection does not work.
Anyway, with all that aside, let's look at the variant I suggested above.
git fetch -p
This updates all your origin/* names,1 including origin/master, without affecting any files in your working tree. The -p is short for --prune, so it deletes any origin/* names that no longer have a corresponding branch in the Git over at the URL stored under the name origin.
1I assume here that you have only one remote, which is named origin. If you have more than one remote, use git fetch origin -p to make sure you're fetching specifically from the one named origin. I also assume you have not configured your Git to be a single-branch clone.
git fetch . refs/remotes/origin/master:refs/heads/master
This rather magic-looking command tells your Git to call itself up. That is, the special name . refers to your own Git repository. We are using this to trick your Git into fast-forwarding your master branch based on your updated origin/master. The final argument is what does this: we say to your Git: OK, my Git, when you talk to that other Git, find out what commit its refs/remotes/origin/master identifies. Then, if that's a fast-forward operation, update my refs/heads/master to match.
Of course, the "other Git" your Git is talking to is itself—so this means fast-forward my master from my origin/master.2 It's roughly equivalent to:
git checkout master && git merge --ff-only origin/master && git checkout -
except that no actual checking-out occurs: no files in your work-tree change.
2You might wonder why some of these use origin/master and some use refs/remotes/origin/master. The longer one is just the full spelling of the name. When using git fetch, it's wise to use the full spellings. In fact, in general, in scripts, you might want to use full spellings more often, but specifically git fetch can become confused if the other Git you talk to accidentally has both a branch and a tag with the same name, for instance. So I'm illustrating the full names with git fetch. You'll use it to talk to your own Git, so if you don't mix up your tags and branch names or otherwise create ambiguity, you won't actually need the full names. But it's a good habit with git fetch.
The above fails if you're on your master
The git fetch command will refuse to fetch into whatever branch name you have checked out. So if you are on master, this git fetch . trick will fail.
In a way, this is OK! If you are on your master, what you should do instead is run:
git merge --ff-only origin/master
or anything equivalent. This is what your git pull --ff-only does: first it runs git fetch (without the -p and limited to fetching only the other Git's master); then it runs git merge --ff-only.
A more complete version
A more complete version of this sequence, then, is to first check: Which branch am I on? To do that, you can use either of two Git commands:
git rev-parse --abbrev-ref HEAD
or:
git symbolic-ref --short HEAD
Both of these will print master if you are currently on your own master branch. The difference between them is what they do if you're on no-branch-at-all: e.g., in the middle of a rebase, when you are in "detached HEAD" state. In that case, the second command—the git symbolic-ref one—errors out, while the first one just prints HEAD.
If you'd like to avoid doing any of this when in such a state, use the second command and check for failure. Otherwise, use the first one. I'll illustrate just the first one here:
if test $(git rev-parse --abbrev-rev HEAD) = master; then
# already on master - use alternative strategy
git fetch -p && git merge --ff-only refs/remotes/origin/master
else
# not currently on master: use fancy tricks to update
git fetch -p &&
git fetch . refs/remotes/origin/master:refs/heads/master &&
git checkout master
fi
The above, while untested, should be suitable as a shell script. If you have Git installed, you have the ability to run shell scripts—or you can turn the above into a very long Git alias, using ! and the appropriate set of semicolons.

git pull -X theirs requires MERGE_MSG editing in ubuntu but not in windows

I have a bash script running on ubuntu and on windows:
git add -A
git commit -a -m "auto l"
git pull -s recursive -X theirs
git push origin
In ubuntu the second to last line causes nano editor to pop up requesting naming or editing of MERGE_MSG. Windows no such problem. EDIT WINDOWS TOO Rerunning the script 2 more times solves problem but that seems sloppy. Exists another -m 'auto' I must add during pull or what?
Check if (on Windows) you do have a pull.rebase setting set to true (as I recommend since Git 2.9)
git config pull.rebase
git config rebase.autoStash
That would means Git on Windows would rebase your local commits on top of origin/yourBranch, instead of trying to create a merge commit between origin/yourBranch and yourBranch.
But if you don't, and if that must create a merge commit, then, as noted by the OP, git pull --no-edit can can be used to accept the auto-generated message (this is generally discouraged, but here useful).

Windows custom git commands

Say I want a new git command, git new, that makes a new branch that is up to date with origin/master.
Is there a way I can make this script and have it available in all repositories on Windows from powershell?
edit: To clarify I want a git script not a powershell function. The only reason I mentioned powershell is because I don't use git bash.
Create a batch file that contains the following commands:
git branch %1 origin/master
git checkout %1
Save it, let's say, as C:\Scripts\new-branch.cmd. (I never worked with PowerShell, I don't know its rules. However, it should work as well using the old Windows Command Prompt).
Test the batch file works as expected by running:
C:\Scripts\new-branch.cmd test1
It should output something along these lines:
Branch test1 set up to track remote branch master from origin by rebasing.
Switched to branch 'test1'
Your branch is up-to-date with 'origin/master'.
If you don't need the new branch to track the remote branch then you just add --no-track to the git branch command.
If everything goes well then run:
git config --global alias.new "!C:/Scripts/new-branch.cmd"
This makes the Git alias new available to your Windows profile in all repositories. If you need it only in one repository then remove --global and run the command when the current directory is in the repository where you need it.
Use it as:
git new test2
You can use a git alias which uses git checkout:
git config --global alias.new 'checkout origin/master -b'
This would then be used as git new new_branch.
(Which is equivolent to git checkout origin/master -b new_branch
See the git docs for checkout. I tried the command and it worked, but when I looked at the docs, I didn't find a syntax that exactly matched my form. (Closest is git checkout [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>])
Note: #axiac, I may have used the !git because it doesn't hurt, and I may have needed to do multiple commands to solve the problem, and didn't remove it when I was done.

Is Git checkout without merging a common file possible?

My goal involves having a file with the same name but different implementations in different branches. For example, I want to develop in a branch with verbose mode and another that works silently. Or, one branch uses a list, but the other uses a hash. Similar to prior question.
In my case, the changes are in a file with the same name. Unfortunately, checkout from one branch to the other merges the files of the same name (content?). In that case, the release version inherits the verbose print statements I had hoped to keep separate.
I learned and succeeded in using stash save; checkout; (edit other branch, add, commit); checkout back; and stash apply (to erase merge changes caused by checkout). It works, but the manual's examples (interrupted workflow, partial commits) suggest this is not the intended workflow. Creating an orphan branch for verbose destroys the history. Is there another way to switch between branches without carrying unintended changes to files with the same name?
Update I can't replicate the behavior any longer, despite seeing it five times before submitting here. It used to show the text below. But, I guess this question should be closed.
$ git checkout master
M Test.java
Switched to branch 'master'
I think the following command is what you are looking for:
git update-index --assume-unchanged <file>
To undo run:
git update-index --no-assume-unchanged <file>
From ""Difference Between 'assume-unchanged' and 'skip-worktree'", I would go with:
git update-index --skip-worktree -- a file
git update-index --no-skip-worktree -- a file
skip-worktree is useful when you instruct git not to touch a specific file ever.
That is handy for an already tracked config file.

Resources