diff --git a/autoload/magit/sign.vim b/autoload/magit/sign.vim
new file mode 100644
index 0000000000000000000000000000000000000000..abf488dd65dab1ed70b9f109aa1ebec33f2f7656
--- /dev/null
+++ b/autoload/magit/sign.vim
@@ -0,0 +1,88 @@
+" Got lot of stuf from vim-gitgutter
+" https://github.com/airblade/vim-gitgutter
+
+" Vim doesn't namespace sign ids so every plugin shares the same
+" namespace.  Sign ids are simply integers so to avoid clashes with other
+" signs we guess at a clear run.
+"
+" Note also we currently never reset s:next_sign_id.
+let s:first_sign_id = 42000
+let s:next_sign_id  = s:first_sign_id
+let s:dummy_sign_id = s:first_sign_id - 1
+" Remove-all-signs optimisation requires Vim 7.3.596+.
+let s:supports_star = v:version > 703 || (v:version == 703 && has("patch596"))
+
+let s:bufnr = bufnr(g:magit_buffer_name)
+
+" magit#sign#remove_signs: unplace a list of signs
+" param[in] sign_ids: list of signs ids
+function! magit#sign#remove_signs(sign_ids)
+    let bufnr = magit#utils#bufnr()
+    for id in a:sign_ids
+        execute "sign unplace" id
+    endfor
+endfunction
+
+" s:get_next_sign_id: helper function to increment sign ids
+function! s:get_next_sign_id()
+	let next_id = s:next_sign_id
+	let s:next_sign_id += 1
+	return next_id
+endfunction
+
+" magit#sign#find_signs: this function returns signs matching a pattern in a
+" range of lines
+" param[in] pattern: regex pattern to match
+" param[in] startline,endline: range of lines
+" FIXME: find since which version "sign place" is sorted
+function! magit#sign#find_signs(pattern, startline, endline)
+	let bufnr = magit#utils#bufnr()
+	" <line_number (string)>: {'id': <id (number)>, 'name': <name (string)>}
+	let found_signs = {}
+
+	redir => signs
+	silent execute "sign place buffer=" . bufnr
+	redir END
+
+	for sign_line in filter(split(signs, '\n'), 'v:val =~# "="')
+		" Typical sign line:  line=88 id=1234 name=GitGutterLineAdded
+		" We assume splitting is faster than a regexp.
+		let components  = split(sign_line)
+		let name        = split(components[2], '=')[1]
+		let line_number = str2nr(split(components[0], '=')[1])
+		if ( name =~# a:pattern &&
+			\ line_number >= a:startline &&
+			\ line_number <= a:endline )
+			let id = str2nr(split(components[1], '=')[1])
+			let found_signs[line_number] = {'id': id, 'name': name}
+		endif
+	endfor
+	return found_signs
+endfunction
+
+" s:magit_mark_sign: string of the sign for lines to be staged
+let s:magit_mark_sign='MagitMark'
+
+" magit#sign#init: initializer function for signs
+function! magit#sign#init()
+	execute "sign define " . s:magit_mark_sign . " text=S> linehl=Visual"
+endfunction
+
+" magit#sign#toggle_signs: toggle marks for range of lines
+" marked lines are unmarked, non marked are marked
+" param[in] startline,endline: range of lines
+function! magit#sign#toggle_signs(startline, endline)
+	let bufnr = magit#utils#bufnr()
+	let current_signs = magit#sign#find_signs(s:magit_mark_sign, a:startline, a:endline)
+	let line = a:startline
+	while ( line <= a:endline )
+		if ( has_key(current_signs, line) == 0 )
+			execute ":sign place " . <SID>get_next_sign_id() .
+				\ " line=" . line . " name=" . s:magit_mark_sign .
+				\ " buffer=" . bufnr
+		else
+			execute ":sign unplace " . current_signs[line].id
+		endif
+		let line += 1
+	endwhile
+endfunction
diff --git a/autoload/magit/utils.vim b/autoload/magit/utils.vim
index 670294453680223135cafbea361f47d45ae3323a..6b8ca64f6d186c6fbb44c9096b74cb063f0349da 100644
--- a/autoload/magit/utils.vim
+++ b/autoload/magit/utils.vim
@@ -162,3 +162,16 @@ function! magit#utils#append_file(file, lines)
 	call writefile(fcontents+a:lines, a:file, 'b')
 endfunction
 
+" s:bufnr: local variable to store current magit buffer id
+let s:bufnr = 0
+" magit#utils#setbufnr: function to set current magit buffer id
+" param[in] bufnr: current magit buffer id
+function! magit#utils#setbufnr(bufnr)
+	let s:bufnr = a:bufnr
+endfunction
+
+" magit#utils#bufnr: function to get current magit buffer id
+" return: current magit buffer id
+function! magit#utils#bufnr()
+	return s:bufnr
+endfunction
diff --git a/plugin/magit.vim b/plugin/magit.vim
index 78d99baf85adf0ff5b8b7c3b8630819b2ea73c6d..99068abc03d0234007291c449eafbaba4942faae 100644
--- a/plugin/magit.vim
+++ b/plugin/magit.vim
@@ -41,6 +41,7 @@ call s:set('g:magit_show_magit_mapping',        '<leader>M' )
 call s:set('g:magit_stage_file_mapping',        'F' )
 call s:set('g:magit_stage_hunk_mapping',        'S' )
 call s:set('g:magit_stage_line_mapping',        'L' )
+call s:set('g:magit_mark_line_mapping',         'M' )
 call s:set('g:magit_discard_hunk_mapping',      'DDD' )
 call s:set('g:magit_commit_mapping_command',    'w<cr>' )
 call s:set('g:magit_commit_mapping',            'CC' )
@@ -456,6 +457,19 @@ function! s:mg_create_diff_from_select(start_select_line, end_select_line)
 	return selection
 endfunction
 
+" s:mg_mark_lines_in_hunk: this function toggle marks for selected lines in a
+" hunk.
+" if a hunk contains marked lines, only these lines will be (un)staged on next
+" (un)stage command
+" param[in] start_select_line,end_select_line: limits of the selection
+function! s:mg_mark_lines_in_hunk(start_select_line, end_select_line)
+	let [starthunk,endhunk] = <SID>mg_select_hunk_block()
+	if ( a:start_select_line < starthunk || a:end_select_line > endhunk )
+		throw 'out of hunk selection'
+	endif
+	return magit#sign#toggle_signs(a:start_select_line, a:end_select_line)
+endfunction
+
 " s:mg_get_section: helper function to get the current section, according to
 " cursor position
 " return: section id, empty string if no section found
@@ -476,6 +490,13 @@ function! s:mg_get_filename()
 	return substitute(getline(search(g:magit_file_re, "cbnW")), g:magit_file_re, '\2', '')
 endfunction
 
+" s:mg_get_hunkheader: helper function to get the current hunk header,
+" according to cursor position
+" return: hunk header
+function! s:mg_get_hunkheader()
+	return getline(search(g:magit_hunk_re, "cbnW"))
+endfunction
+
 " }}}
 
 " {{{ User functions and commands
@@ -592,6 +613,9 @@ function! magit#show_magit(display)
 	silent! execute "bdelete " . g:magit_buffer_name
 	execute "file " . g:magit_buffer_name
 
+	call magit#utils#setbufnr(bufnr(g:magit_buffer_name))
+	call magit#sign#init()
+
 	execute "nnoremap <buffer> <silent> " . g:magit_stage_file_mapping .   " :call magit#stage_file()<cr>"
 	execute "nnoremap <buffer> <silent> " . g:magit_stage_hunk_mapping .   " :call magit#stage_hunk(0)<cr>"
 	execute "nnoremap <buffer> <silent> " . g:magit_discard_hunk_mapping . " :call magit#stage_hunk(1)<cr>"
@@ -607,6 +631,9 @@ function! magit#show_magit(display)
 	execute "nnoremap <buffer> <silent> " . g:magit_stage_line_mapping .   " :call magit#stage_vselect()<cr>"
 	execute "xnoremap <buffer> <silent> " . g:magit_stage_hunk_mapping .   " :call magit#stage_vselect()<cr>"
 	
+	execute "nnoremap <buffer> <silent> " . g:magit_mark_line_mapping .    " :call magit#mark_vselect()<cr>"
+	execute "xnoremap <buffer> <silent> " . g:magit_mark_line_mapping .    " :call magit#mark_vselect()<cr>"
+	
 	for mapping in g:magit_folding_toggle_mapping
 		" trick to pass '<cr>' in a mapping command without being interpreted
 		let func_arg = ( mapping ==? "<cr>" ) ? '+' : mapping
@@ -739,6 +766,12 @@ function! magit#stage_vselect() range
 	return magit#stage_block(selection, 0)
 endfunction
 
+" magit#mark_vselect: wrapper function to mark selected lines (see
+" mg_mark_lines_in_hunk)
+function! magit#mark_vselect() range
+	return <SID>mg_mark_lines_in_hunk(a:firstline, a:lastline)
+endfunction
+
 " magit#ignore_file: this function add the file under cursor to .gitignore
 " FIXME: git diff adds some strange characters to end of line
 function! magit#ignore_file() abort