Exploring Revision control (RC), the management of changes to documents, programs, and other items stored as computer files. This page emphasizes the 2 Version Control Systems (VCS) that I am most familiar: Git and Mercurial. Since I have to use both it's important to know the differences. This page covers what you would do 90% of the time.
On this page place holders are denoted with percent signs. EG:
git help %command%becomes
git help pull.
What is revision control? Revision control manages changes to documents, programs, and other items stored as computer files. This includes any combination of updating documents, adding documents, or deleting documents, or branching documents. Revision control manages who made which changes when and why to what.
Revision control is a concept and it goes by several names:
To implement RC thus goes by several names:
Here are some major specific implementations of revision control as software/system:
Why revision control? The more complex or collaborative a project, the greater the need for revision control.
How is revision control done? While revision control is most commonly associated with program code, people actually experience and do "revision control" all the time: writing documents, retelling stories, rendering and remaking songs, mashing recipes, crafting pictures, and so on. An old school way to do revision control on a document "X" to to periodically "Save As" a dated revision like "X 2014-06-12 12.40".
Each RCS implements things differently. The following is roughly how Git implements its magic.
Most revision control systems have the concept of a working directory or working tree. This is basically a directory with a snapshot of all the source files and subdirectories that you are currently working on. Git has 2 kinds of repositories:
.gitdirectory at the root of the working tree. In Windows it is hidden. This has a mutable
indexfile about the working tree, plus items not yet in the repository.
%project%.git) directory, with no working tree. A Git hosting site like GitHub or BitBucket stores a bare repository (no
indexfile). That is the host does not know or care what you are working on.
The heart of a Git repository (regular or bare) immutable, append-only object database. Each object in the object database is identified by a SHA-1 hash. The ID is stored as a 160 b (20 B) value that is often rendered as a 40 digit hexadecimal. (Mercurial uses SHA-1 for its ChangeSetIDs too.) The object database is implemented primarily as the
.git/objects directory, where the each object is filed by the 1st 2 characters of its hash, thus the directory has 256 folders
ff plus a few other folders.
There are 4 kinds of objects in the object database:
Objects maybe referenced in different ways:
The most common references (run
git show-ref) are stored in
.gitthat references a head in
.git/refs/headsthat corresponds to the working tree (unless detached). For bare repositories
HEADusually corresponds to the
ORIG_HEADis a previous state of
In a RCS project, a file is in 1 of several states:
!=Missing in hg)
R=Removed in hg)
Once you commit a changeset (a subset of files and directories), you have now have a new revision, (aka commit, snapshot, version, changeset). In Git and Mercurial, each revision knows the states of all the files and directories, not just the the changeset.
You can install direct from the source or install via a packages for your platform. Installing from the source ensures you get the latests version. Installing via a package installs multiple items in 1 shot.
For Git on Windows:
For Mercurial on Windows:
One of the 1st things you do after install a VCS is set up some basic configurations like who you are and preferences for editor and file comparison software.
For Git: Run
git config to configure Git. It can be set in different scopes:
C:\Program Files (x86)\Git\etc\gitconfig
Config is set in that order. EG: If both set, then user trumps system.
Here are configs I typically set:
$ git config --global user.name "George Hernandez" $ git config --global user.email firstname.lastname@example.org $ git config --global alias.st status # To make like hg $ git config --global alias.ci commit # To make like hg $ git config --global alias.co checkout # To make like hg update/co $ git config --global core.editor vi $ git config --global merge.tool kdiff3 $ git config --global merge.tool.cmd kdiff3 "C:\Program Files\KDiff3\kdiff3.exe" # "C:\Program Files\TortoiseHg\kdiff3.exe" is also common. $ git config --global push.default simple # New default in Git 2. $ git config --list
For Mercurial: Modify
mercurial.ini with your username and editor preference. The ini file is in
%USERPROFILE%\Mercurial.ini. Here is a typical Mercurial configuration in Windows:
[ui] username = George Hernandez <email@example.com> # verbose output for all commands. Default: False verbose = True # Setup an editor for commit logs # editor = Notepad.exe editor = "C:\Program Files (x86)\Vim\vim74\vim.exe" # editor = "C:\Program Files\Sublime Text 2\sublime_text.exe" # I avoid using an editor that I already have open # Log style - so it doesn't split out all files by default style = compact # Enable extensions [extensions] # Fetch - pull and merge changes automatically (when there are no conflicts) fetch = # Mercurial keyring - saves your password per repo # See http://mercurial.selenic.com/wiki/KeyringExtension mercurial_keyring = # Churn: histogram on user activity churn = # Set diff settings [diff] # ignorews - Ignore whitespace ignorews = True # [extdiff] # cmd.kdiff3 = "C:\Program Files\KDiff3\kdiff3.exe" # "C:\Program Files\TortoiseHg\kdiff3.exe" is also common. [merge-tools] kdiff3.args=--auto --L1 base --L2 local --L3 other $base $local $other -o $output kdiff3.regkey=Software\KDiff3 kdiff3.regkeyalt=Software\Wow6432Node\KDiff3 kdiff3.regappend=\kdiff3.exe kdiff3.fixeol=True kdiff3.gui=True kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
git help git help %command% git %command% --help
$ hg help # hg [%options%] help [%command%] # For a command: hg %command% -help # Commands usually use this syntax: hg %command% [%options%] [%usual_paramemters%] # Additional help topics include: $ hg help config # Configuration files $ hg help dates # Date formats # Faves include: ## "-i" # Within i days of today ## "2011-07-08 13:14:15" # ISO 8601 format ## "2011-7-8 13:14" ## "13:14" # today assumed ## "<%dtm%" # at or before the datetime ## "%dtm1% to %dtm2% # inclusive date range $ hg help patterns # File name patterns # Faves include: (Assuming you are in foo directory) ## path:ack/file.c # No pattern but from repo root ## *.c # Any name ending in ".c" in current dir (foo) ## **.c # Any name ending in ".c" in current dir (foo) or subdirs ## bar/*.c # Any name ending in ".c" in foo/bar dir ## re:.*\.c$ # Perl/Python RegExp for any name ending in ".c" anywhere in repo # Common global options: # --time # Time how long a command takes to run # -h --help # Display help on a command and exit # -d --date [%date%] # See for the date(s) specified # -v --verbose # Enable additional output
Before I jump into either initiating a new repository or cloning an existing repository, I should mention that before your 1st commit, have the VCS ignore or otherwise not track certain files or directories. Ignore is configured by an ignore file in the working directory (
/) to specifiy a directory.
git ls-files --other --ignored --exclude-standard
# No .x files anywhhere: *.x # However DO track any foo.x files: !foo.x # Ignore a.txt file in root but not subdir/a.txt: /a.txt # Ignore all files in any ack/ directories: ack/ # Match bar* literally: bar\* # Match boo in z dir directly: z/boo # Match hoo in z dir or its subdirs: z/**hoo # Match goo any z dir (the ** shouldn't be necessary, but it is in Git): **/z/goo # Match koo in root dir (Git only): /koo
/even in Windows.
*.cwill match a file ending in
.cin any directory, and a regexp pattern of the form
\.c$will do the same. To root a regexp pattern, start it with
^. To root a glob pattern, start it with
/in Git only! Glob rooting this way does not work in Mercurial!. However, patterns specified in other than
.hgignoreare always rooted.
syntax: regexp FilesOrFoldersWithThis FilesEndingWithThis$ FoldersEndingWithThis/ /MatchFolderWithThisInPath/ path/(?!IgnoreAllAtThePathButFilesEndingWithThis$) ^FileInTheRoot\.txt$ /Temp/ syntax: glob FileOrFolderExactlyLikeThis *FileOrFolderWithThis* *FileOrFolderEndingWithThis FileStartingWithThis* FolderExactlyLikeThis/FileStartingWithThis*
syntax: glob # Previous line required for Mercurial. Comment out it in Git. # ? matches 1 char # * matches 0+ char # ** matches all subdirs # [Aa] matches chars in brackets # [0-9] matches range of chars in brackets # End pattern in / to specify a dir # Negate a pattern by starting it with ! # Escape a char with \ # Patterns in ignore files are not rooted # Temp files: *.orig *.bak *.ignore *~ Thumbs.db [Ll]og/ [Tt]emp/ [Dd]ew/ # Security [Ww]eb.config ftpsync.settings # Root with glob like /inc/3rd in Git. syntx: regex # Regex patterns only available in Mercurial. Comment out it in Git. # Root with regex like ^inc/3rd/ in Mercurial.
To initiate a new repository, use the
init command. Luckily this mostly works the same in Git or Mercurial.
$ git init # If the current dir does not have .git, then it makes .git $ git init x # If the given dir x does not exist, then it makes x and x/.git
$ hg init # If the current dir does not have .hg, then it makes .hg $ hg init x # If the given dir x does not exist, then it makes x and x/.hg
To clone an existing repository, use the
clone command. Luckily this mostly works the same in Git or Mercurial.
$ git clone https://repohost.com/fake/d # If the current dir doesn't have a non-empty d dir, # then it makes a d dir with .git and a working directory of the latest version, # else it uses the empty d dir. $ git clone https://repohost.com/fake/d g # If the current dir doesn't have a non-empty g dir, # then it makes a g dir with .git and a working directory of the latest version, # else it uses the empty g dir.
$ hg clone https://repohost.com/fake/d # If the current dir doesn't have a non-empty d dir, # then it makes a d dir with .hg and a working directory of the latest version, # else it uses the empty d dir. $ hg clone https://repohost.com/fake/d g # If the current dir doesn't have a g dir or an empty g dir, # then it makes a g dir with .hg and a working directory of the latest version, # else it uses the empty g dir. # For hg clone: # --rev %rev% (-r) The remote changeset to pull. Trick: For speed: pull -r 1 # --noupdate (-U) Clone an empty repository (no Working Directory)
git remote add origin https://YourAccount@bitbucket.org/YourAccount/yourrepo.git
For Mercurial: If you are syncing with an external repository (like Bitbucket), then you should have paths set in the
hgrc file in the
.hg directory so that you don't have to specify the destination repo every time. If you cloned your repo, then this is set automatically. Note that if you want multiple remotes, then add another line but note that
default is reserved for when no remote repo is specified in commands like
[paths] default = https://YourAccount@bitbucket.org/YourAccount/yourrepo
If you are on a server with a "live" server that multiple developers have access, then the default should not be user specific, but you can still set a user specific target. EG:
[paths] default = https://bitbucket.org/mygroup/somerepo gh = https://GeorgeHernandez@bitbucket.org/mygroup/somerepo # n = N:\Inetpub\wwwroot\SomeSite # Avoid mapped drives that are remote
You can check the status (untracked, tracked and unmodified, tracked and modified, tracked and staged) of files in the repository by using the
status command. Luckily this mostly works the same in Git or Mercurial, except that Git is more verbose.
Let's assume you just cloned a repository. All files should be tracked and unmodified.
$ git status On branch master Your branch is up-to-date with 'origin/master'. nothing to commit, working directory clean
$ hg status # returns empty
You can check the log of snapshots commited in a repository. Luckily this mostly works the same in Git or Mercurial, except that Git is more verbose and a few syntactical differences.
$ git log # The whole history. Use space to scroll, q to quit. $ git log -n %n% # Show just the last n commits. $ git log %file% # Show the commit history of a file. $ git log > %file% # Output log to a file
$ hg status # The whole history $ hg log -l %n% # Show just the last n commits. $ hg log %file% # Show the commit history of a file.
There a few basic file changes you can do with a repository:
For the "too long did not read" (tldnr) crowd, here is a summary of basic changes:
Git Mercurial Note ------------------------------------------------------------------------- OS Created/Added [os], add, ci (same) OS Modified [os], add, ci [os], ci OS Copied [os], add, ci (same) No meta on copy VCS Copied N/A cp, add, ci Meta on copy OS Removed/Missing [os], rm, ci (same) VCS Removed rm, ci (same) Saves a step OS Moved/Renamed [os], add, rm, ci (same) VCS Moved/Renamed mv, ci (same)
For both Git and Mercurial:
I'll show creating a file in Mercurial. It would be effectively be the same thing in Git but more verbose.
# Let's make a scrap repo: $ hg init scrap # Let's go to that dir: $ cd scrap # Let's create a new file called "c.txt". $ echo c > c.txt # Let's check the status: $ hg status ? c.txt # That shows that the status is untracked. # Let's try to commit it: $ hg commit nothing changed # That shows that the commit failed. You cannot commmit an unstaged file. # Some git commit options: # --all (-a) Stage files that have been modified and deleted, then commit. # --patch (-p) Use patch interface to c hoose which changes to commmit. # Answers include y(yes), n(no), q(quit), ?(print help). # --dry-run Do not perform action, just print output. # Some hg commit options: # --addremove (-A) Mark new/missing files as added/removed before committing # --message %text% (-m) Submit text as commit message. # --verbose (-v) # --close-branch Mark a branch as closed # Let's add/stage it: $ hg add c.txt adding c.txt # hg add adds all untracked files by default, # but git add does not; The equivalent would be "git add .". # --dry-run (-n) Do not perform action, just print output. # Let's check the status: $ hg status A c.txt # That shows that the status is A = Added/Staged. # Let's try to commit it: $ hg commit -m "Created c.txt" c.txt committed changeset %some_identifiers_here% # Let's check the status: $ hg status # No message returned indicating no untracked, modified, or staged files. # Let's check the log: $ hg log 0%some_metadata_here% Created c.txt
Here's where there's a significant difference between Git and Mercurial:
addbefore you can
commit. This gives you a chance to be unstage stuff with
git reset HEAD. Tip:
git add .to add all the unstaged files.
commitimmediately. You can however choose to specifiy which file or files you want to commit.
# Let's reuse the c.txt file from before. # Let's modify the file: $ echo foo >> c.txt # Let's check the status: $ hg status M c.txt # That shows that the status is M = Modified. # Let's undo (In Git you would do git checkout HEAD c.txt): hg revert c.txt # Whew! It's unmodified. # Lets modify the file: $ echo bar >> c.txt # Let's try to commit: $ hg commit -m "Modified c.txt" c.txt committed changeset %some identifiers here% # The commit succeeded! In Mercurila modified files are effectively staged. # In Git you would have to run the add command, then commit. # Let's check the log: $ hg log 1%some_metadata_here% Modified c.txt 0%some_metadata_here% Created c.txt
Here's where there's a significant difference between Git and Mercurial:
# Let's reuse the c.txt file from before. # Let's copy the file: $ hg cp c.txt c2.txt copying c.txt to c2.txt # That shows the copy succeeded # Let's check the status: $ hg status A c2.txt # That shows that a new file has beend A = Added/Staged. # To show the metadata that shows what the new file is copied from: $ hg status -C A c2.txt c.txt # Let's commit: $ hg commit -m "Copied c.txt to c2.txt" # Let's check the log: $ hg log 2%some_metadata_here% Copied c.txt to c2.txt 1%some_metadata_here% Modified c.txt 0%some_metadata_here% Created c.txt
Deleting is roughly the same between Git and Mercurial. You could delete a file outside of your VCS (EG: Delete it with the
rm OS commands), but then the VCS will assume the file is missing. The proper way to delete a file in a VCS is with a
rm command (which also stages it), then commit. This will delete and untrack the file.
I'll show creating a file in Mercurial. It would be effectively be the same thing in Git but more verbose.
# Let's reuse the c.txt file from before. # Let's delete the bad way with the OS: $ del c.txt # Let's check the status: $ hg status ! c.txt # That shows that Mercurial thinks the file is missing, possibly accidentally. # If it wasn't an accident then you'd have to run hg rm. # Let's get it back (In Git you would do git checkout HEAD c.txt): $ hg revert c.txt reverting c.txt # Whew! It's back. # Let's delete it with Mercurial: $ hg rm c.txt removing c.txt # Let's check the status: $ hg status R c.txt # That shows that c.txt was staged and marked as R = Removed # You could undo this delete with hg revert c.txt (or git reset HEAD c.txt) # but we won't # Let's commit: $ hg ci c.txt -m "Removed c.txt" committed changeset %some_identifiers_here% # Let's check the status: $ hg status # No message returned indicating no untracked, modified, or staged files. # Let's check the log: $ hg log 3%some meta data here% Removed c.txt 2%some meta data here% Copied c.txt to c2.txt 1%some meta data here% Modified c.txt 0%some meta data here% Created c.txt # If there's a file that you've added to .gitignore, and you don't want it # indexed anymore, but you still want it in your working directory: $ hg --cached rm %file%
Renaming/Moving a file is roughly the same between Git and Mercurial. You could rename or move a file outside of your VCS (EG: Rename/move it with an OS command like
move in DOS or
mv in Linux), but then the VCS will assume the file is missing, plus you would have to add/stage the renamed file, then commit. A more convenient way to rename/move a file in a VCS is with the
mv command (FYI: Mercurial uses mv as an alias for its rename command).
# Let's reuse the c2.txt file from before. # Let's rename it with the OS first: $ mv c2.txt d.txt # Let's check the status: ! c2.txt ? d.txt # That shows that c2.txt is ! = Missing, and d.txt is ? = Untracked # To complete in the VCS you have to add, remove, commit: $ hg add d.txt adding d.txt $ hg rm c2.txt removing c.txt # Let's check the status: R c2.txt A d.txt $ hg ci -m "Renamed c2.txt to d.txt" # Now let's rename with the VCS (using fewer steps): $ hg mv d.txt e.txt moving d.txt to e.txt # In Mercurial the command is rename with aliases of move and mv # Let's check the status: A e.txt R d.txt # That shows that there has been an A = Add/Staged and a R = Renamed/Staged # Now let's commit: $ hg commit -m "Renamed d.txt to e.txt" e.txt committed changeset %some_identifiers_here% # Let's check the log: $ hg log 5%some_metadata_here% Renamed d.txt to e.txt 4%some_metadata_here% Renamed c2.txt to d.txt 3%some_metadata_here% Removed c.txt 2%some_metadata_here% Copied c.txt to c2.txt 1%some_metadata_here% Modified c.txt 0%some_metadata_here% Created c.txt
One of the key things you can do with VCS is undo. You may have noticed, but I snuck in a bit of undoing in the previous section. However with VCS undoing can be more powerful than simply pressing Ctrl+Z. With VCS you can undo to previous versions.
The most common undos are purely local (via the OS or app) and have nothing to do with RCS as long as you haven't staged or committed it.
The second most common undos are after you have staged (
mv) but not committed. For Git it is important to note that
reset OBLITERATES and is meant for local use (like getting rid of a set of commits in order to replace with 1 clean commit).
# To unstage a particular file: $ git reset a.txt # To unstage and undo in the working directory for a particular file: $ git checkout HEAD a.txt # To unstage for all files: $ git reset # To unstage and undo in the working directory for all files: $ git reset --hard
# To unstage and undo in the working directory for particular file: $ hg revert a.txt # --all (-a) Revert all changes # --rev %rev% (-r) Revert to a given revision # --no-backup (-C) Do not save back up copies of files # --dry-run (-n) Do not perform actions, just show output # To unstage and undo in the working directory for all files, use -a or --all: $ hg revert --all # For no back up copies (*.orig) use -C or --no-backup.
The third level of undos involve switching to another version. A version can be a commit, a tag, a branch.
# GIT: # To change a working directory for a file to a particular version: $ git checkout %version% %file% # To change a working directory for all files to a version # just for viewing and playing (as a 'detached HEAD'): $ git checkout %version% # If you want to save any changes, you will have to use something like: $ git branch %branch% %version% # otherwise return to where you were with: $ git checkout %previous_branch% # To change a working directory for all files to a version: $ git revert %version% # A typical use case is to skip over a bad version. # EG: v1, v2, v3, v4, v3; assuming v4 was bad. # To change the current branch tip to a version, # reset the stage to version, but leave the working directory as is # and OBLITERATE (!!!) all versions after the specified version: $ git reset %version% # A typical use case is to get rid of minor commits. # EG: [v1, v1.1, v1.2, v1.3] becomes [v1, v2]. # To change the current branch tip to a version, # reset the stage and working directory to a version, # and OBLITERATE (!!!) all versions after the specified version: $ git reset --hard %version%
# MERCURIAL: # To change a working directory for a file to a version, use -r or --rev: $ hg revert -r %version% %file% # To change a working directory for all files to a version, # but keep staged files: $ hg update %version% # To change a working directory for all files to a version, # but discard staged files, use -C or --clean: $ hg update --clean %version%
Here are other commands related to undoing:
# If your just did a commit but want to edit your commit message or # stage some more files to the commit, then run: $ git add one_more_file $ git commit --amend # Use clean o delete untracked files. # -n to dry run. -d delete untracked directories. -xX for ignore options. git clean -f git rebase hg backout
Since you will make many commits as you develop, now and then you will want to tag specific commits with a tag.
The basics things you do with tags are:
# List tags: $ git tag # List tags by a pattern with --list %pattern% (-l): $ git tag --list 'v1.3*' # The info is stored in: .git/refs/tags/ # Create a Lightweight tag: $ git tag v1.2.33 # A tag cannot contain: spaces ~ ^ : ? * [ ] # Create a Lightweight tag for a different commit: $ git tag v.1.0.1 8dbce02 # Create an Annotated tag with --annotate (-a): $ git tag -a v1.3 -m 'version 1.3' # Usually stored in .git/refs/tags/ # You can GPG sign and verify Annotated tags but I won't show that here. # Show the extra metadata behind an annotated tag: $ git show v1.4 # If you haven't pushed your tag, you can rename the tag with --force (-f): $ git tag -f v1.4 $ Delete a local tag with --delete (-d): $ git tag -d v1.2 $ Delete a remote tag like this: $ git push origin :refs/tags/v1.2 # Push a tag: $ git push origin v1.5 # Push all your tags: $ git push origin --tags
# List tags: $ hg tags # At the very least the auto generated tip tag is shown. # Create a tag: $ hg tag v1 # Create a local tag with --local (-l): $ hg tag -l v1.2 # Stored in .hg/localtags # Create a tag for a different commit with --rev REV (-r): $ hg tag -r 78 v1.1 # Apply a tag already used --force (-f): $ hg tag -r 79 -f v1.1 # Can also force a tag if you're not at a head # Remove a tag with --remove: $ hg tag --remove v1.2 # Mercurial tags are stored in .hgtags and .hg/localtags. # Those 2 files create 2 namespaces. Watch for name collisions.
Here are application versions in revision control lingo:
A version can be a commit, a tag, a branch. A version is ultimately identified by a SHA-1 hash in Git or Mercurial. The ID is stored as a 160 b (20 B) value. For convenience a version is accessible in various ways:
If you have a series of revisions, where each is the basis for the one after it (parent-child), then you have a line of change (or line of development, series of commits, trunk). The last revision (the one with no children) is the head (or leaf) revision. EG:
r1 -- r2 -- r3 -- r4 -- r5 (head)
If you have 2 lines of development with the same parent, then you have a branching (or forking), where each line may also be called a branch (or fork, trunk, head, bookmark). Usually you have a main branch (Git:
default) with periodic branches that either merge with the main, die off, or persist. Usually the latest head is the tip of the repository. EG:
r1 -- r2 -- r3 -- r5 -- r7 (trunk tip) \ -- r4 -- r6 (branch)
git checkout myBranch foo/bar.txt arr/ggh.txt), the commit those files.
The main things you do with branches/boomarks are:
######### View/List branches: # List local branches in the repository: $ git branch * master # * Indicates current branch # The local info is stored in: .git/refs/heads/ # The remote info is stored in: .git/refs/remotes/REMOTE/ ######### Make a branch: # Make a new branch: $ git branch %branch% %revision% # Making a branch does not switch to it, or open it. # Rename current branch with --move (-m): # git branch -m %branch% # Git allows literal / in branch names but don't do it! ######### Switch to a branch: # Start or continue the line of development at branch: $ git checkout %branch% # Switching to a branch updates the Working Directory to that branch # Make a branch and checkout in 1 step: $ git checkout -b %branch% %revision% # Revision can be things like a tag, another branch, a remote branch. # Generally speaking you want to checkout the head of a branch. # If you instead checkout a commit that is not a head, # then you have a detached head for viewing purposes only. ######### Merge a branch: # If you want to merge a branch (insead of keeping or deleting it): # 1st switch the working directory to the target branch (usu. master): $ git checkout master # Then merge a branch into the current branch: $ git merge %branch% # To add a message with the merge: $ git merge -m "My message" %branch% # If the merge is complex, then you may have to manually merge. # You will want to set your merge tool. # Choices include: opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff # diffuse diffmerge ecmerge p4merge araxis bc3 codecompare vimdiff emerge $ git mergetool --tool=kdiff3 ######### Delete a branch: # Delete a branch safely (will not do if unmerged changes) with --delete: $ git branch -d %branch% # Delete a branch (even if it has unmerged changes): $ git branch -D %branch%
Mercurial offers several wasy to branch (see "A Guide to Branching in Mercurial" [http://stevelosh.com/blog/2009/08/a-guide-to-branching-in-mercurial/]): Clones (also available to Gi), Bookmarks (like a Git branch), Named Branches, and Anonymous Branching. I will only cover branching via branches and via bookmarks here.
A Mercurial bookmark is as ephemeral as a Git branch. A Mercurial branch is a more permanent and global sort of creature. Both are pointers to the head of a line of development.
# For later use, check how many heads your repository has: $ hg heads # List bookmarks in the repository: $ hg bookmark # bookmarks/bookmark are the same command while branches/branch are 2 commands. # Make a new bookmark: $ hg bookmark %bookmark% # --rev %revision% (-r) To make for a given revision # --inactive (-i) To mark a bookmark as inactive # If you check heads now, you should see no difference $ hg heads # Rename a given bookmark to a new name with --rename bookmark (-m): $ hg bookmark -m bookmark %newname% # Start or continue the line of development at bookmark: $ hg update bookmark # Remember that hg update has aliases of up, checkout, co # --clean (-C) Discard uncommitted changes (no backup) # --check (-c) Update across branches if no uncommitted changes # If you make commits in the bookmark, then you should see a change in heads: $ hg heads # If you want to merge a bookmark (insead of keeping or deleting it): # 1st switch the working directory to the target branch (usu. default): $ hg update default # Then merge a bookmark into the current branch: $ hg merge %bookmark% # --preview (-P) Review revisions to merge (no merge is done) # If the merge is complex, then you may have to manually merge. # If the merge hiccups, then try: $ hg resolve %file% # If no file specified, then this command marks all files as resolved. # --all (-a) Select all unresolved files # --list (-l) List state of files needing merge # --mark (-m) Mark files as resolved # --unmark (-u) Mark files as unresolved # --tool (-t) Specify merge tool # Delete a bookmark with --delete (-d): $ hg bookmark -d %bookmark% ######### Mercurial branches are more permanent than bookmarks # To list branches in the repository: $ hg branches # --active (-a) show only branches with unmerged heads # --closed (-c) show normal and closed branches # See your current branch: $ hg branch # Make a new branch: $ hg branch %branch%
For more on bookmarks: http://mercurial.selenic.com/wiki/Bookmarks.
For more on pruning named branches: http://mercurial.selenic.com/wiki/PruningDeadBranches.
While it is possible to work with just local repositories, most people work with remote repositories. The tasks for working with remote repositories are similar between Git and Mercurial, but please note that 2 of the commmands
pull are flip-flopped between Git and Mercurial!
######### Manage remotes: # List remote connections: $ git remote origin # --verbose (-v) For more verbose and show the remote URL: origin https://YourRepoHost.com/foo/project.gt # The default remote is usally called origin. # Add a new remote connection: $ git remote add %remote% %URL% # In Git, the default remote is often literally called "origin" # Rename a remote connection to remote2: $git remote rename %remote% %remote2% # Remove a remote connection: $ git remote rm %remote% # The info is stored in: .git/refs/remotes/ ######### Get remote commits: # Get all commits from remote in all branches: $ git fetch %remote% # Get all commits from remote in branch: $ git fetch remote %branch% # To view remote-tracking branches use --remote (-r): $ git branch -r origin/HisBranch origin/HEAD -> origin/master origin/master # Note that the last 2 are equivalent # To view remote-tracking and local branches use --all (-a) $ git branch -a * MyBranch master remotes/origin/HisBranch remotes/origin/HEAD -> origin/master remotes/origin/master # Getting remote commits does not switch to it, or open it. # If you want to work with a remote branch that you don't have a local of, # then you will have to create it locally 1st as a tracking branch: $ git checkout -b %remote%/%branch% # or same thing: $ git checkout --track %remote%/%branch% # If the remote branch was origin/foo, # then a local branch is created called origin/foo # when you probably want foo # To give your tracking branch a different name: $ git checkout -b MineNow orgin/branch% ######### Merge remote commits: # Make sure you are in the branch you want to merge into: $ git checkout %branch% # See the differences between local head and remote head: $ git log %head%..%remote%/%head% # Frequently the heads are "master" or the same branch. $ git log master..origin/master # Note that "git log" defaults to "git log master" # Then merge the remote head into your local branch: $ git merge %remote%/%branch% ######## Get and Merge remote commits: # For convenience you can get and merge in 1 step: $ git pull %remote% %branch% ######## Push commits to remote: # Push to a local branch to a remote branch: $ git push %remote% %branch% # Most commonly: # git push origin master # Push a local branch to a remote branch as a different name: $ git push %remote% %branch%:%branch2% # EG: $ git push origin myBranch:differentName # Push to all branches to remote: $ git push %remote% --all # --tags Push your local tags too # Once you've merged a branch, you already know how to delete the local: $ git branch -d %branch% # but you should also delete the local remote-tracking branch: $ git branch --delete --remotes %remote%/%branch% # same as this: $ git branch -d -r %remote%/%branch% # To delete all local remote-tracking branches with --prune: $ git fetch %remote% -p # and you should also delete the remote branch with this syntax: $ git push %remote% :%branch% # or this syntax as of Git v1.7.0: $ git push %remote% --delete %branch% # These commands should also delete the local remote-tracking branch.
######### Manage remotes: # List remote connections: $ hg paths # Info on remotes is stored in each repo's .hg/hgrc file: [paths] default = https://YourAccount@bitbucket.org/YourAccount/yourrepo MyRemote = https://YourAccount@bitbucket.org/YourAccount/myremote # If you cloned your repo, then default set automatically. # "default" allows commands like pull, fetch, and push to be used without # specifying a remote. ######### Get remote commits: # Get all commits from remote in all branches: $ hg pull %remote% # --update (-u) To pull and update the working directory # --bookmark %book% (-B) To pull bookmark book # --branch %branch% (-b) To pull branch branch # --force (f) To pull even if remote is unrelated ######### Merge remote commits: # 1st switch the working directory to the target branch (default in the EG): $ hg update default # --clean (-C) Discard uncommitted changes (no backup) # --check (-c) Update across branches if no uncommitted changes # Remember that hg update has aliases of up, checkout, co # Then merge a bookmark or branch into your branch: $ hg merge %bookmark_or_branch% ######## Get and Merge remote commits: # For convenience you can get and merge in 1 step: $ hg fetch %remote% # This command must be enabled in the mercurial.ini as an extension # Like running hg pull, hg update remote_head, hg merge local_head, hg ci # Like running hg pull -u ######## Push commits to remote: # Push to remote a branch (usu master): $ git push %remote% %branch% # --bookmark %book% (-B) To push bookmark book # --branch %branch% (-b) To push branch branch # --force (f) To push even if remote is unrelated # --new-branch Allow pushing a new branch
RCS workflow refers to the different ways to use RCS. It is a matter of personal or company preference.
One of the best discussions of RCS workflow is by Atlassian: Comparing Workflows [https://www.atlassian.com/git/tutorials/comparing-workflows/]. The discuss 4 workflows:
My personal workflow is roughly based on Gitflow.
devbranch with a remote of
git co dev
git co -b feature dev
git branch -a
git co branch
git pull remote branch
git push -u origin feature. The 1st time.
git push origin feature. After the 1st time.
dev. Keep in sync with changes by others that might affect your code.
git co feature
git push origin feature
git co dev
git pull origin dev. Fetch and merge in 1 step. Should almost always be a fast forward (ff).
devin separate directories, and do an exploratory read-only kdiff.)
git co feature
git merge --no-ff dev. Forces a commit, even if the merge was ff.
git mergetool --tool=kdiff3
orign/featureas a fall back option.
git push origin feature
feature(see above), then merge
git co dev
git merge --no-ff feature. Forces a commit, even if the merge was ff.
git tag v1.2.3
git push origin dev --tags. Confirm merge and tags at
git branch -d feature. Delete local
git push origin --delete feature. Delete
If using Git from a Windows CLI: Use double quotes (") instead of single quotes (') or parameters with spaces in them, and you quote the parameters ending with the circumflex accent (^) if they are last on the line, as it is a continuation symbol in Windows.
If you have a hiccup in your refs, you may need to use
git update-ref. EG: If a remote branch that has been deleted still shows up when you run
git branch -a, then you may have to run something like
git update-ref -d refs/remotes/REMOTE/BADBRANCH.
Some Mercurial commands of interest.
$ hg diff $ hg export $ hg forget $ hg graft $ hg identify # Aka: id $ hg icoming %source% # Aka: in # Show new changesets in source if pull actually run # -B --bookmarks # comapre bookmarks between local and remote # -b --branch %branch% # A branch to pull # -G --graph # show log as a directed acyclic graph (DAG) # in:pull :: diff:ci $ hg import # Aka: patch $ hg log # Aka: history # See revision history of repository or file. # Shows for each changeset: # %revisionNumber%[%tag%]:%changeSetId%, non-trivial parents, date, user, summary # EG: # 1[tip]:82e55d328c8c 2005-08-26 01:21 -0700 John Doe <firstname.lastname@example.org> # Modified foo.txt $ hg log -d -2 # See log within 2 days of today $ hg log -l 5 # See last 5 items # -b --branch %branch% # show for current or named branch # -G --graph # show log as a directed acyclic graph (DAG) $ hg outgoing [%source%] # Aka: out # Show new changesets in source if pull actually run # -B --bookmarks # comapre bookmarks between local and remote # -b --branch %branch% # A branch to pull # -G --graph # show log as a directed acyclic graph (DAG) $ hg parents [-r %rev%] [%file%] # Aka: par # Show the parents of the working directory or revision $ hg rollback # Rollback the last transaction (dangerous) # Don't do this unless you know what you're doing. $ hg serve # Start a webserver to share the repository # Common options: # -p --port %port% # port to listen on (default: 8000) $ hg summary # Aka: sum # Summaryize the state of the working directory # Info on parents, branch, commit status, available updates $ hg tip # Show the latest changeset
Links that lead to off-site pages about revision control.
Page Modified: (Hand noted: ) (Auto noted: )