Git快速上手指南

I recently set up an intranet server in our company, and we will use Git for version control in future projects. Here are some commonly used operations for reference.

Install Git

Windows: You can download the Git installer/portable version from the official website. The difference between the installer and the portable version is that the portable version requires you to manually add GitBinaryPath/bin to the system’s Path.

Linux (debian): sudo apt-get install git

Initial Setup

After installation, start git.exe (Windows) or enter in the terminal on Linux:

1
2
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"

Generate SSH Key (replace the email address with your own):

1
$ ssh-keygen -t rsa -C "youremail@example.com"

Then press Enter multiple times (or set your own password). After completion, a .ssh directory will be created under C:\Users\${userName}\ (in Linux, it will be under ~/), containing two files: id_rsa and id_rsa.pub.

id_rsa is the private key and should not be disclosed, while id_rsa.pub is the public key, which you can safely share with anyone.

Next, add your SSH Key to your account on the remote Git platform (GitHub, GitLab).

This is necessary because GitHub needs to verify that the commits you push are indeed from you and not someone impersonating you. Git supports the SSH protocol, so once GitHub knows your public key, it can confirm that only you can push.

Of course, GitHub allows you to add multiple keys. Suppose you have several computers, and you submit from the company sometimes and from home at other times. Just add the key from each computer to GitHub, and you can push from each computer.

Create Local Repository

Start Git Bash and then cd (or use Easy Context Menu to add GitBash to the system’s right-click menu) to the directory where you want to create the local repository and execute the following command:

1
2
// Initialize a directory
$ git init

Local Commit Files

1
2
3
$ git add ${file_name}
// commit
$ git commit -m "commit message"

Add Remote Repository

1
2
3
4
5
// Add a remote repository (you can customize ${repo_name})
$ git remote add ${remote_repo_name} git@192.168.9.3:ayconanw/test-ue4.git

// example
$ git remote add origin git@192.168.9.3:ayconanw/test-ue4.git

Remove Remote Repository

1
2
3
4
5
// If you want to remove the remote repository associated with the local repository, you can execute the command below
$ git remote rm ${remote_repo_name}

// example
$ git remote rm origin

Push to Remote Repository

1
2
3
4
// ${branch} defaults to master (main branch) but can be modified (not recommended). -f forces submission while ignoring conflicts.
$ git push ${remote_repo_name} ${branch}
// Pull updates from the remote repository, parameters same as above
$ git pull ${remote_repo_name} ${branch}

Push to Multiple Remote Repositories

There are two ways to push to two or more remote repositories at once.
The first method is to use Git’s set-url --add command:

1
2
3
4
# Add the first remote repository
git remote add blog git@github.com:imzlp/blog-source.git
# Add the second remote repository; repeat the addition with the same remote repository name, using the set-url --add parameter
git remote set-url --add blog git@git.coding.net:visoinsmile/blog.git

The second method involves directly editing the .git/config file to add multiple remote repository addresses under the same remote name:

1
2
3
4
5
6
7
8
$ nano .git/config

# Edit .git/config, adding multiple repository addresses
[remote "blog"]
url = git@github.com:imzlp/blog-source.git
url = git@git.coding.net:visoinsmile/blog.git

$ git push blog

Create New Branch

1
2
3
4
5
6
7
8
9
10
11
// -b means base, which creates a new branch based on the current branch 
$ git branch ${new_branch_name} -b
// Switch branches
$ git checkout ${new_branch_name}

// You can also use `git checkout` with the -b parameter to create and switch
$ git checkout -b ${new_branch_name}

// Equivalent to the following two commands:
$ git branch ${new_branch_name}
$ git checkout ${new_branch_name}

checkout has three uses: switching branches, discarding changes, and checking out from historical versions; these will be discussed later.

1
2
3
4
5
# Create a local branch based on the remote origin/level
$ git checkout -b level origin/level
# The above command is equivalent to the two below
$ git checkout -b origin/level
$ git branch -m level

Push Current Local Branch to Remote Branch

1
2
// If the ${new_branch_name} branch does not exist remotely, it will be created automatically
$ git push ${remote_repo_name} ${new_branch_name}

Merge Branches

1
2
3
4
5
// First, switch back to the branch to merge into, such as merging the dev branch into the master branch
$ git branch master
// Merge the dev branch into the current branch
$ git merge dev
// After the merge is complete, you can delete the dev branch

Cancel Merge

If there are conflicts between the local and remote branches (or local branches), you can cancel the merge to revert the local branch to its prior state.

1
$ git merge --abort

Resolve Conflicts

In Git, if there’s a conflict in text files, you can directly see the diffs during the pull, but if Git is managing binary files, it’s not as straightforward.

If you want to revert a local commit ${123456}, you can use a reset to an earlier version, and then use:

1
2
3
4
5
$ git reset --hard 123455
# Check out the files from commit 123456 into the commit 123455
# Equivalent to making changes from 123456 based on 123455
$ git checkout 123456 .
# Then you can reset or checkout files you don't want to commit

Binary File Conflicts

In Git, you can see merges in text files, but binary file merges can be troublesome.

If we want to keep the remote version of git.exe, we can execute the following command:

1
2
# Keep the remote version of git.exe
$ git checkout --theirs git.exe

To keep the local version of the file, the command is:

1
$ git checkout --ours git.exe

Then execute:

1
2
3
$ git add git.exe
# If the command above was --theirs
$ git commit -m "Merged conflict, kept remote version"

Now your local copy is merged with the remote, and the merge maintains the remote file version.

Delete Branches

1
2
3
4
5
6
7
8
// Delete the local branch
// Note: Prior to deleting branches, they should be merged, otherwise an error will occur (or use -D to force delete)
// error: The branch 'dev' is not fully merged.
// If you are sure you want to delete it, run 'git branch -D dev'.
$ git branch -D dev

// Delete remote branch
$ git push ${repo_name} --delete ${remote_branch_name}

Undo Deleted Branches

Two scenarios

  1. Exited Terminal

Use git reflog to check your last commit SHA1 value

1
$ git branch ${branch_name} ${SHA1}

You can create a branch according to your SHA1 value, using the commit you can choose the commit’s SHA1 from when the branch was deleted.

  1. Not Exited Terminal

When deleting a branch, the SHA1 value will be available.

1
2
$ git branch -d dev
Deleted branch dev (was ffe7c46).

Then use this SHA1 value to recover the deleted branch

1
$ git branch ${branch_name} ffe7c46

Version Rollback

1
2
// View all committed versions
$ git log --pretty=oneline

1
2
// Rollback to a certain version
$ git reset --hard version_number

1
2
// Rollback a file to a certain version
$ git reset version_number filename

Undo Uncommitted File Modifications

Undo Only Local Modifications

Suppose we modify some files within a branch but have not moved them to the staging area (git add).

After modifying the files, use the status command to check the file status

Git prompts us that for files not added to the staging area, we can use git checkout -- to quickly revert local modifications.

1
2
// Quickly undo local modifications
$ git checkout -- ${filename}

Undo Modifications in Both Local and Staging Area

For files that have been added to the staging area, how can we revert local modifications? Use the status command to check file status again:

We can first unstage:

1
$ git reset ${filename}

Then undo local modifications:

1
2
// Quickly undo local modifications
$ git checkout -- ${filename}

One-command Quick Undo Modifications

1
$ git checkout HEAD -- ${filename}

Check Out Files from History

Sometimes, after submitting a new version, certain file changes may feel inappropriate, and you’d like to revert a few files to a previous version while keeping others in the new version. You can use the checkout command.

1
2
3
4
5
6
7
// View committed historical versions
$ git log
// Checkout files from historical versions to the current version
$ git checkout historical_version_number ${filename}

// Check out file.txt from version bde5455fd58079f66f10d1526a579cda2be38190 to the current version
$ git checkout bde5455fd58079f66f10d1526a579cda2be38190 file.txt

Subsequently, you can use add to commit as usual.

Rename Local Branch

1
git branch -m ${CurrentBranchName} ${NewCurrentBranchName}

Clone Remote Repository

1
2
3
4
5
// Clone all branches of the repository
$ git clone ${remote_repo}

// To clone a single branch, use -b to specify the branch name
$ git clone -b ${branch_name} ${remote_repo}

Switch to Remote Branch

After cloning a remote repository, executing git branch will show only:

1
* master

No other branches will be visible, even if there are other branches in the remote repository.

You can use git branch -va to first list all local/remote branches:

1
$ git branch -va

Switch to the remote origin/master branch:

1
$ git checkout remotes/origin/master

Further operations are needed:

1
2
$ git checkout -b ${remote_branch_to_local_branch_name}
// e.g. git checkout -b dev

-b means base; it creates a new branch named dev based on the current branch. Other naming can be used as well. Now, executing git branch -va will show:

And that’s it!

After modifying the dev branch locally, you can directly push it to the remote dev branch:

1
$ git push origin dev

Clone a Specific Branch from Remote Repository

1
$ git clone -b ${OriginRepoBranchName} ${OriginRepoAddr}

For example:

1
git clone -b master git@github.com:imzlp/blog-source.git --depth=2

This pulls the master branch from the remote repository, with --depth specifying the history version pull depth.

Ignore Unnecessary Submitted Files

Sometimes we do not want to submit everything to the repository (e.g., some compiled binary files). We can use .gitignore to exclude files that should not be submitted.
In Windows, we cannot directly create this file because it starts with a dot and has no filename, so we need to use a command in GitBash to create it:

1
$ touch .gitignore

Then edit .gitignore to add the files/directories you wish to exclude.

1
2
3
4
5
6
# Exclude the public folder in the current directory
public/
# Exclude all .exe files
*.exe
# Exclude all .txt files
*.txt

For more content on .gitignore, you can check here — Ignoring Specific Files - Git Tutorial

Compare Differences between Different Versions

1
2
3
4
5
6
7
8
9
10
11
12
# View what portions of unstaged files have been updated
$ git diff
# View what updates have been made to an unstaged file
$ git diff filename
# View the difference between staged files and the last committed version
$ git diff --cached
# View the difference between a staged file and the last committed version
$ git diff --cached filename
# View the difference between two versions
$ git diff ffd98b291e0caa6c33575c1ef465eae661ce40c9 b8e7b00c02b95b320f14b625663fdecf2d63e74c
# View the difference between a specific file in two versions
$ git diff ffd98b291e0caa6c33575c1ef465eae661ce40c9:filename b8e7b00c02b95b320f14b625663fdecf2d63e74c:filename

Diff between Two Branches

A common diff usage is to compare the workspace, index, HEAD, or the diff between two commits. Besides these uses, diff can also compare two branches, as follows:

1
2
3
$ git diff topic master (1)
$ git diff topic..master (2)
$ git diff topic...master (3)

Usage 1: Directly separating two branches with a space will diff the latest commits from both branches, equivalent to diffing two commits.
Usage 2: Splitting branch names with two dot characters has the same effect as usage 1.
Usage 3: Splitting branch names with three dot characters outputs the changes on the master branch since the topic branch was last developed.
Note that here .. and ... should not be confused with .. and ... in git rev-list.

Error Handling

bad signature

If you encounter the following error while using the git terminal:

1
2
3
$ git status
error: bad signature
fatal: index file corrupt

This is caused by a corrupted index, which can be handled as follows:

1
2
rm -f .git/index
git reset

For more details, refer to the discussion on stack overflow about: How to resolve “Error: bad index – Fatal: index file corrupt” when using Git

Disable Git’s SSL Verification

1
$ git config --global http.sslVerify false

Private Key Permissions Too Open Error

When using Git, you may sometimes encounter such prompts (usually when sharing private keys between machines).

1
2
3
4
5
6
7
8
9
10
11
12
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/root/.ssh/id_rsa' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "/root/.ssh/id_rsa": bad permissions
Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

This prompts that your Git private key has overly open read/write permissions (I have it set to 644), and the solution is to change the permissions of the private key (id_rsa) to 600. You can check Tools, Environment Knowledge Collection: Linux File Permissions Modification for Linux read/write permissions.

1
sudo chmod 600 ~/.ssh/id_rsa

For Windows, you can right-click on the file .ssh/id_rsa, go to Properties - Security/Advanced, disable inheritance, change the file owner to your account, and ensure that your account and SYSTEM have Read and Execute permissions, then save.

Alternatively, you can use WSL to set permissions using chmod.

Reference article: Windows SSH: Permissions for ‘private-key’ are too open

Update Remote Branch List in Git

1
$ git remote update origin --prune

Submodule

If you want to use another Git repository within one Git repository, you can handle this relationship using git module.

If you clone another repository B into a subdirectory of Git repository A, you may see a message like:

1
2
3
4
5
6
7
8
9
10
11
12
13
hint: You've added another git repository inside your current repository.
hint: Clones of the outer repository will not contain the contents of
hint: the embedded repository and will not know how to obtain it.
hint: If you meant to add a submodule, use:
hint:
hint: git submodule add <url> Plugins/VaRest
hint:
hint: If you added this path by mistake, you can remove it from the
hint: index with:
hint:
hint: git rm --cached Plugins/VaRest
hint:
hint: See "git help submodule" for more information.

The purpose is to inform you that you have included another Git repository.

The correct way to handle this is to first delete the previously cloned B, then clone B using git submodule:

1
2
3
4
5
6
7
8
9
10
$ git submodule add git@github.com:ufna/VaRest.git Plugins/VaRest
Cloning into 'C:/Users/imzlp/Documents/Unreal Projects/GWorld/Plugins/VaRest'...
remote: Enumerating objects: 123, done.
remote: Counting objects: 100% (123/123), done.
remote: Compressing objects: 100% (81/81), done.
remote: Total 1796 (delta 74), reused 83 (delta 42), pack-reused 1673
Receiving objects: 100% (1796/1796), 624.66 KiB | 44.00 KiB/s, done.
Resolving deltas: 100% (955/955), done.
warning: LF will be replaced by CRLF in .gitmodules.
The file will have its original line endings in your working directory

It will clone B, then you’ll go into B’s directory, change the version record to the desired commit, and then add/commit as usual.

1
2
3
4
5
6
$ git status
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

modified: Plugins/VaRest (new commits)

Then push to the remote repository, and in the remote repository, the B directory in project A will not be directly uploaded files but will link to the actual source repository of B.

Initial Update of Submodule

1
$ git submodule update --init --recursive

Subsequent Updates to Submodule

1
2
3
$ git submodule foreach git fetch
# or
$ git submodule update --remote

Remove Submodule

To remove an added submodule, you need to execute the following steps:

  1. Delete the submodule directory.
  2. Remove the related information about this submodule from .gitmodules.
  3. Remove the related information about this submodule from .git/config.
  4. Delete the submodule directory from .git/modules.

After completing the above steps, that should be enough. However, if there are errors, you may remove the cache:

1
$ git rm --cached submodule_name

Update All Submodules

1
git submodule foreach git pull origin master

git gc

If you see a prompt like this during a pull:

1
2
Auto packing the repository for optimum performance. You may also
run "git help gc" manually. See "git help gc" for more information.

This is because Git uses a format called loose objects to save objects to disk by default. Occasionally, Git packages these objects into a binary file called a packfile to save space and improve efficiency. When there are too many loose objects in the repository, it prompts you to run git gc.

Thus, when you see the above message, simply run:

1
$ git gc

Once completed, you’re good to go.

Pull Branch from Remote

1
2
# Check Remote Branches
$ git branch -r

Method one:

1
2
# Pull a remote branch and create a local branch
$ git checkout -b ${LocalBranchName} origin/${RemoteBranchName}

Method two:

1
$ git fetch origin ${RemoteBranchName}:${LocalBranchName}

fatal: The remote end hung up unexpectedly

You can increase postBuffer to a larger size (in bytes):

1
$ git config http.postBuffer 524288000

Enable Case Sensitivity in Git

Git on Windows defaults to being case-insensitive. You can enable it through configuration:

1
$ git config core.ignorecase false

Git Proxy Settings

1
2
git config --global http.proxy 'socks5://127.0.0.1:1080'
git config --global https.proxy 'socks5://127.0.0.1:1080'

Unset git proxy:

1
2
git config --global --unset http.proxy
git config --global --unset https.proxy

Create a Local Empty Repository

1
$ git --bare init

This command creates an empty repository, which can be added as a remote repository in other projects.


Early EOF Error

1
2
3
4
5
6
7
8
user@USER ~
$ git clone -v git://192.168.8.5/butterfly025.git
Cloning into 'butterfly025'...
remote: Counting objects: 4846, done.
remote: Compressing objects: 100% (3256/3256), done.
fatal: read error: Invalid argument, 255.05 MiB | 1.35 MiB/s
fatal: early EOF
fatal: index-pack failed

Solution: Disable core.compression:

1
git config --global core.compression 0

Repository Not Found

If a pull indicates:

1
2
3
$ git.exe pull --progress -v --no-rebase "origin"
remote: The project you were looking for could not be found.
fatal: repository "https://192.168.2.223/gyvr/GWorldS1g.git/' not found

After ensuring that the server connection is normal and SSH Key is fine, you can resolve this with the methods below.

The issue may arise because another Git account was previously used on this computer, and passwords were saved in the computer settings. When requesting the remote repository, the saved password account will be used by default, and if the request account lacks access to the repository, this error will occur.

Thus, the solution is to delete unnecessary credentials in the control panel:

After this, relaunch the Clone or pull, and you will be prompted to enter the git account and password again.

Export Diff Files

Sometimes, we need to know which files have been updated between two versions without wanting to see the specific changes. You can use the git diff command as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ git diff COMMIT_HASH_1...COMMENT_HASH_2 --name-only > diff.txt 
Config/DefaultEngine.ini
Content/Core/BP_GameMode.uasset
Content/Maps/GameMap.umap
Content/Maps/GameMap_BuiltData.uasset
Content/Maps/LoginMap.umap
Content/Maps/LoginMap_BuiltData.uasset
Content/Pak/Cube.uasset
Content/Pak/Mats/BasicShapeMaterial_Blue.uasset
Content/Pak/Mats/BasicShapeMaterial_White.uasset
Content/Pak/Mats/BasicShapeMaterial_Yellow.uasset
Content/UI/MountPak.uasset
GWorld.uproject

This will list all modified file names, with the key point being the --name-only parameter.

PS: COMMIT_HASH_1...COMMENT_HASH_2 must have the older version first and the newer version second.

List Commit History

Unlike directly using git log, I want a simple list of each commit record without caring about the committer, time, and so on.

You can use Git’s placeholders: Git Basics - Viewing the Commit History

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ git log --pretty=format:"%h \"%s\""
fee8b06 "fix word error"
84a2a8b "Base v0.0.1"
91a9991 "set default map"
5e2499e "add download pak test"
cbcbb9a "add UI"
ca34d8f "fix bugs"
589cf38 "fix bug"
9e9ca9b "add CreateFileByBytes"
64d3b94 "add test document"
52b7562 "enable VaRestPlugin"
5e6adc0 "add VaRest/General Lib"
9562bf8 "set GWorld Project to mobile"
3f16e75 "GWorldSlg init"

The --pretty=format: supports more options:

Option Description of Output
%H Commit hash
%h Abbreviated commit hash
%T Tree hash
%t Abbreviated tree hash
%P Parent hashes
%p Abbreviated parent hashes
%an Author name
%ae Author email
%ad Author date (format respects the –date=option)
%ar Author date, relative
%cn Committer name
%ce Committer email
%cd Committer date
%cr Committer date, relative
%s Subject

Rebase

If you only use merge to combine branches, you will merge all commit information and submission history from the target branch. Sometimes we want to treat a version of features as a single commit rather than treating every small modification as one commit. This is how we can use rebase.

First, here’s the flow of rebase:

  1. Create Branch B based on Branch A: git checkout -b B
  2. Make modifications and commits on Branch B until functionality is satisfactory.
  3. When you want to sync Branch B with Branch A, you can use rebase to fold commit:

Specifically, find the foundational commit hash from Branch A in Branch B and run the following command:

1
2
3
4
# Specify the base version for rebase
$ git rebase -i 0fee16a1a2f20d299c678e9846ef720c2ca228ad
# Or specify how many commits to check upward from HEAD
$ git rebase -i HEAD~3

This will open a vim editor interface with all commits from Branch B listed from the base version to HEAD, all defaulting to pick. Since we need to fold commits, we should merge multiple commits, thus using the squash command provided by git:

  • pick: Keep the commit (abbreviation: p)
  • reword: Keep the commit but I want to modify the commit note (abbreviation: r)
  • edit: Keep the commit but I want to stop to modify (not just the note) (abbreviation: e)
  • squash: Merge the commit with the previous one (abbreviation: s)
  • fixup: Merge the commit with the previous one but do not keep the commit note (abbreviation: f)
  • exec: Execute shell command (abbreviation: x)
  • drop: Discard the commit (abbreviation: d)

Only keep one pick, replacing others with squash should suffice. After that, another commit editing interface will pop up, allowing you to comment out any unwanted commit messages with #, then save and exit, completing the rebase.

  1. The last step is to switch back to branch A and execute the merge operation: git merge B. After merging, you will only see a single commit in branch A.

Clean LFS Cache

When using LFS for a while, the cache can become excessively large. You can use the following command to clean it:

1
git lfs prune

error: cannot lock ref

1
2
3
error: cannot lock ref 'refs/remotes/origin/SN/feature/Terrain_CDLod': is at 076e430ca921e6a02b6c2a431609090e635f8ea8 but expected 48b8b9ebd04ab04a7053d3aca304beca46da16f6
From http://git.code.oa.com/RStudioEngine/UnrealEngine
! 48b8b9ebd04..076e430ca92 SN/feature/Terrain_CDLod -> origin/SN/feature/Terrain_CDLod (unable to update local ref)

Solution:

1
2
$ git update-ref -d refs/remotes/origin/SN/feature/Terrain_CDLod
$ git pull -f

View Files Pending Commit in Git

git diff

AMCR captures four types of operations: Add/Modify/Copy/Rename:

1
git diff --cached --name-only --diff-filter=AMCR

Using -cached detects differences since the last commit after git add.

1
2
3
4
$ git diff --cached --name-only --diff-filter=AMCRX
Maps/Login.umap
PreCommitChecker.py
Texture/texture1.uasset

You can view the specific parameters for Git: –diff-filter.

git status

Using git diff can only compare between versions, or files after git add, but cannot access untracked files.

1
git status -s

This will show a short list:

1
2
3
 M Assets/Scene/BaseMaterials/4X4_MRA.uasset
?? AAA.txt
?? PreCommitChecker.py

The first two characters indicate the file status, followed by a space. ?? represents untracked files. More status codes can be found in git-status.

Revert Commit

1
git reset --soft ^HEAD

Delete Tag on GitHub

1
git push origin --delete tagname

Commit with Username and Password

1
git remote set-url origin https://username:password@github.com/username/repo_name.git

Be sure to modify the LFS as well, and you can edit the .git/config file.

SSH Login with Key Failure

You might edit Mac’s SSH configuration (/etc/ssh/sshd_config):

1
2
3
RSAAuthentication yes
PubkeyAuthentication yes
StrictModes no

Then reload the configuration:

1
2
$ sudo launchctl unload /System/Library/LaunchDaemons/ssh.plist
$ sudo launchctl load -w /System/Library/LaunchDaemons/ssh.plist

error: cannot lock ref

1
error: cannot lock ref 'refs/remotes/origin/SN/feature/avinchen/Develop': is at 651a8bcf620db8e6bbc6c874e0949ff6c09bb37f but expected f0f49ccce3abfe552e2301eca5711741a390af6b

Solution:

1
2
$ git update-ref -d refs/remotes/origin/SN/feature/avinchen/Develop
$ git pull origin

Alternatively, you could force pull all:

1
$ git pull -p

Pull LFS Files

1
2
3
git lfs fetch
# or
git lfs pull

Pull Request to Local

1
git fetch origin pull/58/head:ue5

This means pulling the 58th remote PR to the local ue5 branch.

Historical Files Support LFS

After enabling LFS, you can track files from historical commits as well.

1
2
# Using the current gitattributes configuration
git lfs migrate import --everything

Remove Changed Files

To remove all changed files:

1
git clean -xdf

Note: This will delete all untracked files (including those ignored in gitignore). If you do not wish to delete files ignored in gitignore, omit the x in the command above.

no hostkey alg

When connecting to the SSH Server, if this prompt appears, it is due to the SSH version, as the remote SSHD version is too high while the local SSH version is too low.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
OpenSSH_5.1p1, OpenSSL 0.9.8i 15 Sep 2008
debug2: ssh_connect: needpriv 0
debug1: Connecting to 10.64.222.108 [10.64.222.108] port 2222.
debug1: Connection established.
debug2: key_type_from_name: unknown key type '-----BEGIN'
debug2: key_type_from_name: unknown key type '-----END'
debug1: identity file G:/UnrealProjects/Client/Build/NotForLicensees/SSHKeys/10.64.222.108/buildmachine/RemoteToolChainPrivate.key type -1
debug1: Remote protocol version 2.0, remote software version OpenSSH_9.0
debug1: match: OpenSSH_9.0 pat OpenSSH*
debug1: Enabling compatibility mode for protocol 2.0
debug1: Local version string SSH-2.0-OpenSSH_5.1
debug2: fd 3 setting O_NONBLOCK
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug2: kex_parse_kexinit: diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1
debug2: kex_parse_kexinit: ssh-rsa
debug2: kex_parse_kexinit: aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,arcfour128,arcfour256,arcfour,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,aes128-ctr,aes192-ctr,aes256-ctr
debug2: kex_parse_kexinit: aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,arcfour128,arcfour256,arcfour,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,aes128-ctr,aes192-ctr,aes256-ctr
debug2: kex_parse_kexinit: hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-sha1-96,hmac-md5-96
debug2: kex_parse_kexinit: hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-sha1-96,hmac-md5-96
debug2: kex_parse_kexinit: none,zlib@openssh.com,zlib
debug2: kex_parse_kexinit: none,zlib@openssh.com,zlib
debug2: kex_parse_kexinit:
debug2: kex_parse_kexinit:
debug2: kex_parse_kexinit: first_kex_follows 0
debug2: kex_parse_kexinit: reserved 0
debug2: kex_parse_kexinit: sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256
debug2: kex_parse_kexinit: rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp256,ssh-ed25519
debug2: kex_parse_kexinit: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
debug2: kex_parse_kexinit: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
debug2: kex_parse_kexinit: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: kex_parse_kexinit: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: kex_parse_kexinit: none,zlib@openssh.com
debug2: kex_parse_kexinit: none,zlib@openssh.com
debug2: kex_parse_kexinit:
debug2: kex_parse_kexinit:
debug2: kex_parse_kexinit: first_kex_follows 0
debug2: kex_parse_kexinit: reserved 0
debug2: mac_setup: found hmac-sha1
debug1: kex: server->client aes128-ctr hmac-sha1 none
debug2: mac_setup: found hmac-sha1
debug1: kex: client->server aes128-ctr hmac-sha1 none
no hostkey alg

UE updated the client’s SSH version in the 4.27 engine: Replace Windows ssh and rsync from DeltaCopy with new version from cw…

If it is an engine version prior to 4.27, upgrading to MacOS13+ will lead to this issue during remote builds. ## detected dubious ownership in repository at

1
2
3
4
5
6
7
8
9
$ git remote set-url origin https://git.woa.com/xxxx/UnrealEngine.git
fatal: detected dubious ownership in repository at 'D:/agent/workspace/MasterPackage/SOURCE/Engine'
'D:/agent/workspace/MasterPackage/SOURCE/Engine' is owned by:
'S-1-5-21-1333135361-625243220-14044502-1020188'
but the current user is:
'S-1-5-21-1333135361-625243220-14044502-1052754'
To add an exception for this directory, call:

git config --global --add safe.directory D:/agent/workspace/MasterPackage/SOURCE/Engine

If you’ve switched to a new account, the above error will appear for repositories pulled using the previous account.

The solution is to add all paths to the safe directory:

1
git config --global --add safe.directory "*"

Note: This command should be executed in the new account.

Warning: the ECDSA host key

When you encounter this warning while committing to GitHub: Warning: the ECDSA host key for 'github.com' differs from the key for the IP address '192.30.255.112', it means your SSH client has previously authenticated a different host matching the target IP address.

To resolve this issue, follow these steps:

1. Remove the old key from the known hosts file:
Open the ~/.ssh/known_hosts file with a text editor (Windows users should locate it in %UserProfile%\.ssh\known_hosts) and find the corresponding entry (referencing the IP address and hostname), then delete the corresponding line.

For example, in the known_hosts file, find an entry like:

1
github.com,192.30.255.112 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hX ...

Delete that line.

2. Verify GitHub’s SSH host key fingerprint:
Confirm the current valid TLS public key fingerprint and SSH public key fingerprint from GitHub’s official documentation.

3. Reconnect via SSH and add the new host key:
In the terminal or command prompt, run the following command:

1
ssh -T -oStrictHostKeyChecking=accept-new git@github.com

This will automatically add the new github.com host key.

If prompted “Hi username! You’ve successfully authenticated, but GitHub does not provide shell access.”, it means SSH authentication was successful and the issue is resolved.

Now, you should be able to commit code to GitHub without the warning message reappearing.

pull/push prompts for password every time

You can execute the following command:

1
git config --global credential.helper store

Get the latest commit information of the current repository

If you only want to get the latest commit ID, you can use the following command:

1
2
$ git rev-parse HEAD
5c342b5145720c8cd4642e50774990db1c4294c3

If you want to see the branch and commit information, you can use the command below:

1
2
$ git log -1 --pretty=oneline
5c342b5145720c8cd4642e50774990db1c4294c3 (HEAD -> master, origin/master, origin/HEAD) AUTO: Commit 58ca381 - (by UGit)

The -1 parameter lists only the latest commit.

If you add --decorate, you can see the branch information (depending on the Git version, newer versions may show branch information by default):

1
2
$ git log -1 --pretty=oneline --decorate
2d7b647a0f7330e3470ebe05fe039d9107624479 (HEAD -> dev, origin/dev) copy file

SSH key login failed

If the SSH connection prompts the following error:

1
lipengzha@192.168.31.55: Permission denied (publickey,password,keyboard-interactive).

You can modify the server-side SSH configuration (/etc/ssh/sshd_config):

1
2
RSAAuthentication yes
PubkeyAuthentication yes

Then restart the SSHD service.

View the last modifier of a particular line of code

To view the last committer of code from line n~m of a file:

1
git blame filename -L n,m

Will output:

1
2
e74836f557b (lipengzha 2024-01-10 09:43:02 +0800 541)                                   TArray<FString> FoundShaderLibs = UFlibShaderCodeLibraryHelper::FindCookedShaderLibByPlatform(PlatformName,SavePath,false);

1
git blame -L 541,541 --incremental --minimal Plugins\\HotPatcher\\HotPatcher\\Source\\HotPatcherCore\\Private\\CreatePatch\\PatcherProxy.cpp

Output:

1
2
3
4
5
6
7
8
9
10
11
12
e74836f557bc5ddf01de3f5f8d3b797107d9a767 541 541 1
author lipengzha
author-mail <lipengzha@gmail.com>
author-time 1704850982
author-tz +0800
committer lipengzha
committer-mail <lipengzha@gmail.com>
committer-time 1704850982
committer-tz +0800
summary 【Build】Build Requirement Summary
previous 00ea9b4597dad3fe867b8af8747d12d10be105e2 Plugins/HotPatcher/HotPatcher/Source/HotPatcherCore/Private/CreatePatch/PatcherProxy.cpp
filename Plugins/HotPatcher/HotPatcher/Source/HotPatcherCore/Private/CreatePatch/PatcherProxy.cpp

Clear all commit history

To clear all history in a Git repository while keeping only the latest commit, follow these steps:

  1. Create a new branch:
    First, ensure you are in a clean working directory (no uncommitted changes) and create a new branch.

    1
    git checkout --orphan latest_branch

    This command creates a new branch without any history.

  2. Add all files:
    Add all files to the new branch.

    1
    git add -A
  3. Commit the changes:
    Commit all files.

    1
    git commit -am "Initial commit with latest state"
  4. Delete the old branch:
    Switch back to the main branch and delete the old branch.

    1
    git branch -D main
  5. Rename the new branch to the main branch:
    Rename the new branch to the main branch.

    1
    git branch -m main
  6. Force push to the remote repository:
    Finally, force push the changes to the remote repository. Note: This will overwrite all history in the remote repository.

    1
    git push -f origin main

This operation will clear all historical records, keeping only the latest commit. This operation is irreversible, and all historical records will be permanently deleted, so ensure you truly do not need these historical records before executing this operation.

Differences in line endings between Win and Linux

Windows uses carriage return line feed (CRLF, \r\n), while Linux uses line feed (LF, \n). If you create or modify files on Windows and then view them on Linux, Git may think the files have changed due to the different line endings.

Solution: You can configure Git to handle line endings. Use the following commands to set Git to automatically convert line endings:

1
2
git config --global core.autocrlf true  # Windows
git config --global core.autocrlf input # Linux
  • Use the .gitattributes file to specify the handling of line endings for specific files, for example:
1
* text=auto

UPDATE

CheatSheet

References

Detailed explanation of Git remote operations
Creating and merging branches
Undoing branch deletion in Git
Using git checkout command to undo modifications
Diffing two branches with git diff

The article is finished. If you have any questions, please comment and communicate.

Scan the QR code on WeChat and follow me.

Title:Git快速上手指南
Author:LIPENGZHA
Publish Date:2016/09/29 22:19
Update Date:2018/08/15 00:57
World Count:16k Words
Link:https://en.imzlp.com/posts/53696/
License: CC BY-NC-SA 4.0
Reprinting of the full article is prohibited.
Your donation will encourage me to keep creating!