Skip to content

NOT a Git manual

WARNING: This page has not been reviewed and may contains typos or conceptual mistakes.

This article supposes that readers understand basic Git operations including git init/clone/status/add/rm/stash/log. git reset is not mandatory but preferred.

If you have no idea about these commands, read 菜鸟教程 or any other manual before proceeding to the next part.

From ... ?

A repository can be created by git init or git clone <git-url>.

For example, if a local Git repository is created through cloning from GitHub, then git remote --verbose will display something like:

shell
(HTTPS)

 git remote -v
origin  https://<url>/<owner>/<repo> (fetch)
origin  https://<url>/<owner>/<repo> (push)

(SSH)

 git remote -v
origin  git@<url>:<owner>/<repo>.git (fetch)
origin  git@<url>:<owner>/<repo>.git (push)

(SSH, fork repository)
 git remote -v
origin  git@<url>:<fork-owner>/<repo>.git (fetch)
origin  git@<url>:<fork-owner>/<repo>.git (push)
upstream  git@<url>:<owner>/<repo>.git (fetch)
upstream  git@<url>:<owner>/<repo>.git (push)

But how can we manually configure remote repos?

Suppose that we have a repo with a remote repo origin and a local branch master.

shell
git push -u origin master

This command will push the local branch master to the remote branch origin/master, and record upstream tracking master -> origin/master (This can be checked through git status).

Below is a workflow example. (Copied from nixpkgs/CONTRIBUTING.md)

shell
# Add a tracked remote repo as `upstream`
git remote add upstream <git-url>

# Make sure you have the latest changes from `upstream`
# Append `--depth 1` if you only want the latest commit from upstream
git fetch upstream

# Create and switch to a new branch `update-hello` based off the master branch from `upstream`
# Same as `git checkout --branch/-b`
git switch --create update-hello upstream/master

# Or based off from a specific commit
git switch --create update-hello <the desired base commit>

Then you can start adding your changes on this branch and saved by git commit.

shell

# If `EDITOR` environment variable is set, this will trigger the corresponding editor executable and display current change status.
# Then you should edit commit message in editor.
# append `--message/-m "<commit message>" to commit changes directly without opening the editor
git commit

Merge branches / Resolve merge conflicts / Pull Request

TODO

Rewriting History

git stash is very simple, RTFM plz.

So is rewriting HEAD.

shell
# Rewrite current commit (supposed you are not in HEAD detached mode).
# append `--no-edit` if you do not want to edit commit message.
git commit --amend

If you want to rewrite multiple commits or commits far from HEAD, you can use interactive rebase. This is a powerful tool that allows you to modify multiple commits in your history. Here are the specific steps:

shell
# Rebase interactively onto a commit in the history
# This should be the parent commit of the earliest commit you would like to edit.
# Use <TAB> button to utilize shell completion (if exists).
git rebase --interactive <commit>

For example, if you want to modify the last three commits, you need to specify the fourth most recent commit:

shell
git rebase -i HEAD~4

This will open an editor displaying something like this:

shell
pick f7f3f6d Change configuration file
pick 310154e Update user interface
pick a5f4a0d Fix bug
pick 368fab6 Add new feature

# Rebase 710f0f8..a5f4a0d onto 710f0f8
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#

To modify commit messages, change "pick" to "edit" (or "e" for short) for the commits you want to modify. For example:

shell
e f7f3f6d Change configuration file
e 310154e Update user interface
e a5f4a0d Fix bug
pick 368fab6 Add new feature

Then Git will rebase onto the first commit to be modified.

Once complete, run git rebase --continue and Git will apply all changes, rewriting history and rebase onto the next commit to be modified.

You can skip changes by git rebase --skip or quit rebase mode by git rebase --abort.

Note: This process changes the SHA-1 values of commits, so use it with caution, especially on commits that have already been pushed to a shared repository. If you need to perform this operation on pushed commits, make sure to communicate with your team and use git push --force-with-lease to push the changes.

See: Is git push --force-with-lease always safe?

More

Check out the official Pro Git book for advanced Git usage.