Search:

PmWiki

pmwiki.org

edit SideBar

Main / Git

Git is all about power for the maintainer, at the expense of the contributor. It makes code changes for the single user intentionally difficult, but allows the custodian of large projects with many distributed workers to keep an eye on everything.

Git has a weak UI susceptible to errors because the Git folks have a tendency to let implementation dictate interface.

Using git subtrees -- Using git submodules -- Using git tags -- Using .gitignore -- Troubleshooting

GitLab tips

Getting started with a new project, and some basics

https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository

When you create a new repo, it automatically starts with branch 'master' on github, but is empty on bitbucket.

What about updating Ubuntu with the latest git releases?
http://stackoverflow.com/questions/19109542/installing-latest-version-of-git-in-ubuntu

You can't actually work on different branches in the same local workspace in separate terminal windows. Once you set the branch, all git commands are for that branch. You can do this if you set up another local workspace.

A fork is different from a branch, because it actually creates a new project - i.e. a new server-side repo. With a branch, you are dealing with only one remote.

You can find all the comments you've made from the search bar on the github site by searching for commenter:username.

There is a small but growing crew that have recognized git's severe limitations and the fact that it can be a poor choice for small teams, startups, or small projects: https://medium.com/@ottovw/forget-git-just-use-subversion-or-perforce-8412dc1b1644 and https://stevebennett.me/2012/02/24/10-things-i-hate-about-git/ and https://svnvsgit.com/

Authentication

Suspend those prompts!!

git config --global credential.helper cache
git config --global credential.helper 'cache --timeout=3600'
# Set the cache to timeout after 1 hour (setting is in seconds)

Walkthrough init

A new git repo is created after git init.

user@machine MINGW64 /l/sbc (master)
$ git branch

Nothing's listed for branches even though it says master? This is true, and expected behavior. Git will not create a master branch until you commit something.

But can I create the master branch then when it's empty?

user@machine MINGW64 /l/sbc (master)
$ git branch master
fatal: Not a valid object name: 'master'.

Nope! That is again correct behavior. Until you commit, there is no master branch.

user@machine MINGW64 /l/sbc (master)
$ git status
On branch master

No commits yet

nothing to commit (create/copy files and use "git add" to track)

Ah, this makes more sense.

Best Practices

(1) Workflow
Most people use some variation of the Git Flow model to branching and releases: https://datasift.github.io/gitflow/IntroducingGitFlow.html

Try to keep feature branches short-lived and limited to one developer. Favor rebase over merge. Try to avoid branching off of a branch. As a general rule, don't rebase a public branch.

(2) Peer Review
Git does not work very well as a peer review tool, because the process of making minor code changes based on peer comments is far more time-consuming than it should be and is also subject to coherency errors with conflicts because of an active target branch.

The best approach is to find a way to do the review when the work on the feature branch is still local, or at least before a pull request is issued. The pull request ideally should be little more than a formality, with changes already reviewed and approved. While it's a good medium for making in-line comments, it's not the place to catch issues and be surprised.

Delays to these peer reviews is also a big problem, so this is another reason it should have nothing new and should be approved and merged very quickly.

(3) Branch Naming
Using a naming scheme for branches that includes the slash (like feature/myname/issue) is poor practice. First, these slashes are already used as Linux path folder dividers so the mind is not trained to create a different association. Second, git already uses them in an analogous fashion so making them part of the branch name is confusing at best. Choose something else like dash or dot.

Renaming or Moving or Deleting Files

These are handled differently by git than modifying tracked files or adding new untracked files. Note that it's cleanest to use the git version of commands like git rm or git mv because then git is better at knowing what you are doing. Otherwise it's dumb, and a move could be seen as a file deletion + addition instead of a move. When you use the git commands, a deletion or rename will be automatically staged and ready for commit rather than needing to be staged (i.e. added).

Gitflow Branching Suggestions

Glossary

branch - a pointer to a commit; this command lists your local branches; you can have tons of these, and all use the same local workspace - when you change branches, your workspace is actually modified
check in - means the same as commit
checkout - sync; you can get a specific repo tag with git checkout tag
commit - this means put into your imaginary local repo from the stage
fetch - get the repo updates to your imaginary repo, but do NOT merge; after fetch you can compare to local branches; note that fetch does NOT update your local workspace
fork - local copy of another user's repo
head - term meaning current snapshot; git checkout updates HEAD to point to a branch or commit... if commit, then you get the 'detached head' complaint
hook - a script that runs on a particular event in a git repo, for customizing git behavior
master - default development branch, is the active branch on repo creation
origin - server alias assigned by default if none specified
prune - remove references to deleted branches
publish - when you merge your branch and its changes back into master
pull - fetch changes AND merge them, pull them into your local copy
pull request - AKA a request to merge; submitted by a user and accepted or rejected by collaborators
push - sending your local imaginary committed changes to a remote repo (how is this different from publish?)
rebase - the act of moving a branch's begin point; like doing a branch to branch merge that is more destructive and leaves a cleaner history (never do this with a publicly-worked branch)
reset - change head to point to a new place, like a branch or commit or tag
stage - a list of modded files that are "checked in to index" (gitk speak) and ready for commit
tag - marks/names a specific point in the repo history, like a release candidate

Commands

How to do an update?

Git is strange compared to other tools in that there are many ways to update. To update your local directory, use git checkout . This doesn't always work when the repo has advanced and left you behind. If you get a status message about being commits behind, then use git rebase <remote>.

Use git fetch to pull from the repo. If you are looking at gitk, then this will actually update your local machine to understand the position of the server branch. Before then, it may look outdated and indeed your local versions of the files won't match the server. Then use git rebase <server_alias>/<server_branch>, i.e. git rebase origin/master. Operates on the currently active local branch. If using auth tokens, must use git username plus token as password, rather than git web login. origin in this case is the default name for the server the repo came from.

How to clone something? A specific branch?

After running git clone, you must checkout a specific branch: http://stackoverflow.com/questions/9004192/the-git-clone-is-empty-why-this-happens. Note that until you do the checkout for a specific branch, that branch will not show up when you list git branch. One option is to do it all with the clone command like so git clone -b <branchname> --single-branch <github.com>/<owner>/<project>.git. Git’s clone command automatically names it (the server) origin for you, pulls down all its data, creates a pointer to where its master branch is, and names it origin/master locally. Git also gives you your own local master branch starting at the same place as origin’s master branch, so you have something to work from. There is nothing special about the terms "origin" or "master". https://git-scm.com/book/en/v2/Git-Branching-Remote-Branches

To get the branch after you've already cloned, use git checkout -b <branch> <remote>/<branch>

If you want submodules (pointers to other repos), then include --recurse-submodules

See the rev history of a single file

gitk --follow <filename>

Which remote branches are my locals tracking?

git branch -vv

How to list modified files?

git status -uno or git status -uno -s

How to diff against any local or remote branch?

You'd think you could just insert the path and it would work for your local working branch, but it doesn't. Instead you have to specify HEAD or the branch name: git diff origin/<branchname>:<path/to/file.txt> HEAD:<path/to/file.txt>. You can use the --name-status or --summary options to skip details.

How to revert (locally)?

For a single modified, tracked file use git checkout -- <filepath>

Bulk:
Type 1. Staged Tracked files
Type 2. Unstaged Tracked files
Type 3. Unstaged UnTracked files a.k.a UnTracked files

git checkout . Removes Unstaged Tracked files ONLY [Type 2]
git clean -fd Removes Unstaged UnTracked files/dirs ONLY [Type 3]
git reset --hard Removes Staged Tracked and UnStaged Tracked files ONLY[Type 1, Type 2]
git stash -u Removes all changes [Type 1, Type 2, Type 3]

git clean -n does a dry run

But if you've already committed locally:
git reset --soft HEAD^ will move the commits back down to being Staged. Then you can move over to the correct branch with git checkout <branchname> and re-commit.

How to server commit?

Have to be on a local branch that is synced to the server.
git add <file> - stages the change
git commit -m "message" - commits the change to the imaginary repo, NOT the server
git push <server_alias> <branch_name>:<server_repo_name>
... for example git push origin branchDTB:master

If you get an error trying to do this about being out of date, it's because you missed a rebase. You can recover with a pull if you like (git pull origin master which uses the remote URL alias and the server repo name, then do the push. Note that a pull and rebase do a merge. There is no SVN-style update to sync up all the files you haven't touched while leaving modded files alone. Instead, the merge happens on the stuff you are working on.

Another way to do the push is git push -u origin <mybranchname> which will actually create the branch on the server, meaning it doesn't only exist locally on your machine anymore. Then, you can go to the server page and request a pull from your branch to master.

How to make git recognize my new .gitignore file?

Make sure everything you need is saved, stable, committed, etc.

git rm -r --cached .
git add .
git commit -m "fixed untracked files"

How to make changes to code in a pull request?

When sending a pull, comments message at the bottom that says something like: Add more commits by pushing to the 'branchname' branch on 'remote'/'branch'.

So to update a PR you just need to:
# Fetch the PR (git fetch and git checkout <branchname>).
# Add new commits, amend, rebase, do whatever you like.
# Push or push force (git push remote <branchname>).
And after that the PR will be automagically updated. What happens is that a new commit is appended to the PR.

How to refresh?

If you've been messing around with a directory and blow it away and want to get it restored, use git fetch followed by git checkout .

What repo am I using?

git remote show origin will give the URL

How to update/sync a fork to a tag in the trunk?

git checkout <tag>

How to create a new branch locally?

Locate to the folder you want to branch. Use git checkout -b <branchname>. Note that this will switch you to the new branch. If you had uncommitted changes in the previous branch, this will be a problem for going back to the previous branch you were on if you want those edits to stay where you left them (by default they come with you to the new branch). You can stash those and move on with git stash. Now you can switch freely between branches with git checkout <branchname>. To list all the branches use git branch. Note that when you switch between branches, untracked files remain.

How to store an existing local project in git?

Go to project directory and do git init .. Then do a git add <file> for everything you want to be controlled. Create a .gitignore file. When you do a git commit . it's now committed to your imaginary local repo. To get it uploaded, follow with:

git remote add origin <URL>.git 
git push -u origin master 

How to copy a single file from another branch into my current branch?

git checkout <otherbranch> <neededfile>

How to amend a commit?

The git commit --amend command will open up the editor with the previous commit's message which you can alter or add to. (Using git commit --amend -m "an updated commit message" will replace the previous message. This is an entirely new commit and the previous commit will be blown away. Then you end up with a local branch that is now deviated from the remote copy of this same branch name, and the server doesn't appear to know about it. Note that trying to do a push in this case to get the updates onto the server branch will NOT work. This actually seems really messy to me if you are trying to change code because then you have to merge down the server's version of the branch into your local version. In fact, it actually sticks garbage into the edited file. Don't use on a public branch. If you need to back further than just the previous commit, you'll need to use other tools: https://www.atlassian.com/git/tutorials/rewriting-history.

How to delete a remote branch?

git push origin --delete <branchname>

How to remove local reference to deleted remote branch? Pruning?

Pruning appears to be used to clean up hanging references to deleted branches. For example, when you merge a pull request on the server and delete the feature branch, your local git will still see remotes/origin/<branchname>. A simple fetch or other updates won't make it go away, illogically. Instead it seems you must specify a prune action. I have found git fetch -p does it, and I'm assuming it handles all hanging references it finds. These are other (untested as of yet) similar options potentially:

  • git remote prune origin = prune all of the remote zombie branches
  • git branch -d -r origin/<branchname> = delete remote tracking branches locally
  • git fetch --prune origin/<branchname> = prune a specific remote zombie branch

How to force current branch back to match another branch?

This will get rid of commits that have modified the current branch: git reset --hard <desiredbranchname>. Note that untracked files in the local folders where you are working will remain regardless of their status inside other branches. This also works with commit IDs instead of a branch name.

How to migrate to a new remote server?

https://www.smashingmagazine.com/2014/05/moving-git-repository-new-server/

It basically boils down to:

  • Make sure you have all your local branches updated and remote branches tracked
  • Add the new remote
  • Push everything to the new remote
  • Delete the old remote
  • Rename the new remote

However, there is a faster method, especially useful for a repo you haven't cloned before or worked in:

git clone --mirror <oldremote>
cd <project>
git push --mirror <newremote>
cd ..

How to search across branches for a certain file?

https://stackoverflow.com/questions/372506/how-can-i-search-git-branches-for-a-file-or-directory

git log --all -- <filename> (and might need to be correct full path)

Mastering Merge and Rebase

How to merge local branches?

git checkout <branchA> followed by git merge <branchB>. Note that if you create a new branch to merge into, it will match the current active branch where you invoke the branch creation. So if you want your new branch to be the offspring of two different branches, simply start in one of those and then you only need to do one merge op. To undo a mistaken branch merge, where you'll see "Unmerged paths:" in the status, use git merge --abort. If you have merge conflicts and you need to interactively review them, you need to download a merge application (like kdiff3) and invoke git mergetool --tool=<toolname> after issuing the merge command. If done, you must then commit the merged results in the branch before you'll be allowed to see and commit further manual changes, and all the merged files have to be joined in one commit (git won't let you split them up).

However, it may be advisable to use a rebase instead which many admins prefer. The above describes a "merge up" which keeps the commit history for both branches and is therefore longer. A rebase let's you shorten things by keeping the history of just one of the branches (anything on the rebased branch prior to the rebase commit location is lost, apparently).

Handling merge conflict on a pull request (via merge rather than rebase)

Bitbucket suggests the following procedure:

Step 1: Checkout the target branch and merge in the changes from the source branch. Resolve conflicts.
git checkout <targetbranch>
git pull origin <sourcebranch>

Step 2: After the merge conflicts are resolved, stage the changes accordingly, commit the changes and push.
git commit
git push origin HEAD

Step 3: The pull request will be updated and marked as merged.

Guide to interactive rebase

A rebase can be initiated by checking out the directory you want to rebase (or update) and executing git rebase <base_branch>. I find that a rebase usually results in a conflict and you'll end up with an

error: could not apply <commitID>... <commitMESG>
--OR--
error: Failed to merge in the changes.

The best option is to resolve and continue. The file in question can be found by doing a git status and looking under "Unmerged paths". Open that file and make the necessary edits to resolve and save it. Then you must do a git add <file> on it. DO NOT DO A COMMIT WITH THE ADDED FILE. Then, you can run git rebase --continue as instructed. It may come back and say it's still not done, and throw the same file at you in "Unmerged paths" if there are still things it's conflicted about, such as three different edits to the same file it can't resolve rather than two. I'm thinking the conflicts are only marked two sources at a time, so you'll need to endure this process for each extra commit. It also seems to only present you with one file at a time in "Unmerged paths" so you have to review, edit, add, and continue for each problematic file at least once. When finally done, you'll get a "Successfully rebased and updated" message.

The rebase usually however does not end cleanly on it's own, so you might have to issue a git rebase --skip after it's all done but it won't let you out.

If you are trying to use git merge to resolve conflicts and it's not working because you get an error about unmerged paths, try using git mergetool --tool=<difftool> and this seems to kick it in the pants.

When doing a rebase, how do I accept all the conflicting changes from the new base branch?

You have to remember the paradigm shift here that the new base is considered to be "ours", which is opposite the way a merge works. So, when trying to rebase feature-branch onto master-branch, you can do a git checkout --ours filename and then a git add filename. You'll probably need to follow that with git rebase --continue.

After rebase, I want to push the rebased branch but I can't because of a "behind the tip" error.

You need to force it, Check to make sure the branch is rebased properly and you want to save it on the remote as it is locally. Then do git push -f.


Page last modified on June 27, 2023, at 04:14 PM